aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--application/Utils.php2
-rw-r--r--application/container/ContainerBuilder.php81
-rw-r--r--application/container/ShaarliContainer.php30
-rw-r--r--application/front/ShaarliMiddleware.php57
-rw-r--r--application/front/controllers/LoginController.php48
-rw-r--r--application/front/controllers/ShaarliController.php69
-rw-r--r--application/front/exceptions/LoginBannedException.php15
-rw-r--r--application/front/exceptions/ShaarliException.php23
-rw-r--r--application/render/PageBuilder.php17
-rw-r--r--application/security/SessionManager.php6
-rw-r--r--assets/default/scss/shaarli.scss13
-rw-r--r--composer.json4
-rw-r--r--doc/md/Translations.md32
-rw-r--r--inc/languages/ja/LC_MESSAGES/shaarli.po1293
-rw-r--r--index.php101
-rw-r--r--tests/UtilsTest.php4
-rw-r--r--tests/container/ContainerBuilderTest.php49
-rw-r--r--tests/front/ShaarliMiddlewareTest.php70
-rw-r--r--tests/front/controller/LoginControllerTest.php178
-rw-r--r--tests/front/controller/ShaarliControllerTest.php116
-rw-r--r--tpl/default/404.html2
-rw-r--r--tpl/default/error.html22
-rw-r--r--tpl/default/loginform.html60
-rw-r--r--tpl/default/page.header.html4
-rw-r--r--tpl/default/tag.cloud.html1
-rw-r--r--tpl/default/thumbnails.html2
-rw-r--r--tpl/vintage/error.html25
-rw-r--r--tpl/vintage/loginform.html44
-rw-r--r--tpl/vintage/page.header.html2
-rw-r--r--tpl/vintage/thumbnails.html2
31 files changed, 2235 insertions, 141 deletions
diff --git a/Makefile b/Makefile
index 917fab7d..b52ba22f 100644
--- a/Makefile
+++ b/Makefile
@@ -123,7 +123,8 @@ release_tar: composer_dependencies htmldoc translate build_frontend
123### generate a release zip and include 3rd-party dependencies and translations 123### generate a release zip and include 3rd-party dependencies and translations
124release_zip: composer_dependencies htmldoc translate build_frontend 124release_zip: composer_dependencies htmldoc translate build_frontend
125 git archive --prefix=$(ARCHIVE_PREFIX) -o $(ARCHIVE_VERSION).zip -9 HEAD 125 git archive --prefix=$(ARCHIVE_PREFIX) -o $(ARCHIVE_VERSION).zip -9 HEAD
126 mkdir -p $(ARCHIVE_PREFIX)/{doc,vendor} 126 mkdir -p $(ARCHIVE_PREFIX)/doc
127 mkdir -p $(ARCHIVE_PREFIX)/vendor
127 rsync -a doc/html/ $(ARCHIVE_PREFIX)doc/html/ 128 rsync -a doc/html/ $(ARCHIVE_PREFIX)doc/html/
128 zip -r $(ARCHIVE_VERSION).zip $(ARCHIVE_PREFIX)doc/ 129 zip -r $(ARCHIVE_VERSION).zip $(ARCHIVE_PREFIX)doc/
129 rsync -a vendor/ $(ARCHIVE_PREFIX)vendor/ 130 rsync -a vendor/ $(ARCHIVE_PREFIX)vendor/
@@ -155,6 +156,7 @@ phpdoc: clean
155htmldoc: 156htmldoc:
156 python3 -m venv venv/ 157 python3 -m venv venv/
157 bash -c 'source venv/bin/activate; \ 158 bash -c 'source venv/bin/activate; \
159 pip install wheel; \
158 pip install mkdocs; \ 160 pip install mkdocs; \
159 mkdocs build --clean' 161 mkdocs build --clean'
160 find doc/html/ -type f -exec chmod a-x '{}' \; 162 find doc/html/ -type f -exec chmod a-x '{}' \;
diff --git a/application/Utils.php b/application/Utils.php
index 56f5b9a2..4b7fc546 100644
--- a/application/Utils.php
+++ b/application/Utils.php
@@ -159,7 +159,7 @@ function checkDateFormat($format, $string)
159 */ 159 */
160function generateLocation($referer, $host, $loopTerms = array()) 160function generateLocation($referer, $host, $loopTerms = array())
161{ 161{
162 $finalReferer = '?'; 162 $finalReferer = './?';
163 163
164 // No referer if it contains any value in $loopCriteria. 164 // No referer if it contains any value in $loopCriteria.
165 foreach (array_filter($loopTerms) as $value) { 165 foreach (array_filter($loopTerms) as $value) {
diff --git a/application/container/ContainerBuilder.php b/application/container/ContainerBuilder.php
new file mode 100644
index 00000000..e2c78ccc
--- /dev/null
+++ b/application/container/ContainerBuilder.php
@@ -0,0 +1,81 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Container;
6
7use Shaarli\Bookmark\BookmarkFileService;
8use Shaarli\Bookmark\BookmarkServiceInterface;
9use Shaarli\Config\ConfigManager;
10use Shaarli\History;
11use Shaarli\Plugin\PluginManager;
12use Shaarli\Render\PageBuilder;
13use Shaarli\Security\LoginManager;
14use Shaarli\Security\SessionManager;
15
16/**
17 * Class ContainerBuilder
18 *
19 * Helper used to build a Slim container instance with Shaarli's object dependencies.
20 * Note that most injected objects MUST be added as closures, to let the container instantiate
21 * only the objects it requires during the execution.
22 *
23 * @package Container
24 */
25class ContainerBuilder
26{
27 /** @var ConfigManager */
28 protected $conf;
29
30 /** @var SessionManager */
31 protected $session;
32
33 /** @var LoginManager */
34 protected $login;
35
36 public function __construct(ConfigManager $conf, SessionManager $session, LoginManager $login)
37 {
38 $this->conf = $conf;
39 $this->session = $session;
40 $this->login = $login;
41 }
42
43 public function build(): ShaarliContainer
44 {
45 $container = new ShaarliContainer();
46 $container['conf'] = $this->conf;
47 $container['sessionManager'] = $this->session;
48 $container['loginManager'] = $this->login;
49 $container['plugins'] = function (ShaarliContainer $container): PluginManager {
50 return new PluginManager($container->conf);
51 };
52
53 $container['history'] = function (ShaarliContainer $container): History {
54 return new History($container->conf->get('resource.history'));
55 };
56
57 $container['bookmarkService'] = function (ShaarliContainer $container): BookmarkServiceInterface {
58 return new BookmarkFileService(
59 $container->conf,
60 $container->history,
61 $container->loginManager->isLoggedIn()
62 );
63 };
64
65 $container['pageBuilder'] = function (ShaarliContainer $container): PageBuilder {
66 return new PageBuilder(
67 $container->conf,
68 $container->sessionManager->getSession(),
69 $container->bookmarkService,
70 $container->sessionManager->generateToken(),
71 $container->loginManager->isLoggedIn()
72 );
73 };
74
75 $container['pluginManager'] = function (ShaarliContainer $container): PluginManager {
76 return new PluginManager($container->conf);
77 };
78
79 return $container;
80 }
81}
diff --git a/application/container/ShaarliContainer.php b/application/container/ShaarliContainer.php
new file mode 100644
index 00000000..3fa9116e
--- /dev/null
+++ b/application/container/ShaarliContainer.php
@@ -0,0 +1,30 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Container;
6
7use Shaarli\Bookmark\BookmarkServiceInterface;
8use Shaarli\Config\ConfigManager;
9use Shaarli\History;
10use Shaarli\Plugin\PluginManager;
11use Shaarli\Render\PageBuilder;
12use Shaarli\Security\LoginManager;
13use Shaarli\Security\SessionManager;
14use Slim\Container;
15
16/**
17 * Extension of Slim container to document the injected objects.
18 *
19 * @property ConfigManager $conf
20 * @property SessionManager $sessionManager
21 * @property LoginManager $loginManager
22 * @property History $history
23 * @property BookmarkServiceInterface $bookmarkService
24 * @property PageBuilder $pageBuilder
25 * @property PluginManager $pluginManager
26 */
27class ShaarliContainer extends Container
28{
29
30}
diff --git a/application/front/ShaarliMiddleware.php b/application/front/ShaarliMiddleware.php
new file mode 100644
index 00000000..fa6c6467
--- /dev/null
+++ b/application/front/ShaarliMiddleware.php
@@ -0,0 +1,57 @@
1<?php
2
3namespace Shaarli\Front;
4
5use Shaarli\Container\ShaarliContainer;
6use Shaarli\Front\Exception\ShaarliException;
7use Slim\Http\Request;
8use Slim\Http\Response;
9
10/**
11 * Class ShaarliMiddleware
12 *
13 * This will be called before accessing any Shaarli controller.
14 */
15class ShaarliMiddleware
16{
17 /** @var ShaarliContainer contains all Shaarli DI */
18 protected $container;
19
20 public function __construct(ShaarliContainer $container)
21 {
22 $this->container = $container;
23 }
24
25 /**
26 * Middleware execution:
27 * - execute the controller
28 * - return the response
29 *
30 * In case of error, the error template will be displayed with the exception message.
31 *
32 * @param Request $request Slim request
33 * @param Response $response Slim response
34 * @param callable $next Next action
35 *
36 * @return Response response.
37 */
38 public function __invoke(Request $request, Response $response, callable $next)
39 {
40 try {
41 $response = $next($request, $response);
42 } catch (ShaarliException $e) {
43 $this->container->pageBuilder->assign('message', $e->getMessage());
44 if ($this->container->conf->get('dev.debug', false)) {
45 $this->container->pageBuilder->assign(
46 'stacktrace',
47 nl2br(get_class($this) .': '. $e->getTraceAsString())
48 );
49 }
50
51 $response = $response->withStatus($e->getCode());
52 $response = $response->write($this->container->pageBuilder->render('error'));
53 }
54
55 return $response;
56 }
57}
diff --git a/application/front/controllers/LoginController.php b/application/front/controllers/LoginController.php
new file mode 100644
index 00000000..ae3599e0
--- /dev/null
+++ b/application/front/controllers/LoginController.php
@@ -0,0 +1,48 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller;
6
7use Shaarli\Front\Exception\LoginBannedException;
8use Slim\Http\Request;
9use Slim\Http\Response;
10
11/**
12 * Class LoginController
13 *
14 * Slim controller used to render the login page.
15 *
16 * The login page is not available if the user is banned
17 * or if open shaarli setting is enabled.
18 *
19 * @package Front\Controller
20 */
21class LoginController extends ShaarliController
22{
23 public function index(Request $request, Response $response): Response
24 {
25 if ($this->container->loginManager->isLoggedIn()
26 || $this->container->conf->get('security.open_shaarli', false)
27 ) {
28 return $response->withRedirect('./');
29 }
30
31 $userCanLogin = $this->container->loginManager->canLogin($request->getServerParams());
32 if ($userCanLogin !== true) {
33 throw new LoginBannedException();
34 }
35
36 if ($request->getParam('username') !== null) {
37 $this->assignView('username', escape($request->getParam('username')));
38 }
39
40 $this
41 ->assignView('returnurl', escape($request->getServerParam('HTTP_REFERER')))
42 ->assignView('remember_user_default', $this->container->conf->get('privacy.remember_user_default', true))
43 ->assignView('pagetitle', t('Login') .' - '. $this->container->conf->get('general.title', 'Shaarli'))
44 ;
45
46 return $response->write($this->render('loginform'));
47 }
48}
diff --git a/application/front/controllers/ShaarliController.php b/application/front/controllers/ShaarliController.php
new file mode 100644
index 00000000..2b828588
--- /dev/null
+++ b/application/front/controllers/ShaarliController.php
@@ -0,0 +1,69 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller;
6
7use Shaarli\Bookmark\BookmarkFilter;
8use Shaarli\Container\ShaarliContainer;
9
10abstract class ShaarliController
11{
12 /** @var ShaarliContainer */
13 protected $container;
14
15 /** @param ShaarliContainer $container Slim container (extended for attribute completion). */
16 public function __construct(ShaarliContainer $container)
17 {
18 $this->container = $container;
19 }
20
21 /**
22 * Assign variables to RainTPL template through the PageBuilder.
23 *
24 * @param mixed $value Value to assign to the template
25 */
26 protected function assignView(string $name, $value): self
27 {
28 $this->container->pageBuilder->assign($name, $value);
29
30 return $this;
31 }
32
33 protected function render(string $template): string
34 {
35 $this->assignView('linkcount', $this->container->bookmarkService->count(BookmarkFilter::$ALL));
36 $this->assignView('privateLinkcount', $this->container->bookmarkService->count(BookmarkFilter::$PRIVATE));
37 $this->assignView('plugin_errors', $this->container->pluginManager->getErrors());
38
39 $this->executeDefaultHooks($template);
40
41 return $this->container->pageBuilder->render($template);
42 }
43
44 /**
45 * Call plugin hooks for header, footer and includes, specifying which page will be rendered.
46 * Then assign generated data to RainTPL.
47 */
48 protected function executeDefaultHooks(string $template): void
49 {
50 $common_hooks = [
51 'includes',
52 'header',
53 'footer',
54 ];
55
56 foreach ($common_hooks as $name) {
57 $plugin_data = [];
58 $this->container->pluginManager->executeHooks(
59 'render_' . $name,
60 $plugin_data,
61 [
62 'target' => $template,
63 'loggedin' => $this->container->loginManager->isLoggedIn()
64 ]
65 );
66 $this->assignView('plugins_' . $name, $plugin_data);
67 }
68 }
69}
diff --git a/application/front/exceptions/LoginBannedException.php b/application/front/exceptions/LoginBannedException.php
new file mode 100644
index 00000000..b31a4a14
--- /dev/null
+++ b/application/front/exceptions/LoginBannedException.php
@@ -0,0 +1,15 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Exception;
6
7class LoginBannedException extends ShaarliException
8{
9 public function __construct()
10 {
11 $message = t('You have been banned after too many failed login attempts. Try again later.');
12
13 parent::__construct($message, 401);
14 }
15}
diff --git a/application/front/exceptions/ShaarliException.php b/application/front/exceptions/ShaarliException.php
new file mode 100644
index 00000000..800bfbec
--- /dev/null
+++ b/application/front/exceptions/ShaarliException.php
@@ -0,0 +1,23 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Exception;
6
7use Throwable;
8
9/**
10 * Class ShaarliException
11 *
12 * Abstract exception class used to defined any custom exception thrown during front rendering.
13 *
14 * @package Front\Exception
15 */
16abstract class ShaarliException extends \Exception
17{
18 /** Override parent constructor to force $message and $httpCode parameters to be set. */
19 public function __construct(string $message, int $httpCode, Throwable $previous = null)
20 {
21 parent::__construct($message, $httpCode, $previous);
22 }
23}
diff --git a/application/render/PageBuilder.php b/application/render/PageBuilder.php
index 65e85aaf..f4fefda8 100644
--- a/application/render/PageBuilder.php
+++ b/application/render/PageBuilder.php
@@ -200,6 +200,23 @@ class PageBuilder
200 } 200 }
201 201
202 /** 202 /**
203 * Render a specific page as string (using a template file).
204 * e.g. $pb->render('picwall');
205 *
206 * @param string $page Template filename (without extension).
207 *
208 * @return string Processed template content
209 */
210 public function render(string $page): string
211 {
212 if ($this->tpl === false) {
213 $this->initialize();
214 }
215
216 return $this->tpl->draw($page, true);
217 }
218
219 /**
203 * Render a 404 page (uses the template : tpl/404.tpl) 220 * Render a 404 page (uses the template : tpl/404.tpl)
204 * usage: $PAGE->render404('The link was deleted') 221 * usage: $PAGE->render404('The link was deleted')
205 * 222 *
diff --git a/application/security/SessionManager.php b/application/security/SessionManager.php
index b8b8ab8d..994fcbe5 100644
--- a/application/security/SessionManager.php
+++ b/application/security/SessionManager.php
@@ -196,4 +196,10 @@ class SessionManager
196 } 196 }
197 return true; 197 return true;
198 } 198 }
199
200 /** @return array Local reference to the global $_SESSION array */
201 public function getSession(): array
202 {
203 return $this->session;
204 }
199} 205}
diff --git a/assets/default/scss/shaarli.scss b/assets/default/scss/shaarli.scss
index cd5dd9e6..243ab1b2 100644
--- a/assets/default/scss/shaarli.scss
+++ b/assets/default/scss/shaarli.scss
@@ -1236,8 +1236,19 @@ form {
1236 color: $dark-grey; 1236 color: $dark-grey;
1237} 1237}
1238 1238
1239.page404-container { 1239.page-error-container {
1240 color: $dark-grey; 1240 color: $dark-grey;
1241
1242 h2 {
1243 margin: 70px 0 25px;
1244 }
1245
1246 pre {
1247 margin: 0 20%;
1248 padding: 20px 0;
1249 text-align: left;
1250 line-height: .7em;
1251 }
1241} 1252}
1242 1253
1243// EDIT LINK 1254// EDIT LINK
diff --git a/composer.json b/composer.json
index ada06a74..6b670fa2 100644
--- a/composer.json
+++ b/composer.json
@@ -48,9 +48,13 @@
48 "Shaarli\\Bookmark\\Exception\\": "application/bookmark/exception", 48 "Shaarli\\Bookmark\\Exception\\": "application/bookmark/exception",
49 "Shaarli\\Config\\": "application/config/", 49 "Shaarli\\Config\\": "application/config/",
50 "Shaarli\\Config\\Exception\\": "application/config/exception", 50 "Shaarli\\Config\\Exception\\": "application/config/exception",
51 "Shaarli\\Container\\": "application/container",
51 "Shaarli\\Exceptions\\": "application/exceptions", 52 "Shaarli\\Exceptions\\": "application/exceptions",
52 "Shaarli\\Feed\\": "application/feed", 53 "Shaarli\\Feed\\": "application/feed",
53 "Shaarli\\Formatter\\": "application/formatter", 54 "Shaarli\\Formatter\\": "application/formatter",
55 "Shaarli\\Front\\": "application/front",
56 "Shaarli\\Front\\Controller\\": "application/front/controllers",
57 "Shaarli\\Front\\Exception\\": "application/front/exceptions",
54 "Shaarli\\Http\\": "application/http", 58 "Shaarli\\Http\\": "application/http",
55 "Shaarli\\Legacy\\": "application/legacy", 59 "Shaarli\\Legacy\\": "application/legacy",
56 "Shaarli\\Netscape\\": "application/netscape", 60 "Shaarli\\Netscape\\": "application/netscape",
diff --git a/doc/md/Translations.md b/doc/md/Translations.md
index c7d33855..58b92da3 100644
--- a/doc/md/Translations.md
+++ b/doc/md/Translations.md
@@ -7,8 +7,8 @@ Note that only the `default` theme supports translations.
7 7
8### Contributing 8### Contributing
9 9
10We encourage the community to contribute to Shaarli's translation either by improving existing 10We encourage the community to contribute to Shaarli's translation either by improving existing
11translations or submitting a new language. 11translations or submitting a new language.
12 12
13Contributing to the translation does not require development skill. 13Contributing to the translation does not require development skill.
14 14
@@ -21,8 +21,8 @@ First, install [Poedit](https://poedit.net/) tool.
21 21
22Poedit will extract strings to translate from the PHP source code. 22Poedit will extract strings to translate from the PHP source code.
23 23
24**Important**: due to the usage of a template engine, it's important to generate PHP cache files to extract 24**Important**: due to the usage of a template engine, it's important to generate PHP cache files to extract
25every translatable string. 25every translatable string.
26 26
27You can either use [this script](https://gist.github.com/ArthurHoaro/5d0323f758ab2401ef444a53f54e9a07) (recommended) 27You can either use [this script](https://gist.github.com/ArthurHoaro/5d0323f758ab2401ef444a53f54e9a07) (recommended)
28or visit every template page in your browser to generate cache files, while logged in. 28or visit every template page in your browser to generate cache files, while logged in.
@@ -41,7 +41,7 @@ http://<replace_domain>/?do=daily
41http://<replace_domain>/?post 41http://<replace_domain>/?post
42http://<replace_domain>/?do=export 42http://<replace_domain>/?do=export
43http://<replace_domain>/?do=import 43http://<replace_domain>/?do=import
44http://<replace_domain>/?do=login 44http://<replace_domain>/login
45http://<replace_domain>/?do=picwall 45http://<replace_domain>/?do=picwall
46http://<replace_domain>/?do=pluginadmin 46http://<replace_domain>/?do=pluginadmin
47http://<replace_domain>/?do=tagcloud 47http://<replace_domain>/?do=tagcloud
@@ -50,8 +50,8 @@ http://<replace_domain>/?do=taglist
50 50
51#### Improve existing translation 51#### Improve existing translation
52 52
53In Poedit, click on "Edit a Translation", and from Shaarli's directory open 53In Poedit, click on "Edit a Translation", and from Shaarli's directory open
54`inc/languages/<lang>/LC_MESSAGES/shaarli.po`. 54`inc/languages/<lang>/LC_MESSAGES/shaarli.po`.
55 55
56The existing list of translatable strings should have been loaded, then click on the "Update" button. 56The existing list of translatable strings should have been loaded, then click on the "Update" button.
57 57
@@ -63,20 +63,20 @@ Save when you're done, then you can submit a pull request containing the updated
63 63
64#### Add a new language 64#### Add a new language
65 65
66Open Poedit and select "Create New Translation", then from Shaarli's directory open 66Open Poedit and select "Create New Translation", then from Shaarli's directory open
67`inc/languages/<lang>/LC_MESSAGES/shaarli.po`. 67`inc/languages/<lang>/LC_MESSAGES/shaarli.po`.
68 68
69Then select the language you want to create. 69Then select the language you want to create.
70 70
71Click on `File > Save as...`, and save your file in `<shaarli directory>/inc/language/<new language>/LC_MESSAGES/shaarli.po`. 71Click on `File > Save as...`, and save your file in `<shaarli directory>/inc/language/<new language>/LC_MESSAGES/shaarli.po`.
72`<new language>` here should be the language code respecting the [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-2) 72`<new language>` here should be the language code respecting the [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-2)
73format in lowercase (e.g. `de` for German). 73format in lowercase (e.g. `de` for German).
74 74
75Then click on the "Update" button, and you can start to translate every available string. 75Then click on the "Update" button, and you can start to translate every available string.
76 76
77Save when you're done, then you can submit a pull request containing the new `shaarli.po`. 77Save when you're done, then you can submit a pull request containing the new `shaarli.po`.
78 78
79### Theme translations 79### Theme translations
80 80
81Theme translation extensions are loaded automatically if they're present. 81Theme translation extensions are loaded automatically if they're present.
82 82
@@ -85,7 +85,7 @@ As a theme developer, all you have to do is to add the `.po` and `.mo` compiled
85 tpl/<theme name>/language/<lang>/LC_MESSAGES/<theme name>.po 85 tpl/<theme name>/language/<lang>/LC_MESSAGES/<theme name>.po
86 tpl/<theme name>/language/<lang>/LC_MESSAGES/<theme name>.mo 86 tpl/<theme name>/language/<lang>/LC_MESSAGES/<theme name>.mo
87 87
88Where `<lang>` is the ISO 3166-1 alpha-2 language code. 88Where `<lang>` is the ISO 3166-1 alpha-2 language code.
89Read the following section "Extend Shaarli's translation" to learn how to generate those files. 89Read the following section "Extend Shaarli's translation" to learn how to generate those files.
90 90
91### Extend Shaarli's translation 91### Extend Shaarli's translation
@@ -106,7 +106,7 @@ First, create your translation files tree directory:
106Your `.po` files must be named like your domain. E.g. if your translation domain is `my_theme`, then your file will be 106Your `.po` files must be named like your domain. E.g. if your translation domain is `my_theme`, then your file will be
107`my_theme.po`. 107`my_theme.po`.
108 108
109Users have to register your extension in their configuration with the parameter 109Users have to register your extension in their configuration with the parameter
110`translation.extensions.<domain>: <translation files path>`. 110`translation.extensions.<domain>: <translation files path>`.
111 111
112Example: 112Example:
@@ -151,11 +151,11 @@ When you're done, open Poedit and load translation strings from sources:
151 1. `File > New` 151 1. `File > New`
152 2. Choose your language 152 2. Choose your language
153 3. Save your `PO` file in `<your_module>/languages/<language code>/LC_MESSAGES/my_theme.po`. 153 3. Save your `PO` file in `<your_module>/languages/<language code>/LC_MESSAGES/my_theme.po`.
154 4. Go to `Catalog > Properties...` 154 4. Go to `Catalog > Properties...`
155 5. Fill the `Translation Properties` tab 155 5. Fill the `Translation Properties` tab
156 6. Add your source path in the `Sources Paths` tab 156 6. Add your source path in the `Sources Paths` tab
157 7. In the `Sources Keywords` tab uncheck "Also use default keywords" and add the following lines: 157 7. In the `Sources Keywords` tab uncheck "Also use default keywords" and add the following lines:
158 158
159``` 159```
160my_theme_t 160my_theme_t
161my_theme_t:1,2 161my_theme_t:1,2
diff --git a/inc/languages/ja/LC_MESSAGES/shaarli.po b/inc/languages/ja/LC_MESSAGES/shaarli.po
new file mode 100644
index 00000000..b420bb51
--- /dev/null
+++ b/inc/languages/ja/LC_MESSAGES/shaarli.po
@@ -0,0 +1,1293 @@
1msgid ""
2msgstr ""
3"Project-Id-Version: Shaarli\n"
4"Report-Msgid-Bugs-To: \n"
5"POT-Creation-Date: 2020-02-11 09:31+0900\n"
6"PO-Revision-Date: 2020-02-11 10:54+0900\n"
7"Last-Translator: yude <yudesleepy@gmail.com>\n"
8"Language-Team: Shaarli\n"
9"Language: ja\n"
10"MIME-Version: 1.0\n"
11"Content-Type: text/plain; charset=UTF-8\n"
12"Content-Transfer-Encoding: 8bit\n"
13"X-Generator: Poedit 2.3\n"
14"X-Poedit-Basepath: ../../../..\n"
15"Plural-Forms: nplurals=2; plural=(n != 1);\n"
16"X-Poedit-SourceCharset: UTF-8\n"
17"X-Poedit-KeywordsList: t:1,2;t\n"
18"X-Poedit-SearchPath-0: .\n"
19"X-Poedit-SearchPathExcluded-0: node_modules\n"
20"X-Poedit-SearchPathExcluded-1: vendor\n"
21
22#: application/ApplicationUtils.php:153
23#, php-format
24msgid ""
25"Your PHP version is obsolete! Shaarli requires at least PHP %s, and thus "
26"cannot run. Your PHP version has known security vulnerabilities and should "
27"be updated as soon as possible."
28msgstr ""
29"使用している PHP のバージョンが古すぎます! Shaarli の実行には最低でも PHP %s "
30"が必要です。 現在使用している PHP のバージョンには脆弱性があり、できるだけ速"
31"やかにアップデートするべきです。"
32
33#: application/ApplicationUtils.php:183 application/ApplicationUtils.php:195
34msgid "directory is not readable"
35msgstr "ディレクトリを読み込めません"
36
37#: application/ApplicationUtils.php:198
38msgid "directory is not writable"
39msgstr "ディレクトリに書き込めません"
40
41#: application/ApplicationUtils.php:216
42msgid "file is not readable"
43msgstr "ファイルを読み取る権限がありません"
44
45#: application/ApplicationUtils.php:219
46msgid "file is not writable"
47msgstr "ファイルを書き込む権限がありません"
48
49#: application/Cache.php:16
50#, php-format
51msgid "Cannot purge %s: no directory"
52msgstr "%s を削除できません: ディレクトリが存在しません"
53
54#: application/FeedBuilder.php:151
55msgid "Direct link"
56msgstr "ダイレクトリンク"
57
58#: application/FeedBuilder.php:153
59#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:88
60#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:178
61msgid "Permalink"
62msgstr "パーマリンク"
63
64#: application/History.php:174
65msgid "History file isn't readable or writable"
66msgstr "履歴ファイルを読み込む、または書き込むための権限がありません"
67
68#: application/History.php:185
69msgid "Could not parse history file"
70msgstr "履歴ファイルを正常に復元できませんでした"
71
72#: application/Languages.php:177
73msgid "Automatic"
74msgstr "自動"
75
76#: application/Languages.php:178
77msgid "English"
78msgstr "英語"
79
80#: application/Languages.php:179
81msgid "French"
82msgstr "フランス語"
83
84#: application/Languages.php:180
85msgid "German"
86msgstr "ドイツ語"
87
88#: application/LinkDB.php:136
89msgid "You are not authorized to add a link."
90msgstr "リンクを追加するには、ログインする必要があります。"
91
92#: application/LinkDB.php:139
93msgid "Internal Error: A link should always have an id and URL."
94msgstr "エラー: リンクにはIDとURLを登録しなければなりません。"
95
96#: application/LinkDB.php:142
97msgid "You must specify an integer as a key."
98msgstr "正常なキーの値ではありません。"
99
100#: application/LinkDB.php:145
101msgid "Array offset and link ID must be equal."
102msgstr "Array オフセットとリンクのIDは同じでなければなりません。"
103
104#: application/LinkDB.php:251
105#: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14
106#: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:48
107#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:14
108#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:48
109msgid ""
110"The personal, minimalist, super-fast, database free, bookmarking service"
111msgstr ""
112"個人向けの、ミニマムで高速でかつデータベースのいらないブックマークサービス"
113
114#: application/LinkDB.php:253
115msgid ""
116"Welcome to Shaarli! This is your first public bookmark. To edit or delete "
117"me, you must first login.\n"
118"\n"
119"To learn how to use Shaarli, consult the link \"Documentation\" at the "
120"bottom of this page.\n"
121"\n"
122"You use the community supported version of the original Shaarli project, by "
123"Sebastien Sauvage."
124msgstr ""
125"Shaarli へようこそ! これはあなたの最初の公開ブックマークです。これを編集した"
126"り削除したりするには、ログインする必要があります。\n"
127"\n"
128"Shaarli の使い方を知るには、このページの下にある「ドキュメント」のリンクを開"
129"いてください。\n"
130"\n"
131"あなたは Sebastien Sauvage による、コミュニティーサポートのあるバージョンのオ"
132"リジナルのShaarli プロジェクトを使用しています。"
133
134#: application/LinkDB.php:267
135msgid "My secret stuff... - Pastebin.com"
136msgstr "わたしのひ💗み💗つ💗 - Pastebin.com"
137
138#: application/LinkDB.php:269
139msgid "Shhhh! I'm a private link only YOU can see. You can delete me too."
140msgstr ""
141"シーッ! これはあなたしか見られないプライベートリンクです。消すこともできま"
142"す。"
143
144#: application/LinkFilter.php:452
145msgid "The link you are trying to reach does not exist or has been deleted."
146msgstr "開こうとしたリンクは存在しないか、削除されています。"
147
148#: application/NetscapeBookmarkUtils.php:35
149msgid "Invalid export selection:"
150msgstr "不正なエクスポートの選択:"
151
152#: application/NetscapeBookmarkUtils.php:81
153#, php-format
154msgid "File %s (%d bytes) "
155msgstr "ファイル %s (%d バイト) "
156
157#: application/NetscapeBookmarkUtils.php:83
158msgid "has an unknown file format. Nothing was imported."
159msgstr "は不明なファイル形式です。インポートは中止されました。"
160
161#: application/NetscapeBookmarkUtils.php:86
162#, php-format
163msgid ""
164"was successfully processed in %d seconds: %d links imported, %d links "
165"overwritten, %d links skipped."
166msgstr ""
167"が %d 秒で処理され、%d 件のリンクがインポートされ、%d 件のリンクが上書きさ"
168"れ、%d 件のリンクがスキップされました。"
169
170#: application/PageBuilder.php:168
171msgid "The page you are trying to reach does not exist or has been deleted."
172msgstr "あなたが開こうとしたページは存在しないか、削除されています。"
173
174#: application/PageBuilder.php:170
175msgid "404 Not Found"
176msgstr "404 ページが存在しません"
177
178#: application/PluginManager.php:243
179#, php-format
180msgid "Plugin \"%s\" files not found."
181msgstr "プラグイン「%s」のファイルが存在しません。"
182
183#: application/Updater.php:76
184msgid "Couldn't retrieve Updater class methods."
185msgstr "アップデーターのクラスメゾットを受信できませんでした。"
186
187#: application/Updater.php:532
188msgid "An error occurred while running the update "
189msgstr "更新中に問題が発生しました "
190
191#: application/Updater.php:572
192msgid "Updates file path is not set, can't write updates."
193msgstr "更新するファイルのパスが指定されていないため、更新を書き込めません。"
194
195#: application/Updater.php:577
196msgid "Unable to write updates in "
197msgstr "更新を次の項目に書き込めませんでした: "
198
199#: application/Utils.php:376 tests/UtilsTest.php:340
200msgid "Setting not set"
201msgstr "未設定"
202
203#: application/Utils.php:383 tests/UtilsTest.php:338 tests/UtilsTest.php:339
204msgid "Unlimited"
205msgstr "無制限"
206
207#: application/Utils.php:386 tests/UtilsTest.php:335 tests/UtilsTest.php:336
208#: tests/UtilsTest.php:350
209msgid "B"
210msgstr "B"
211
212#: application/Utils.php:386 tests/UtilsTest.php:329 tests/UtilsTest.php:330
213#: tests/UtilsTest.php:337
214msgid "kiB"
215msgstr "kiB"
216
217#: application/Utils.php:386 tests/UtilsTest.php:331 tests/UtilsTest.php:332
218#: tests/UtilsTest.php:348 tests/UtilsTest.php:349
219msgid "MiB"
220msgstr "MiB"
221
222#: application/Utils.php:386 tests/UtilsTest.php:333 tests/UtilsTest.php:334
223msgid "GiB"
224msgstr "GiB"
225
226#: application/config/ConfigJson.php:52 application/config/ConfigPhp.php:121
227msgid ""
228"Shaarli could not create the config file. Please make sure Shaarli has the "
229"right to write in the folder is it installed in."
230msgstr ""
231"Shaarli は設定ファイルを作成できませんでした。Shaarli が正しい権限下に置かれ"
232"ていて、インストールされているディレクトリに書き込みできることを確認してくだ"
233"さい。"
234
235#: application/config/ConfigManager.php:135
236msgid "Invalid setting key parameter. String expected, got: "
237msgstr ""
238"不正なキーの値です。文字列が想定されていますが、次のように入力されました: "
239
240#: application/config/exception/MissingFieldConfigException.php:21
241#, php-format
242msgid "Configuration value is required for %s"
243msgstr "%s には設定が必要です"
244
245#: application/config/exception/PluginConfigOrderException.php:15
246msgid "An error occurred while trying to save plugins loading order."
247msgstr "プラグインの読込順を変更する際にエラーが発生しました。"
248
249#: application/config/exception/UnauthorizedConfigException.php:16
250msgid "You are not authorized to alter config."
251msgstr "設定を変更する権限がありません。"
252
253#: application/exceptions/IOException.php:19
254msgid "Error accessing"
255msgstr "読込中にエラーが発生しました"
256
257#: index.php:142
258msgid "Shared links on "
259msgstr "次において共有されたリンク:"
260
261#: index.php:164
262msgid "Insufficient permissions:"
263msgstr "権限がありません:"
264
265#: index.php:303
266msgid "I said: NO. You are banned for the moment. Go away."
267msgstr "あなたはこのサーバーからBANされています。"
268
269#: index.php:368
270msgid "Wrong login/password."
271msgstr "不正なユーザー名、またはパスワードです。"
272
273#: index.php:576 tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:42
274#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:42
275msgid "Daily"
276msgstr "デイリー"
277
278#: index.php:681 tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:28
279#: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:44
280#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:71
281#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:95
282#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:71
283#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:95
284msgid "Login"
285msgstr "ログイン"
286
287#: index.php:722 tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:39
288#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:39
289msgid "Picture wall"
290msgstr "ピクチャウォール"
291
292#: index.php:770 tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36
293#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:36
294#: tmp/tag.cloud.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19
295msgid "Tag cloud"
296msgstr "タグクラウド"
297
298#: index.php:803 tmp/tag.list.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19
299msgid "Tag list"
300msgstr "タグ一覧"
301
302#: index.php:1028 tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:31
303#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:31
304msgid "Tools"
305msgstr "ツール"
306
307#: index.php:1037
308msgid "You are not supposed to change a password on an Open Shaarli."
309msgstr ""
310"公開されている Shaarli において、パスワードを変更することは想定されていませ"
311"ん。"
312
313#: index.php:1042 index.php:1084 index.php:1160 index.php:1191 index.php:1291
314msgid "Wrong token."
315msgstr "不正なトークンです。"
316
317#: index.php:1047
318msgid "The old password is not correct."
319msgstr "元のパスワードが正しくありません。"
320
321#: index.php:1067
322msgid "Your password has been changed"
323msgstr "あなたのパスワードは変更されました"
324
325#: index.php:1072
326#: tmp/changepassword.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13
327#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:29
328msgid "Change password"
329msgstr "パスワードを変更"
330
331#: index.php:1120
332msgid "Configuration was saved."
333msgstr "設定は保存されました。"
334
335#: index.php:1143 tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:24
336msgid "Configure"
337msgstr "設定"
338
339#: index.php:1154 tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13
340#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36
341msgid "Manage tags"
342msgstr "タグを設定"
343
344#: index.php:1172
345#, php-format
346msgid "The tag was removed from %d link."
347msgid_plural "The tag was removed from %d links."
348msgstr[0] "%d 件のリンクからタグが削除されました。"
349msgstr[1] "The tag was removed from %d links."
350
351#: index.php:1173
352#, php-format
353msgid "The tag was renamed in %d link."
354msgid_plural "The tag was renamed in %d links."
355msgstr[0] "タグが %d 件のリンクにおいて、名前が変更されました。"
356msgstr[1] "タグが %d 件のリンクにおいて、名前が変更されました。"
357
358#: index.php:1181 tmp/addlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13
359msgid "Shaare a new link"
360msgstr "新しいリンクを追加"
361
362#: index.php:1351 tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14
363#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:170
364msgid "Edit"
365msgstr "共有"
366
367#: index.php:1351 index.php:1421
368#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
369#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:26
370#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:26
371msgid "Shaare"
372msgstr "Shaare"
373
374#: index.php:1390
375msgid "Note: "
376msgstr "注: "
377
378#: index.php:1430 tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:65
379msgid "Export"
380msgstr "エクスポート"
381
382#: index.php:1492 tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:83
383msgid "Import"
384msgstr "インポート"
385
386#: index.php:1502
387#, php-format
388msgid ""
389"The file you are trying to upload is probably bigger than what this "
390"webserver can accept (%s). Please upload in smaller chunks."
391msgstr ""
392"あなたがアップロードしようとしているファイルは、サーバーが許可しているファイ"
393"ルサイズ (%s) よりも大きいです。もう少し小さいものをアップロードしてくださ"
394"い。"
395
396#: index.php:1541 tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:26
397#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:22
398msgid "Plugin administration"
399msgstr "プラグイン管理"
400
401#: index.php:1706
402msgid "Search: "
403msgstr "検索: "
404
405#: index.php:1933
406#, php-format
407msgid ""
408"<pre>Sessions do not seem to work correctly on your server.<br>Make sure the "
409"variable \"session.save_path\" is set correctly in your PHP config, and that "
410"you have write access to it.<br>It currently points to %s.<br>On some "
411"browsers, accessing your server via a hostname like 'localhost' or any "
412"custom hostname without a dot causes cookie storage to fail. We recommend "
413"accessing your server via it's IP address or Fully Qualified Domain Name.<br>"
414msgstr ""
415"<pre>セッションが正常にあなたのサーバー上で稼働していないようです。<br>PHP の"
416"設定ファイル内にて、正しく \"session.save_path\" の値が設定されていることと、"
417"権限が間違っていないことを確認してください。<br>現在 %s からPHPの設定ファイル"
418"を読み込んでいます。<br>一部のブラウザーにおいて、localhost や他のドットを含"
419"まないホスト名にてサーバーにアクセスする際に、クッキーを保存できないことがあ"
420"ります。IP アドレスや完全なドメイン名でサーバーにアクセスすることをおすすめし"
421"ます。<br>"
422
423#: index.php:1943
424msgid "Click to try again."
425msgstr "クリックして再度試します。"
426
427#: plugins/addlink_toolbar/addlink_toolbar.php:29
428msgid "URI"
429msgstr "URI"
430
431#: plugins/addlink_toolbar/addlink_toolbar.php:33
432#: tmp/addlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19
433msgid "Add link"
434msgstr "リンクを追加"
435
436#: plugins/addlink_toolbar/addlink_toolbar.php:50
437msgid "Adds the addlink input on the linklist page."
438msgstr "リンク一覧のページに、リンクを追加するためのフォームを表示する。"
439
440#: plugins/archiveorg/archiveorg.php:23
441msgid "View on archive.org"
442msgstr "archive.org 上で表示する"
443
444#: plugins/archiveorg/archiveorg.php:36
445msgid "For each link, add an Archive.org icon."
446msgstr "それぞれのリンクに、Archive.org のアイコンを追加する。"
447
448#: plugins/demo_plugin/demo_plugin.php:465
449msgid ""
450"A demo plugin covering all use cases for template designers and plugin "
451"developers."
452msgstr ""
453"テンプレートのデザイナーや、プラグインの開発者のためのすべての状況に対応でき"
454"るデモプラグインです。"
455
456#: plugins/isso/isso.php:20
457msgid ""
458"Isso plugin error: Please define the \"ISSO_SERVER\" setting in the plugin "
459"administration page."
460msgstr ""
461"Isso プラグインエラー: \"ISSO_SERVER\" の値をプラグイン管理ページにて指定して"
462"ください。"
463
464#: plugins/isso/isso.php:63
465msgid "Let visitor comment your shaares on permalinks with Isso."
466msgstr ""
467"Isso を使って、あなたのパーマリンク上のリンクに第三者がコメントを残すことがで"
468"きます。"
469
470#: plugins/isso/isso.php:64
471msgid "Isso server URL (without 'http://')"
472msgstr "Isso server URL ('http://' 抜き)"
473
474#: plugins/markdown/markdown.php:158
475msgid "Description will be rendered with"
476msgstr "説明は次の方法で描画されます:"
477
478#: plugins/markdown/markdown.php:159
479msgid "Markdown syntax documentation"
480msgstr "マークダウン形式のドキュメント"
481
482#: plugins/markdown/markdown.php:160
483msgid "Markdown syntax"
484msgstr "マークダウン形式"
485
486#: plugins/markdown/markdown.php:339
487msgid ""
488"Render shaare description with Markdown syntax.<br><strong>Warning</"
489"strong>:\n"
490"If your shaared descriptions contained HTML tags before enabling the "
491"markdown plugin,\n"
492"enabling it might break your page.\n"
493"See the <a href=\"https://github.com/shaarli/Shaarli/tree/master/plugins/"
494"markdown#html-rendering\">README</a>."
495msgstr ""
496"リンクの説明をマークダウン形式で表示します。<br><strong>警告</strong>:\n"
497"リンクの説明にHTMLタグがこのプラグインを有効にする前に含まれていた場合、\n"
498"正常にページを表示できなくなるかもしれません。\n"
499"詳しくは <a href=\"https://github.com/shaarli/Shaarli/tree/master/plugins/"
500"markdown#html-rendering\">README</a> をご覧ください。"
501
502#: plugins/piwik/piwik.php:21
503msgid ""
504"Piwik plugin error: Please define PIWIK_URL and PIWIK_SITEID in the plugin "
505"administration page."
506msgstr ""
507"Piwik プラグインエラー: PIWIK_URL と PIWIK_SITEID の値をプラグイン管理ページ"
508"で指定してください。"
509
510#: plugins/piwik/piwik.php:70
511msgid "A plugin that adds Piwik tracking code to Shaarli pages."
512msgstr "Piwik のトラッキングコードをShaarliに追加するプラグインです。"
513
514#: plugins/piwik/piwik.php:71
515msgid "Piwik URL"
516msgstr "Piwik URL"
517
518#: plugins/piwik/piwik.php:72
519msgid "Piwik site ID"
520msgstr "Piwik サイトID"
521
522#: plugins/playvideos/playvideos.php:22
523msgid "Video player"
524msgstr "動画プレイヤー"
525
526#: plugins/playvideos/playvideos.php:25
527msgid "Play Videos"
528msgstr "動画を再生"
529
530#: plugins/playvideos/playvideos.php:56
531msgid "Add a button in the toolbar allowing to watch all videos."
532msgstr "すべての動画を閲覧するボタンをツールバーに追加します。"
533
534#: plugins/playvideos/youtube_playlist.js:214
535msgid "plugins/playvideos/jquery-1.11.2.min.js"
536msgstr "plugins/playvideos/jquery-1.11.2.min.js"
537
538#: plugins/pubsubhubbub/pubsubhubbub.php:69
539#, php-format
540msgid "Could not publish to PubSubHubbub: %s"
541msgstr "PubSubHubbub に登録できませんでした: %s"
542
543#: plugins/pubsubhubbub/pubsubhubbub.php:95
544#, php-format
545msgid "Could not post to %s"
546msgstr "%s に登録できませんでした"
547
548#: plugins/pubsubhubbub/pubsubhubbub.php:99
549#, php-format
550msgid "Bad response from the hub %s"
551msgstr "ハブ %s からの不正なレスポンス"
552
553#: plugins/pubsubhubbub/pubsubhubbub.php:110
554msgid "Enable PubSubHubbub feed publishing."
555msgstr "PubSubHubbub へのフィードを公開する。"
556
557#: plugins/qrcode/qrcode.php:69 plugins/wallabag/wallabag.php:68
558msgid "For each link, add a QRCode icon."
559msgstr "それぞれのリンクについて、QRコードのアイコンを追加する。"
560
561#: plugins/wallabag/wallabag.php:21
562msgid ""
563"Wallabag plugin error: Please define the \"WALLABAG_URL\" setting in the "
564"plugin administration page."
565msgstr ""
566"Wallabag プラグインエラー: \"WALLABAG_URL\" の値をプラグイン管理ページにおい"
567"て指定してください。"
568
569#: plugins/wallabag/wallabag.php:47
570msgid "Save to wallabag"
571msgstr "Wallabag に保存"
572
573#: plugins/wallabag/wallabag.php:69
574msgid "Wallabag API URL"
575msgstr "Wallabag のAPIのURL"
576
577#: plugins/wallabag/wallabag.php:70
578msgid "Wallabag API version (1 or 2)"
579msgstr "Wallabag のAPIのバージョン (1 または 2)"
580
581#: tests/LanguagesTest.php:214 tests/LanguagesTest.php:227
582#: tests/languages/fr/LanguagesFrTest.php:160
583#: tests/languages/fr/LanguagesFrTest.php:173
584#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:81
585#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:81
586msgid "Search"
587msgid_plural "Search"
588msgstr[0] "検索"
589msgstr[1] "検索"
590
591#: tmp/404.b91ef64efc3688266305ea9b42e5017e.rtpl.php:12
592msgid "Sorry, nothing to see here."
593msgstr "すみませんが、ここには何もありません。"
594
595#: tmp/addlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
596msgid "URL or leave empty to post a note"
597msgstr "URL を入力するか、空欄にするとノートを投稿します"
598
599#: tmp/changepassword.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
600msgid "Current password"
601msgstr "現在のパスワード"
602
603#: tmp/changepassword.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19
604msgid "New password"
605msgstr "新しいパスワード"
606
607#: tmp/changepassword.b91ef64efc3688266305ea9b42e5017e.rtpl.php:23
608msgid "Change"
609msgstr "変更"
610
611#: tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
612#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:77
613msgid "Tag"
614msgstr "タグ"
615
616#: tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:24
617msgid "New name"
618msgstr "変更先の名前"
619
620#: tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:31
621msgid "Case sensitive"
622msgstr "大文字と小文字を区別"
623
624#: tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:34
625msgid "Rename"
626msgstr "名前を変更"
627
628#: tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:35
629#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:79
630#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:172
631msgid "Delete"
632msgstr "削除"
633
634#: tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:39
635msgid "You can also edit tags in the"
636msgstr "次に含まれるタグを編集することもできます:"
637
638#: tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:39
639msgid "tag list"
640msgstr "タグ一覧"
641
642#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:29
643msgid "title"
644msgstr "タイトル"
645
646#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:43
647msgid "Home link"
648msgstr "ホームのリンク先"
649
650#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:44
651msgid "Default value"
652msgstr "既定の値"
653
654#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:58
655msgid "Theme"
656msgstr "テーマ"
657
658#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:87
659#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:78
660msgid "Language"
661msgstr "言語"
662
663#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:116
664#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:102
665msgid "Timezone"
666msgstr "タイムゾーン"
667
668#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:117
669#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:103
670msgid "Continent"
671msgstr "大陸"
672
673#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:117
674#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:103
675msgid "City"
676msgstr "町"
677
678#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:164
679msgid "Disable session cookie hijacking protection"
680msgstr "不正ログイン防止のためのセッションクッキーを無効化"
681
682#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:166
683msgid "Check this if you get disconnected or if your IP address changes often"
684msgstr ""
685"あなたが切断されたり、IPアドレスが頻繁に変わる環境下であるならチェックを入れ"
686"てください"
687
688#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:183
689msgid "Private links by default"
690msgstr "既定でプライベートリンク"
691
692#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:184
693msgid "All new links are private by default"
694msgstr "すべての新規リンクをプライベートで作成"
695
696#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:199
697msgid "RSS direct links"
698msgstr "RSS 直リンク"
699
700#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:200
701msgid "Check this to use direct URL instead of permalink in feeds"
702msgstr "フィードでパーマリンクの代わりに直リンクを使う"
703
704#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:215
705msgid "Hide public links"
706msgstr "公開リンクを隠す"
707
708#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:216
709msgid "Do not show any links if the user is not logged in"
710msgstr "ログインしていないユーザーには何のリンクも表示しない"
711
712#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:231
713#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:150
714msgid "Check updates"
715msgstr "更新を確認"
716
717#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:232
718#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:152
719msgid "Notify me when a new release is ready"
720msgstr "新しいバージョンがリリースされたときに通知"
721
722#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:247
723#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:169
724msgid "Enable REST API"
725msgstr "REST API を有効化"
726
727#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:248
728#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:170
729msgid "Allow third party software to use Shaarli such as mobile application"
730msgstr ""
731"モバイルアプリといったサードパーティーのソフトウェアにShaarliを使用することを"
732"許可"
733
734#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:263
735msgid "API secret"
736msgstr "API シークレット"
737
738#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:274
739#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:74
740#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:139
741#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:199
742msgid "Save"
743msgstr "保存"
744
745#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:15
746msgid "The Daily Shaarli"
747msgstr "デイリーSharli"
748
749#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:17
750msgid "1 RSS entry per day"
751msgstr "各日1つずつのRSS項目"
752
753#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:37
754msgid "Previous day"
755msgstr "前日"
756
757#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:44
758msgid "All links of one day in a single page."
759msgstr "1日に作成されたすべてのリンクです。"
760
761#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:51
762msgid "Next day"
763msgstr "翌日"
764
765#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:25
766msgid "Created:"
767msgstr "作成:"
768
769#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:28
770msgid "URL"
771msgstr "URL"
772
773#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:34
774msgid "Title"
775msgstr "タイトル"
776
777#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:40
778#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:42
779#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:75
780#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:99
781#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:124
782msgid "Description"
783msgstr "説明"
784
785#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:46
786msgid "Tags"
787msgstr "タグ"
788
789#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:59
790#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36
791#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:168
792msgid "Private"
793msgstr "プライベート"
794
795#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:74
796msgid "Apply Changes"
797msgstr "変更を適用"
798
799#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
800msgid "Export Database"
801msgstr "データベースをエクスポート"
802
803#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:24
804msgid "Selection"
805msgstr "選択済み"
806
807#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:31
808msgid "All"
809msgstr "すべて"
810
811#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41
812msgid "Public"
813msgstr "公開"
814
815#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:52
816msgid "Prepend note permalinks with this Shaarli instance's URL"
817msgstr "この Shaarli のインスタンスのURL にノートへのパーマリンクを付け加える"
818
819#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:53
820msgid "Useful to import bookmarks in a web browser"
821msgstr "ウェブブラウザーのリンクをインポートするのに有効です"
822
823#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
824msgid "Import Database"
825msgstr "データベースをインポート"
826
827#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:23
828msgid "Maximum size allowed:"
829msgstr "最大サイズ:"
830
831#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:29
832msgid "Visibility"
833msgstr "可視性"
834
835#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36
836msgid "Use values from the imported file, default to public"
837msgstr "インポート元のファイルの値を使用 (既定は公開リンクとなります)"
838
839#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41
840msgid "Import all bookmarks as private"
841msgstr "すべてのブックマーク項目をプライベートリンクとしてインポート"
842
843#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:46
844msgid "Import all bookmarks as public"
845msgstr "すべてのブックマーク項目を公開リンクとしてインポート"
846
847#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:57
848msgid "Overwrite existing bookmarks"
849msgstr "既に存在しているブックマークを上書き"
850
851#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:58
852msgid "Duplicates based on URL"
853msgstr "URL による重複"
854
855#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:72
856msgid "Add default tags"
857msgstr "既定のタグを追加"
858
859#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:22
860msgid "Install Shaarli"
861msgstr "Shaarli をインストール"
862
863#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:25
864msgid "It looks like it's the first time you run Shaarli. Please configure it."
865msgstr "どうやら Shaarli を初めて起動しているようです。設定してください。"
866
867#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:33
868#: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:30
869#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:147
870#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:147
871msgid "Username"
872msgstr "ユーザー名"
873
874#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:48
875#: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:34
876#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:148
877#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:148
878msgid "Password"
879msgstr "パスワード"
880
881#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:63
882msgid "Shaarli title"
883msgstr "Shaarli のタイトル"
884
885#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:69
886msgid "My links"
887msgstr "自分のリンク"
888
889#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:182
890msgid "Install"
891msgstr "インストール"
892
893#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14
894#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:80
895msgid "shaare"
896msgid_plural "shaares"
897msgstr[0] "共有"
898msgstr[1] "共有"
899
900#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:18
901#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:84
902msgid "private link"
903msgid_plural "private links"
904msgstr[0] "プライベートリンク"
905msgstr[1] "プライベートリンク"
906
907#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:31
908#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:117
909#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:117
910msgid "Search text"
911msgstr "文字列で検索"
912
913#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:38
914#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:124
915#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:124
916#: tmp/tag.cloud.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36
917#: tmp/tag.cloud.b91ef64efc3688266305ea9b42e5017e.rtpl.php:64
918#: tmp/tag.list.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36
919#: tmp/tag.list.b91ef64efc3688266305ea9b42e5017e.rtpl.php:74
920msgid "Filter by tag"
921msgstr "タグによって分類"
922
923#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:111
924msgid "Nothing found."
925msgstr "何も見つかりませんでした。"
926
927#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:119
928#, php-format
929msgid "%s result"
930msgid_plural "%s results"
931msgstr[0] "%s 件の結果"
932msgstr[1] "%s 件の結果"
933
934#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:123
935msgid "for"
936msgstr "for"
937
938#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:130
939msgid "tagged"
940msgstr "タグ付けされた"
941
942#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:134
943msgid "Remove tag"
944msgstr "タグを削除"
945
946#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:143
947msgid "with status"
948msgstr "with status"
949
950#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:154
951msgid "without any tag"
952msgstr "タグなし"
953
954#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:174
955#: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:42
956#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:42
957msgid "Fold"
958msgstr "畳む"
959
960#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:176
961msgid "Edited: "
962msgstr "編集済み: "
963
964#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:180
965msgid "permalink"
966msgstr "パーマリンク"
967
968#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:182
969msgid "Add tag"
970msgstr "タグを追加"
971
972#: tmp/linklist.paging.b91ef64efc3688266305ea9b42e5017e.rtpl.php:7
973#: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:7
974msgid "Filters"
975msgstr "分類"
976
977#: tmp/linklist.paging.b91ef64efc3688266305ea9b42e5017e.rtpl.php:12
978#: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:12
979msgid "Only display private links"
980msgstr "プライベートリンクのみを表示"
981
982#: tmp/linklist.paging.b91ef64efc3688266305ea9b42e5017e.rtpl.php:15
983#: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:15
984msgid "Only display public links"
985msgstr "公開リンクのみを表示"
986
987#: tmp/linklist.paging.b91ef64efc3688266305ea9b42e5017e.rtpl.php:20
988#: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:20
989msgid "Filter untagged links"
990msgstr "タグ付けされていないリンクで分類"
991
992#: tmp/linklist.paging.b91ef64efc3688266305ea9b42e5017e.rtpl.php:24
993#: tmp/linklist.paging.b91ef64efc3688266305ea9b42e5017e.rtpl.php:76
994#: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:24
995#: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:76
996#: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:43
997#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:43
998msgid "Fold all"
999msgstr "すべて畳む"
1000
1001#: tmp/linklist.paging.b91ef64efc3688266305ea9b42e5017e.rtpl.php:69
1002#: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:69
1003msgid "Links per page"
1004msgstr "各ページをリンク"
1005
1006#: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:15
1007msgid ""
1008"You have been banned after too many failed login attempts. Try again later."
1009msgstr "複数回に渡るログインへの失敗を検出しました。後でまた試してください。"
1010
1011#: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41
1012#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:151
1013#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:151
1014msgid "Remember me"
1015msgstr "パスワードを保存"
1016
1017#: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14
1018#: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:48
1019#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:14
1020#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:48
1021msgid "by the Shaarli community"
1022msgstr "by Shaarli コミュニティ"
1023
1024#: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:15
1025#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:15
1026msgid "Documentation"
1027msgstr "ドキュメント"
1028
1029#: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:44
1030#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:44
1031msgid "Expand"
1032msgstr "展開する"
1033
1034#: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:45
1035#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:45
1036msgid "Expand all"
1037msgstr "すべて展開する"
1038
1039#: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:46
1040#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:46
1041msgid "Are you sure you want to delete this link?"
1042msgstr "本当にこのリンクを削除しますか?"
1043
1044#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:61
1045#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:86
1046#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:61
1047#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:86
1048msgid "RSS Feed"
1049msgstr "RSS フィード"
1050
1051#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:66
1052#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:102
1053#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:66
1054#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:102
1055msgid "Logout"
1056msgstr "ログアウト"
1057
1058#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:169
1059#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:169
1060msgid "is available"
1061msgstr "が利用可能"
1062
1063#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:176
1064#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:176
1065msgid "Error"
1066msgstr "エラー"
1067
1068#: tmp/picwall.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
1069msgid "Picture Wall"
1070msgstr "ピクチャーウォール"
1071
1072#: tmp/picwall.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
1073msgid "pics"
1074msgstr "画像"
1075
1076#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:15
1077msgid "You need to enable Javascript to change plugin loading order."
1078msgstr ""
1079"プラグインを読み込む順番を変更するには、Javascriptを有効にする必要がありま"
1080"す。"
1081
1082#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:29
1083msgid "Enabled Plugins"
1084msgstr "有効なプラグイン"
1085
1086#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:34
1087#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:155
1088msgid "No plugin enabled."
1089msgstr "有効なプラグインはありません。"
1090
1091#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:40
1092#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:73
1093msgid "Disable"
1094msgstr "無効化"
1095
1096#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41
1097#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:74
1098#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:98
1099#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:123
1100msgid "Name"
1101msgstr "名前"
1102
1103#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:43
1104#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:76
1105msgid "Order"
1106msgstr "順序"
1107
1108#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:86
1109msgid "Disabled Plugins"
1110msgstr "無効なプラグイン"
1111
1112#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:91
1113msgid "No plugin disabled."
1114msgstr "無効なプラグインはありません。"
1115
1116#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:97
1117#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:122
1118msgid "Enable"
1119msgstr "有効化"
1120
1121#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:134
1122msgid "More plugins available"
1123msgstr "さらに利用できるプラグインがあります"
1124
1125#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:136
1126msgid "in the documentation"
1127msgstr "ドキュメント内"
1128
1129#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:150
1130msgid "Plugin configuration"
1131msgstr "プラグイン設定"
1132
1133#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:195
1134msgid "No parameter available."
1135msgstr "利用可能な設定項目はありません。"
1136
1137#: tmp/tag.cloud.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19
1138#: tmp/tag.list.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19
1139msgid "tags"
1140msgstr "タグ"
1141
1142#: tmp/tag.cloud.b91ef64efc3688266305ea9b42e5017e.rtpl.php:24
1143#: tmp/tag.list.b91ef64efc3688266305ea9b42e5017e.rtpl.php:24
1144msgid "List all links with those tags"
1145msgstr "このタグが付いているリンクをリスト化する"
1146
1147#: tmp/tag.sort.b91ef64efc3688266305ea9b42e5017e.rtpl.php:3
1148#: tmp/tag.sort.cedf684561d925457130839629000a81.rtpl.php:3
1149msgid "Sort by:"
1150msgstr "分類:"
1151
1152#: tmp/tag.sort.b91ef64efc3688266305ea9b42e5017e.rtpl.php:5
1153#: tmp/tag.sort.cedf684561d925457130839629000a81.rtpl.php:5
1154msgid "Cloud"
1155msgstr "クラウド"
1156
1157#: tmp/tag.sort.b91ef64efc3688266305ea9b42e5017e.rtpl.php:6
1158#: tmp/tag.sort.cedf684561d925457130839629000a81.rtpl.php:6
1159msgid "Most used"
1160msgstr "もっとも使われた"
1161
1162#: tmp/tag.sort.b91ef64efc3688266305ea9b42e5017e.rtpl.php:7
1163#: tmp/tag.sort.cedf684561d925457130839629000a81.rtpl.php:7
1164msgid "Alphabetical"
1165msgstr "アルファベット順"
1166
1167#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14
1168msgid "Settings"
1169msgstr "設定"
1170
1171#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
1172msgid "Change Shaarli settings: title, timezone, etc."
1173msgstr "Shaarli の設定を変更: タイトル、タイムゾーンなど。"
1174
1175#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:17
1176msgid "Configure your Shaarli"
1177msgstr "あなたの Shaarli を設定"
1178
1179#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:21
1180msgid "Enable, disable and configure plugins"
1181msgstr "プラグインを有効化、無効化、設定する"
1182
1183#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:28
1184msgid "Change your password"
1185msgstr "パスワードを変更"
1186
1187#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:35
1188msgid "Rename or delete a tag in all links"
1189msgstr "すべてのリンクのタグの名前を変更する、または削除する"
1190
1191#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41
1192msgid ""
1193"Import Netscape HTML bookmarks (as exported from Firefox, Chrome, Opera, "
1194"delicious...)"
1195msgstr ""
1196"Netscape HTML 形式のブックマークをインポートする (Firefox、Chrome、Operaと"
1197"いったブラウザーが含まれます)"
1198
1199#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:42
1200msgid "Import links"
1201msgstr "リンクをインポート"
1202
1203#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:47
1204msgid ""
1205"Export Netscape HTML bookmarks (which can be imported in Firefox, Chrome, "
1206"Opera, delicious...)"
1207msgstr ""
1208"Netscape HTML 形式のブックマークをエクスポートする (Firefox、Chrome、Operaと"
1209"いったブラウザーが含まれます)"
1210
1211#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:48
1212msgid "Export database"
1213msgstr "リンクをエクスポート"
1214
1215#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:71
1216msgid ""
1217"Drag one of these button to your bookmarks toolbar or right-click it and "
1218"\"Bookmark This Link\""
1219msgstr ""
1220"これらのボタンのうち1つををブックマークバーにドラッグするか、右クリックして"
1221"「このリンクをブックマークに追加」してください"
1222
1223#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:72
1224msgid "then click on the bookmarklet in any page you want to share."
1225msgstr "共有したいページでブックマークレットをクリックしてください。"
1226
1227#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:76
1228#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:100
1229msgid ""
1230"Drag this link to your bookmarks toolbar or right-click it and Bookmark This "
1231"Link"
1232msgstr ""
1233"このリンクをブックマークバーにドラッグするか、右クリックして「このリンクを"
1234"ブックマークに追加」してください"
1235
1236#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:77
1237msgid "then click ✚Shaare link button in any page you want to share"
1238msgstr "✚リンクを共有 ボタンをクリックすることで、どこでもリンクを共有できます"
1239
1240#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:86
1241#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:108
1242msgid "The selected text is too long, it will be truncated."
1243msgstr "選択された文字列は長すぎるので、一部が切り捨てられます。"
1244
1245#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:96
1246msgid "Shaare link"
1247msgstr "共有リンク"
1248
1249#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:101
1250msgid ""
1251"Then click ✚Add Note button anytime to start composing a private Note (text "
1252"post) to your Shaarli"
1253msgstr ""
1254"✚ノートを追加 ボタンをクリックすることで、いつでもプライベートノート(テキスト"
1255"形式)をShaarli上に作成できます"
1256
1257#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:117
1258msgid "Add Note"
1259msgstr "ノートを追加"
1260
1261#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:129
1262msgid ""
1263"You need to browse your Shaarli over <strong>HTTPS</strong> to use this "
1264"functionality."
1265msgstr ""
1266"この機能を使用するには、<strong>HTTPS</strong> 経由でShaarliに接続してくださ"
1267"い。"
1268
1269#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:134
1270msgid "Add to"
1271msgstr "次に追加:"
1272
1273#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:145
1274msgid "3rd party"
1275msgstr "サードパーティー"
1276
1277#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:147
1278#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:153
1279msgid "Plugin"
1280msgstr "プラグイン"
1281
1282#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:148
1283#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:154
1284msgid "plugin"
1285msgstr "プラグイン"
1286
1287#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:175
1288msgid ""
1289"Drag this link to your bookmarks toolbar, or right-click it and choose "
1290"Bookmark This Link"
1291msgstr ""
1292"このリンクをブックマークバーにドラッグするか、右クリックして「このリンクを"
1293"ブックマークに追加」してください"
diff --git a/index.php b/index.php
index a39fc762..b53b16fe 100644
--- a/index.php
+++ b/index.php
@@ -61,29 +61,31 @@ require_once 'application/FileUtils.php';
61require_once 'application/TimeZone.php'; 61require_once 'application/TimeZone.php';
62require_once 'application/Utils.php'; 62require_once 'application/Utils.php';
63 63
64use \Shaarli\ApplicationUtils; 64use Shaarli\ApplicationUtils;
65use Shaarli\Bookmark\BookmarkServiceInterface;
66use \Shaarli\Bookmark\Exception\BookmarkNotFoundException;
67use Shaarli\Bookmark\Bookmark; 65use Shaarli\Bookmark\Bookmark;
68use Shaarli\Bookmark\BookmarkFilter;
69use Shaarli\Bookmark\BookmarkFileService; 66use Shaarli\Bookmark\BookmarkFileService;
70use \Shaarli\Config\ConfigManager; 67use Shaarli\Bookmark\BookmarkFilter;
71use \Shaarli\Feed\CachedPage; 68use Shaarli\Bookmark\BookmarkServiceInterface;
72use \Shaarli\Feed\FeedBuilder; 69use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
70use Shaarli\Config\ConfigManager;
71use Shaarli\Container\ContainerBuilder;
72use Shaarli\Feed\CachedPage;
73use Shaarli\Feed\FeedBuilder;
73use Shaarli\Formatter\BookmarkMarkdownFormatter; 74use Shaarli\Formatter\BookmarkMarkdownFormatter;
74use Shaarli\Formatter\FormatterFactory; 75use Shaarli\Formatter\FormatterFactory;
75use \Shaarli\History; 76use Shaarli\History;
76use \Shaarli\Languages; 77use Shaarli\Languages;
77use \Shaarli\Netscape\NetscapeBookmarkUtils; 78use Shaarli\Netscape\NetscapeBookmarkUtils;
78use \Shaarli\Plugin\PluginManager; 79use Shaarli\Plugin\PluginManager;
79use \Shaarli\Render\PageBuilder; 80use Shaarli\Render\PageBuilder;
80use \Shaarli\Render\ThemeUtils; 81use Shaarli\Render\ThemeUtils;
81use \Shaarli\Router; 82use Shaarli\Router;
82use \Shaarli\Security\LoginManager; 83use Shaarli\Security\LoginManager;
83use \Shaarli\Security\SessionManager; 84use Shaarli\Security\SessionManager;
84use \Shaarli\Thumbnailer; 85use Shaarli\Thumbnailer;
85use \Shaarli\Updater\Updater; 86use Shaarli\Updater\Updater;
86use \Shaarli\Updater\UpdaterUtils; 87use Shaarli\Updater\UpdaterUtils;
88use Slim\App;
87 89
88// Ensure the PHP version is supported 90// Ensure the PHP version is supported
89try { 91try {
@@ -243,12 +245,14 @@ if (isset($_POST['login'])) {
243 } 245 }
244 246
245 // Send cookie with the new expiration date to the browser 247 // Send cookie with the new expiration date to the browser
248 session_destroy();
246 session_set_cookie_params($expirationTime, $cookiedir, $_SERVER['SERVER_NAME']); 249 session_set_cookie_params($expirationTime, $cookiedir, $_SERVER['SERVER_NAME']);
250 session_start();
247 session_regenerate_id(true); 251 session_regenerate_id(true);
248 252
249 // Optional redirect after login: 253 // Optional redirect after login:
250 if (isset($_GET['post'])) { 254 if (isset($_GET['post'])) {
251 $uri = '?post='. urlencode($_GET['post']); 255 $uri = './?post='. urlencode($_GET['post']);
252 foreach (array('description', 'source', 'title', 'tags') as $param) { 256 foreach (array('description', 'source', 'title', 'tags') as $param) {
253 if (!empty($_GET[$param])) { 257 if (!empty($_GET[$param])) {
254 $uri .= '&'.$param.'='.urlencode($_GET[$param]); 258 $uri .= '&'.$param.'='.urlencode($_GET[$param]);
@@ -259,22 +263,22 @@ if (isset($_POST['login'])) {
259 } 263 }
260 264
261 if (isset($_GET['edit_link'])) { 265 if (isset($_GET['edit_link'])) {
262 header('Location: ?edit_link='. escape($_GET['edit_link'])); 266 header('Location: ./?edit_link='. escape($_GET['edit_link']));
263 exit; 267 exit;
264 } 268 }
265 269
266 if (isset($_POST['returnurl'])) { 270 if (isset($_POST['returnurl'])) {
267 // Prevent loops over login screen. 271 // Prevent loops over login screen.
268 if (strpos($_POST['returnurl'], 'do=login') === false) { 272 if (strpos($_POST['returnurl'], '/login') === false) {
269 header('Location: '. generateLocation($_POST['returnurl'], $_SERVER['HTTP_HOST'])); 273 header('Location: '. generateLocation($_POST['returnurl'], $_SERVER['HTTP_HOST']));
270 exit; 274 exit;
271 } 275 }
272 } 276 }
273 header('Location: ?'); 277 header('Location: ./?');
274 exit; 278 exit;
275 } else { 279 } else {
276 $loginManager->handleFailedLogin($_SERVER); 280 $loginManager->handleFailedLogin($_SERVER);
277 $redir = '&username='. urlencode($_POST['login']); 281 $redir = '?username='. urlencode($_POST['login']);
278 if (isset($_GET['post'])) { 282 if (isset($_GET['post'])) {
279 $redir .= '&post=' . urlencode($_GET['post']); 283 $redir .= '&post=' . urlencode($_GET['post']);
280 foreach (array('description', 'source', 'title', 'tags') as $param) { 284 foreach (array('description', 'source', 'title', 'tags') as $param) {
@@ -284,7 +288,7 @@ if (isset($_POST['login'])) {
284 } 288 }
285 } 289 }
286 // Redirect to login screen. 290 // Redirect to login screen.
287 echo '<script>alert("'. t("Wrong login/password.") .'");document.location=\'?do=login'.$redir.'\';</script>'; 291 echo '<script>alert("'. t("Wrong login/password.") .'");document.location=\'./login'.$redir.'\';</script>';
288 exit; 292 exit;
289 } 293 }
290} 294}
@@ -592,19 +596,7 @@ function renderPage($conf, $pluginManager, $bookmarkService, $history, $sessionM
592 596
593 // -------- Display login form. 597 // -------- Display login form.
594 if ($targetPage == Router::$PAGE_LOGIN) { 598 if ($targetPage == Router::$PAGE_LOGIN) {
595 if ($conf->get('security.open_shaarli')) { 599 header('Location: ./login');
596 header('Location: ?');
597 exit;
598 } // No need to login for open Shaarli
599 if (isset($_GET['username'])) {
600 $PAGE->assign('username', escape($_GET['username']));
601 }
602 $PAGE->assign('returnurl', (isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']):''));
603 // add default state of the 'remember me' checkbox
604 $PAGE->assign('remember_user_default', $conf->get('privacy.remember_user_default'));
605 $PAGE->assign('user_can_login', $loginManager->canLogin($_SERVER));
606 $PAGE->assign('pagetitle', t('Login') .' - '. $conf->get('general.title', 'Shaarli'));
607 $PAGE->renderPage('loginform');
608 exit; 600 exit;
609 } 601 }
610 // -------- User wants to logout. 602 // -------- User wants to logout.
@@ -667,6 +659,7 @@ function renderPage($conf, $pluginManager, $bookmarkService, $history, $sessionM
667 659
668 alphabetical_sort($tags, false, true); 660 alphabetical_sort($tags, false, true);
669 661
662 $logMaxCount = $maxcount > 1 ? log($maxcount, 30) : 1;
670 $tagList = array(); 663 $tagList = array();
671 foreach ($tags as $key => $value) { 664 foreach ($tags as $key => $value) {
672 if (in_array($key, $filteringTags)) { 665 if (in_array($key, $filteringTags)) {
@@ -674,8 +667,8 @@ function renderPage($conf, $pluginManager, $bookmarkService, $history, $sessionM
674 } 667 }
675 // Tag font size scaling: 668 // Tag font size scaling:
676 // default 15 and 30 logarithm bases affect scaling, 669 // default 15 and 30 logarithm bases affect scaling,
677 // 22 and 6 are arbitrary font sizes for max and min sizes. 670 // 2.2 and 0.8 are arbitrary font sizes in em.
678 $size = log($value, 15) / log($maxcount, 30) * 2.2 + 0.8; 671 $size = log($value, 15) / $logMaxCount * 2.2 + 0.8;
679 $tagList[$key] = array( 672 $tagList[$key] = array(
680 'count' => $value, 673 'count' => $value,
681 'size' => number_format($size, 2, '.', ''), 674 'size' => number_format($size, 2, '.', ''),
@@ -931,7 +924,7 @@ function renderPage($conf, $pluginManager, $bookmarkService, $history, $sessionM
931 // Show login screen, then redirect to ?post=... 924 // Show login screen, then redirect to ?post=...
932 if (isset($_GET['post'])) { 925 if (isset($_GET['post'])) {
933 header( // Redirect to login page, then back to post link. 926 header( // Redirect to login page, then back to post link.
934 'Location: ?do=login&post='.urlencode($_GET['post']). 927 'Location: /login?post='.urlencode($_GET['post']).
935 (!empty($_GET['title'])?'&title='.urlencode($_GET['title']):''). 928 (!empty($_GET['title'])?'&title='.urlencode($_GET['title']):'').
936 (!empty($_GET['description'])?'&description='.urlencode($_GET['description']):''). 929 (!empty($_GET['description'])?'&description='.urlencode($_GET['description']):'').
937 (!empty($_GET['tags'])?'&tags='.urlencode($_GET['tags']):''). 930 (!empty($_GET['tags'])?'&tags='.urlencode($_GET['tags']):'').
@@ -942,7 +935,7 @@ function renderPage($conf, $pluginManager, $bookmarkService, $history, $sessionM
942 935
943 showLinkList($PAGE, $bookmarkService, $conf, $pluginManager, $loginManager); 936 showLinkList($PAGE, $bookmarkService, $conf, $pluginManager, $loginManager);
944 if (isset($_GET['edit_link'])) { 937 if (isset($_GET['edit_link'])) {
945 header('Location: ?do=login&edit_link='. escape($_GET['edit_link'])); 938 header('Location: /login?edit_link='. escape($_GET['edit_link']));
946 exit; 939 exit;
947 } 940 }
948 941
@@ -1898,7 +1891,7 @@ function install($conf, $sessionManager, $loginManager)
1898 echo '<script>alert(' 1891 echo '<script>alert('
1899 .'"Shaarli is now configured. ' 1892 .'"Shaarli is now configured. '
1900 .'Please enter your login/password and start shaaring your bookmarks!"' 1893 .'Please enter your login/password and start shaaring your bookmarks!"'
1901 .');document.location=\'?do=login\';</script>'; 1894 .');document.location=\'./login\';</script>';
1902 exit; 1895 exit;
1903 } 1896 }
1904 1897
@@ -1928,20 +1921,18 @@ if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=
1928 exit; 1921 exit;
1929} 1922}
1930 1923
1931$container = new \Slim\Container(); 1924$containerBuilder = new ContainerBuilder($conf, $sessionManager, $loginManager);
1932$container['conf'] = $conf; 1925$container = $containerBuilder->build();
1933$container['plugins'] = $pluginManager; 1926$app = new App($container);
1934$container['history'] = $history;
1935$app = new \Slim\App($container);
1936 1927
1937// REST API routes 1928// REST API routes
1938$app->group('/api/v1', function () { 1929$app->group('/api/v1', function () {
1939 $this->get('/info', '\Shaarli\Api\Controllers\Info:getInfo')->setName('getInfo'); 1930 $this->get('/info', '\Shaarli\Api\Controllers\Info:getInfo')->setName('getInfo');
1940 $this->get('/bookmarks', '\Shaarli\Api\Controllers\Links:getLinks')->setName('getLinks'); 1931 $this->get('/links', '\Shaarli\Api\Controllers\Links:getLinks')->setName('getLinks');
1941 $this->get('/bookmarks/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:getLink')->setName('getLink'); 1932 $this->get('/links/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:getLink')->setName('getLink');
1942 $this->post('/bookmarks', '\Shaarli\Api\Controllers\Links:postLink')->setName('postLink'); 1933 $this->post('/links', '\Shaarli\Api\Controllers\Links:postLink')->setName('postLink');
1943 $this->put('/bookmarks/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:putLink')->setName('putLink'); 1934 $this->put('/links/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:putLink')->setName('putLink');
1944 $this->delete('/bookmarks/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:deleteLink')->setName('deleteLink'); 1935 $this->delete('/links/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:deleteLink')->setName('deleteLink');
1945 1936
1946 $this->get('/tags', '\Shaarli\Api\Controllers\Tags:getTags')->setName('getTags'); 1937 $this->get('/tags', '\Shaarli\Api\Controllers\Tags:getTags')->setName('getTags');
1947 $this->get('/tags/{tagName:[\w]+}', '\Shaarli\Api\Controllers\Tags:getTag')->setName('getTag'); 1938 $this->get('/tags/{tagName:[\w]+}', '\Shaarli\Api\Controllers\Tags:getTag')->setName('getTag');
@@ -1951,6 +1942,10 @@ $app->group('/api/v1', function () {
1951 $this->get('/history', '\Shaarli\Api\Controllers\HistoryController:getHistory')->setName('getHistory'); 1942 $this->get('/history', '\Shaarli\Api\Controllers\HistoryController:getHistory')->setName('getHistory');
1952})->add('\Shaarli\Api\ApiMiddleware'); 1943})->add('\Shaarli\Api\ApiMiddleware');
1953 1944
1945$app->group('', function () {
1946 $this->get('/login', '\Shaarli\Front\Controller\LoginController:index')->setName('login');
1947})->add('\Shaarli\Front\ShaarliMiddleware');
1948
1954$response = $app->run(true); 1949$response = $app->run(true);
1955 1950
1956// Hack to make Slim and Shaarli router work together: 1951// Hack to make Slim and Shaarli router work together:
diff --git a/tests/UtilsTest.php b/tests/UtilsTest.php
index 8225d95a..26d2a6b8 100644
--- a/tests/UtilsTest.php
+++ b/tests/UtilsTest.php
@@ -203,7 +203,7 @@ class UtilsTest extends PHPUnit\Framework\TestCase
203 public function testGenerateLocationLoop() 203 public function testGenerateLocationLoop()
204 { 204 {
205 $ref = 'http://localhost/?test'; 205 $ref = 'http://localhost/?test';
206 $this->assertEquals('?', generateLocation($ref, 'localhost', array('test'))); 206 $this->assertEquals('./?', generateLocation($ref, 'localhost', array('test')));
207 } 207 }
208 208
209 /** 209 /**
@@ -212,7 +212,7 @@ class UtilsTest extends PHPUnit\Framework\TestCase
212 public function testGenerateLocationOut() 212 public function testGenerateLocationOut()
213 { 213 {
214 $ref = 'http://somewebsite.com/?test'; 214 $ref = 'http://somewebsite.com/?test';
215 $this->assertEquals('?', generateLocation($ref, 'localhost')); 215 $this->assertEquals('./?', generateLocation($ref, 'localhost'));
216 } 216 }
217 217
218 218
diff --git a/tests/container/ContainerBuilderTest.php b/tests/container/ContainerBuilderTest.php
new file mode 100644
index 00000000..9b97ed6d
--- /dev/null
+++ b/tests/container/ContainerBuilderTest.php
@@ -0,0 +1,49 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Container;
6
7use PHPUnit\Framework\TestCase;
8use Shaarli\Bookmark\BookmarkServiceInterface;
9use Shaarli\Config\ConfigManager;
10use Shaarli\History;
11use Shaarli\Render\PageBuilder;
12use Shaarli\Security\LoginManager;
13use Shaarli\Security\SessionManager;
14
15class ContainerBuilderTest extends TestCase
16{
17 /** @var ConfigManager */
18 protected $conf;
19
20 /** @var SessionManager */
21 protected $sessionManager;
22
23 /** @var LoginManager */
24 protected $loginManager;
25
26 /** @var ContainerBuilder */
27 protected $containerBuilder;
28
29 public function setUp(): void
30 {
31 $this->conf = new ConfigManager('tests/utils/config/configJson');
32 $this->sessionManager = $this->createMock(SessionManager::class);
33 $this->loginManager = $this->createMock(LoginManager::class);
34
35 $this->containerBuilder = new ContainerBuilder($this->conf, $this->sessionManager, $this->loginManager);
36 }
37
38 public function testBuildContainer(): void
39 {
40 $container = $this->containerBuilder->build();
41
42 static::assertInstanceOf(ConfigManager::class, $container->conf);
43 static::assertInstanceOf(SessionManager::class, $container->sessionManager);
44 static::assertInstanceOf(LoginManager::class, $container->loginManager);
45 static::assertInstanceOf(History::class, $container->history);
46 static::assertInstanceOf(BookmarkServiceInterface::class, $container->bookmarkService);
47 static::assertInstanceOf(PageBuilder::class, $container->pageBuilder);
48 }
49}
diff --git a/tests/front/ShaarliMiddlewareTest.php b/tests/front/ShaarliMiddlewareTest.php
new file mode 100644
index 00000000..80974f37
--- /dev/null
+++ b/tests/front/ShaarliMiddlewareTest.php
@@ -0,0 +1,70 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front;
6
7use PHPUnit\Framework\TestCase;
8use Shaarli\Config\ConfigManager;
9use Shaarli\Container\ShaarliContainer;
10use Shaarli\Front\Exception\LoginBannedException;
11use Shaarli\Render\PageBuilder;
12use Slim\Http\Request;
13use Slim\Http\Response;
14
15class ShaarliMiddlewareTest extends TestCase
16{
17 /** @var ShaarliContainer */
18 protected $container;
19
20 /** @var ShaarliMiddleware */
21 protected $middleware;
22
23 public function setUp(): void
24 {
25 $this->container = $this->createMock(ShaarliContainer::class);
26 $this->middleware = new ShaarliMiddleware($this->container);
27 }
28
29 public function testMiddlewareExecution(): void
30 {
31 $request = $this->createMock(Request::class);
32 $response = new Response();
33 $controller = function (Request $request, Response $response): Response {
34 return $response->withStatus(418); // I'm a tea pot
35 };
36
37 /** @var Response $result */
38 $result = $this->middleware->__invoke($request, $response, $controller);
39
40 static::assertInstanceOf(Response::class, $result);
41 static::assertSame(418, $result->getStatusCode());
42 }
43
44 public function testMiddlewareExecutionWithException(): void
45 {
46 $request = $this->createMock(Request::class);
47 $response = new Response();
48 $controller = function (): void {
49 $exception = new LoginBannedException();
50
51 throw new $exception;
52 };
53
54 $pageBuilder = $this->createMock(PageBuilder::class);
55 $pageBuilder->method('render')->willReturnCallback(function (string $message): string {
56 return $message;
57 });
58 $this->container->pageBuilder = $pageBuilder;
59
60 $conf = $this->createMock(ConfigManager::class);
61 $this->container->conf = $conf;
62
63 /** @var Response $result */
64 $result = $this->middleware->__invoke($request, $response, $controller);
65
66 static::assertInstanceOf(Response::class, $result);
67 static::assertSame(401, $result->getStatusCode());
68 static::assertContains('error', (string) $result->getBody());
69 }
70}
diff --git a/tests/front/controller/LoginControllerTest.php b/tests/front/controller/LoginControllerTest.php
new file mode 100644
index 00000000..8cf8ece7
--- /dev/null
+++ b/tests/front/controller/LoginControllerTest.php
@@ -0,0 +1,178 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller;
6
7use PHPUnit\Framework\TestCase;
8use Shaarli\Bookmark\BookmarkServiceInterface;
9use Shaarli\Config\ConfigManager;
10use Shaarli\Container\ShaarliContainer;
11use Shaarli\Front\Exception\LoginBannedException;
12use Shaarli\Plugin\PluginManager;
13use Shaarli\Render\PageBuilder;
14use Shaarli\Security\LoginManager;
15use Slim\Http\Request;
16use Slim\Http\Response;
17
18class LoginControllerTest extends TestCase
19{
20 /** @var ShaarliContainer */
21 protected $container;
22
23 /** @var LoginController */
24 protected $controller;
25
26 public function setUp(): void
27 {
28 $this->container = $this->createMock(ShaarliContainer::class);
29 $this->controller = new LoginController($this->container);
30 }
31
32 public function testValidControllerInvoke(): void
33 {
34 $this->createValidContainerMockSet();
35
36 $request = $this->createMock(Request::class);
37 $request->expects(static::once())->method('getServerParam')->willReturn('> referer');
38 $response = new Response();
39
40 $assignedVariables = [];
41 $this->container->pageBuilder
42 ->method('assign')
43 ->willReturnCallback(function ($key, $value) use (&$assignedVariables) {
44 $assignedVariables[$key] = $value;
45
46 return $this;
47 })
48 ;
49
50 $result = $this->controller->index($request, $response);
51
52 static::assertInstanceOf(Response::class, $result);
53 static::assertSame(200, $result->getStatusCode());
54 static::assertSame('loginform', (string) $result->getBody());
55
56 static::assertSame('&gt; referer', $assignedVariables['returnurl']);
57 static::assertSame(true, $assignedVariables['remember_user_default']);
58 static::assertSame('Login - Shaarli', $assignedVariables['pagetitle']);
59 }
60
61 public function testValidControllerInvokeWithUserName(): void
62 {
63 $this->createValidContainerMockSet();
64
65 $request = $this->createMock(Request::class);
66 $request->expects(static::once())->method('getServerParam')->willReturn('> referer');
67 $request->expects(static::exactly(2))->method('getParam')->willReturn('myUser>');
68 $response = new Response();
69
70 $assignedVariables = [];
71 $this->container->pageBuilder
72 ->method('assign')
73 ->willReturnCallback(function ($key, $value) use (&$assignedVariables) {
74 $assignedVariables[$key] = $value;
75
76 return $this;
77 })
78 ;
79
80 $result = $this->controller->index($request, $response);
81
82 static::assertInstanceOf(Response::class, $result);
83 static::assertSame(200, $result->getStatusCode());
84 static::assertSame('loginform', (string) $result->getBody());
85
86 static::assertSame('myUser&gt;', $assignedVariables['username']);
87 static::assertSame('&gt; referer', $assignedVariables['returnurl']);
88 static::assertSame(true, $assignedVariables['remember_user_default']);
89 static::assertSame('Login - Shaarli', $assignedVariables['pagetitle']);
90 }
91
92 public function testLoginControllerWhileLoggedIn(): void
93 {
94 $request = $this->createMock(Request::class);
95 $response = new Response();
96
97 $loginManager = $this->createMock(LoginManager::class);
98 $loginManager->expects(static::once())->method('isLoggedIn')->willReturn(true);
99 $this->container->loginManager = $loginManager;
100
101 $result = $this->controller->index($request, $response);
102
103 static::assertInstanceOf(Response::class, $result);
104 static::assertSame(302, $result->getStatusCode());
105 static::assertSame(['./'], $result->getHeader('Location'));
106 }
107
108 public function testLoginControllerOpenShaarli(): void
109 {
110 $this->createValidContainerMockSet();
111
112 $request = $this->createMock(Request::class);
113 $response = new Response();
114
115 $conf = $this->createMock(ConfigManager::class);
116 $conf->method('get')->willReturnCallback(function (string $parameter, $default) {
117 if ($parameter === 'security.open_shaarli') {
118 return true;
119 }
120 return $default;
121 });
122 $this->container->conf = $conf;
123
124 $result = $this->controller->index($request, $response);
125
126 static::assertInstanceOf(Response::class, $result);
127 static::assertSame(302, $result->getStatusCode());
128 static::assertSame(['./'], $result->getHeader('Location'));
129 }
130
131 public function testLoginControllerWhileBanned(): void
132 {
133 $this->createValidContainerMockSet();
134
135 $request = $this->createMock(Request::class);
136 $response = new Response();
137
138 $loginManager = $this->createMock(LoginManager::class);
139 $loginManager->method('isLoggedIn')->willReturn(false);
140 $loginManager->method('canLogin')->willReturn(false);
141 $this->container->loginManager = $loginManager;
142
143 $this->expectException(LoginBannedException::class);
144
145 $this->controller->index($request, $response);
146 }
147
148 protected function createValidContainerMockSet(): void
149 {
150 // User logged out
151 $loginManager = $this->createMock(LoginManager::class);
152 $loginManager->method('isLoggedIn')->willReturn(false);
153 $loginManager->method('canLogin')->willReturn(true);
154 $this->container->loginManager = $loginManager;
155
156 // Config
157 $conf = $this->createMock(ConfigManager::class);
158 $conf->method('get')->willReturnCallback(function (string $parameter, $default) {
159 return $default;
160 });
161 $this->container->conf = $conf;
162
163 // PageBuilder
164 $pageBuilder = $this->createMock(PageBuilder::class);
165 $pageBuilder
166 ->method('render')
167 ->willReturnCallback(function (string $template): string {
168 return $template;
169 })
170 ;
171 $this->container->pageBuilder = $pageBuilder;
172
173 $pluginManager = $this->createMock(PluginManager::class);
174 $this->container->pluginManager = $pluginManager;
175 $bookmarkService = $this->createMock(BookmarkServiceInterface::class);
176 $this->container->bookmarkService = $bookmarkService;
177 }
178}
diff --git a/tests/front/controller/ShaarliControllerTest.php b/tests/front/controller/ShaarliControllerTest.php
new file mode 100644
index 00000000..6fa3feb9
--- /dev/null
+++ b/tests/front/controller/ShaarliControllerTest.php
@@ -0,0 +1,116 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller;
6
7use PHPUnit\Framework\TestCase;
8use Shaarli\Bookmark\BookmarkFilter;
9use Shaarli\Bookmark\BookmarkServiceInterface;
10use Shaarli\Container\ShaarliContainer;
11use Shaarli\Plugin\PluginManager;
12use Shaarli\Render\PageBuilder;
13use Shaarli\Security\LoginManager;
14
15/**
16 * Class ShaarliControllerTest
17 *
18 * This class is used to test default behavior of ShaarliController abstract class.
19 * It uses a dummy non abstract controller.
20 */
21class ShaarliControllerTest extends TestCase
22{
23 /** @var ShaarliContainer */
24 protected $container;
25
26 /** @var LoginController */
27 protected $controller;
28
29 /** @var mixed[] List of variable assigned to the template */
30 protected $assignedValues;
31
32 public function setUp(): void
33 {
34 $this->container = $this->createMock(ShaarliContainer::class);
35 $this->controller = new class($this->container) extends ShaarliController
36 {
37 public function assignView(string $key, $value): ShaarliController
38 {
39 return parent::assignView($key, $value);
40 }
41
42 public function render(string $template): string
43 {
44 return parent::render($template);
45 }
46 };
47 $this->assignedValues = [];
48 }
49
50 public function testAssignView(): void
51 {
52 $this->createValidContainerMockSet();
53
54 $self = $this->controller->assignView('variableName', 'variableValue');
55
56 static::assertInstanceOf(ShaarliController::class, $self);
57 static::assertSame('variableValue', $this->assignedValues['variableName']);
58 }
59
60 public function testRender(): void
61 {
62 $this->createValidContainerMockSet();
63
64 $render = $this->controller->render('templateName');
65
66 static::assertSame('templateName', $render);
67
68 static::assertSame(10, $this->assignedValues['linkcount']);
69 static::assertSame(5, $this->assignedValues['privateLinkcount']);
70 static::assertSame(['error'], $this->assignedValues['plugin_errors']);
71
72 static::assertSame('templateName', $this->assignedValues['plugins_includes']['render_includes']['target']);
73 static::assertTrue($this->assignedValues['plugins_includes']['render_includes']['loggedin']);
74 static::assertSame('templateName', $this->assignedValues['plugins_header']['render_header']['target']);
75 static::assertTrue($this->assignedValues['plugins_header']['render_header']['loggedin']);
76 static::assertSame('templateName', $this->assignedValues['plugins_footer']['render_footer']['target']);
77 static::assertTrue($this->assignedValues['plugins_footer']['render_footer']['loggedin']);
78 }
79
80 protected function createValidContainerMockSet(): void
81 {
82 $pageBuilder = $this->createMock(PageBuilder::class);
83 $pageBuilder
84 ->method('assign')
85 ->willReturnCallback(function (string $key, $value): void {
86 $this->assignedValues[$key] = $value;
87 });
88 $pageBuilder
89 ->method('render')
90 ->willReturnCallback(function (string $template): string {
91 return $template;
92 });
93 $this->container->pageBuilder = $pageBuilder;
94
95 $bookmarkService = $this->createMock(BookmarkServiceInterface::class);
96 $bookmarkService
97 ->method('count')
98 ->willReturnCallback(function (string $visibility): int {
99 return $visibility === BookmarkFilter::$PRIVATE ? 5 : 10;
100 });
101 $this->container->bookmarkService = $bookmarkService;
102
103 $pluginManager = $this->createMock(PluginManager::class);
104 $pluginManager
105 ->method('executeHooks')
106 ->willReturnCallback(function (string $hook, array &$data, array $params): array {
107 return $data[$hook] = $params;
108 });
109 $pluginManager->method('getErrors')->willReturn(['error']);
110 $this->container->pluginManager = $pluginManager;
111
112 $loginManager = $this->createMock(LoginManager::class);
113 $loginManager->method('isLoggedIn')->willReturn(true);
114 $this->container->loginManager = $loginManager;
115 }
116}
diff --git a/tpl/default/404.html b/tpl/default/404.html
index 472566a6..09737b4b 100644
--- a/tpl/default/404.html
+++ b/tpl/default/404.html
@@ -6,7 +6,7 @@
6<body> 6<body>
7<div id="pageheader"> 7<div id="pageheader">
8 {include="page.header"} 8 {include="page.header"}
9<div class="center" id="page404" class="page404-container"> 9<div id="pageError" class="page-error-container center">
10 <h2>{'Sorry, nothing to see here.'|t}</h2> 10 <h2>{'Sorry, nothing to see here.'|t}</h2>
11 <img src="img/sad_star.png" alt=""> 11 <img src="img/sad_star.png" alt="">
12 <p>{$error_message}</p> 12 <p>{$error_message}</p>
diff --git a/tpl/default/error.html b/tpl/default/error.html
new file mode 100644
index 00000000..ef1dfd73
--- /dev/null
+++ b/tpl/default/error.html
@@ -0,0 +1,22 @@
1<!DOCTYPE html>
2<html{if="$language !== 'auto'"} lang="{$language}"{/if}>
3<head>
4 {include="includes"}
5</head>
6<body>
7<div id="pageheader">
8 {include="page.header"}
9<div id="pageError" class="page-error-container center">
10 <h2>{$message}</h2>
11
12 {if="!empty($stacktrace)"}
13 <pre>
14 {$stacktrace}
15 </pre>
16 {/if}
17
18 <img src="img/sad_star.png" alt="">
19</div>
20{include="page.footer"}
21</body>
22</html>
diff --git a/tpl/default/loginform.html b/tpl/default/loginform.html
index 761aec0c..90c2b2b6 100644
--- a/tpl/default/loginform.html
+++ b/tpl/default/loginform.html
@@ -5,44 +5,32 @@
5</head> 5</head>
6<body> 6<body>
7{include="page.header"} 7{include="page.header"}
8{if="!$user_can_login"} 8<div class="pure-g">
9<div class="pure-g pure-alert pure-alert-error pure-alert-closable center"> 9 <div class="pure-u-lg-1-3 pure-u-1-24"></div>
10 <div class="pure-u-2-24"></div> 10 <div id="login-form" class="page-form page-form-light pure-u-lg-1-3 pure-u-22-24 login-form-container">
11 <div class="pure-u-20-24"> 11 <form method="post" name="loginform">
12 <p>{'You have been banned after too many failed login attempts. Try again later.'|t}</p> 12 <h2 class="window-title">{'Login'|t}</h2>
13 </div> 13 <div>
14 <div class="pure-u-2-24"> 14 <input type="text" name="login" aria-label="{'Username'|t}" placeholder="{'Username'|t}"
15 <i class="fa fa-times pure-alert-close"></i> 15 {if="!empty($username)"}value="{$username}"{/if} class="autofocus">
16 </div>
17 <div>
18 <input type="password" name="password" aria-label="{'Password'|t}" placeholder="{'Password'|t}" class="autofocus">
19 </div>
20 <div class="remember-me">
21 <input type="checkbox" name="longlastingsession" id="longlastingsessionform"
22 {if="$remember_user_default"}checked="checked"{/if}>
23 <label for="longlastingsessionform">{'Remember me'|t}</label>
24 </div>
25 <div>
26 <input type="submit" value="{'Login'|t}" class="bigbutton">
27 </div>
28 <input type="hidden" name="token" value="{$token}">
29 {if="$returnurl"}<input type="hidden" name="returnurl" value="{$returnurl}">{/if}
30 </form>
16 </div> 31 </div>
32 <div class="pure-u-lg-1-3 pure-u-1-8"></div>
17</div> 33</div>
18{else}
19 <div class="pure-g">
20 <div class="pure-u-lg-1-3 pure-u-1-24"></div>
21 <div id="login-form" class="page-form page-form-light pure-u-lg-1-3 pure-u-22-24 login-form-container">
22 <form method="post" name="loginform">
23 <h2 class="window-title">{'Login'|t}</h2>
24 <div>
25 <input type="text" name="login" aria-label="{'Username'|t}" placeholder="{'Username'|t}"
26 {if="!empty($username)"}value="{$username}"{/if} class="autofocus">
27 </div>
28 <div>
29 <input type="password" name="password" aria-label="{'Password'|t}" placeholder="{'Password'|t}" class="autofocus">
30 </div>
31 <div class="remember-me">
32 <input type="checkbox" name="longlastingsession" id="longlastingsessionform"
33 {if="$remember_user_default"}checked="checked"{/if}>
34 <label for="longlastingsessionform">{'Remember me'|t}</label>
35 </div>
36 <div>
37 <input type="submit" value="{'Login'|t}" class="bigbutton">
38 </div>
39 <input type="hidden" name="token" value="{$token}">
40 {if="$returnurl"}<input type="hidden" name="returnurl" value="{$returnurl}">{/if}
41 </form>
42 </div>
43 <div class="pure-u-lg-1-3 pure-u-1-8"></div>
44 </div>
45{/if}
46 34
47{include="page.footer"} 35{include="page.footer"}
48</body> 36</body>
diff --git a/tpl/default/page.header.html b/tpl/default/page.header.html
index 4f063dc3..82f8ebf1 100644
--- a/tpl/default/page.header.html
+++ b/tpl/default/page.header.html
@@ -60,7 +60,7 @@
60 </li> 60 </li>
61 {else} 61 {else}
62 <li class="pure-menu-item pure-u-lg-0 shaarli-menu-mobile" id="shaarli-menu-mobile-login"> 62 <li class="pure-menu-item pure-u-lg-0 shaarli-menu-mobile" id="shaarli-menu-mobile-login">
63 <a href="?do=login" class="pure-menu-link">{'Login'|t}</a> 63 <a href="/login" class="pure-menu-link">{'Login'|t}</a>
64 </li> 64 </li>
65 {/if} 65 {/if}
66 </ul> 66 </ul>
@@ -80,7 +80,7 @@
80 </li> 80 </li>
81 {if="!$is_logged_in"} 81 {if="!$is_logged_in"}
82 <li class="pure-menu-item" id="shaarli-menu-desktop-login"> 82 <li class="pure-menu-item" id="shaarli-menu-desktop-login">
83 <a href="?do=login" class="pure-menu-link" 83 <a href="/login" class="pure-menu-link"
84 data-open-id="header-login-form" 84 data-open-id="header-login-form"
85 id="login-button" aria-label="{'Login'|t}" title="{'Login'|t}"> 85 id="login-button" aria-label="{'Login'|t}" title="{'Login'|t}">
86 <i class="fa fa-user" aria-hidden="true"></i> 86 <i class="fa fa-user" aria-hidden="true"></i>
diff --git a/tpl/default/tag.cloud.html b/tpl/default/tag.cloud.html
index b9c0b162..7839fcca 100644
--- a/tpl/default/tag.cloud.html
+++ b/tpl/default/tag.cloud.html
@@ -32,6 +32,7 @@
32 {/if} 32 {/if}
33 autocomplete="off" data-multiple data-autofirst data-minChars="1" 33 autocomplete="off" data-multiple data-autofirst data-minChars="1"
34 data-list="{loop="$tags"}{$key}, {/loop}" 34 data-list="{loop="$tags"}{$key}, {/loop}"
35 class="autofocus"
35 > 36 >
36 <button type="submit" class="search-button" aria-label="{'Search'|t}"><i class="fa fa-search" aria-hidden="true"></i></button> 37 <button type="submit" class="search-button" aria-label="{'Search'|t}"><i class="fa fa-search" aria-hidden="true"></i></button>
37 </form> 38 </form>
diff --git a/tpl/default/thumbnails.html b/tpl/default/thumbnails.html
index f1939798..5f9bef08 100644
--- a/tpl/default/thumbnails.html
+++ b/tpl/default/thumbnails.html
@@ -38,7 +38,7 @@
38 </div> 38 </div>
39 </div> 39 </div>
40 40
41 <input type="hidden" name="ids" value="{function="implode($ids, ',')"}" /> 41 <input type="hidden" name="ids" value="{function="implode(',', $ids)"}" />
42 </div> 42 </div>
43</div> 43</div>
44 44
diff --git a/tpl/vintage/error.html b/tpl/vintage/error.html
new file mode 100644
index 00000000..b6e62be0
--- /dev/null
+++ b/tpl/vintage/error.html
@@ -0,0 +1,25 @@
1<!DOCTYPE html>
2<html>
3<head>
4 {include="includes"}
5</head>
6<body>
7<div id="pageheader">
8 {include="page.header"}
9</div>
10<div class="error-container">
11 <h1>Error</h1>
12 <p>{$message}</p>
13
14 {if="!empty($stacktrace)"}
15 <br>
16 <pre>
17 {$stacktrace}
18 </pre>
19 {/if}
20
21 <p>Would you mind <a href="?">clicking here</a>?</p>
22</div>
23{include="page.footer"}
24</body>
25</html>
diff --git a/tpl/vintage/loginform.html b/tpl/vintage/loginform.html
index 0f7d6387..a3792066 100644
--- a/tpl/vintage/loginform.html
+++ b/tpl/vintage/loginform.html
@@ -2,36 +2,30 @@
2<html> 2<html>
3<head>{include="includes"}</head> 3<head>{include="includes"}</head>
4<body 4<body
5{if="$user_can_login"} 5{if="empty($username)"}
6 {if="empty($username)"} 6 onload="document.loginform.login.focus();"
7 onload="document.loginform.login.focus();" 7{else}
8 {else} 8 onload="document.loginform.password.focus();"
9 onload="document.loginform.password.focus();"
10 {/if}
11{/if}> 9{/if}>
12<div id="pageheader"> 10<div id="pageheader">
13 {include="page.header"} 11 {include="page.header"}
14 12
15 <div id="headerform"> 13 <div id="headerform">
16 {if="!$user_can_login"} 14 <form method="post" name="loginform">
17 You have been banned from login after too many failed attempts. Try later. 15 <label for="login">Login: <input type="text" id="login" name="login" tabindex="1"
18 {else} 16 {if="!empty($username)"}value="{$username}"{/if}>
19 <form method="post" name="loginform"> 17 </label>
20 <label for="login">Login: <input type="text" id="login" name="login" tabindex="1" 18 <label for="password">Password: <input type="password" id="password" name="password" tabindex="2">
21 {if="!empty($username)"}value="{$username}"{/if}> 19 </label>
22 </label> 20 <input type="submit" value="Login" class="bigbutton" tabindex="4">
23 <label for="password">Password: <input type="password" id="password" name="password" tabindex="2"> 21 <label for="longlastingsession">
24 </label> 22 <input type="checkbox" name="longlastingsession"
25 <input type="submit" value="Login" class="bigbutton" tabindex="4"> 23 id="longlastingsession" tabindex="3"
26 <label for="longlastingsession"> 24 {if="$remember_user_default"}checked="checked"{/if}>
27 <input type="checkbox" name="longlastingsession" 25 Stay signed in (Do not check on public computers)</label>
28 id="longlastingsession" tabindex="3" 26 <input type="hidden" name="token" value="{$token}">
29 {if="$remember_user_default"}checked="checked"{/if}> 27 {if="$returnurl"}<input type="hidden" name="returnurl" value="{$returnurl}">{/if}
30 Stay signed in (Do not check on public computers)</label> 28 </form>
31 <input type="hidden" name="token" value="{$token}">
32 {if="$returnurl"}<input type="hidden" name="returnurl" value="{$returnurl}">{/if}
33 </form>
34 {/if}
35 </div> 29 </div>
36</div> 30</div>
37 31
diff --git a/tpl/vintage/page.header.html b/tpl/vintage/page.header.html
index 40c53e5b..a37926d2 100644
--- a/tpl/vintage/page.header.html
+++ b/tpl/vintage/page.header.html
@@ -25,7 +25,7 @@
25 <li><a href="?do=tools">Tools</a></li> 25 <li><a href="?do=tools">Tools</a></li>
26 <li><a href="?do=addlink">Add link</a></li> 26 <li><a href="?do=addlink">Add link</a></li>
27 {else} 27 {else}
28 <li><a href="?do=login">Login</a></li> 28 <li><a href="/login">Login</a></li>
29 {/if} 29 {/if}
30 <li><a href="{$feedurl}?do=rss{$searchcrits}" class="nomobile">RSS Feed</a></li> 30 <li><a href="{$feedurl}?do=rss{$searchcrits}" class="nomobile">RSS Feed</a></li>
31 {if="$showatom"} 31 {if="$showatom"}
diff --git a/tpl/vintage/thumbnails.html b/tpl/vintage/thumbnails.html
index 79aebf8d..5cad845b 100644
--- a/tpl/vintage/thumbnails.html
+++ b/tpl/vintage/thumbnails.html
@@ -20,7 +20,7 @@
20 </div> 20 </div>
21</div> 21</div>
22 22
23<input type="hidden" name="ids" value="{function="implode($ids, ',')"}" /> 23<input type="hidden" name="ids" value="{function="implode(',', $ids)"}" />
24 24
25{include="page.footer"} 25{include="page.footer"}
26<script src="js/thumbnails_update.min.js?v={$version_hash}"></script> 26<script src="js/thumbnails_update.min.js?v={$version_hash}"></script>