diff options
Diffstat (limited to 'application')
-rw-r--r-- | application/ApplicationUtils.php | 19 | ||||
-rw-r--r-- | application/Cache.php | 2 | ||||
-rw-r--r-- | application/FeedBuilder.php | 6 | ||||
-rw-r--r-- | application/History.php | 4 | ||||
-rw-r--r-- | application/HttpUtils.php | 9 | ||||
-rw-r--r-- | application/Languages.php | 167 | ||||
-rw-r--r-- | application/LinkDB.php | 37 | ||||
-rw-r--r-- | application/LinkFilter.php | 8 | ||||
-rw-r--r-- | application/LinkUtils.php | 15 | ||||
-rw-r--r-- | application/NetscapeBookmarkUtils.php | 15 | ||||
-rw-r--r-- | application/PageBuilder.php | 13 | ||||
-rw-r--r-- | application/PluginManager.php | 7 | ||||
-rw-r--r-- | application/SessionManager.php | 83 | ||||
-rw-r--r-- | application/Updater.php | 17 | ||||
-rw-r--r-- | application/Utils.php | 47 | ||||
-rw-r--r-- | application/config/ConfigJson.php | 15 | ||||
-rw-r--r-- | application/config/ConfigManager.php | 6 | ||||
-rw-r--r-- | application/config/ConfigPhp.php | 4 | ||||
-rw-r--r-- | application/config/exception/MissingFieldConfigException.php | 2 | ||||
-rw-r--r-- | application/config/exception/PluginConfigOrderException.php | 2 | ||||
-rw-r--r-- | application/config/exception/UnauthorizedConfigException.php | 2 | ||||
-rw-r--r-- | application/exceptions/IOException.php | 2 |
22 files changed, 367 insertions, 115 deletions
diff --git a/application/ApplicationUtils.php b/application/ApplicationUtils.php index 5643f4a0..911873a0 100644 --- a/application/ApplicationUtils.php +++ b/application/ApplicationUtils.php | |||
@@ -149,12 +149,13 @@ class ApplicationUtils | |||
149 | public static function checkPHPVersion($minVersion, $curVersion) | 149 | public static function checkPHPVersion($minVersion, $curVersion) |
150 | { | 150 | { |
151 | if (version_compare($curVersion, $minVersion) < 0) { | 151 | if (version_compare($curVersion, $minVersion) < 0) { |
152 | throw new Exception( | 152 | $msg = t( |
153 | 'Your PHP version is obsolete!' | 153 | 'Your PHP version is obsolete!' |
154 | .' Shaarli requires at least PHP '.$minVersion.', and thus cannot run.' | 154 | . ' Shaarli requires at least PHP %s, and thus cannot run.' |
155 | .' Your PHP version has known security vulnerabilities and should be' | 155 | . ' Your PHP version has known security vulnerabilities and should be' |
156 | .' updated as soon as possible.' | 156 | . ' updated as soon as possible.' |
157 | ); | 157 | ); |
158 | throw new Exception(sprintf($msg, $minVersion)); | ||
158 | } | 159 | } |
159 | } | 160 | } |
160 | 161 | ||
@@ -179,7 +180,7 @@ class ApplicationUtils | |||
179 | $rainTplDir.'/'.$conf->get('resource.theme'), | 180 | $rainTplDir.'/'.$conf->get('resource.theme'), |
180 | ) as $path) { | 181 | ) as $path) { |
181 | if (! is_readable(realpath($path))) { | 182 | if (! is_readable(realpath($path))) { |
182 | $errors[] = '"'.$path.'" directory is not readable'; | 183 | $errors[] = '"'.$path.'" '. t('directory is not readable'); |
183 | } | 184 | } |
184 | } | 185 | } |
185 | 186 | ||
@@ -191,10 +192,10 @@ class ApplicationUtils | |||
191 | $conf->get('resource.raintpl_tmp'), | 192 | $conf->get('resource.raintpl_tmp'), |
192 | ) as $path) { | 193 | ) as $path) { |
193 | if (! is_readable(realpath($path))) { | 194 | if (! is_readable(realpath($path))) { |
194 | $errors[] = '"'.$path.'" directory is not readable'; | 195 | $errors[] = '"'.$path.'" '. t('directory is not readable'); |
195 | } | 196 | } |
196 | if (! is_writable(realpath($path))) { | 197 | if (! is_writable(realpath($path))) { |
197 | $errors[] = '"'.$path.'" directory is not writable'; | 198 | $errors[] = '"'.$path.'" '. t('directory is not writable'); |
198 | } | 199 | } |
199 | } | 200 | } |
200 | 201 | ||
@@ -212,10 +213,10 @@ class ApplicationUtils | |||
212 | } | 213 | } |
213 | 214 | ||
214 | if (! is_readable(realpath($path))) { | 215 | if (! is_readable(realpath($path))) { |
215 | $errors[] = '"'.$path.'" file is not readable'; | 216 | $errors[] = '"'.$path.'" '. t('file is not readable'); |
216 | } | 217 | } |
217 | if (! is_writable(realpath($path))) { | 218 | if (! is_writable(realpath($path))) { |
218 | $errors[] = '"'.$path.'" file is not writable'; | 219 | $errors[] = '"'.$path.'" '. t('file is not writable'); |
219 | } | 220 | } |
220 | } | 221 | } |
221 | 222 | ||
diff --git a/application/Cache.php b/application/Cache.php index 5d050165..e5d43e61 100644 --- a/application/Cache.php +++ b/application/Cache.php | |||
@@ -13,7 +13,7 @@ | |||
13 | function purgeCachedPages($pageCacheDir) | 13 | function purgeCachedPages($pageCacheDir) |
14 | { | 14 | { |
15 | if (! is_dir($pageCacheDir)) { | 15 | if (! is_dir($pageCacheDir)) { |
16 | $error = 'Cannot purge '.$pageCacheDir.': no directory'; | 16 | $error = sprintf(t('Cannot purge %s: no directory'), $pageCacheDir); |
17 | error_log($error); | 17 | error_log($error); |
18 | return $error; | 18 | return $error; |
19 | } | 19 | } |
diff --git a/application/FeedBuilder.php b/application/FeedBuilder.php index 7377bcec..ebae18b4 100644 --- a/application/FeedBuilder.php +++ b/application/FeedBuilder.php | |||
@@ -148,11 +148,11 @@ class FeedBuilder | |||
148 | $link['url'] = $pageaddr . $link['url']; | 148 | $link['url'] = $pageaddr . $link['url']; |
149 | } | 149 | } |
150 | if ($this->usePermalinks === true) { | 150 | if ($this->usePermalinks === true) { |
151 | $permalink = '<a href="'. $link['url'] .'" title="Direct link">Direct link</a>'; | 151 | $permalink = '<a href="'. $link['url'] .'" title="'. t('Direct link') .'">'. t('Direct link') .'</a>'; |
152 | } else { | 152 | } else { |
153 | $permalink = '<a href="'. $link['guid'] .'" title="Permalink">Permalink</a>'; | 153 | $permalink = '<a href="'. $link['guid'] .'" title="'. t('Permalink') .'">'. t('Permalink') .'</a>'; |
154 | } | 154 | } |
155 | $link['description'] = format_description($link['description'], '', $pageaddr); | 155 | $link['description'] = format_description($link['description'], '', false, $pageaddr); |
156 | $link['description'] .= PHP_EOL .'<br>— '. $permalink; | 156 | $link['description'] .= PHP_EOL .'<br>— '. $permalink; |
157 | 157 | ||
158 | $pubDate = $link['created']; | 158 | $pubDate = $link['created']; |
diff --git a/application/History.php b/application/History.php index 5e3b1b72..35ec016a 100644 --- a/application/History.php +++ b/application/History.php | |||
@@ -171,7 +171,7 @@ class History | |||
171 | } | 171 | } |
172 | 172 | ||
173 | if (! is_writable($this->historyFilePath)) { | 173 | if (! is_writable($this->historyFilePath)) { |
174 | throw new Exception('History file isn\'t readable or writable'); | 174 | throw new Exception(t('History file isn\'t readable or writable')); |
175 | } | 175 | } |
176 | } | 176 | } |
177 | 177 | ||
@@ -182,7 +182,7 @@ class History | |||
182 | { | 182 | { |
183 | $this->history = FileUtils::readFlatDB($this->historyFilePath, []); | 183 | $this->history = FileUtils::readFlatDB($this->historyFilePath, []); |
184 | if ($this->history === false) { | 184 | if ($this->history === false) { |
185 | throw new Exception('Could not parse history file'); | 185 | throw new Exception(t('Could not parse history file')); |
186 | } | 186 | } |
187 | } | 187 | } |
188 | 188 | ||
diff --git a/application/HttpUtils.php b/application/HttpUtils.php index 00835966..c9371b55 100644 --- a/application/HttpUtils.php +++ b/application/HttpUtils.php | |||
@@ -76,7 +76,7 @@ function get_http_response($url, $timeout = 30, $maxBytes = 4194304) | |||
76 | curl_setopt($ch, CURLOPT_USERAGENT, $userAgent); | 76 | curl_setopt($ch, CURLOPT_USERAGENT, $userAgent); |
77 | 77 | ||
78 | // Max download size management | 78 | // Max download size management |
79 | curl_setopt($ch, CURLOPT_BUFFERSIZE, 1024); | 79 | curl_setopt($ch, CURLOPT_BUFFERSIZE, 1024*16); |
80 | curl_setopt($ch, CURLOPT_NOPROGRESS, false); | 80 | curl_setopt($ch, CURLOPT_NOPROGRESS, false); |
81 | curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, | 81 | curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, |
82 | function($arg0, $arg1, $arg2, $arg3, $arg4 = 0) use ($maxBytes) | 82 | function($arg0, $arg1, $arg2, $arg3, $arg4 = 0) use ($maxBytes) |
@@ -302,6 +302,13 @@ function server_url($server) | |||
302 | $port = $server['HTTP_X_FORWARDED_PORT']; | 302 | $port = $server['HTTP_X_FORWARDED_PORT']; |
303 | } | 303 | } |
304 | 304 | ||
305 | // This is a workaround for proxies that don't forward the scheme properly. | ||
306 | // Connecting over port 443 has to be in HTTPS. | ||
307 | // See https://github.com/shaarli/Shaarli/issues/1022 | ||
308 | if ($port == '443') { | ||
309 | $scheme = 'https'; | ||
310 | } | ||
311 | |||
305 | if (($scheme == 'http' && $port != '80') | 312 | if (($scheme == 'http' && $port != '80') |
306 | || ($scheme == 'https' && $port != '443') | 313 | || ($scheme == 'https' && $port != '443') |
307 | ) { | 314 | ) { |
diff --git a/application/Languages.php b/application/Languages.php index c8b0a25a..357c7524 100644 --- a/application/Languages.php +++ b/application/Languages.php | |||
@@ -1,21 +1,164 @@ | |||
1 | <?php | 1 | <?php |
2 | 2 | ||
3 | namespace Shaarli; | ||
4 | |||
5 | use Gettext\GettextTranslator; | ||
6 | use Gettext\Merge; | ||
7 | use Gettext\Translations; | ||
8 | use Gettext\Translator; | ||
9 | use Gettext\TranslatorInterface; | ||
10 | use Shaarli\Config\ConfigManager; | ||
11 | |||
3 | /** | 12 | /** |
4 | * Wrapper function for translation which match the API | 13 | * Class Languages |
5 | * of gettext()/_() and ngettext(). | 14 | * |
15 | * Load Shaarli translations using 'gettext/gettext'. | ||
16 | * This class allows to either use PHP gettext extension, or a PHP implementation of gettext, | ||
17 | * with a fixed language, or dynamically using autoLocale(). | ||
6 | * | 18 | * |
7 | * Not doing translation for now. | 19 | * Translation files PO/MO files follow gettext standard and must be placed under: |
20 | * <translation path>/<language>/LC_MESSAGES/<domain>.[po|mo] | ||
8 | * | 21 | * |
9 | * @param string $text Text to translate. | 22 | * Pros/cons: |
10 | * @param string $nText The plural message ID. | 23 | * - gettext extension is faster |
11 | * @param int $nb The number of items for plural forms. | 24 | * - gettext is very system dependent (PHP extension, the locale must be installed, and web server reloaded) |
12 | * | 25 | * |
13 | * @return String Text translated. | 26 | * Settings: |
27 | * - translation.mode: | ||
28 | * - auto: use default setting (PHP implementation) | ||
29 | * - php: use PHP implementation | ||
30 | * - gettext: use gettext wrapper | ||
31 | * - translation.language: | ||
32 | * - auto: use autoLocale() and the language change according to user HTTP headers | ||
33 | * - fixed language: e.g. 'fr' | ||
34 | * - translation.extensions: | ||
35 | * - domain => translation_path: allow plugins and themes to extend the defaut extension | ||
36 | * The domain must be unique, and translation path must be relative, and contains the tree mentioned above. | ||
37 | * | ||
38 | * @package Shaarli | ||
14 | */ | 39 | */ |
15 | function t($text, $nText = '', $nb = 0) { | 40 | class Languages |
16 | if (empty($nText)) { | 41 | { |
17 | return $text; | 42 | /** |
43 | * Core translations domain | ||
44 | */ | ||
45 | const DEFAULT_DOMAIN = 'shaarli'; | ||
46 | |||
47 | /** | ||
48 | * @var TranslatorInterface | ||
49 | */ | ||
50 | protected $translator; | ||
51 | |||
52 | /** | ||
53 | * @var string | ||
54 | */ | ||
55 | protected $language; | ||
56 | |||
57 | /** | ||
58 | * @var ConfigManager | ||
59 | */ | ||
60 | protected $conf; | ||
61 | |||
62 | /** | ||
63 | * Languages constructor. | ||
64 | * | ||
65 | * @param string $language lang determined by autoLocale(), can be overridden. | ||
66 | * @param ConfigManager $conf instance. | ||
67 | */ | ||
68 | public function __construct($language, $conf) | ||
69 | { | ||
70 | $this->conf = $conf; | ||
71 | $confLanguage = $this->conf->get('translation.language', 'auto'); | ||
72 | if ($confLanguage === 'auto' || ! $this->isValidLanguage($confLanguage)) { | ||
73 | $this->language = substr($language, 0, 5); | ||
74 | } else { | ||
75 | $this->language = $confLanguage; | ||
76 | } | ||
77 | |||
78 | if (! extension_loaded('gettext') | ||
79 | || in_array($this->conf->get('translation.mode', 'auto'), ['auto', 'php']) | ||
80 | ) { | ||
81 | $this->initPhpTranslator(); | ||
82 | } else { | ||
83 | $this->initGettextTranslator(); | ||
84 | } | ||
85 | |||
86 | // Register default functions (e.g. '__()') to use our Translator | ||
87 | $this->translator->register(); | ||
88 | } | ||
89 | |||
90 | /** | ||
91 | * Initialize the translator using php gettext extension (gettext dependency act as a wrapper). | ||
92 | */ | ||
93 | protected function initGettextTranslator () | ||
94 | { | ||
95 | $this->translator = new GettextTranslator(); | ||
96 | $this->translator->setLanguage($this->language); | ||
97 | $this->translator->loadDomain(self::DEFAULT_DOMAIN, 'inc/languages'); | ||
98 | |||
99 | foreach ($this->conf->get('translation.extensions', []) as $domain => $translationPath) { | ||
100 | if ($domain !== self::DEFAULT_DOMAIN) { | ||
101 | $this->translator->loadDomain($domain, $translationPath, false); | ||
102 | } | ||
103 | } | ||
104 | } | ||
105 | |||
106 | /** | ||
107 | * Initialize the translator using a PHP implementation of gettext. | ||
108 | * | ||
109 | * Note that if language po file doesn't exist, errors are ignored (e.g. not installed language). | ||
110 | */ | ||
111 | protected function initPhpTranslator() | ||
112 | { | ||
113 | $this->translator = new Translator(); | ||
114 | $translations = new Translations(); | ||
115 | // Core translations | ||
116 | try { | ||
117 | /** @var Translations $translations */ | ||
118 | $translations = $translations->addFromPoFile('inc/languages/'. $this->language .'/LC_MESSAGES/shaarli.po'); | ||
119 | $translations->setDomain('shaarli'); | ||
120 | $this->translator->loadTranslations($translations); | ||
121 | } catch (\InvalidArgumentException $e) {} | ||
122 | |||
123 | |||
124 | // Extension translations (plugins, themes, etc.). | ||
125 | foreach ($this->conf->get('translation.extensions', []) as $domain => $translationPath) { | ||
126 | if ($domain === self::DEFAULT_DOMAIN) { | ||
127 | continue; | ||
128 | } | ||
129 | |||
130 | try { | ||
131 | /** @var Translations $extension */ | ||
132 | $extension = Translations::fromPoFile($translationPath . $this->language .'/LC_MESSAGES/'. $domain .'.po'); | ||
133 | $extension->setDomain($domain); | ||
134 | $this->translator->loadTranslations($extension); | ||
135 | } catch (\InvalidArgumentException $e) {} | ||
136 | } | ||
137 | } | ||
138 | |||
139 | /** | ||
140 | * Checks if a language string is valid. | ||
141 | * | ||
142 | * @param string $language e.g. 'fr' or 'en_US' | ||
143 | * | ||
144 | * @return bool true if valid, false otherwise | ||
145 | */ | ||
146 | protected function isValidLanguage($language) | ||
147 | { | ||
148 | return preg_match('/^[a-z]{2}(_[A-Z]{2})?/', $language) === 1; | ||
149 | } | ||
150 | |||
151 | /** | ||
152 | * Get the list of available languages for Shaarli. | ||
153 | * | ||
154 | * @return array List of available languages, with their label. | ||
155 | */ | ||
156 | public static function getAvailableLanguages() | ||
157 | { | ||
158 | return [ | ||
159 | 'auto' => t('Automatic'), | ||
160 | 'en' => t('English'), | ||
161 | 'fr' => t('French'), | ||
162 | ]; | ||
18 | } | 163 | } |
19 | $actualForm = $nb > 1 ? $nText : $text; | ||
20 | return sprintf($actualForm, $nb); | ||
21 | } | 164 | } |
diff --git a/application/LinkDB.php b/application/LinkDB.php index 22c1f0ab..c1661d52 100644 --- a/application/LinkDB.php +++ b/application/LinkDB.php | |||
@@ -133,16 +133,16 @@ class LinkDB implements Iterator, Countable, ArrayAccess | |||
133 | { | 133 | { |
134 | // TODO: use exceptions instead of "die" | 134 | // TODO: use exceptions instead of "die" |
135 | if (!$this->loggedIn) { | 135 | if (!$this->loggedIn) { |
136 | die('You are not authorized to add a link.'); | 136 | die(t('You are not authorized to add a link.')); |
137 | } | 137 | } |
138 | if (!isset($value['id']) || empty($value['url'])) { | 138 | if (!isset($value['id']) || empty($value['url'])) { |
139 | die('Internal Error: A link should always have an id and URL.'); | 139 | die(t('Internal Error: A link should always have an id and URL.')); |
140 | } | 140 | } |
141 | if (($offset !== null && ! is_int($offset)) || ! is_int($value['id'])) { | 141 | if (($offset !== null && ! is_int($offset)) || ! is_int($value['id'])) { |
142 | die('You must specify an integer as a key.'); | 142 | die(t('You must specify an integer as a key.')); |
143 | } | 143 | } |
144 | if ($offset !== null && $offset !== $value['id']) { | 144 | if ($offset !== null && $offset !== $value['id']) { |
145 | die('Array offset and link ID must be equal.'); | 145 | die(t('Array offset and link ID must be equal.')); |
146 | } | 146 | } |
147 | 147 | ||
148 | // If the link exists, we reuse the real offset, otherwise new entry | 148 | // If the link exists, we reuse the real offset, otherwise new entry |
@@ -248,13 +248,13 @@ class LinkDB implements Iterator, Countable, ArrayAccess | |||
248 | $this->links = array(); | 248 | $this->links = array(); |
249 | $link = array( | 249 | $link = array( |
250 | 'id' => 1, | 250 | 'id' => 1, |
251 | 'title'=>' Shaarli: the personal, minimalist, super-fast, no-database delicious clone', | 251 | 'title'=> t('The personal, minimalist, super-fast, database free, bookmarking service'), |
252 | 'url'=>'https://shaarli.readthedocs.io', | 252 | 'url'=>'https://shaarli.readthedocs.io', |
253 | 'description'=>'Welcome to Shaarli! This is your first public bookmark. To edit or delete me, you must first login. | 253 | 'description'=>t('Welcome to Shaarli! This is your first public bookmark. To edit or delete me, you must first login. |
254 | 254 | ||
255 | To learn how to use Shaarli, consult the link "Help/documentation" at the bottom of this page. | 255 | To learn how to use Shaarli, consult the link "Documentation" at the bottom of this page. |
256 | 256 | ||
257 | You use the community supported version of the original Shaarli project, by Sebastien Sauvage.', | 257 | You use the community supported version of the original Shaarli project, by Sebastien Sauvage.'), |
258 | 'private'=>0, | 258 | 'private'=>0, |
259 | 'created'=> new DateTime(), | 259 | 'created'=> new DateTime(), |
260 | 'tags'=>'opensource software' | 260 | 'tags'=>'opensource software' |
@@ -264,9 +264,9 @@ You use the community supported version of the original Shaarli project, by Seba | |||
264 | 264 | ||
265 | $link = array( | 265 | $link = array( |
266 | 'id' => 0, | 266 | 'id' => 0, |
267 | 'title'=>'My secret stuff... - Pastebin.com', | 267 | 'title'=> t('My secret stuff... - Pastebin.com'), |
268 | 'url'=>'http://sebsauvage.net/paste/?8434b27936c09649#bR7XsXhoTiLcqCpQbmOpBi3rq2zzQUC5hBI7ZT1O3x8=', | 268 | 'url'=>'http://sebsauvage.net/paste/?8434b27936c09649#bR7XsXhoTiLcqCpQbmOpBi3rq2zzQUC5hBI7ZT1O3x8=', |
269 | 'description'=>'Shhhh! I\'m a private link only YOU can see. You can delete me too.', | 269 | 'description'=> t('Shhhh! I\'m a private link only YOU can see. You can delete me too.'), |
270 | 'private'=>1, | 270 | 'private'=>1, |
271 | 'created'=> new DateTime('1 minute ago'), | 271 | 'created'=> new DateTime('1 minute ago'), |
272 | 'tags'=>'secretstuff', | 272 | 'tags'=>'secretstuff', |
@@ -289,13 +289,15 @@ You use the community supported version of the original Shaarli project, by Seba | |||
289 | return; | 289 | return; |
290 | } | 290 | } |
291 | 291 | ||
292 | $this->urls = []; | ||
293 | $this->ids = []; | ||
292 | $this->links = FileUtils::readFlatDB($this->datastore, []); | 294 | $this->links = FileUtils::readFlatDB($this->datastore, []); |
293 | 295 | ||
294 | $toremove = array(); | 296 | $toremove = array(); |
295 | foreach ($this->links as $key => &$link) { | 297 | foreach ($this->links as $key => &$link) { |
296 | if (! $this->loggedIn && $link['private'] != 0) { | 298 | if (! $this->loggedIn && $link['private'] != 0) { |
297 | // Transition for not upgraded databases. | 299 | // Transition for not upgraded databases. |
298 | $toremove[] = $key; | 300 | unset($this->links[$key]); |
299 | continue; | 301 | continue; |
300 | } | 302 | } |
301 | 303 | ||
@@ -329,14 +331,10 @@ You use the community supported version of the original Shaarli project, by Seba | |||
329 | } | 331 | } |
330 | $link['shorturl'] = smallHash($link['linkdate']); | 332 | $link['shorturl'] = smallHash($link['linkdate']); |
331 | } | 333 | } |
332 | } | ||
333 | 334 | ||
334 | // If user is not logged in, filter private links. | 335 | $this->urls[$link['url']] = $key; |
335 | foreach ($toremove as $offset) { | 336 | $this->ids[$link['id']] = $key; |
336 | unset($this->links[$offset]); | ||
337 | } | 337 | } |
338 | |||
339 | $this->reorder(); | ||
340 | } | 338 | } |
341 | 339 | ||
342 | /** | 340 | /** |
@@ -346,6 +344,7 @@ You use the community supported version of the original Shaarli project, by Seba | |||
346 | */ | 344 | */ |
347 | private function write() | 345 | private function write() |
348 | { | 346 | { |
347 | $this->reorder(); | ||
349 | FileUtils::writeFlatDB($this->datastore, $this->links); | 348 | FileUtils::writeFlatDB($this->datastore, $this->links); |
350 | } | 349 | } |
351 | 350 | ||
@@ -528,8 +527,8 @@ You use the community supported version of the original Shaarli project, by Seba | |||
528 | return $a['created'] < $b['created'] ? 1 * $order : -1 * $order; | 527 | return $a['created'] < $b['created'] ? 1 * $order : -1 * $order; |
529 | }); | 528 | }); |
530 | 529 | ||
531 | $this->urls = array(); | 530 | $this->urls = []; |
532 | $this->ids = array(); | 531 | $this->ids = []; |
533 | foreach ($this->links as $key => $link) { | 532 | foreach ($this->links as $key => $link) { |
534 | $this->urls[$link['url']] = $key; | 533 | $this->urls[$link['url']] = $key; |
535 | $this->ids[$link['id']] = $key; | 534 | $this->ids[$link['id']] = $key; |
diff --git a/application/LinkFilter.php b/application/LinkFilter.php index 99ecd1e2..12376e27 100644 --- a/application/LinkFilter.php +++ b/application/LinkFilter.php | |||
@@ -444,5 +444,11 @@ class LinkFilter | |||
444 | 444 | ||
445 | class LinkNotFoundException extends Exception | 445 | class LinkNotFoundException extends Exception |
446 | { | 446 | { |
447 | protected $message = 'The link you are trying to reach does not exist or has been deleted.'; | 447 | /** |
448 | * LinkNotFoundException constructor. | ||
449 | */ | ||
450 | public function __construct() | ||
451 | { | ||
452 | $this->message = t('The link you are trying to reach does not exist or has been deleted.'); | ||
453 | } | ||
448 | } | 454 | } |
diff --git a/application/LinkUtils.php b/application/LinkUtils.php index 267e62cd..e3d95d08 100644 --- a/application/LinkUtils.php +++ b/application/LinkUtils.php | |||
@@ -102,12 +102,13 @@ function count_private($links) | |||
102 | * | 102 | * |
103 | * @param string $text input string. | 103 | * @param string $text input string. |
104 | * @param string $redirector if a redirector is set, use it to gerenate links. | 104 | * @param string $redirector if a redirector is set, use it to gerenate links. |
105 | * @param bool $urlEncode Use `urlencode()` on the URL after the redirector or not. | ||
105 | * | 106 | * |
106 | * @return string returns $text with all links converted to HTML links. | 107 | * @return string returns $text with all links converted to HTML links. |
107 | * | 108 | * |
108 | * @see Function inspired from http://www.php.net/manual/en/function.preg-replace.php#85722 | 109 | * @see Function inspired from http://www.php.net/manual/en/function.preg-replace.php#85722 |
109 | */ | 110 | */ |
110 | function text2clickable($text, $redirector = '') | 111 | function text2clickable($text, $redirector = '', $urlEncode = true) |
111 | { | 112 | { |
112 | $regex = '!(((?:https?|ftp|file)://|apt:|magnet:)\S+[a-z0-9\(\)]/?)!si'; | 113 | $regex = '!(((?:https?|ftp|file)://|apt:|magnet:)\S+[a-z0-9\(\)]/?)!si'; |
113 | 114 | ||
@@ -117,8 +118,9 @@ function text2clickable($text, $redirector = '') | |||
117 | // Redirector is set, urlencode the final URL. | 118 | // Redirector is set, urlencode the final URL. |
118 | return preg_replace_callback( | 119 | return preg_replace_callback( |
119 | $regex, | 120 | $regex, |
120 | function ($matches) use ($redirector) { | 121 | function ($matches) use ($redirector, $urlEncode) { |
121 | return '<a href="' . $redirector . urlencode($matches[1]) .'">'. $matches[1] .'</a>'; | 122 | $url = $urlEncode ? urlencode($matches[1]) : $matches[1]; |
123 | return '<a href="' . $redirector . $url .'">'. $matches[1] .'</a>'; | ||
122 | }, | 124 | }, |
123 | $text | 125 | $text |
124 | ); | 126 | ); |
@@ -164,12 +166,13 @@ function space2nbsp($text) | |||
164 | * | 166 | * |
165 | * @param string $description shaare's description. | 167 | * @param string $description shaare's description. |
166 | * @param string $redirector if a redirector is set, use it to gerenate links. | 168 | * @param string $redirector if a redirector is set, use it to gerenate links. |
169 | * @param bool $urlEncode Use `urlencode()` on the URL after the redirector or not. | ||
167 | * @param string $indexUrl URL to Shaarli's index. | 170 | * @param string $indexUrl URL to Shaarli's index. |
168 | * | 171 | |
169 | * @return string formatted description. | 172 | * @return string formatted description. |
170 | */ | 173 | */ |
171 | function format_description($description, $redirector = '', $indexUrl = '') { | 174 | function format_description($description, $redirector = '', $urlEncode = true, $indexUrl = '') { |
172 | return nl2br(space2nbsp(hashtag_autolink(text2clickable($description, $redirector), $indexUrl))); | 175 | return nl2br(space2nbsp(hashtag_autolink(text2clickable($description, $redirector, $urlEncode), $indexUrl))); |
173 | } | 176 | } |
174 | 177 | ||
175 | /** | 178 | /** |
diff --git a/application/NetscapeBookmarkUtils.php b/application/NetscapeBookmarkUtils.php index 31796367..dd7057f8 100644 --- a/application/NetscapeBookmarkUtils.php +++ b/application/NetscapeBookmarkUtils.php | |||
@@ -32,11 +32,10 @@ class NetscapeBookmarkUtils | |||
32 | { | 32 | { |
33 | // see tpl/export.html for possible values | 33 | // see tpl/export.html for possible values |
34 | if (! in_array($selection, array('all', 'public', 'private'))) { | 34 | if (! in_array($selection, array('all', 'public', 'private'))) { |
35 | throw new Exception('Invalid export selection: "'.$selection.'"'); | 35 | throw new Exception(t('Invalid export selection:') .' "'.$selection.'"'); |
36 | } | 36 | } |
37 | 37 | ||
38 | $bookmarkLinks = array(); | 38 | $bookmarkLinks = array(); |
39 | |||
40 | foreach ($linkDb as $link) { | 39 | foreach ($linkDb as $link) { |
41 | if ($link['private'] != 0 && $selection == 'public') { | 40 | if ($link['private'] != 0 && $selection == 'public') { |
42 | continue; | 41 | continue; |
@@ -79,14 +78,14 @@ class NetscapeBookmarkUtils | |||
79 | $duration=0 | 78 | $duration=0 |
80 | ) | 79 | ) |
81 | { | 80 | { |
82 | $status = 'File '.$filename.' ('.$filesize.' bytes) '; | 81 | $status = sprintf(t('File %s (%d bytes) '), $filename, $filesize); |
83 | if ($importCount == 0 && $overwriteCount == 0 && $skipCount == 0) { | 82 | if ($importCount == 0 && $overwriteCount == 0 && $skipCount == 0) { |
84 | $status .= 'has an unknown file format. Nothing was imported.'; | 83 | $status .= t('has an unknown file format. Nothing was imported.'); |
85 | } else { | 84 | } else { |
86 | $status .= 'was successfully processed in '. $duration .' seconds: '; | 85 | $status .= vsprintf( |
87 | $status .= $importCount.' links imported, '; | 86 | t('was successfully processed in %d seconds: %d links imported, %d links overwritten, %d links skipped.'), |
88 | $status .= $overwriteCount.' links overwritten, '; | 87 | [$duration, $importCount, $overwriteCount, $skipCount] |
89 | $status .= $skipCount.' links skipped.'; | 88 | ); |
90 | } | 89 | } |
91 | return $status; | 90 | return $status; |
92 | } | 91 | } |
diff --git a/application/PageBuilder.php b/application/PageBuilder.php index 291860ad..468f144b 100644 --- a/application/PageBuilder.php +++ b/application/PageBuilder.php | |||
@@ -32,12 +32,14 @@ class PageBuilder | |||
32 | * | 32 | * |
33 | * @param ConfigManager $conf Configuration Manager instance (reference). | 33 | * @param ConfigManager $conf Configuration Manager instance (reference). |
34 | * @param LinkDB $linkDB instance. | 34 | * @param LinkDB $linkDB instance. |
35 | * @param string $token Session token | ||
35 | */ | 36 | */ |
36 | public function __construct(&$conf, $linkDB = null) | 37 | public function __construct(&$conf, $linkDB = null, $token = null) |
37 | { | 38 | { |
38 | $this->tpl = false; | 39 | $this->tpl = false; |
39 | $this->conf = $conf; | 40 | $this->conf = $conf; |
40 | $this->linkDB = $linkDB; | 41 | $this->linkDB = $linkDB; |
42 | $this->token = $token; | ||
41 | } | 43 | } |
42 | 44 | ||
43 | /** | 45 | /** |
@@ -92,7 +94,7 @@ class PageBuilder | |||
92 | $this->tpl->assign('showatom', $this->conf->get('feed.show_atom', true)); | 94 | $this->tpl->assign('showatom', $this->conf->get('feed.show_atom', true)); |
93 | $this->tpl->assign('feed_type', $this->conf->get('feed.show_atom', true) !== false ? 'atom' : 'rss'); | 95 | $this->tpl->assign('feed_type', $this->conf->get('feed.show_atom', true) !== false ? 'atom' : 'rss'); |
94 | $this->tpl->assign('hide_timestamps', $this->conf->get('privacy.hide_timestamps', false)); | 96 | $this->tpl->assign('hide_timestamps', $this->conf->get('privacy.hide_timestamps', false)); |
95 | $this->tpl->assign('token', getToken($this->conf)); | 97 | $this->tpl->assign('token', $this->token); |
96 | 98 | ||
97 | if ($this->linkDB !== null) { | 99 | if ($this->linkDB !== null) { |
98 | $this->tpl->assign('tags', $this->linkDB->linksCountPerTag()); | 100 | $this->tpl->assign('tags', $this->linkDB->linksCountPerTag()); |
@@ -159,9 +161,12 @@ class PageBuilder | |||
159 | * | 161 | * |
160 | * @param string $message A messate to display what is not found | 162 | * @param string $message A messate to display what is not found |
161 | */ | 163 | */ |
162 | public function render404($message = 'The page you are trying to reach does not exist or has been deleted.') | 164 | public function render404($message = '') |
163 | { | 165 | { |
164 | header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found'); | 166 | if (empty($message)) { |
167 | $message = t('The page you are trying to reach does not exist or has been deleted.'); | ||
168 | } | ||
169 | header($_SERVER['SERVER_PROTOCOL'] .' '. t('404 Not Found')); | ||
165 | $this->tpl->assign('error_message', $message); | 170 | $this->tpl->assign('error_message', $message); |
166 | $this->renderPage('404'); | 171 | $this->renderPage('404'); |
167 | } | 172 | } |
diff --git a/application/PluginManager.php b/application/PluginManager.php index 59ece4fa..cf603845 100644 --- a/application/PluginManager.php +++ b/application/PluginManager.php | |||
@@ -188,6 +188,9 @@ class PluginManager | |||
188 | $metaData[$plugin] = parse_ini_file($metaFile); | 188 | $metaData[$plugin] = parse_ini_file($metaFile); |
189 | $metaData[$plugin]['order'] = array_search($plugin, $this->authorizedPlugins); | 189 | $metaData[$plugin]['order'] = array_search($plugin, $this->authorizedPlugins); |
190 | 190 | ||
191 | if (isset($metaData[$plugin]['description'])) { | ||
192 | $metaData[$plugin]['description'] = t($metaData[$plugin]['description']); | ||
193 | } | ||
191 | // Read parameters and format them into an array. | 194 | // Read parameters and format them into an array. |
192 | if (isset($metaData[$plugin]['parameters'])) { | 195 | if (isset($metaData[$plugin]['parameters'])) { |
193 | $params = explode(';', $metaData[$plugin]['parameters']); | 196 | $params = explode(';', $metaData[$plugin]['parameters']); |
@@ -203,7 +206,7 @@ class PluginManager | |||
203 | $metaData[$plugin]['parameters'][$param]['value'] = ''; | 206 | $metaData[$plugin]['parameters'][$param]['value'] = ''; |
204 | // Optional parameter description in parameter.PARAM_NAME= | 207 | // Optional parameter description in parameter.PARAM_NAME= |
205 | if (isset($metaData[$plugin]['parameter.'. $param])) { | 208 | if (isset($metaData[$plugin]['parameter.'. $param])) { |
206 | $metaData[$plugin]['parameters'][$param]['desc'] = $metaData[$plugin]['parameter.'. $param]; | 209 | $metaData[$plugin]['parameters'][$param]['desc'] = t($metaData[$plugin]['parameter.'. $param]); |
207 | } | 210 | } |
208 | } | 211 | } |
209 | } | 212 | } |
@@ -237,6 +240,6 @@ class PluginFileNotFoundException extends Exception | |||
237 | */ | 240 | */ |
238 | public function __construct($pluginName) | 241 | public function __construct($pluginName) |
239 | { | 242 | { |
240 | $this->message = 'Plugin "'. $pluginName .'" files not found.'; | 243 | $this->message = sprintf(t('Plugin "%s" files not found.'), $pluginName); |
241 | } | 244 | } |
242 | } | 245 | } |
diff --git a/application/SessionManager.php b/application/SessionManager.php new file mode 100644 index 00000000..71f0b38d --- /dev/null +++ b/application/SessionManager.php | |||
@@ -0,0 +1,83 @@ | |||
1 | <?php | ||
2 | namespace Shaarli; | ||
3 | |||
4 | /** | ||
5 | * Manages the server-side session | ||
6 | */ | ||
7 | class SessionManager | ||
8 | { | ||
9 | protected $session = []; | ||
10 | |||
11 | /** | ||
12 | * Constructor | ||
13 | * | ||
14 | * @param array $session The $_SESSION array (reference) | ||
15 | * @param ConfigManager $conf ConfigManager instance | ||
16 | */ | ||
17 | public function __construct(& $session, $conf) | ||
18 | { | ||
19 | $this->session = &$session; | ||
20 | $this->conf = $conf; | ||
21 | } | ||
22 | |||
23 | /** | ||
24 | * Generates a session token | ||
25 | * | ||
26 | * @return string token | ||
27 | */ | ||
28 | public function generateToken() | ||
29 | { | ||
30 | $token = sha1(uniqid('', true) .'_'. mt_rand() . $this->conf->get('credentials.salt')); | ||
31 | $this->session['tokens'][$token] = 1; | ||
32 | return $token; | ||
33 | } | ||
34 | |||
35 | /** | ||
36 | * Checks the validity of a session token, and destroys it afterwards | ||
37 | * | ||
38 | * @param string $token The token to check | ||
39 | * | ||
40 | * @return bool true if the token is valid, else false | ||
41 | */ | ||
42 | public function checkToken($token) | ||
43 | { | ||
44 | if (! isset($this->session['tokens'][$token])) { | ||
45 | // the token is wrong, or has already been used | ||
46 | return false; | ||
47 | } | ||
48 | |||
49 | // destroy the token to prevent future use | ||
50 | unset($this->session['tokens'][$token]); | ||
51 | return true; | ||
52 | } | ||
53 | |||
54 | /** | ||
55 | * Validate session ID to prevent Full Path Disclosure. | ||
56 | * | ||
57 | * See #298. | ||
58 | * The session ID's format depends on the hash algorithm set in PHP settings | ||
59 | * | ||
60 | * @param string $sessionId Session ID | ||
61 | * | ||
62 | * @return true if valid, false otherwise. | ||
63 | * | ||
64 | * @see http://php.net/manual/en/function.hash-algos.php | ||
65 | * @see http://php.net/manual/en/session.configuration.php | ||
66 | */ | ||
67 | public static function checkId($sessionId) | ||
68 | { | ||
69 | if (empty($sessionId)) { | ||
70 | return false; | ||
71 | } | ||
72 | |||
73 | if (!$sessionId) { | ||
74 | return false; | ||
75 | } | ||
76 | |||
77 | if (!preg_match('/^[a-zA-Z0-9,-]{2,128}$/', $sessionId)) { | ||
78 | return false; | ||
79 | } | ||
80 | |||
81 | return true; | ||
82 | } | ||
83 | } | ||
diff --git a/application/Updater.php b/application/Updater.php index 72b2def0..8d2bd577 100644 --- a/application/Updater.php +++ b/application/Updater.php | |||
@@ -73,7 +73,7 @@ class Updater | |||
73 | } | 73 | } |
74 | 74 | ||
75 | if ($this->methods === null) { | 75 | if ($this->methods === null) { |
76 | throw new UpdaterException('Couldn\'t retrieve Updater class methods.'); | 76 | throw new UpdaterException(t('Couldn\'t retrieve Updater class methods.')); |
77 | } | 77 | } |
78 | 78 | ||
79 | foreach ($this->methods as $method) { | 79 | foreach ($this->methods as $method) { |
@@ -436,6 +436,15 @@ class Updater | |||
436 | } | 436 | } |
437 | return true; | 437 | return true; |
438 | } | 438 | } |
439 | |||
440 | /** | ||
441 | * Save the datastore -> the link order is now applied when links are saved. | ||
442 | */ | ||
443 | public function updateMethodReorderDatastore() | ||
444 | { | ||
445 | $this->linkDB->save($this->conf->get('resource.page_cache')); | ||
446 | return true; | ||
447 | } | ||
439 | } | 448 | } |
440 | 449 | ||
441 | /** | 450 | /** |
@@ -482,7 +491,7 @@ class UpdaterException extends Exception | |||
482 | } | 491 | } |
483 | 492 | ||
484 | if (! empty($this->method)) { | 493 | if (! empty($this->method)) { |
485 | $out .= 'An error occurred while running the update '. $this->method . PHP_EOL; | 494 | $out .= t('An error occurred while running the update ') . $this->method . PHP_EOL; |
486 | } | 495 | } |
487 | 496 | ||
488 | if (! empty($this->previous)) { | 497 | if (! empty($this->previous)) { |
@@ -522,11 +531,11 @@ function read_updates_file($updatesFilepath) | |||
522 | function write_updates_file($updatesFilepath, $updates) | 531 | function write_updates_file($updatesFilepath, $updates) |
523 | { | 532 | { |
524 | if (empty($updatesFilepath)) { | 533 | if (empty($updatesFilepath)) { |
525 | throw new Exception('Updates file path is not set, can\'t write updates.'); | 534 | throw new Exception(t('Updates file path is not set, can\'t write updates.')); |
526 | } | 535 | } |
527 | 536 | ||
528 | $res = file_put_contents($updatesFilepath, implode(';', $updates)); | 537 | $res = file_put_contents($updatesFilepath, implode(';', $updates)); |
529 | if ($res === false) { | 538 | if ($res === false) { |
530 | throw new Exception('Unable to write updates in '. $updatesFilepath . '.'); | 539 | throw new Exception(t('Unable to write updates in '. $updatesFilepath . '.')); |
531 | } | 540 | } |
532 | } | 541 | } |
diff --git a/application/Utils.php b/application/Utils.php index 4a2f5561..97b12fcf 100644 --- a/application/Utils.php +++ b/application/Utils.php | |||
@@ -182,36 +182,6 @@ function generateLocation($referer, $host, $loopTerms = array()) | |||
182 | } | 182 | } |
183 | 183 | ||
184 | /** | 184 | /** |
185 | * Validate session ID to prevent Full Path Disclosure. | ||
186 | * | ||
187 | * See #298. | ||
188 | * The session ID's format depends on the hash algorithm set in PHP settings | ||
189 | * | ||
190 | * @param string $sessionId Session ID | ||
191 | * | ||
192 | * @return true if valid, false otherwise. | ||
193 | * | ||
194 | * @see http://php.net/manual/en/function.hash-algos.php | ||
195 | * @see http://php.net/manual/en/session.configuration.php | ||
196 | */ | ||
197 | function is_session_id_valid($sessionId) | ||
198 | { | ||
199 | if (empty($sessionId)) { | ||
200 | return false; | ||
201 | } | ||
202 | |||
203 | if (!$sessionId) { | ||
204 | return false; | ||
205 | } | ||
206 | |||
207 | if (!preg_match('/^[a-zA-Z0-9,-]{2,128}$/', $sessionId)) { | ||
208 | return false; | ||
209 | } | ||
210 | |||
211 | return true; | ||
212 | } | ||
213 | |||
214 | /** | ||
215 | * Sniff browser language to set the locale automatically. | 185 | * Sniff browser language to set the locale automatically. |
216 | * Note that is may not work on your server if the corresponding locale is not installed. | 186 | * Note that is may not work on your server if the corresponding locale is not installed. |
217 | * | 187 | * |
@@ -452,7 +422,7 @@ function get_max_upload_size($limitPost, $limitUpload, $format = true) | |||
452 | */ | 422 | */ |
453 | function alphabetical_sort(&$data, $reverse = false, $byKeys = false) | 423 | function alphabetical_sort(&$data, $reverse = false, $byKeys = false) |
454 | { | 424 | { |
455 | $callback = function($a, $b) use ($reverse) { | 425 | $callback = function ($a, $b) use ($reverse) { |
456 | // Collator is part of PHP intl. | 426 | // Collator is part of PHP intl. |
457 | if (class_exists('Collator')) { | 427 | if (class_exists('Collator')) { |
458 | $collator = new Collator(setlocale(LC_COLLATE, 0)); | 428 | $collator = new Collator(setlocale(LC_COLLATE, 0)); |
@@ -470,3 +440,18 @@ function alphabetical_sort(&$data, $reverse = false, $byKeys = false) | |||
470 | usort($data, $callback); | 440 | usort($data, $callback); |
471 | } | 441 | } |
472 | } | 442 | } |
443 | |||
444 | /** | ||
445 | * Wrapper function for translation which match the API | ||
446 | * of gettext()/_() and ngettext(). | ||
447 | * | ||
448 | * @param string $text Text to translate. | ||
449 | * @param string $nText The plural message ID. | ||
450 | * @param int $nb The number of items for plural forms. | ||
451 | * @param string $domain The domain where the translation is stored (default: shaarli). | ||
452 | * | ||
453 | * @return string Text translated. | ||
454 | */ | ||
455 | function t($text, $nText = '', $nb = 1, $domain = 'shaarli') { | ||
456 | return dn__($domain, $text, $nText, $nb); | ||
457 | } | ||
diff --git a/application/config/ConfigJson.php b/application/config/ConfigJson.php index 9ef2ef56..8c8d5610 100644 --- a/application/config/ConfigJson.php +++ b/application/config/ConfigJson.php | |||
@@ -22,10 +22,15 @@ class ConfigJson implements ConfigIO | |||
22 | $data = json_decode($data, true); | 22 | $data = json_decode($data, true); |
23 | if ($data === null) { | 23 | if ($data === null) { |
24 | $errorCode = json_last_error(); | 24 | $errorCode = json_last_error(); |
25 | $error = 'An error occurred while parsing JSON configuration file ('. $filepath .'): error code #'; | 25 | $error = sprintf( |
26 | $error .= $errorCode. '<br>➜ <code>' . json_last_error_msg() .'</code>'; | 26 | 'An error occurred while parsing JSON configuration file (%s): error code #%d', |
27 | $filepath, | ||
28 | $errorCode | ||
29 | ); | ||
30 | $error .= '<br>➜ <code>' . json_last_error_msg() .'</code>'; | ||
27 | if ($errorCode === JSON_ERROR_SYNTAX) { | 31 | if ($errorCode === JSON_ERROR_SYNTAX) { |
28 | $error .= '<br>Please check your JSON syntax (without PHP comment tags) using a JSON lint tool such as '; | 32 | $error .= '<br>'; |
33 | $error .= 'Please check your JSON syntax (without PHP comment tags) using a JSON lint tool such as '; | ||
29 | $error .= '<a href="http://jsonlint.com/">jsonlint.com</a>.'; | 34 | $error .= '<a href="http://jsonlint.com/">jsonlint.com</a>.'; |
30 | } | 35 | } |
31 | throw new \Exception($error); | 36 | throw new \Exception($error); |
@@ -44,8 +49,8 @@ class ConfigJson implements ConfigIO | |||
44 | if (!file_put_contents($filepath, $data)) { | 49 | if (!file_put_contents($filepath, $data)) { |
45 | throw new \IOException( | 50 | throw new \IOException( |
46 | $filepath, | 51 | $filepath, |
47 | 'Shaarli could not create the config file. | 52 | t('Shaarli could not create the config file. '. |
48 | Please make sure Shaarli has the right to write in the folder is it installed in.' | 53 | 'Please make sure Shaarli has the right to write in the folder is it installed in.') |
49 | ); | 54 | ); |
50 | } | 55 | } |
51 | } | 56 | } |
diff --git a/application/config/ConfigManager.php b/application/config/ConfigManager.php index 7ff2fe67..9e4c9f63 100644 --- a/application/config/ConfigManager.php +++ b/application/config/ConfigManager.php | |||
@@ -132,7 +132,7 @@ class ConfigManager | |||
132 | public function set($setting, $value, $write = false, $isLoggedIn = false) | 132 | public function set($setting, $value, $write = false, $isLoggedIn = false) |
133 | { | 133 | { |
134 | if (empty($setting) || ! is_string($setting)) { | 134 | if (empty($setting) || ! is_string($setting)) { |
135 | throw new \Exception('Invalid setting key parameter. String expected, got: '. gettype($setting)); | 135 | throw new \Exception(t('Invalid setting key parameter. String expected, got: '). gettype($setting)); |
136 | } | 136 | } |
137 | 137 | ||
138 | // During the ConfigIO transition, map legacy settings to the new ones. | 138 | // During the ConfigIO transition, map legacy settings to the new ones. |
@@ -339,6 +339,10 @@ class ConfigManager | |||
339 | $this->setEmpty('redirector.url', ''); | 339 | $this->setEmpty('redirector.url', ''); |
340 | $this->setEmpty('redirector.encode_url', true); | 340 | $this->setEmpty('redirector.encode_url', true); |
341 | 341 | ||
342 | $this->setEmpty('translation.language', 'auto'); | ||
343 | $this->setEmpty('translation.mode', 'php'); | ||
344 | $this->setEmpty('translation.extensions', []); | ||
345 | |||
342 | $this->setEmpty('plugins', array()); | 346 | $this->setEmpty('plugins', array()); |
343 | } | 347 | } |
344 | 348 | ||
diff --git a/application/config/ConfigPhp.php b/application/config/ConfigPhp.php index 2633824d..2f66e8e0 100644 --- a/application/config/ConfigPhp.php +++ b/application/config/ConfigPhp.php | |||
@@ -118,8 +118,8 @@ class ConfigPhp implements ConfigIO | |||
118 | ) { | 118 | ) { |
119 | throw new \IOException( | 119 | throw new \IOException( |
120 | $filepath, | 120 | $filepath, |
121 | 'Shaarli could not create the config file. | 121 | t('Shaarli could not create the config file. '. |
122 | Please make sure Shaarli has the right to write in the folder is it installed in.' | 122 | 'Please make sure Shaarli has the right to write in the folder is it installed in.') |
123 | ); | 123 | ); |
124 | } | 124 | } |
125 | } | 125 | } |
diff --git a/application/config/exception/MissingFieldConfigException.php b/application/config/exception/MissingFieldConfigException.php index 6346c6a9..9e0a9359 100644 --- a/application/config/exception/MissingFieldConfigException.php +++ b/application/config/exception/MissingFieldConfigException.php | |||
@@ -18,6 +18,6 @@ class MissingFieldConfigException extends \Exception | |||
18 | public function __construct($field) | 18 | public function __construct($field) |
19 | { | 19 | { |
20 | $this->field = $field; | 20 | $this->field = $field; |
21 | $this->message = 'Configuration value is required for '. $this->field; | 21 | $this->message = sprintf(t('Configuration value is required for %s'), $this->field); |
22 | } | 22 | } |
23 | } | 23 | } |
diff --git a/application/config/exception/PluginConfigOrderException.php b/application/config/exception/PluginConfigOrderException.php index f9d68750..f82ec26e 100644 --- a/application/config/exception/PluginConfigOrderException.php +++ b/application/config/exception/PluginConfigOrderException.php | |||
@@ -12,6 +12,6 @@ class PluginConfigOrderException extends \Exception | |||
12 | */ | 12 | */ |
13 | public function __construct() | 13 | public function __construct() |
14 | { | 14 | { |
15 | $this->message = 'An error occurred while trying to save plugins loading order.'; | 15 | $this->message = t('An error occurred while trying to save plugins loading order.'); |
16 | } | 16 | } |
17 | } | 17 | } |
diff --git a/application/config/exception/UnauthorizedConfigException.php b/application/config/exception/UnauthorizedConfigException.php index 79672c1b..72311fae 100644 --- a/application/config/exception/UnauthorizedConfigException.php +++ b/application/config/exception/UnauthorizedConfigException.php | |||
@@ -13,6 +13,6 @@ class UnauthorizedConfigException extends \Exception | |||
13 | */ | 13 | */ |
14 | public function __construct() | 14 | public function __construct() |
15 | { | 15 | { |
16 | $this->message = 'You are not authorized to alter config.'; | 16 | $this->message = t('You are not authorized to alter config.'); |
17 | } | 17 | } |
18 | } | 18 | } |
diff --git a/application/exceptions/IOException.php b/application/exceptions/IOException.php index b563b23d..18e46b77 100644 --- a/application/exceptions/IOException.php +++ b/application/exceptions/IOException.php | |||
@@ -16,7 +16,7 @@ class IOException extends Exception | |||
16 | public function __construct($path, $message = '') | 16 | public function __construct($path, $message = '') |
17 | { | 17 | { |
18 | $this->path = $path; | 18 | $this->path = $path; |
19 | $this->message = empty($message) ? 'Error accessing' : $message; | 19 | $this->message = empty($message) ? t('Error accessing') : $message; |
20 | $this->message .= ' "' . $this->path .'"'; | 20 | $this->message .= ' "' . $this->path .'"'; |
21 | } | 21 | } |
22 | } | 22 | } |