diff options
Diffstat (limited to 'application')
41 files changed, 885 insertions, 486 deletions
diff --git a/application/ApplicationUtils.php b/application/ApplicationUtils.php index 911873a0..7fe3cb32 100644 --- a/application/ApplicationUtils.php +++ b/application/ApplicationUtils.php | |||
@@ -1,4 +1,9 @@ | |||
1 | <?php | 1 | <?php |
2 | namespace Shaarli; | ||
3 | |||
4 | use Exception; | ||
5 | use Shaarli\Config\ConfigManager; | ||
6 | |||
2 | /** | 7 | /** |
3 | * Shaarli (application) utilities | 8 | * Shaarli (application) utilities |
4 | */ | 9 | */ |
@@ -24,7 +29,7 @@ class ApplicationUtils | |||
24 | * | 29 | * |
25 | * @return mixed the version code from the repository if available, else 'false' | 30 | * @return mixed the version code from the repository if available, else 'false' |
26 | */ | 31 | */ |
27 | public static function getLatestGitVersionCode($url, $timeout=2) | 32 | public static function getLatestGitVersionCode($url, $timeout = 2) |
28 | { | 33 | { |
29 | list($headers, $data) = get_http_response($url, $timeout); | 34 | list($headers, $data) = get_http_response($url, $timeout); |
30 | 35 | ||
@@ -51,7 +56,7 @@ class ApplicationUtils | |||
51 | return false; | 56 | return false; |
52 | } | 57 | } |
53 | } else { | 58 | } else { |
54 | if (! is_file($remote)) { | 59 | if (!is_file($remote)) { |
55 | return false; | 60 | return false; |
56 | } | 61 | } |
57 | $data = file_get_contents($remote); | 62 | $data = file_get_contents($remote); |
@@ -86,17 +91,18 @@ class ApplicationUtils | |||
86 | * | 91 | * |
87 | * @return mixed the new version code if available and greater, else 'false' | 92 | * @return mixed the new version code if available and greater, else 'false' |
88 | */ | 93 | */ |
89 | public static function checkUpdate($currentVersion, | 94 | public static function checkUpdate( |
90 | $updateFile, | 95 | $currentVersion, |
91 | $checkInterval, | 96 | $updateFile, |
92 | $enableCheck, | 97 | $checkInterval, |
93 | $isLoggedIn, | 98 | $enableCheck, |
94 | $branch='stable') | 99 | $isLoggedIn, |
95 | { | 100 | $branch = 'stable' |
101 | ) { | ||
96 | // Do not check versions for visitors | 102 | // Do not check versions for visitors |
97 | // Do not check if the user doesn't want to | 103 | // Do not check if the user doesn't want to |
98 | // Do not check with dev version | 104 | // Do not check with dev version |
99 | if (! $isLoggedIn || empty($enableCheck) || $currentVersion === 'dev') { | 105 | if (!$isLoggedIn || empty($enableCheck) || $currentVersion === 'dev') { |
100 | return false; | 106 | return false; |
101 | } | 107 | } |
102 | 108 | ||
@@ -110,7 +116,7 @@ class ApplicationUtils | |||
110 | return false; | 116 | return false; |
111 | } | 117 | } |
112 | 118 | ||
113 | if (! in_array($branch, self::$GIT_BRANCHES)) { | 119 | if (!in_array($branch, self::$GIT_BRANCHES)) { |
114 | throw new Exception( | 120 | throw new Exception( |
115 | 'Invalid branch selected for updates: "' . $branch . '"' | 121 | 'Invalid branch selected for updates: "' . $branch . '"' |
116 | ); | 122 | ); |
@@ -122,7 +128,7 @@ class ApplicationUtils | |||
122 | self::$GIT_URL . '/' . $branch . '/' . self::$VERSION_FILE | 128 | self::$GIT_URL . '/' . $branch . '/' . self::$VERSION_FILE |
123 | ); | 129 | ); |
124 | 130 | ||
125 | if (! $latestVersion) { | 131 | if (!$latestVersion) { |
126 | // Only update the file's modification date | 132 | // Only update the file's modification date |
127 | file_put_contents($updateFile, $currentVersion); | 133 | file_put_contents($updateFile, $currentVersion); |
128 | return false; | 134 | return false; |
@@ -151,9 +157,9 @@ class ApplicationUtils | |||
151 | if (version_compare($curVersion, $minVersion) < 0) { | 157 | if (version_compare($curVersion, $minVersion) < 0) { |
152 | $msg = t( | 158 | $msg = t( |
153 | 'Your PHP version is obsolete!' | 159 | 'Your PHP version is obsolete!' |
154 | . ' Shaarli requires at least PHP %s, and thus cannot run.' | 160 | . ' Shaarli requires at least PHP %s, and thus cannot run.' |
155 | . ' Your PHP version has known security vulnerabilities and should be' | 161 | . ' Your PHP version has known security vulnerabilities and should be' |
156 | . ' updated as soon as possible.' | 162 | . ' updated as soon as possible.' |
157 | ); | 163 | ); |
158 | throw new Exception(sprintf($msg, $minVersion)); | 164 | throw new Exception(sprintf($msg, $minVersion)); |
159 | } | 165 | } |
@@ -173,50 +179,50 @@ class ApplicationUtils | |||
173 | 179 | ||
174 | // Check script and template directories are readable | 180 | // Check script and template directories are readable |
175 | foreach (array( | 181 | foreach (array( |
176 | 'application', | 182 | 'application', |
177 | 'inc', | 183 | 'inc', |
178 | 'plugins', | 184 | 'plugins', |
179 | $rainTplDir, | 185 | $rainTplDir, |
180 | $rainTplDir.'/'.$conf->get('resource.theme'), | 186 | $rainTplDir . '/' . $conf->get('resource.theme'), |
181 | ) as $path) { | 187 | ) as $path) { |
182 | if (! is_readable(realpath($path))) { | 188 | if (!is_readable(realpath($path))) { |
183 | $errors[] = '"'.$path.'" '. t('directory is not readable'); | 189 | $errors[] = '"' . $path . '" ' . t('directory is not readable'); |
184 | } | 190 | } |
185 | } | 191 | } |
186 | 192 | ||
187 | // Check cache and data directories are readable and writable | 193 | // Check cache and data directories are readable and writable |
188 | foreach (array( | 194 | foreach (array( |
189 | $conf->get('resource.thumbnails_cache'), | 195 | $conf->get('resource.thumbnails_cache'), |
190 | $conf->get('resource.data_dir'), | 196 | $conf->get('resource.data_dir'), |
191 | $conf->get('resource.page_cache'), | 197 | $conf->get('resource.page_cache'), |
192 | $conf->get('resource.raintpl_tmp'), | 198 | $conf->get('resource.raintpl_tmp'), |
193 | ) as $path) { | 199 | ) as $path) { |
194 | if (! is_readable(realpath($path))) { | 200 | if (!is_readable(realpath($path))) { |
195 | $errors[] = '"'.$path.'" '. t('directory is not readable'); | 201 | $errors[] = '"' . $path . '" ' . t('directory is not readable'); |
196 | } | 202 | } |
197 | if (! is_writable(realpath($path))) { | 203 | if (!is_writable(realpath($path))) { |
198 | $errors[] = '"'.$path.'" '. t('directory is not writable'); | 204 | $errors[] = '"' . $path . '" ' . t('directory is not writable'); |
199 | } | 205 | } |
200 | } | 206 | } |
201 | 207 | ||
202 | // Check configuration files are readable and writable | 208 | // Check configuration files are readable and writable |
203 | foreach (array( | 209 | foreach (array( |
204 | $conf->getConfigFileExt(), | 210 | $conf->getConfigFileExt(), |
205 | $conf->get('resource.datastore'), | 211 | $conf->get('resource.datastore'), |
206 | $conf->get('resource.ban_file'), | 212 | $conf->get('resource.ban_file'), |
207 | $conf->get('resource.log'), | 213 | $conf->get('resource.log'), |
208 | $conf->get('resource.update_check'), | 214 | $conf->get('resource.update_check'), |
209 | ) as $path) { | 215 | ) as $path) { |
210 | if (! is_file(realpath($path))) { | 216 | if (!is_file(realpath($path))) { |
211 | # the file may not exist yet | 217 | # the file may not exist yet |
212 | continue; | 218 | continue; |
213 | } | 219 | } |
214 | 220 | ||
215 | if (! is_readable(realpath($path))) { | 221 | if (!is_readable(realpath($path))) { |
216 | $errors[] = '"'.$path.'" '. t('file is not readable'); | 222 | $errors[] = '"' . $path . '" ' . t('file is not readable'); |
217 | } | 223 | } |
218 | if (! is_writable(realpath($path))) { | 224 | if (!is_writable(realpath($path))) { |
219 | $errors[] = '"'.$path.'" '. t('file is not writable'); | 225 | $errors[] = '"' . $path . '" ' . t('file is not writable'); |
220 | } | 226 | } |
221 | } | 227 | } |
222 | 228 | ||
diff --git a/application/FileUtils.php b/application/FileUtils.php index b89ea12b..30560bfc 100644 --- a/application/FileUtils.php +++ b/application/FileUtils.php | |||
@@ -1,6 +1,8 @@ | |||
1 | <?php | 1 | <?php |
2 | 2 | ||
3 | require_once 'exceptions/IOException.php'; | 3 | namespace Shaarli; |
4 | |||
5 | use Shaarli\Exceptions\IOException; | ||
4 | 6 | ||
5 | /** | 7 | /** |
6 | * Class FileUtils | 8 | * Class FileUtils |
@@ -44,7 +46,7 @@ class FileUtils | |||
44 | 46 | ||
45 | return file_put_contents( | 47 | return file_put_contents( |
46 | $file, | 48 | $file, |
47 | self::$phpPrefix.base64_encode(gzdeflate(serialize($content))).self::$phpSuffix | 49 | self::$phpPrefix . base64_encode(gzdeflate(serialize($content))) . self::$phpSuffix |
48 | ); | 50 | ); |
49 | } | 51 | } |
50 | 52 | ||
@@ -62,7 +64,7 @@ class FileUtils | |||
62 | { | 64 | { |
63 | // Note that gzinflate is faster than gzuncompress. | 65 | // Note that gzinflate is faster than gzuncompress. |
64 | // See: http://www.php.net/manual/en/function.gzdeflate.php#96439 | 66 | // See: http://www.php.net/manual/en/function.gzdeflate.php#96439 |
65 | if (! is_readable($file)) { | 67 | if (!is_readable($file)) { |
66 | return $default; | 68 | return $default; |
67 | } | 69 | } |
68 | 70 | ||
diff --git a/application/History.php b/application/History.php index 35ec016a..a5846652 100644 --- a/application/History.php +++ b/application/History.php | |||
@@ -1,4 +1,8 @@ | |||
1 | <?php | 1 | <?php |
2 | namespace Shaarli; | ||
3 | |||
4 | use DateTime; | ||
5 | use Exception; | ||
2 | 6 | ||
3 | /** | 7 | /** |
4 | * Class History | 8 | * Class History |
@@ -66,7 +70,7 @@ class History | |||
66 | * History constructor. | 70 | * History constructor. |
67 | * | 71 | * |
68 | * @param string $historyFilePath History file path. | 72 | * @param string $historyFilePath History file path. |
69 | * @param int $retentionTime History content rentention time in seconds. | 73 | * @param int $retentionTime History content retention time in seconds. |
70 | * | 74 | * |
71 | * @throws Exception if something goes wrong. | 75 | * @throws Exception if something goes wrong. |
72 | */ | 76 | */ |
@@ -166,11 +170,11 @@ class History | |||
166 | */ | 170 | */ |
167 | protected function check() | 171 | protected function check() |
168 | { | 172 | { |
169 | if (! is_file($this->historyFilePath)) { | 173 | if (!is_file($this->historyFilePath)) { |
170 | FileUtils::writeFlatDB($this->historyFilePath, []); | 174 | FileUtils::writeFlatDB($this->historyFilePath, []); |
171 | } | 175 | } |
172 | 176 | ||
173 | if (! is_writable($this->historyFilePath)) { | 177 | if (!is_writable($this->historyFilePath)) { |
174 | throw new Exception(t('History file isn\'t readable or writable')); | 178 | throw new Exception(t('History file isn\'t readable or writable')); |
175 | } | 179 | } |
176 | } | 180 | } |
@@ -191,7 +195,7 @@ class History | |||
191 | */ | 195 | */ |
192 | protected function write() | 196 | protected function write() |
193 | { | 197 | { |
194 | $comparaison = new DateTime('-'. $this->retentionTime . ' seconds'); | 198 | $comparaison = new DateTime('-' . $this->retentionTime . ' seconds'); |
195 | foreach ($this->history as $key => $value) { | 199 | foreach ($this->history as $key => $value) { |
196 | if ($value['datetime'] < $comparaison) { | 200 | if ($value['datetime'] < $comparaison) { |
197 | unset($this->history[$key]); | 201 | unset($this->history[$key]); |
diff --git a/application/Languages.php b/application/Languages.php index 4fa32426..5cda802e 100644 --- a/application/Languages.php +++ b/application/Languages.php | |||
@@ -3,7 +3,6 @@ | |||
3 | namespace Shaarli; | 3 | namespace Shaarli; |
4 | 4 | ||
5 | use Gettext\GettextTranslator; | 5 | use Gettext\GettextTranslator; |
6 | use Gettext\Merge; | ||
7 | use Gettext\Translations; | 6 | use Gettext\Translations; |
8 | use Gettext\Translator; | 7 | use Gettext\Translator; |
9 | use Gettext\TranslatorInterface; | 8 | use Gettext\TranslatorInterface; |
@@ -92,7 +91,7 @@ class Languages | |||
92 | /** | 91 | /** |
93 | * Initialize the translator using php gettext extension (gettext dependency act as a wrapper). | 92 | * Initialize the translator using php gettext extension (gettext dependency act as a wrapper). |
94 | */ | 93 | */ |
95 | protected function initGettextTranslator () | 94 | protected function initGettextTranslator() |
96 | { | 95 | { |
97 | $this->translator = new GettextTranslator(); | 96 | $this->translator = new GettextTranslator(); |
98 | $this->translator->setLanguage($this->language); | 97 | $this->translator->setLanguage($this->language); |
@@ -125,7 +124,8 @@ class Languages | |||
125 | $translations = $translations->addFromPoFile('inc/languages/'. $this->language .'/LC_MESSAGES/shaarli.po'); | 124 | $translations = $translations->addFromPoFile('inc/languages/'. $this->language .'/LC_MESSAGES/shaarli.po'); |
126 | $translations->setDomain('shaarli'); | 125 | $translations->setDomain('shaarli'); |
127 | $this->translator->loadTranslations($translations); | 126 | $this->translator->loadTranslations($translations); |
128 | } catch (\InvalidArgumentException $e) {} | 127 | } catch (\InvalidArgumentException $e) { |
128 | } | ||
129 | 129 | ||
130 | // Default extension translation from the current theme | 130 | // Default extension translation from the current theme |
131 | $theme = $this->conf->get('theme'); | 131 | $theme = $this->conf->get('theme'); |
@@ -137,7 +137,8 @@ class Languages | |||
137 | ); | 137 | ); |
138 | $translations->setDomain($theme); | 138 | $translations->setDomain($theme); |
139 | $this->translator->loadTranslations($translations); | 139 | $this->translator->loadTranslations($translations); |
140 | } catch (\InvalidArgumentException $e) {} | 140 | } catch (\InvalidArgumentException $e) { |
141 | } | ||
141 | } | 142 | } |
142 | 143 | ||
143 | // Extension translations (plugins, themes, etc.). | 144 | // Extension translations (plugins, themes, etc.). |
@@ -147,10 +148,13 @@ class Languages | |||
147 | } | 148 | } |
148 | 149 | ||
149 | try { | 150 | try { |
150 | $extension = Translations::fromPoFile($translationPath . $this->language .'/LC_MESSAGES/'. $domain .'.po'); | 151 | $extension = Translations::fromPoFile( |
152 | $translationPath . $this->language .'/LC_MESSAGES/'. $domain .'.po' | ||
153 | ); | ||
151 | $extension->setDomain($domain); | 154 | $extension->setDomain($domain); |
152 | $this->translator->loadTranslations($extension); | 155 | $this->translator->loadTranslations($extension); |
153 | } catch (\InvalidArgumentException $e) {} | 156 | } catch (\InvalidArgumentException $e) { |
157 | } | ||
154 | } | 158 | } |
155 | } | 159 | } |
156 | 160 | ||
diff --git a/application/Router.php b/application/Router.php index 4df0387c..05877acd 100644 --- a/application/Router.php +++ b/application/Router.php | |||
@@ -1,4 +1,5 @@ | |||
1 | <?php | 1 | <?php |
2 | namespace Shaarli; | ||
2 | 3 | ||
3 | /** | 4 | /** |
4 | * Class Router | 5 | * Class Router |
@@ -7,6 +8,8 @@ | |||
7 | */ | 8 | */ |
8 | class Router | 9 | class Router |
9 | { | 10 | { |
11 | public static $AJAX_THUMB_UPDATE = 'ajax_thumb_update'; | ||
12 | |||
10 | public static $PAGE_LOGIN = 'login'; | 13 | public static $PAGE_LOGIN = 'login'; |
11 | 14 | ||
12 | public static $PAGE_PICWALL = 'picwall'; | 15 | public static $PAGE_PICWALL = 'picwall'; |
@@ -35,6 +38,8 @@ class Router | |||
35 | 38 | ||
36 | public static $PAGE_DELETELINK = 'delete_link'; | 39 | public static $PAGE_DELETELINK = 'delete_link'; |
37 | 40 | ||
41 | public static $PAGE_PINLINK = 'pin'; | ||
42 | |||
38 | public static $PAGE_EXPORT = 'export'; | 43 | public static $PAGE_EXPORT = 'export'; |
39 | 44 | ||
40 | public static $PAGE_IMPORT = 'import'; | 45 | public static $PAGE_IMPORT = 'import'; |
@@ -47,6 +52,8 @@ class Router | |||
47 | 52 | ||
48 | public static $PAGE_SAVE_PLUGINSADMIN = 'save_pluginadmin'; | 53 | public static $PAGE_SAVE_PLUGINSADMIN = 'save_pluginadmin'; |
49 | 54 | ||
55 | public static $PAGE_THUMBS_UPDATE = 'thumbs_update'; | ||
56 | |||
50 | public static $GET_TOKEN = 'token'; | 57 | public static $GET_TOKEN = 'token'; |
51 | 58 | ||
52 | /** | 59 | /** |
@@ -69,60 +76,68 @@ class Router | |||
69 | return self::$PAGE_LINKLIST; | 76 | return self::$PAGE_LINKLIST; |
70 | } | 77 | } |
71 | 78 | ||
72 | if (startsWith($query, 'do='. self::$PAGE_LOGIN) && $loggedIn === false) { | 79 | if (startsWith($query, 'do=' . self::$PAGE_LOGIN) && $loggedIn === false) { |
73 | return self::$PAGE_LOGIN; | 80 | return self::$PAGE_LOGIN; |
74 | } | 81 | } |
75 | 82 | ||
76 | if (startsWith($query, 'do='. self::$PAGE_PICWALL)) { | 83 | if (startsWith($query, 'do=' . self::$PAGE_PICWALL)) { |
77 | return self::$PAGE_PICWALL; | 84 | return self::$PAGE_PICWALL; |
78 | } | 85 | } |
79 | 86 | ||
80 | if (startsWith($query, 'do='. self::$PAGE_TAGCLOUD)) { | 87 | if (startsWith($query, 'do=' . self::$PAGE_TAGCLOUD)) { |
81 | return self::$PAGE_TAGCLOUD; | 88 | return self::$PAGE_TAGCLOUD; |
82 | } | 89 | } |
83 | 90 | ||
84 | if (startsWith($query, 'do='. self::$PAGE_TAGLIST)) { | 91 | if (startsWith($query, 'do=' . self::$PAGE_TAGLIST)) { |
85 | return self::$PAGE_TAGLIST; | 92 | return self::$PAGE_TAGLIST; |
86 | } | 93 | } |
87 | 94 | ||
88 | if (startsWith($query, 'do='. self::$PAGE_OPENSEARCH)) { | 95 | if (startsWith($query, 'do=' . self::$PAGE_OPENSEARCH)) { |
89 | return self::$PAGE_OPENSEARCH; | 96 | return self::$PAGE_OPENSEARCH; |
90 | } | 97 | } |
91 | 98 | ||
92 | if (startsWith($query, 'do='. self::$PAGE_DAILY)) { | 99 | if (startsWith($query, 'do=' . self::$PAGE_DAILY)) { |
93 | return self::$PAGE_DAILY; | 100 | return self::$PAGE_DAILY; |
94 | } | 101 | } |
95 | 102 | ||
96 | if (startsWith($query, 'do='. self::$PAGE_FEED_ATOM)) { | 103 | if (startsWith($query, 'do=' . self::$PAGE_FEED_ATOM)) { |
97 | return self::$PAGE_FEED_ATOM; | 104 | return self::$PAGE_FEED_ATOM; |
98 | } | 105 | } |
99 | 106 | ||
100 | if (startsWith($query, 'do='. self::$PAGE_FEED_RSS)) { | 107 | if (startsWith($query, 'do=' . self::$PAGE_FEED_RSS)) { |
101 | return self::$PAGE_FEED_RSS; | 108 | return self::$PAGE_FEED_RSS; |
102 | } | 109 | } |
103 | 110 | ||
111 | if (startsWith($query, 'do=' . self::$PAGE_THUMBS_UPDATE)) { | ||
112 | return self::$PAGE_THUMBS_UPDATE; | ||
113 | } | ||
114 | |||
115 | if (startsWith($query, 'do=' . self::$AJAX_THUMB_UPDATE)) { | ||
116 | return self::$AJAX_THUMB_UPDATE; | ||
117 | } | ||
118 | |||
104 | // At this point, only loggedin pages. | 119 | // At this point, only loggedin pages. |
105 | if (!$loggedIn) { | 120 | if (!$loggedIn) { |
106 | return self::$PAGE_LINKLIST; | 121 | return self::$PAGE_LINKLIST; |
107 | } | 122 | } |
108 | 123 | ||
109 | if (startsWith($query, 'do='. self::$PAGE_TOOLS)) { | 124 | if (startsWith($query, 'do=' . self::$PAGE_TOOLS)) { |
110 | return self::$PAGE_TOOLS; | 125 | return self::$PAGE_TOOLS; |
111 | } | 126 | } |
112 | 127 | ||
113 | if (startsWith($query, 'do='. self::$PAGE_CHANGEPASSWORD)) { | 128 | if (startsWith($query, 'do=' . self::$PAGE_CHANGEPASSWORD)) { |
114 | return self::$PAGE_CHANGEPASSWORD; | 129 | return self::$PAGE_CHANGEPASSWORD; |
115 | } | 130 | } |
116 | 131 | ||
117 | if (startsWith($query, 'do='. self::$PAGE_CONFIGURE)) { | 132 | if (startsWith($query, 'do=' . self::$PAGE_CONFIGURE)) { |
118 | return self::$PAGE_CONFIGURE; | 133 | return self::$PAGE_CONFIGURE; |
119 | } | 134 | } |
120 | 135 | ||
121 | if (startsWith($query, 'do='. self::$PAGE_CHANGETAG)) { | 136 | if (startsWith($query, 'do=' . self::$PAGE_CHANGETAG)) { |
122 | return self::$PAGE_CHANGETAG; | 137 | return self::$PAGE_CHANGETAG; |
123 | } | 138 | } |
124 | 139 | ||
125 | if (startsWith($query, 'do='. self::$PAGE_ADDLINK)) { | 140 | if (startsWith($query, 'do=' . self::$PAGE_ADDLINK)) { |
126 | return self::$PAGE_ADDLINK; | 141 | return self::$PAGE_ADDLINK; |
127 | } | 142 | } |
128 | 143 | ||
@@ -134,23 +149,27 @@ class Router | |||
134 | return self::$PAGE_DELETELINK; | 149 | return self::$PAGE_DELETELINK; |
135 | } | 150 | } |
136 | 151 | ||
137 | if (startsWith($query, 'do='. self::$PAGE_EXPORT)) { | 152 | if (startsWith($query, 'do=' . self::$PAGE_PINLINK)) { |
153 | return self::$PAGE_PINLINK; | ||
154 | } | ||
155 | |||
156 | if (startsWith($query, 'do=' . self::$PAGE_EXPORT)) { | ||
138 | return self::$PAGE_EXPORT; | 157 | return self::$PAGE_EXPORT; |
139 | } | 158 | } |
140 | 159 | ||
141 | if (startsWith($query, 'do='. self::$PAGE_IMPORT)) { | 160 | if (startsWith($query, 'do=' . self::$PAGE_IMPORT)) { |
142 | return self::$PAGE_IMPORT; | 161 | return self::$PAGE_IMPORT; |
143 | } | 162 | } |
144 | 163 | ||
145 | if (startsWith($query, 'do='. self::$PAGE_PLUGINSADMIN)) { | 164 | if (startsWith($query, 'do=' . self::$PAGE_PLUGINSADMIN)) { |
146 | return self::$PAGE_PLUGINSADMIN; | 165 | return self::$PAGE_PLUGINSADMIN; |
147 | } | 166 | } |
148 | 167 | ||
149 | if (startsWith($query, 'do='. self::$PAGE_SAVE_PLUGINSADMIN)) { | 168 | if (startsWith($query, 'do=' . self::$PAGE_SAVE_PLUGINSADMIN)) { |
150 | return self::$PAGE_SAVE_PLUGINSADMIN; | 169 | return self::$PAGE_SAVE_PLUGINSADMIN; |
151 | } | 170 | } |
152 | 171 | ||
153 | if (startsWith($query, 'do='. self::$GET_TOKEN)) { | 172 | if (startsWith($query, 'do=' . self::$GET_TOKEN)) { |
154 | return self::$GET_TOKEN; | 173 | return self::$GET_TOKEN; |
155 | } | 174 | } |
156 | 175 | ||
diff --git a/application/Thumbnailer.php b/application/Thumbnailer.php new file mode 100644 index 00000000..a23f98e9 --- /dev/null +++ b/application/Thumbnailer.php | |||
@@ -0,0 +1,130 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Shaarli; | ||
4 | |||
5 | use Shaarli\Config\ConfigManager; | ||
6 | use WebThumbnailer\Application\ConfigManager as WTConfigManager; | ||
7 | use WebThumbnailer\Exception\WebThumbnailerException; | ||
8 | use WebThumbnailer\WebThumbnailer; | ||
9 | |||
10 | /** | ||
11 | * Class Thumbnailer | ||
12 | * | ||
13 | * Utility class used to retrieve thumbnails using web-thumbnailer dependency. | ||
14 | */ | ||
15 | class Thumbnailer | ||
16 | { | ||
17 | const COMMON_MEDIA_DOMAINS = [ | ||
18 | 'imgur.com', | ||
19 | 'flickr.com', | ||
20 | 'youtube.com', | ||
21 | 'wikimedia.org', | ||
22 | 'redd.it', | ||
23 | 'gfycat.com', | ||
24 | 'media.giphy.com', | ||
25 | 'twitter.com', | ||
26 | 'twimg.com', | ||
27 | 'instagram.com', | ||
28 | 'pinterest.com', | ||
29 | 'pinterest.fr', | ||
30 | 'tumblr.com', | ||
31 | 'deviantart.com', | ||
32 | ]; | ||
33 | |||
34 | const MODE_ALL = 'all'; | ||
35 | const MODE_COMMON = 'common'; | ||
36 | const MODE_NONE = 'none'; | ||
37 | |||
38 | /** | ||
39 | * @var WebThumbnailer instance. | ||
40 | */ | ||
41 | protected $wt; | ||
42 | |||
43 | /** | ||
44 | * @var ConfigManager instance. | ||
45 | */ | ||
46 | protected $conf; | ||
47 | |||
48 | /** | ||
49 | * Thumbnailer constructor. | ||
50 | * | ||
51 | * @param ConfigManager $conf instance. | ||
52 | */ | ||
53 | public function __construct($conf) | ||
54 | { | ||
55 | $this->conf = $conf; | ||
56 | |||
57 | if (! $this->checkRequirements()) { | ||
58 | $this->conf->set('thumbnails.enabled', false); | ||
59 | $this->conf->write(true); | ||
60 | // TODO: create a proper error handling system able to catch exceptions... | ||
61 | die(t( | ||
62 | 'php-gd extension must be loaded to use thumbnails. ' | ||
63 | .'Thumbnails are now disabled. Please reload the page.' | ||
64 | )); | ||
65 | } | ||
66 | |||
67 | $this->wt = new WebThumbnailer(); | ||
68 | WTConfigManager::addFile('inc/web-thumbnailer.json'); | ||
69 | $this->wt->maxWidth($this->conf->get('thumbnails.width')) | ||
70 | ->maxHeight($this->conf->get('thumbnails.height')) | ||
71 | ->crop(true) | ||
72 | ->debug($this->conf->get('dev.debug', false)); | ||
73 | } | ||
74 | |||
75 | /** | ||
76 | * Retrieve a thumbnail for given URL | ||
77 | * | ||
78 | * @param string $url where to look for a thumbnail. | ||
79 | * | ||
80 | * @return bool|string The thumbnail relative cache file path, or false if none has been found. | ||
81 | */ | ||
82 | public function get($url) | ||
83 | { | ||
84 | if ($this->conf->get('thumbnails.mode') === self::MODE_COMMON | ||
85 | && ! $this->isCommonMediaOrImage($url) | ||
86 | ) { | ||
87 | return false; | ||
88 | } | ||
89 | |||
90 | try { | ||
91 | return $this->wt->thumbnail($url); | ||
92 | } catch (WebThumbnailerException $e) { | ||
93 | // Exceptions are only thrown in debug mode. | ||
94 | error_log(get_class($e) . ': ' . $e->getMessage()); | ||
95 | } | ||
96 | return false; | ||
97 | } | ||
98 | |||
99 | /** | ||
100 | * We check weather the given URL is from a common media domain, | ||
101 | * or if the file extension is an image. | ||
102 | * | ||
103 | * @param string $url to check | ||
104 | * | ||
105 | * @return bool true if it's an image or from a common media domain, false otherwise. | ||
106 | */ | ||
107 | public function isCommonMediaOrImage($url) | ||
108 | { | ||
109 | foreach (self::COMMON_MEDIA_DOMAINS as $domain) { | ||
110 | if (strpos($url, $domain) !== false) { | ||
111 | return true; | ||
112 | } | ||
113 | } | ||
114 | |||
115 | if (endsWith($url, '.jpg') || endsWith($url, '.png') || endsWith($url, '.jpeg')) { | ||
116 | return true; | ||
117 | } | ||
118 | |||
119 | return false; | ||
120 | } | ||
121 | |||
122 | /** | ||
123 | * Make sure that requirements are match to use thumbnails: | ||
124 | * - php-gd is loaded | ||
125 | */ | ||
126 | protected function checkRequirements() | ||
127 | { | ||
128 | return extension_loaded('gd'); | ||
129 | } | ||
130 | } | ||
diff --git a/application/Utils.php b/application/Utils.php index 97b12fcf..925e1a22 100644 --- a/application/Utils.php +++ b/application/Utils.php | |||
@@ -97,7 +97,7 @@ function escape($input) | |||
97 | 97 | ||
98 | if (is_array($input)) { | 98 | if (is_array($input)) { |
99 | $out = array(); | 99 | $out = array(); |
100 | foreach($input as $key => $value) { | 100 | foreach ($input as $key => $value) { |
101 | $out[$key] = escape($value); | 101 | $out[$key] = escape($value); |
102 | } | 102 | } |
103 | return $out; | 103 | return $out; |
@@ -355,10 +355,13 @@ function return_bytes($val) | |||
355 | $val = trim($val); | 355 | $val = trim($val); |
356 | $last = strtolower($val[strlen($val)-1]); | 356 | $last = strtolower($val[strlen($val)-1]); |
357 | $val = intval(substr($val, 0, -1)); | 357 | $val = intval(substr($val, 0, -1)); |
358 | switch($last) { | 358 | switch ($last) { |
359 | case 'g': $val *= 1024; | 359 | case 'g': |
360 | case 'm': $val *= 1024; | 360 | $val *= 1024; |
361 | case 'k': $val *= 1024; | 361 | case 'm': |
362 | $val *= 1024; | ||
363 | case 'k': | ||
364 | $val *= 1024; | ||
362 | } | 365 | } |
363 | return $val; | 366 | return $val; |
364 | } | 367 | } |
@@ -452,6 +455,7 @@ function alphabetical_sort(&$data, $reverse = false, $byKeys = false) | |||
452 | * | 455 | * |
453 | * @return string Text translated. | 456 | * @return string Text translated. |
454 | */ | 457 | */ |
455 | function t($text, $nText = '', $nb = 1, $domain = 'shaarli') { | 458 | function t($text, $nText = '', $nb = 1, $domain = 'shaarli') |
459 | { | ||
456 | return dn__($domain, $text, $nText, $nb); | 460 | return dn__($domain, $text, $nText, $nb); |
457 | } | 461 | } |
diff --git a/application/api/ApiMiddleware.php b/application/api/ApiMiddleware.php index ff209393..5ffb8c6d 100644 --- a/application/api/ApiMiddleware.php +++ b/application/api/ApiMiddleware.php | |||
@@ -1,9 +1,8 @@ | |||
1 | <?php | 1 | <?php |
2 | namespace Shaarli\Api; | 2 | namespace Shaarli\Api; |
3 | 3 | ||
4 | use Shaarli\Api\Exceptions\ApiException; | ||
5 | use Shaarli\Api\Exceptions\ApiAuthorizationException; | 4 | use Shaarli\Api\Exceptions\ApiAuthorizationException; |
6 | 5 | use Shaarli\Api\Exceptions\ApiException; | |
7 | use Shaarli\Config\ConfigManager; | 6 | use Shaarli\Config\ConfigManager; |
8 | use Slim\Container; | 7 | use Slim\Container; |
9 | use Slim\Http\Request; | 8 | use Slim\Http\Request; |
@@ -65,7 +64,7 @@ class ApiMiddleware | |||
65 | try { | 64 | try { |
66 | $this->checkRequest($request); | 65 | $this->checkRequest($request); |
67 | $response = $next($request, $response); | 66 | $response = $next($request, $response); |
68 | } catch(ApiException $e) { | 67 | } catch (ApiException $e) { |
69 | $e->setResponse($response); | 68 | $e->setResponse($response); |
70 | $e->setDebug($this->conf->get('dev.debug', false)); | 69 | $e->setDebug($this->conf->get('dev.debug', false)); |
71 | $response = $e->getApiResponse(); | 70 | $response = $e->getApiResponse(); |
@@ -98,7 +97,8 @@ class ApiMiddleware | |||
98 | * | 97 | * |
99 | * @throws ApiAuthorizationException The token couldn't be validated. | 98 | * @throws ApiAuthorizationException The token couldn't be validated. |
100 | */ | 99 | */ |
101 | protected function checkToken($request) { | 100 | protected function checkToken($request) |
101 | { | ||
102 | if (! $request->hasHeader('Authorization')) { | 102 | if (! $request->hasHeader('Authorization')) { |
103 | throw new ApiAuthorizationException('JWT token not provided'); | 103 | throw new ApiAuthorizationException('JWT token not provided'); |
104 | } | 104 | } |
@@ -126,7 +126,7 @@ class ApiMiddleware | |||
126 | */ | 126 | */ |
127 | protected function setLinkDb($conf) | 127 | protected function setLinkDb($conf) |
128 | { | 128 | { |
129 | $linkDb = new \LinkDB( | 129 | $linkDb = new \Shaarli\Bookmark\LinkDB( |
130 | $conf->get('resource.datastore'), | 130 | $conf->get('resource.datastore'), |
131 | true, | 131 | true, |
132 | $conf->get('privacy.hide_public_links'), | 132 | $conf->get('privacy.hide_public_links'), |
diff --git a/application/api/ApiUtils.php b/application/api/ApiUtils.php index fc5ecaf1..1824b5d0 100644 --- a/application/api/ApiUtils.php +++ b/application/api/ApiUtils.php | |||
@@ -1,8 +1,8 @@ | |||
1 | <?php | 1 | <?php |
2 | namespace Shaarli\Api; | 2 | namespace Shaarli\Api; |
3 | 3 | ||
4 | use Shaarli\Base64Url; | ||
5 | use Shaarli\Api\Exceptions\ApiAuthorizationException; | 4 | use Shaarli\Api\Exceptions\ApiAuthorizationException; |
5 | use Shaarli\Http\Base64Url; | ||
6 | 6 | ||
7 | /** | 7 | /** |
8 | * REST API utilities | 8 | * REST API utilities |
@@ -12,7 +12,7 @@ class ApiUtils | |||
12 | /** | 12 | /** |
13 | * Validates a JWT token authenticity. | 13 | * Validates a JWT token authenticity. |
14 | * | 14 | * |
15 | * @param string $token JWT token extracted from the headers. | 15 | * @param string $token JWT token extracted from the headers. |
16 | * @param string $secret API secret set in the settings. | 16 | * @param string $secret API secret set in the settings. |
17 | * | 17 | * |
18 | * @throws ApiAuthorizationException the token is not valid. | 18 | * @throws ApiAuthorizationException the token is not valid. |
@@ -50,7 +50,7 @@ class ApiUtils | |||
50 | /** | 50 | /** |
51 | * Format a Link for the REST API. | 51 | * Format a Link for the REST API. |
52 | * | 52 | * |
53 | * @param array $link Link data read from the datastore. | 53 | * @param array $link Link data read from the datastore. |
54 | * @param string $indexUrl Shaarli's index URL (used for relative URL). | 54 | * @param string $indexUrl Shaarli's index URL (used for relative URL). |
55 | * | 55 | * |
56 | * @return array Link data formatted for the REST API. | 56 | * @return array Link data formatted for the REST API. |
diff --git a/application/api/controllers/ApiController.php b/application/api/controllers/ApiController.php index 3be85b98..a6e7cbab 100644 --- a/application/api/controllers/ApiController.php +++ b/application/api/controllers/ApiController.php | |||
@@ -2,8 +2,9 @@ | |||
2 | 2 | ||
3 | namespace Shaarli\Api\Controllers; | 3 | namespace Shaarli\Api\Controllers; |
4 | 4 | ||
5 | use Shaarli\Bookmark\LinkDB; | ||
5 | use Shaarli\Config\ConfigManager; | 6 | use Shaarli\Config\ConfigManager; |
6 | use \Slim\Container; | 7 | use Slim\Container; |
7 | 8 | ||
8 | /** | 9 | /** |
9 | * Abstract Class ApiController | 10 | * Abstract Class ApiController |
@@ -25,12 +26,12 @@ abstract class ApiController | |||
25 | protected $conf; | 26 | protected $conf; |
26 | 27 | ||
27 | /** | 28 | /** |
28 | * @var \LinkDB | 29 | * @var LinkDB |
29 | */ | 30 | */ |
30 | protected $linkDb; | 31 | protected $linkDb; |
31 | 32 | ||
32 | /** | 33 | /** |
33 | * @var \History | 34 | * @var HistoryController |
34 | */ | 35 | */ |
35 | protected $history; | 36 | protected $history; |
36 | 37 | ||
@@ -41,7 +42,7 @@ abstract class ApiController | |||
41 | 42 | ||
42 | /** | 43 | /** |
43 | * ApiController constructor. | 44 | * ApiController constructor. |
44 | * | 45 | * |
45 | * Note: enabling debug mode displays JSON with readable formatting. | 46 | * Note: enabling debug mode displays JSON with readable formatting. |
46 | * | 47 | * |
47 | * @param Container $ci Slim container. | 48 | * @param Container $ci Slim container. |
diff --git a/application/api/controllers/History.php b/application/api/controllers/HistoryController.php index 5cc453bf..9afcfa26 100644 --- a/application/api/controllers/History.php +++ b/application/api/controllers/HistoryController.php | |||
@@ -14,7 +14,7 @@ use Slim\Http\Response; | |||
14 | * | 14 | * |
15 | * @package Shaarli\Api\Controllers | 15 | * @package Shaarli\Api\Controllers |
16 | */ | 16 | */ |
17 | class History extends ApiController | 17 | class HistoryController extends ApiController |
18 | { | 18 | { |
19 | /** | 19 | /** |
20 | * Service providing operation regarding Shaarli datastore and settings. | 20 | * Service providing operation regarding Shaarli datastore and settings. |
@@ -35,8 +35,7 @@ class History extends ApiController | |||
35 | $offset = $request->getParam('offset'); | 35 | $offset = $request->getParam('offset'); |
36 | if (empty($offset)) { | 36 | if (empty($offset)) { |
37 | $offset = 0; | 37 | $offset = 0; |
38 | } | 38 | } elseif (ctype_digit($offset)) { |
39 | elseif (ctype_digit($offset)) { | ||
40 | $offset = (int) $offset; | 39 | $offset = (int) $offset; |
41 | } else { | 40 | } else { |
42 | throw new ApiBadParametersException('Invalid offset'); | 41 | throw new ApiBadParametersException('Invalid offset'); |
diff --git a/application/api/controllers/Info.php b/application/api/controllers/Info.php index 25433f72..f37dcae5 100644 --- a/application/api/controllers/Info.php +++ b/application/api/controllers/Info.php | |||
@@ -7,7 +7,7 @@ use Slim\Http\Response; | |||
7 | 7 | ||
8 | /** | 8 | /** |
9 | * Class Info | 9 | * Class Info |
10 | * | 10 | * |
11 | * REST API Controller: /info | 11 | * REST API Controller: /info |
12 | * | 12 | * |
13 | * @package Api\Controllers | 13 | * @package Api\Controllers |
@@ -17,7 +17,7 @@ class Info extends ApiController | |||
17 | { | 17 | { |
18 | /** | 18 | /** |
19 | * Service providing various information about Shaarli instance. | 19 | * Service providing various information about Shaarli instance. |
20 | * | 20 | * |
21 | * @param Request $request Slim request. | 21 | * @param Request $request Slim request. |
22 | * @param Response $response Slim response. | 22 | * @param Response $response Slim response. |
23 | * | 23 | * |
diff --git a/application/api/controllers/Tags.php b/application/api/controllers/Tags.php index 6dd78750..82f3ef74 100644 --- a/application/api/controllers/Tags.php +++ b/application/api/controllers/Tags.php | |||
@@ -4,7 +4,6 @@ namespace Shaarli\Api\Controllers; | |||
4 | 4 | ||
5 | use Shaarli\Api\ApiUtils; | 5 | use Shaarli\Api\ApiUtils; |
6 | use Shaarli\Api\Exceptions\ApiBadParametersException; | 6 | use Shaarli\Api\Exceptions\ApiBadParametersException; |
7 | use Shaarli\Api\Exceptions\ApiLinkNotFoundException; | ||
8 | use Shaarli\Api\Exceptions\ApiTagNotFoundException; | 7 | use Shaarli\Api\Exceptions\ApiTagNotFoundException; |
9 | use Slim\Http\Request; | 8 | use Slim\Http\Request; |
10 | use Slim\Http\Response; | 9 | use Slim\Http\Response; |
diff --git a/application/api/exceptions/ApiException.php b/application/api/exceptions/ApiException.php index c8490e0c..d6b66323 100644 --- a/application/api/exceptions/ApiException.php +++ b/application/api/exceptions/ApiException.php | |||
@@ -10,7 +10,8 @@ use Slim\Http\Response; | |||
10 | * Parent Exception related to the API, able to generate a valid Response (ResponseInterface). | 10 | * Parent Exception related to the API, able to generate a valid Response (ResponseInterface). |
11 | * Also can include various information in debug mode. | 11 | * Also can include various information in debug mode. |
12 | */ | 12 | */ |
13 | abstract class ApiException extends \Exception { | 13 | abstract class ApiException extends \Exception |
14 | { | ||
14 | 15 | ||
15 | /** | 16 | /** |
16 | * @var Response instance from Slim. | 17 | * @var Response instance from Slim. |
@@ -27,7 +28,7 @@ abstract class ApiException extends \Exception { | |||
27 | * | 28 | * |
28 | * @return Response Final response to give. | 29 | * @return Response Final response to give. |
29 | */ | 30 | */ |
30 | public abstract function getApiResponse(); | 31 | abstract public function getApiResponse(); |
31 | 32 | ||
32 | /** | 33 | /** |
33 | * Creates ApiResponse body. | 34 | * Creates ApiResponse body. |
@@ -36,7 +37,8 @@ abstract class ApiException extends \Exception { | |||
36 | * | 37 | * |
37 | * @return array|string response body | 38 | * @return array|string response body |
38 | */ | 39 | */ |
39 | protected function getApiResponseBody() { | 40 | protected function getApiResponseBody() |
41 | { | ||
40 | if ($this->debug !== true) { | 42 | if ($this->debug !== true) { |
41 | return $this->getMessage(); | 43 | return $this->getMessage(); |
42 | } | 44 | } |
diff --git a/application/api/exceptions/ApiLinkNotFoundException.php b/application/api/exceptions/ApiLinkNotFoundException.php index de7e14f5..7c2bb56e 100644 --- a/application/api/exceptions/ApiLinkNotFoundException.php +++ b/application/api/exceptions/ApiLinkNotFoundException.php | |||
@@ -2,9 +2,6 @@ | |||
2 | 2 | ||
3 | namespace Shaarli\Api\Exceptions; | 3 | namespace Shaarli\Api\Exceptions; |
4 | 4 | ||
5 | |||
6 | use Slim\Http\Response; | ||
7 | |||
8 | /** | 5 | /** |
9 | * Class ApiLinkNotFoundException | 6 | * Class ApiLinkNotFoundException |
10 | * | 7 | * |
diff --git a/application/api/exceptions/ApiTagNotFoundException.php b/application/api/exceptions/ApiTagNotFoundException.php index eed5afa5..66ace8bf 100644 --- a/application/api/exceptions/ApiTagNotFoundException.php +++ b/application/api/exceptions/ApiTagNotFoundException.php | |||
@@ -2,9 +2,6 @@ | |||
2 | 2 | ||
3 | namespace Shaarli\Api\Exceptions; | 3 | namespace Shaarli\Api\Exceptions; |
4 | 4 | ||
5 | |||
6 | use Slim\Http\Response; | ||
7 | |||
8 | /** | 5 | /** |
9 | * Class ApiTagNotFoundException | 6 | * Class ApiTagNotFoundException |
10 | * | 7 | * |
diff --git a/application/LinkDB.php b/application/bookmark/LinkDB.php index cd0f2967..c13a1141 100644 --- a/application/LinkDB.php +++ b/application/bookmark/LinkDB.php | |||
@@ -1,4 +1,15 @@ | |||
1 | <?php | 1 | <?php |
2 | |||
3 | namespace Shaarli\Bookmark; | ||
4 | |||
5 | use ArrayAccess; | ||
6 | use Countable; | ||
7 | use DateTime; | ||
8 | use Iterator; | ||
9 | use Shaarli\Bookmark\Exception\LinkNotFoundException; | ||
10 | use Shaarli\Exceptions\IOException; | ||
11 | use Shaarli\FileUtils; | ||
12 | |||
2 | /** | 13 | /** |
3 | * Data storage for links. | 14 | * Data storage for links. |
4 | * | 15 | * |
@@ -107,8 +118,8 @@ class LinkDB implements Iterator, Countable, ArrayAccess | |||
107 | $hidePublicLinks, | 118 | $hidePublicLinks, |
108 | $redirector = '', | 119 | $redirector = '', |
109 | $redirectorEncode = true | 120 | $redirectorEncode = true |
110 | ) | 121 | ) { |
111 | { | 122 | |
112 | $this->datastore = $datastore; | 123 | $this->datastore = $datastore; |
113 | $this->loggedIn = $isLoggedIn; | 124 | $this->loggedIn = $isLoggedIn; |
114 | $this->hidePublicLinks = $hidePublicLinks; | 125 | $this->hidePublicLinks = $hidePublicLinks; |
@@ -138,7 +149,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess | |||
138 | if (!isset($value['id']) || empty($value['url'])) { | 149 | if (!isset($value['id']) || empty($value['url'])) { |
139 | die(t('Internal Error: A link should always have an id and URL.')); | 150 | die(t('Internal Error: A link should always have an id and URL.')); |
140 | } | 151 | } |
141 | if (($offset !== null && ! is_int($offset)) || ! is_int($value['id'])) { | 152 | if (($offset !== null && !is_int($offset)) || !is_int($value['id'])) { |
142 | die(t('You must specify an integer as a key.')); | 153 | die(t('You must specify an integer as a key.')); |
143 | } | 154 | } |
144 | if ($offset !== null && $offset !== $value['id']) { | 155 | if ($offset !== null && $offset !== $value['id']) { |
@@ -248,28 +259,31 @@ class LinkDB implements Iterator, Countable, ArrayAccess | |||
248 | $this->links = array(); | 259 | $this->links = array(); |
249 | $link = array( | 260 | $link = array( |
250 | 'id' => 1, | 261 | 'id' => 1, |
251 | 'title'=> t('The personal, minimalist, super-fast, database free, bookmarking service'), | 262 | 'title' => t('The personal, minimalist, super-fast, database free, bookmarking service'), |
252 | 'url'=>'https://shaarli.readthedocs.io', | 263 | 'url' => 'https://shaarli.readthedocs.io', |
253 | 'description'=>t('Welcome to Shaarli! This is your first public bookmark. To edit or delete me, you must first login. | 264 | 'description' => t( |
265 | 'Welcome to Shaarli! This is your first public bookmark. ' | ||
266 | . 'To edit or delete me, you must first login. | ||
254 | 267 | ||
255 | To learn how to use Shaarli, consult the link "Documentation" at the bottom of this page. | 268 | To learn how to use Shaarli, consult the link "Documentation" at the bottom of this page. |
256 | 269 | ||
257 | You use the community supported version of the original Shaarli project, by Sebastien Sauvage.'), | 270 | You use the community supported version of the original Shaarli project, by Sebastien Sauvage.' |
258 | 'private'=>0, | 271 | ), |
259 | 'created'=> new DateTime(), | 272 | 'private' => 0, |
260 | 'tags'=>'opensource software' | 273 | 'created' => new DateTime(), |
274 | 'tags' => 'opensource software' | ||
261 | ); | 275 | ); |
262 | $link['shorturl'] = link_small_hash($link['created'], $link['id']); | 276 | $link['shorturl'] = link_small_hash($link['created'], $link['id']); |
263 | $this->links[1] = $link; | 277 | $this->links[1] = $link; |
264 | 278 | ||
265 | $link = array( | 279 | $link = array( |
266 | 'id' => 0, | 280 | 'id' => 0, |
267 | 'title'=> t('My secret stuff... - Pastebin.com'), | 281 | 'title' => t('My secret stuff... - Pastebin.com'), |
268 | 'url'=>'http://sebsauvage.net/paste/?8434b27936c09649#bR7XsXhoTiLcqCpQbmOpBi3rq2zzQUC5hBI7ZT1O3x8=', | 282 | 'url' => 'http://sebsauvage.net/paste/?8434b27936c09649#bR7XsXhoTiLcqCpQbmOpBi3rq2zzQUC5hBI7ZT1O3x8=', |
269 | 'description'=> t('Shhhh! I\'m a private link only YOU can see. You can delete me too.'), | 283 | 'description' => t('Shhhh! I\'m a private link only YOU can see. You can delete me too.'), |
270 | 'private'=>1, | 284 | 'private' => 1, |
271 | 'created'=> new DateTime('1 minute ago'), | 285 | 'created' => new DateTime('1 minute ago'), |
272 | 'tags'=>'secretstuff', | 286 | 'tags' => 'secretstuff', |
273 | ); | 287 | ); |
274 | $link['shorturl'] = link_small_hash($link['created'], $link['id']); | 288 | $link['shorturl'] = link_small_hash($link['created'], $link['id']); |
275 | $this->links[0] = $link; | 289 | $this->links[0] = $link; |
@@ -295,7 +309,7 @@ You use the community supported version of the original Shaarli project, by Seba | |||
295 | 309 | ||
296 | $toremove = array(); | 310 | $toremove = array(); |
297 | foreach ($this->links as $key => &$link) { | 311 | foreach ($this->links as $key => &$link) { |
298 | if (! $this->loggedIn && $link['private'] != 0) { | 312 | if (!$this->loggedIn && $link['private'] != 0) { |
299 | // Transition for not upgraded databases. | 313 | // Transition for not upgraded databases. |
300 | unset($this->links[$key]); | 314 | unset($this->links[$key]); |
301 | continue; | 315 | continue; |
@@ -305,7 +319,7 @@ You use the community supported version of the original Shaarli project, by Seba | |||
305 | sanitizeLink($link); | 319 | sanitizeLink($link); |
306 | 320 | ||
307 | // Remove private tags if the user is not logged in. | 321 | // Remove private tags if the user is not logged in. |
308 | if (! $this->loggedIn) { | 322 | if (!$this->loggedIn) { |
309 | $link['tags'] = preg_replace('/(^|\s+)\.[^($|\s)]+\s*/', ' ', $link['tags']); | 323 | $link['tags'] = preg_replace('/(^|\s+)\.[^($|\s)]+\s*/', ' ', $link['tags']); |
310 | } | 324 | } |
311 | 325 | ||
@@ -317,16 +331,15 @@ You use the community supported version of the original Shaarli project, by Seba | |||
317 | } else { | 331 | } else { |
318 | $link['real_url'] .= $link['url']; | 332 | $link['real_url'] .= $link['url']; |
319 | } | 333 | } |
320 | } | 334 | } else { |
321 | else { | ||
322 | $link['real_url'] = $link['url']; | 335 | $link['real_url'] = $link['url']; |
323 | } | 336 | } |
324 | 337 | ||
325 | // To be able to load links before running the update, and prepare the update | 338 | // To be able to load links before running the update, and prepare the update |
326 | if (! isset($link['created'])) { | 339 | if (!isset($link['created'])) { |
327 | $link['id'] = $link['linkdate']; | 340 | $link['id'] = $link['linkdate']; |
328 | $link['created'] = DateTime::createFromFormat(self::LINK_DATE_FORMAT, $link['linkdate']); | 341 | $link['created'] = DateTime::createFromFormat(self::LINK_DATE_FORMAT, $link['linkdate']); |
329 | if (! empty($link['updated'])) { | 342 | if (!empty($link['updated'])) { |
330 | $link['updated'] = DateTime::createFromFormat(self::LINK_DATE_FORMAT, $link['updated']); | 343 | $link['updated'] = DateTime::createFromFormat(self::LINK_DATE_FORMAT, $link['updated']); |
331 | } | 344 | } |
332 | $link['shorturl'] = smallHash($link['linkdate']); | 345 | $link['shorturl'] = smallHash($link['linkdate']); |
@@ -403,7 +416,8 @@ You use the community supported version of the original Shaarli project, by Seba | |||
403 | * | 416 | * |
404 | * @return array list of shaare found. | 417 | * @return array list of shaare found. |
405 | */ | 418 | */ |
406 | public function filterDay($request) { | 419 | public function filterDay($request) |
420 | { | ||
407 | $linkFilter = new LinkFilter($this->links); | 421 | $linkFilter = new LinkFilter($this->links); |
408 | return $linkFilter->filter(LinkFilter::$FILTER_DAY, $request); | 422 | return $linkFilter->filter(LinkFilter::$FILTER_DAY, $request); |
409 | } | 423 | } |
@@ -411,17 +425,22 @@ You use the community supported version of the original Shaarli project, by Seba | |||
411 | /** | 425 | /** |
412 | * Filter links according to search parameters. | 426 | * Filter links according to search parameters. |
413 | * | 427 | * |
414 | * @param array $filterRequest Search request content. Supported keys: | 428 | * @param array $filterRequest Search request content. Supported keys: |
415 | * - searchtags: list of tags | 429 | * - searchtags: list of tags |
416 | * - searchterm: term search | 430 | * - searchterm: term search |
417 | * @param bool $casesensitive Optional: Perform case sensitive filter | 431 | * @param bool $casesensitive Optional: Perform case sensitive filter |
418 | * @param string $visibility return only all/private/public links | 432 | * @param string $visibility return only all/private/public links |
419 | * @param string $untaggedonly return only untagged links | 433 | * @param bool $untaggedonly return only untagged links |
420 | * | 434 | * |
421 | * @return array filtered links, all links if no suitable filter was provided. | 435 | * @return array filtered links, all links if no suitable filter was provided. |
422 | */ | 436 | */ |
423 | public function filterSearch($filterRequest = array(), $casesensitive = false, $visibility = 'all', $untaggedonly = false) | 437 | public function filterSearch( |
424 | { | 438 | $filterRequest = array(), |
439 | $casesensitive = false, | ||
440 | $visibility = 'all', | ||
441 | $untaggedonly = false | ||
442 | ) { | ||
443 | |||
425 | // Filter link database according to parameters. | 444 | // Filter link database according to parameters. |
426 | $searchtags = isset($filterRequest['searchtags']) ? escape($filterRequest['searchtags']) : ''; | 445 | $searchtags = isset($filterRequest['searchtags']) ? escape($filterRequest['searchtags']) : ''; |
427 | $searchterm = isset($filterRequest['searchterm']) ? escape($filterRequest['searchterm']) : ''; | 446 | $searchterm = isset($filterRequest['searchterm']) ? escape($filterRequest['searchterm']) : ''; |
@@ -437,8 +456,8 @@ You use the community supported version of the original Shaarli project, by Seba | |||
437 | /** | 456 | /** |
438 | * Returns the list tags appearing in the links with the given tags | 457 | * Returns the list tags appearing in the links with the given tags |
439 | * | 458 | * |
440 | * @param array $filteringTags tags selecting the links to consider | 459 | * @param array $filteringTags tags selecting the links to consider |
441 | * @param string $visibility process only all/private/public links | 460 | * @param string $visibility process only all/private/public links |
442 | * | 461 | * |
443 | * @return array tag => linksCount | 462 | * @return array tag => linksCount |
444 | */ | 463 | */ |
@@ -492,8 +511,7 @@ You use the community supported version of the original Shaarli project, by Seba | |||
492 | $delete = empty($to); | 511 | $delete = empty($to); |
493 | // True for case-sensitive tag search. | 512 | // True for case-sensitive tag search. |
494 | $linksToAlter = $this->filterSearch(['searchtags' => $from], true); | 513 | $linksToAlter = $this->filterSearch(['searchtags' => $from], true); |
495 | foreach($linksToAlter as $key => &$value) | 514 | foreach ($linksToAlter as $key => &$value) { |
496 | { | ||
497 | $tags = preg_split('/\s+/', trim($value['tags'])); | 515 | $tags = preg_split('/\s+/', trim($value['tags'])); |
498 | if (($pos = array_search($from, $tags)) !== false) { | 516 | if (($pos = array_search($from, $tags)) !== false) { |
499 | if ($delete) { | 517 | if ($delete) { |
@@ -536,7 +554,10 @@ You use the community supported version of the original Shaarli project, by Seba | |||
536 | { | 554 | { |
537 | $order = $order === 'ASC' ? -1 : 1; | 555 | $order = $order === 'ASC' ? -1 : 1; |
538 | // Reorder array by dates. | 556 | // Reorder array by dates. |
539 | usort($this->links, function($a, $b) use ($order) { | 557 | usort($this->links, function ($a, $b) use ($order) { |
558 | if (isset($a['sticky']) && isset($b['sticky']) && $a['sticky'] !== $b['sticky']) { | ||
559 | return $a['sticky'] ? -1 : 1; | ||
560 | } | ||
540 | return $a['created'] < $b['created'] ? 1 * $order : -1 * $order; | 561 | return $a['created'] < $b['created'] ? 1 * $order : -1 * $order; |
541 | }); | 562 | }); |
542 | 563 | ||
diff --git a/application/LinkFilter.php b/application/bookmark/LinkFilter.php index e52239b8..9b966307 100644 --- a/application/LinkFilter.php +++ b/application/bookmark/LinkFilter.php | |||
@@ -1,5 +1,10 @@ | |||
1 | <?php | 1 | <?php |
2 | 2 | ||
3 | namespace Shaarli\Bookmark; | ||
4 | |||
5 | use Exception; | ||
6 | use Shaarli\Bookmark\Exception\LinkNotFoundException; | ||
7 | |||
3 | /** | 8 | /** |
4 | * Class LinkFilter. | 9 | * Class LinkFilter. |
5 | * | 10 | * |
@@ -10,22 +15,22 @@ class LinkFilter | |||
10 | /** | 15 | /** |
11 | * @var string permalinks. | 16 | * @var string permalinks. |
12 | */ | 17 | */ |
13 | public static $FILTER_HASH = 'permalink'; | 18 | public static $FILTER_HASH = 'permalink'; |
14 | 19 | ||
15 | /** | 20 | /** |
16 | * @var string text search. | 21 | * @var string text search. |
17 | */ | 22 | */ |
18 | public static $FILTER_TEXT = 'fulltext'; | 23 | public static $FILTER_TEXT = 'fulltext'; |
19 | 24 | ||
20 | /** | 25 | /** |
21 | * @var string tag filter. | 26 | * @var string tag filter. |
22 | */ | 27 | */ |
23 | public static $FILTER_TAG = 'tags'; | 28 | public static $FILTER_TAG = 'tags'; |
24 | 29 | ||
25 | /** | 30 | /** |
26 | * @var string filter by day. | 31 | * @var string filter by day. |
27 | */ | 32 | */ |
28 | public static $FILTER_DAY = 'FILTER_DAY'; | 33 | public static $FILTER_DAY = 'FILTER_DAY'; |
29 | 34 | ||
30 | /** | 35 | /** |
31 | * @var string Allowed characters for hashtags (regex syntax). | 36 | * @var string Allowed characters for hashtags (regex syntax). |
@@ -58,11 +63,11 @@ class LinkFilter | |||
58 | */ | 63 | */ |
59 | public function filter($type, $request, $casesensitive = false, $visibility = 'all', $untaggedonly = false) | 64 | public function filter($type, $request, $casesensitive = false, $visibility = 'all', $untaggedonly = false) |
60 | { | 65 | { |
61 | if (! in_array($visibility, ['all', 'public', 'private'])) { | 66 | if (!in_array($visibility, ['all', 'public', 'private'])) { |
62 | $visibility = 'all'; | 67 | $visibility = 'all'; |
63 | } | 68 | } |
64 | 69 | ||
65 | switch($type) { | 70 | switch ($type) { |
66 | case self::$FILTER_HASH: | 71 | case self::$FILTER_HASH: |
67 | return $this->filterSmallHash($request); | 72 | return $this->filterSmallHash($request); |
68 | case self::$FILTER_TAG | self::$FILTER_TEXT: // == "vuotext" | 73 | case self::$FILTER_TAG | self::$FILTER_TEXT: // == "vuotext" |
@@ -117,7 +122,7 @@ class LinkFilter | |||
117 | foreach ($this->links as $key => $value) { | 122 | foreach ($this->links as $key => $value) { |
118 | if ($value['private'] && $visibility === 'private') { | 123 | if ($value['private'] && $visibility === 'private') { |
119 | $out[$key] = $value; | 124 | $out[$key] = $value; |
120 | } elseif (! $value['private'] && $visibility === 'public') { | 125 | } elseif (!$value['private'] && $visibility === 'public') { |
121 | $out[$key] = $value; | 126 | $out[$key] = $value; |
122 | } | 127 | } |
123 | } | 128 | } |
@@ -132,7 +137,7 @@ class LinkFilter | |||
132 | * | 137 | * |
133 | * @return array $filtered array containing permalink data. | 138 | * @return array $filtered array containing permalink data. |
134 | * | 139 | * |
135 | * @throws LinkNotFoundException if the smallhash doesn't match any link. | 140 | * @throws \Shaarli\Bookmark\Exception\LinkNotFoundException if the smallhash doesn't match any link. |
136 | */ | 141 | */ |
137 | private function filterSmallHash($smallHash) | 142 | private function filterSmallHash($smallHash) |
138 | { | 143 | { |
@@ -169,7 +174,7 @@ class LinkFilter | |||
169 | * - see https://github.com/shaarli/Shaarli/issues/75 for examples | 174 | * - see https://github.com/shaarli/Shaarli/issues/75 for examples |
170 | * | 175 | * |
171 | * @param string $searchterms search query. | 176 | * @param string $searchterms search query. |
172 | * @param string $visibility Optional: return only all/private/public links. | 177 | * @param string $visibility Optional: return only all/private/public links. |
173 | * | 178 | * |
174 | * @return array search results. | 179 | * @return array search results. |
175 | */ | 180 | */ |
@@ -205,10 +210,9 @@ class LinkFilter | |||
205 | 210 | ||
206 | // Iterate over every stored link. | 211 | // Iterate over every stored link. |
207 | foreach ($this->links as $id => $link) { | 212 | foreach ($this->links as $id => $link) { |
208 | |||
209 | // ignore non private links when 'privatonly' is on. | 213 | // ignore non private links when 'privatonly' is on. |
210 | if ($visibility !== 'all') { | 214 | if ($visibility !== 'all') { |
211 | if (! $link['private'] && $visibility === 'private') { | 215 | if (!$link['private'] && $visibility === 'private') { |
212 | continue; | 216 | continue; |
213 | } elseif ($link['private'] && $visibility === 'public') { | 217 | } elseif ($link['private'] && $visibility === 'public') { |
214 | continue; | 218 | continue; |
@@ -251,17 +255,19 @@ class LinkFilter | |||
251 | 255 | ||
252 | /** | 256 | /** |
253 | * generate a regex fragment out of a tag | 257 | * generate a regex fragment out of a tag |
258 | * | ||
254 | * @param string $tag to to generate regexs from. may start with '-' to negate, contain '*' as wildcard | 259 | * @param string $tag to to generate regexs from. may start with '-' to negate, contain '*' as wildcard |
260 | * | ||
255 | * @return string generated regex fragment | 261 | * @return string generated regex fragment |
256 | */ | 262 | */ |
257 | private static function tag2regex($tag) | 263 | private static function tag2regex($tag) |
258 | { | 264 | { |
259 | $len = strlen($tag); | 265 | $len = strlen($tag); |
260 | if(!$len || $tag === "-" || $tag === "*"){ | 266 | if (!$len || $tag === "-" || $tag === "*") { |
261 | // nothing to search, return empty regex | 267 | // nothing to search, return empty regex |
262 | return ''; | 268 | return ''; |
263 | } | 269 | } |
264 | if($tag[0] === "-") { | 270 | if ($tag[0] === "-") { |
265 | // query is negated | 271 | // query is negated |
266 | $i = 1; // use offset to start after '-' character | 272 | $i = 1; // use offset to start after '-' character |
267 | $regex = '(?!'; // create negative lookahead | 273 | $regex = '(?!'; // create negative lookahead |
@@ -271,14 +277,14 @@ class LinkFilter | |||
271 | } | 277 | } |
272 | $regex .= '.*(?:^| )'; // before tag may only be a space or the beginning | 278 | $regex .= '.*(?:^| )'; // before tag may only be a space or the beginning |
273 | // iterate over string, separating it into placeholder and content | 279 | // iterate over string, separating it into placeholder and content |
274 | for(; $i < $len; $i++){ | 280 | for (; $i < $len; $i++) { |
275 | if($tag[$i] === '*'){ | 281 | if ($tag[$i] === '*') { |
276 | // placeholder found | 282 | // placeholder found |
277 | $regex .= '[^ ]*?'; | 283 | $regex .= '[^ ]*?'; |
278 | } else { | 284 | } else { |
279 | // regular characters | 285 | // regular characters |
280 | $offset = strpos($tag, '*', $i); | 286 | $offset = strpos($tag, '*', $i); |
281 | if($offset === false){ | 287 | if ($offset === false) { |
282 | // no placeholder found, set offset to end of string | 288 | // no placeholder found, set offset to end of string |
283 | $offset = $len; | 289 | $offset = $len; |
284 | } | 290 | } |
@@ -310,19 +316,19 @@ class LinkFilter | |||
310 | { | 316 | { |
311 | // get single tags (we may get passed an array, even though the docs say different) | 317 | // get single tags (we may get passed an array, even though the docs say different) |
312 | $inputTags = $tags; | 318 | $inputTags = $tags; |
313 | if(!is_array($tags)) { | 319 | if (!is_array($tags)) { |
314 | // we got an input string, split tags | 320 | // we got an input string, split tags |
315 | $inputTags = preg_split('/(?:\s+)|,/', $inputTags, -1, PREG_SPLIT_NO_EMPTY); | 321 | $inputTags = preg_split('/(?:\s+)|,/', $inputTags, -1, PREG_SPLIT_NO_EMPTY); |
316 | } | 322 | } |
317 | 323 | ||
318 | if(!count($inputTags)){ | 324 | if (!count($inputTags)) { |
319 | // no input tags | 325 | // no input tags |
320 | return $this->noFilter($visibility); | 326 | return $this->noFilter($visibility); |
321 | } | 327 | } |
322 | 328 | ||
323 | // build regex from all tags | 329 | // build regex from all tags |
324 | $re = '/^' . implode(array_map("self::tag2regex", $inputTags)) . '.*$/'; | 330 | $re = '/^' . implode(array_map("self::tag2regex", $inputTags)) . '.*$/'; |
325 | if(!$casesensitive) { | 331 | if (!$casesensitive) { |
326 | // make regex case insensitive | 332 | // make regex case insensitive |
327 | $re .= 'i'; | 333 | $re .= 'i'; |
328 | } | 334 | } |
@@ -335,14 +341,14 @@ class LinkFilter | |||
335 | // check level of visibility | 341 | // check level of visibility |
336 | // ignore non private links when 'privateonly' is on. | 342 | // ignore non private links when 'privateonly' is on. |
337 | if ($visibility !== 'all') { | 343 | if ($visibility !== 'all') { |
338 | if (! $link['private'] && $visibility === 'private') { | 344 | if (!$link['private'] && $visibility === 'private') { |
339 | continue; | 345 | continue; |
340 | } elseif ($link['private'] && $visibility === 'public') { | 346 | } elseif ($link['private'] && $visibility === 'public') { |
341 | continue; | 347 | continue; |
342 | } | 348 | } |
343 | } | 349 | } |
344 | $search = $link['tags']; // build search string, start with tags of current link | 350 | $search = $link['tags']; // build search string, start with tags of current link |
345 | if(strlen(trim($link['description'])) && strpos($link['description'], '#') !== false){ | 351 | if (strlen(trim($link['description'])) && strpos($link['description'], '#') !== false) { |
346 | // description given and at least one possible tag found | 352 | // description given and at least one possible tag found |
347 | $descTags = array(); | 353 | $descTags = array(); |
348 | // find all tags in the form of #tag in the description | 354 | // find all tags in the form of #tag in the description |
@@ -351,13 +357,13 @@ class LinkFilter | |||
351 | $link['description'], | 357 | $link['description'], |
352 | $descTags | 358 | $descTags |
353 | ); | 359 | ); |
354 | if(count($descTags[1])){ | 360 | if (count($descTags[1])) { |
355 | // there were some tags in the description, add them to the search string | 361 | // there were some tags in the description, add them to the search string |
356 | $search .= ' ' . implode(' ', $descTags[1]); | 362 | $search .= ' ' . implode(' ', $descTags[1]); |
357 | } | 363 | } |
358 | }; | 364 | }; |
359 | // match regular expression with search string | 365 | // match regular expression with search string |
360 | if(!preg_match($re, $search)){ | 366 | if (!preg_match($re, $search)) { |
361 | // this entry does _not_ match our regex | 367 | // this entry does _not_ match our regex |
362 | continue; | 368 | continue; |
363 | } | 369 | } |
@@ -378,7 +384,7 @@ class LinkFilter | |||
378 | $filtered = []; | 384 | $filtered = []; |
379 | foreach ($this->links as $key => $link) { | 385 | foreach ($this->links as $key => $link) { |
380 | if ($visibility !== 'all') { | 386 | if ($visibility !== 'all') { |
381 | if (! $link['private'] && $visibility === 'private') { | 387 | if (!$link['private'] && $visibility === 'private') { |
382 | continue; | 388 | continue; |
383 | } elseif ($link['private'] && $visibility === 'public') { | 389 | } elseif ($link['private'] && $visibility === 'public') { |
384 | continue; | 390 | continue; |
@@ -407,7 +413,7 @@ class LinkFilter | |||
407 | */ | 413 | */ |
408 | public function filterDay($day) | 414 | public function filterDay($day) |
409 | { | 415 | { |
410 | if (! checkDateFormat('Ymd', $day)) { | 416 | if (!checkDateFormat('Ymd', $day)) { |
411 | throw new Exception('Invalid date format'); | 417 | throw new Exception('Invalid date format'); |
412 | } | 418 | } |
413 | 419 | ||
@@ -441,14 +447,3 @@ class LinkFilter | |||
441 | return preg_split('/\s+/', $tagsOut, -1, PREG_SPLIT_NO_EMPTY); | 447 | return preg_split('/\s+/', $tagsOut, -1, PREG_SPLIT_NO_EMPTY); |
442 | } | 448 | } |
443 | } | 449 | } |
444 | |||
445 | class LinkNotFoundException extends Exception | ||
446 | { | ||
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 | } | ||
454 | } | ||
diff --git a/application/LinkUtils.php b/application/bookmark/LinkUtils.php index 4df5c0ca..de5b61cb 100644 --- a/application/LinkUtils.php +++ b/application/bookmark/LinkUtils.php | |||
@@ -1,11 +1,13 @@ | |||
1 | <?php | 1 | <?php |
2 | 2 | ||
3 | use Shaarli\Bookmark\LinkDB; | ||
4 | |||
3 | /** | 5 | /** |
4 | * Get cURL callback function for CURLOPT_WRITEFUNCTION | 6 | * Get cURL callback function for CURLOPT_WRITEFUNCTION |
5 | * | 7 | * |
6 | * @param string $charset to extract from the downloaded page (reference) | 8 | * @param string $charset to extract from the downloaded page (reference) |
7 | * @param string $title to extract from the downloaded page (reference) | 9 | * @param string $title to extract from the downloaded page (reference) |
8 | * @param string $curlGetInfo Optionnaly overrides curl_getinfo function | 10 | * @param string $curlGetInfo Optionally overrides curl_getinfo function |
9 | * | 11 | * |
10 | * @return Closure | 12 | * @return Closure |
11 | */ | 13 | */ |
@@ -23,7 +25,7 @@ function get_curl_download_callback(&$charset, &$title, $curlGetInfo = 'curl_get | |||
23 | * | 25 | * |
24 | * @return int|bool length of $data or false if we need to stop the download | 26 | * @return int|bool length of $data or false if we need to stop the download |
25 | */ | 27 | */ |
26 | return function(&$ch, $data) use ($curlGetInfo, &$charset, &$title, &$isRedirected) { | 28 | return function (&$ch, $data) use ($curlGetInfo, &$charset, &$title, &$isRedirected) { |
27 | $responseCode = $curlGetInfo($ch, CURLINFO_RESPONSE_CODE); | 29 | $responseCode = $curlGetInfo($ch, CURLINFO_RESPONSE_CODE); |
28 | if (!empty($responseCode) && in_array($responseCode, [301, 302])) { | 30 | if (!empty($responseCode) && in_array($responseCode, [301, 302])) { |
29 | $isRedirected = true; | 31 | $isRedirected = true; |
@@ -196,12 +198,13 @@ function space2nbsp($text) | |||
196 | * | 198 | * |
197 | * @param string $description shaare's description. | 199 | * @param string $description shaare's description. |
198 | * @param string $redirector if a redirector is set, use it to gerenate links. | 200 | * @param string $redirector if a redirector is set, use it to gerenate links. |
199 | * @param bool $urlEncode Use `urlencode()` on the URL after the redirector or not. | 201 | * @param bool $urlEncode Use `urlencode()` on the URL after the redirector or not. |
200 | * @param string $indexUrl URL to Shaarli's index. | 202 | * @param string $indexUrl URL to Shaarli's index. |
201 | 203 | ||
202 | * @return string formatted description. | 204 | * @return string formatted description. |
203 | */ | 205 | */ |
204 | function format_description($description, $redirector = '', $urlEncode = true, $indexUrl = '') { | 206 | function format_description($description, $redirector = '', $urlEncode = true, $indexUrl = '') |
207 | { | ||
205 | return nl2br(space2nbsp(hashtag_autolink(text2clickable($description, $redirector, $urlEncode), $indexUrl))); | 208 | return nl2br(space2nbsp(hashtag_autolink(text2clickable($description, $redirector, $urlEncode), $indexUrl))); |
206 | } | 209 | } |
207 | 210 | ||
diff --git a/application/bookmark/exception/LinkNotFoundException.php b/application/bookmark/exception/LinkNotFoundException.php new file mode 100644 index 00000000..f9414428 --- /dev/null +++ b/application/bookmark/exception/LinkNotFoundException.php | |||
@@ -0,0 +1,15 @@ | |||
1 | <?php | ||
2 | namespace Shaarli\Bookmark\Exception; | ||
3 | |||
4 | use Exception; | ||
5 | |||
6 | class LinkNotFoundException extends Exception | ||
7 | { | ||
8 | /** | ||
9 | * LinkNotFoundException constructor. | ||
10 | */ | ||
11 | public function __construct() | ||
12 | { | ||
13 | $this->message = t('The link you are trying to reach does not exist or has been deleted.'); | ||
14 | } | ||
15 | } | ||
diff --git a/application/config/ConfigJson.php b/application/config/ConfigJson.php index 8c8d5610..4509357c 100644 --- a/application/config/ConfigJson.php +++ b/application/config/ConfigJson.php | |||
@@ -47,7 +47,7 @@ class ConfigJson implements ConfigIO | |||
47 | $print = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0; | 47 | $print = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0; |
48 | $data = self::getPhpHeaders() . json_encode($conf, $print) . self::getPhpSuffix(); | 48 | $data = self::getPhpHeaders() . json_encode($conf, $print) . self::getPhpSuffix(); |
49 | if (!file_put_contents($filepath, $data)) { | 49 | if (!file_put_contents($filepath, $data)) { |
50 | throw new \IOException( | 50 | throw new \Shaarli\Exceptions\IOException( |
51 | $filepath, | 51 | $filepath, |
52 | t('Shaarli could not create the config file. '. | 52 | t('Shaarli could not create the config file. '. |
53 | '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.') |
diff --git a/application/config/ConfigManager.php b/application/config/ConfigManager.php index 82f4a368..e6c35073 100644 --- a/application/config/ConfigManager.php +++ b/application/config/ConfigManager.php | |||
@@ -148,6 +148,33 @@ class ConfigManager | |||
148 | } | 148 | } |
149 | 149 | ||
150 | /** | 150 | /** |
151 | * Remove a config element from the config file. | ||
152 | * | ||
153 | * @param string $setting Asked setting, keys separated with dots. | ||
154 | * @param bool $write Write the new setting in the config file, default false. | ||
155 | * @param bool $isLoggedIn User login state, default false. | ||
156 | * | ||
157 | * @throws \Exception Invalid | ||
158 | */ | ||
159 | public function remove($setting, $write = false, $isLoggedIn = false) | ||
160 | { | ||
161 | if (empty($setting) || ! is_string($setting)) { | ||
162 | throw new \Exception(t('Invalid setting key parameter. String expected, got: '). gettype($setting)); | ||
163 | } | ||
164 | |||
165 | // During the ConfigIO transition, map legacy settings to the new ones. | ||
166 | if ($this->configIO instanceof ConfigPhp && isset(ConfigPhp::$LEGACY_KEYS_MAPPING[$setting])) { | ||
167 | $setting = ConfigPhp::$LEGACY_KEYS_MAPPING[$setting]; | ||
168 | } | ||
169 | |||
170 | $settings = explode('.', $setting); | ||
171 | self::removeConfig($settings, $this->loadedConfig); | ||
172 | if ($write) { | ||
173 | $this->write($isLoggedIn); | ||
174 | } | ||
175 | } | ||
176 | |||
177 | /** | ||
151 | * Check if a settings exists. | 178 | * Check if a settings exists. |
152 | * | 179 | * |
153 | * Supports nested settings with dot separated keys. | 180 | * Supports nested settings with dot separated keys. |
@@ -180,7 +207,7 @@ class ConfigManager | |||
180 | * | 207 | * |
181 | * @throws MissingFieldConfigException: a mandatory field has not been provided in $conf. | 208 | * @throws MissingFieldConfigException: a mandatory field has not been provided in $conf. |
182 | * @throws UnauthorizedConfigException: user is not authorize to change configuration. | 209 | * @throws UnauthorizedConfigException: user is not authorize to change configuration. |
183 | * @throws \IOException: an error occurred while writing the new config file. | 210 | * @throws \Shaarli\Exceptions\IOException: an error occurred while writing the new config file. |
184 | */ | 211 | */ |
185 | public function write($isLoggedIn) | 212 | public function write($isLoggedIn) |
186 | { | 213 | { |
@@ -272,7 +299,7 @@ class ConfigManager | |||
272 | * | 299 | * |
273 | * @param array $settings Ordered array which contains keys to find. | 300 | * @param array $settings Ordered array which contains keys to find. |
274 | * @param mixed $value | 301 | * @param mixed $value |
275 | * @param array $conf Loaded settings, then sub-array. | 302 | * @param array $conf Loaded settings, then sub-array. |
276 | * | 303 | * |
277 | * @return mixed Found setting or NOT_FOUND flag. | 304 | * @return mixed Found setting or NOT_FOUND flag. |
278 | */ | 305 | */ |
@@ -290,6 +317,27 @@ class ConfigManager | |||
290 | } | 317 | } |
291 | 318 | ||
292 | /** | 319 | /** |
320 | * Recursive function which find asked setting in the loaded config and deletes it. | ||
321 | * | ||
322 | * @param array $settings Ordered array which contains keys to find. | ||
323 | * @param array $conf Loaded settings, then sub-array. | ||
324 | * | ||
325 | * @return mixed Found setting or NOT_FOUND flag. | ||
326 | */ | ||
327 | protected static function removeConfig($settings, &$conf) | ||
328 | { | ||
329 | if (!is_array($settings) || count($settings) == 0) { | ||
330 | return self::$NOT_FOUND; | ||
331 | } | ||
332 | |||
333 | $setting = array_shift($settings); | ||
334 | if (count($settings) > 0) { | ||
335 | return self::removeConfig($settings, $conf[$setting]); | ||
336 | } | ||
337 | unset($conf[$setting]); | ||
338 | } | ||
339 | |||
340 | /** | ||
293 | * Set a bunch of default values allowing Shaarli to start without a config file. | 341 | * Set a bunch of default values allowing Shaarli to start without a config file. |
294 | */ | 342 | */ |
295 | protected function setDefaultValues() | 343 | protected function setDefaultValues() |
@@ -333,12 +381,12 @@ class ConfigManager | |||
333 | // default state of the 'remember me' checkbox of the login form | 381 | // default state of the 'remember me' checkbox of the login form |
334 | $this->setEmpty('privacy.remember_user_default', true); | 382 | $this->setEmpty('privacy.remember_user_default', true); |
335 | 383 | ||
336 | $this->setEmpty('thumbnail.enable_thumbnails', true); | ||
337 | $this->setEmpty('thumbnail.enable_localcache', true); | ||
338 | |||
339 | $this->setEmpty('redirector.url', ''); | 384 | $this->setEmpty('redirector.url', ''); |
340 | $this->setEmpty('redirector.encode_url', true); | 385 | $this->setEmpty('redirector.encode_url', true); |
341 | 386 | ||
387 | $this->setEmpty('thumbnails.width', '125'); | ||
388 | $this->setEmpty('thumbnails.height', '90'); | ||
389 | |||
342 | $this->setEmpty('translation.language', 'auto'); | 390 | $this->setEmpty('translation.language', 'auto'); |
343 | $this->setEmpty('translation.mode', 'php'); | 391 | $this->setEmpty('translation.mode', 'php'); |
344 | $this->setEmpty('translation.extensions', []); | 392 | $this->setEmpty('translation.extensions', []); |
diff --git a/application/config/ConfigPhp.php b/application/config/ConfigPhp.php index 8add8bcd..cad34594 100644 --- a/application/config/ConfigPhp.php +++ b/application/config/ConfigPhp.php | |||
@@ -27,7 +27,7 @@ class ConfigPhp implements ConfigIO | |||
27 | /** | 27 | /** |
28 | * Map legacy config keys with the new ones. | 28 | * Map legacy config keys with the new ones. |
29 | * If ConfigPhp is used, getting <newkey> will actually look for <legacykey>. | 29 | * If ConfigPhp is used, getting <newkey> will actually look for <legacykey>. |
30 | * The Updater will use this array to transform keys when switching to JSON. | 30 | * The updater will use this array to transform keys when switching to JSON. |
31 | * | 31 | * |
32 | * @var array current key => legacy key. | 32 | * @var array current key => legacy key. |
33 | */ | 33 | */ |
@@ -104,19 +104,27 @@ class ConfigPhp implements ConfigIO | |||
104 | 104 | ||
105 | // Store all $conf['config'] | 105 | // Store all $conf['config'] |
106 | foreach ($conf['config'] as $key => $value) { | 106 | foreach ($conf['config'] as $key => $value) { |
107 | $configStr .= '$GLOBALS[\'config\'][\''. $key .'\'] = '.var_export($conf['config'][$key], true).';'. PHP_EOL; | 107 | $configStr .= '$GLOBALS[\'config\'][\'' |
108 | . $key | ||
109 | .'\'] = ' | ||
110 | .var_export($conf['config'][$key], true).';' | ||
111 | . PHP_EOL; | ||
108 | } | 112 | } |
109 | 113 | ||
110 | if (isset($conf['plugins'])) { | 114 | if (isset($conf['plugins'])) { |
111 | foreach ($conf['plugins'] as $key => $value) { | 115 | foreach ($conf['plugins'] as $key => $value) { |
112 | $configStr .= '$GLOBALS[\'plugins\'][\''. $key .'\'] = '.var_export($conf['plugins'][$key], true).';'. PHP_EOL; | 116 | $configStr .= '$GLOBALS[\'plugins\'][\'' |
117 | . $key | ||
118 | .'\'] = ' | ||
119 | .var_export($conf['plugins'][$key], true).';' | ||
120 | . PHP_EOL; | ||
113 | } | 121 | } |
114 | } | 122 | } |
115 | 123 | ||
116 | if (!file_put_contents($filepath, $configStr) | 124 | if (!file_put_contents($filepath, $configStr) |
117 | || strcmp(file_get_contents($filepath), $configStr) != 0 | 125 | || strcmp(file_get_contents($filepath), $configStr) != 0 |
118 | ) { | 126 | ) { |
119 | throw new \IOException( | 127 | throw new \Shaarli\Exceptions\IOException( |
120 | $filepath, | 128 | $filepath, |
121 | t('Shaarli could not create the config file. '. | 129 | 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.') | 130 | 'Please make sure Shaarli has the right to write in the folder is it installed in.') |
diff --git a/application/config/ConfigPlugin.php b/application/config/ConfigPlugin.php index b3d9752b..dbb24937 100644 --- a/application/config/ConfigPlugin.php +++ b/application/config/ConfigPlugin.php | |||
@@ -34,8 +34,7 @@ function save_plugin_config($formData) | |||
34 | // If there is no order, it means a disabled plugin has been enabled. | 34 | // If there is no order, it means a disabled plugin has been enabled. |
35 | if (isset($formData['order_' . $key])) { | 35 | if (isset($formData['order_' . $key])) { |
36 | $plugins[(int) $formData['order_' . $key]] = $key; | 36 | $plugins[(int) $formData['order_' . $key]] = $key; |
37 | } | 37 | } else { |
38 | else { | ||
39 | $newEnabledPlugins[] = $key; | 38 | $newEnabledPlugins[] = $key; |
40 | } | 39 | } |
41 | } | 40 | } |
diff --git a/application/exceptions/IOException.php b/application/exceptions/IOException.php index 18e46b77..2aa25e5c 100644 --- a/application/exceptions/IOException.php +++ b/application/exceptions/IOException.php | |||
@@ -1,4 +1,7 @@ | |||
1 | <?php | 1 | <?php |
2 | namespace Shaarli\Exceptions; | ||
3 | |||
4 | use Exception; | ||
2 | 5 | ||
3 | /** | 6 | /** |
4 | * Exception class thrown when a filesystem access failure happens | 7 | * Exception class thrown when a filesystem access failure happens |
@@ -17,6 +20,6 @@ class IOException extends Exception | |||
17 | { | 20 | { |
18 | $this->path = $path; | 21 | $this->path = $path; |
19 | $this->message = empty($message) ? t('Error accessing') : $message; | 22 | $this->message = empty($message) ? t('Error accessing') : $message; |
20 | $this->message .= ' "' . $this->path .'"'; | 23 | $this->message .= ' "' . $this->path . '"'; |
21 | } | 24 | } |
22 | } | 25 | } |
diff --git a/application/Cache.php b/application/feed/Cache.php index e5d43e61..e5d43e61 100644 --- a/application/Cache.php +++ b/application/feed/Cache.php | |||
diff --git a/application/CachedPage.php b/application/feed/CachedPage.php index e11cc52d..d809bdd9 100644 --- a/application/CachedPage.php +++ b/application/feed/CachedPage.php | |||
@@ -1,4 +1,7 @@ | |||
1 | <?php | 1 | <?php |
2 | |||
3 | namespace Shaarli\Feed; | ||
4 | |||
2 | /** | 5 | /** |
3 | * Simple cache system, mainly for the RSS/ATOM feeds | 6 | * Simple cache system, mainly for the RSS/ATOM feeds |
4 | */ | 7 | */ |
@@ -24,7 +27,7 @@ class CachedPage | |||
24 | { | 27 | { |
25 | // TODO: check write access to the cache directory | 28 | // TODO: check write access to the cache directory |
26 | $this->cacheDir = $cacheDir; | 29 | $this->cacheDir = $cacheDir; |
27 | $this->filename = $this->cacheDir.'/'.sha1($url).'.cache'; | 30 | $this->filename = $this->cacheDir . '/' . sha1($url) . '.cache'; |
28 | $this->shouldBeCached = $shouldBeCached; | 31 | $this->shouldBeCached = $shouldBeCached; |
29 | } | 32 | } |
30 | 33 | ||
diff --git a/application/FeedBuilder.php b/application/feed/FeedBuilder.php index ebae18b4..b66f2f91 100644 --- a/application/FeedBuilder.php +++ b/application/feed/FeedBuilder.php | |||
@@ -1,4 +1,7 @@ | |||
1 | <?php | 1 | <?php |
2 | namespace Shaarli\Feed; | ||
3 | |||
4 | use DateTime; | ||
2 | 5 | ||
3 | /** | 6 | /** |
4 | * FeedBuilder class. | 7 | * FeedBuilder class. |
@@ -28,7 +31,7 @@ class FeedBuilder | |||
28 | public static $DEFAULT_NB_LINKS = 50; | 31 | public static $DEFAULT_NB_LINKS = 50; |
29 | 32 | ||
30 | /** | 33 | /** |
31 | * @var LinkDB instance. | 34 | * @var \Shaarli\Bookmark\LinkDB instance. |
32 | */ | 35 | */ |
33 | protected $linkDB; | 36 | protected $linkDB; |
34 | 37 | ||
@@ -38,12 +41,12 @@ class FeedBuilder | |||
38 | protected $feedType; | 41 | protected $feedType; |
39 | 42 | ||
40 | /** | 43 | /** |
41 | * @var array $_SERVER. | 44 | * @var array $_SERVER |
42 | */ | 45 | */ |
43 | protected $serverInfo; | 46 | protected $serverInfo; |
44 | 47 | ||
45 | /** | 48 | /** |
46 | * @var array $_GET. | 49 | * @var array $_GET |
47 | */ | 50 | */ |
48 | protected $userInput; | 51 | protected $userInput; |
49 | 52 | ||
@@ -75,11 +78,12 @@ class FeedBuilder | |||
75 | /** | 78 | /** |
76 | * Feed constructor. | 79 | * Feed constructor. |
77 | * | 80 | * |
78 | * @param LinkDB $linkDB LinkDB instance. | 81 | * @param \Shaarli\Bookmark\LinkDB $linkDB LinkDB instance. |
79 | * @param string $feedType Type of feed. | 82 | * @param string $feedType Type of feed. |
80 | * @param array $serverInfo $_SERVER. | 83 | * @param array $serverInfo $_SERVER. |
81 | * @param array $userInput $_GET. | 84 | * @param array $userInput $_GET. |
82 | * @param boolean $isLoggedIn True if the user is currently logged in, false otherwise. | 85 | * @param boolean $isLoggedIn True if the user is currently logged in, |
86 | * false otherwise. | ||
83 | */ | 87 | */ |
84 | public function __construct($linkDB, $feedType, $serverInfo, $userInput, $isLoggedIn) | 88 | public function __construct($linkDB, $feedType, $serverInfo, $userInput, $isLoggedIn) |
85 | { | 89 | { |
@@ -124,7 +128,7 @@ class FeedBuilder | |||
124 | $data['show_dates'] = !$this->hideDates || $this->isLoggedIn; | 128 | $data['show_dates'] = !$this->hideDates || $this->isLoggedIn; |
125 | // Remove leading slash from REQUEST_URI. | 129 | // Remove leading slash from REQUEST_URI. |
126 | $data['self_link'] = escape(server_url($this->serverInfo)) | 130 | $data['self_link'] = escape(server_url($this->serverInfo)) |
127 | . escape($this->serverInfo['REQUEST_URI']); | 131 | . escape($this->serverInfo['REQUEST_URI']); |
128 | $data['index_url'] = $pageaddr; | 132 | $data['index_url'] = $pageaddr; |
129 | $data['usepermalinks'] = $this->usePermalinks === true; | 133 | $data['usepermalinks'] = $this->usePermalinks === true; |
130 | $data['links'] = $linkDisplayed; | 134 | $data['links'] = $linkDisplayed; |
@@ -142,18 +146,18 @@ class FeedBuilder | |||
142 | */ | 146 | */ |
143 | protected function buildItem($link, $pageaddr) | 147 | protected function buildItem($link, $pageaddr) |
144 | { | 148 | { |
145 | $link['guid'] = $pageaddr .'?'. $link['shorturl']; | 149 | $link['guid'] = $pageaddr . '?' . $link['shorturl']; |
146 | // Check for both signs of a note: starting with ? and 7 chars long. | 150 | // Check for both signs of a note: starting with ? and 7 chars long. |
147 | if ($link['url'][0] === '?' && strlen($link['url']) === 7) { | 151 | if ($link['url'][0] === '?' && strlen($link['url']) === 7) { |
148 | $link['url'] = $pageaddr . $link['url']; | 152 | $link['url'] = $pageaddr . $link['url']; |
149 | } | 153 | } |
150 | if ($this->usePermalinks === true) { | 154 | if ($this->usePermalinks === true) { |
151 | $permalink = '<a href="'. $link['url'] .'" title="'. t('Direct link') .'">'. t('Direct link') .'</a>'; | 155 | $permalink = '<a href="' . $link['url'] . '" title="' . t('Direct link') . '">' . t('Direct link') . '</a>'; |
152 | } else { | 156 | } else { |
153 | $permalink = '<a href="'. $link['guid'] .'" title="'. t('Permalink') .'">'. t('Permalink') .'</a>'; | 157 | $permalink = '<a href="' . $link['guid'] . '" title="' . t('Permalink') . '">' . t('Permalink') . '</a>'; |
154 | } | 158 | } |
155 | $link['description'] = format_description($link['description'], '', false, $pageaddr); | 159 | $link['description'] = format_description($link['description'], '', false, $pageaddr); |
156 | $link['description'] .= PHP_EOL .'<br>— '. $permalink; | 160 | $link['description'] .= PHP_EOL . '<br>— ' . $permalink; |
157 | 161 | ||
158 | $pubDate = $link['created']; | 162 | $pubDate = $link['created']; |
159 | $link['pub_iso_date'] = $this->getIsoDate($pubDate); | 163 | $link['pub_iso_date'] = $this->getIsoDate($pubDate); |
@@ -163,7 +167,7 @@ class FeedBuilder | |||
163 | $upDate = $link['updated']; | 167 | $upDate = $link['updated']; |
164 | $link['up_iso_date'] = $this->getIsoDate($upDate, DateTime::ATOM); | 168 | $link['up_iso_date'] = $this->getIsoDate($upDate, DateTime::ATOM); |
165 | } else { | 169 | } else { |
166 | $link['up_iso_date'] = $this->getIsoDate($pubDate, DateTime::ATOM);; | 170 | $link['up_iso_date'] = $this->getIsoDate($pubDate, DateTime::ATOM); |
167 | } | 171 | } |
168 | 172 | ||
169 | // Save the more recent item. | 173 | // Save the more recent item. |
@@ -222,11 +226,11 @@ class FeedBuilder | |||
222 | public function getTypeLanguage() | 226 | public function getTypeLanguage() |
223 | { | 227 | { |
224 | // Use the locale do define the language, if available. | 228 | // Use the locale do define the language, if available. |
225 | if (! empty($this->locale) && preg_match('/^\w{2}[_\-]\w{2}/', $this->locale)) { | 229 | if (!empty($this->locale) && preg_match('/^\w{2}[_\-]\w{2}/', $this->locale)) { |
226 | $length = ($this->feedType == self::$FEED_RSS) ? 5 : 2; | 230 | $length = ($this->feedType === self::$FEED_RSS) ? 5 : 2; |
227 | return str_replace('_', '-', substr($this->locale, 0, $length)); | 231 | return str_replace('_', '-', substr($this->locale, 0, $length)); |
228 | } | 232 | } |
229 | return ($this->feedType == self::$FEED_RSS) ? 'en-en' : 'en'; | 233 | return ($this->feedType === self::$FEED_RSS) ? 'en-en' : 'en'; |
230 | } | 234 | } |
231 | 235 | ||
232 | /** | 236 | /** |
@@ -261,7 +265,6 @@ class FeedBuilder | |||
261 | } | 265 | } |
262 | if ($this->feedType == self::$FEED_RSS) { | 266 | if ($this->feedType == self::$FEED_RSS) { |
263 | return $date->format(DateTime::RSS); | 267 | return $date->format(DateTime::RSS); |
264 | |||
265 | } | 268 | } |
266 | return $date->format(DateTime::ATOM); | 269 | return $date->format(DateTime::ATOM); |
267 | } | 270 | } |
@@ -287,7 +290,7 @@ class FeedBuilder | |||
287 | } | 290 | } |
288 | 291 | ||
289 | $intNb = intval($this->userInput['nb']); | 292 | $intNb = intval($this->userInput['nb']); |
290 | if (! is_int($intNb) || $intNb == 0) { | 293 | if (!is_int($intNb) || $intNb == 0) { |
291 | return self::$DEFAULT_NB_LINKS; | 294 | return self::$DEFAULT_NB_LINKS; |
292 | } | 295 | } |
293 | 296 | ||
diff --git a/application/Base64Url.php b/application/http/Base64Url.php index 61590e43..33fa7c1f 100644 --- a/application/Base64Url.php +++ b/application/http/Base64Url.php | |||
@@ -1,7 +1,6 @@ | |||
1 | <?php | 1 | <?php |
2 | 2 | ||
3 | namespace Shaarli; | 3 | namespace Shaarli\Http; |
4 | |||
5 | 4 | ||
6 | /** | 5 | /** |
7 | * URL-safe Base64 operations | 6 | * URL-safe Base64 operations |
@@ -17,7 +16,8 @@ class Base64Url | |||
17 | * | 16 | * |
18 | * @return string Base64Url-encoded data | 17 | * @return string Base64Url-encoded data |
19 | */ | 18 | */ |
20 | public static function encode($data) { | 19 | public static function encode($data) |
20 | { | ||
21 | return rtrim(strtr(base64_encode($data), '+/', '-_'), '='); | 21 | return rtrim(strtr(base64_encode($data), '+/', '-_'), '='); |
22 | } | 22 | } |
23 | 23 | ||
@@ -28,7 +28,8 @@ class Base64Url | |||
28 | * | 28 | * |
29 | * @return string Decoded data | 29 | * @return string Decoded data |
30 | */ | 30 | */ |
31 | public static function decode($data) { | 31 | public static function decode($data) |
32 | { | ||
32 | return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT)); | 33 | return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT)); |
33 | } | 34 | } |
34 | } | 35 | } |
diff --git a/application/HttpUtils.php b/application/http/HttpUtils.php index e9282506..2ea9195d 100644 --- a/application/HttpUtils.php +++ b/application/http/HttpUtils.php | |||
@@ -1,4 +1,7 @@ | |||
1 | <?php | 1 | <?php |
2 | |||
3 | use Shaarli\Http\Url; | ||
4 | |||
2 | /** | 5 | /** |
3 | * GET an HTTP URL to retrieve its content | 6 | * GET an HTTP URL to retrieve its content |
4 | * Uses the cURL library or a fallback method | 7 | * Uses the cURL library or a fallback method |
@@ -7,7 +10,8 @@ | |||
7 | * @param int $timeout network timeout (in seconds) | 10 | * @param int $timeout network timeout (in seconds) |
8 | * @param int $maxBytes maximum downloaded bytes (default: 4 MiB) | 11 | * @param int $maxBytes maximum downloaded bytes (default: 4 MiB) |
9 | * @param callable|string $curlWriteFunction Optional callback called during the download (cURL CURLOPT_WRITEFUNCTION). | 12 | * @param callable|string $curlWriteFunction Optional callback called during the download (cURL CURLOPT_WRITEFUNCTION). |
10 | * Can be used to add download conditions on the headers (response code, content type, etc.). | 13 | * Can be used to add download conditions on the |
14 | * headers (response code, content type, etc.). | ||
11 | * | 15 | * |
12 | * @return array HTTP response headers, downloaded content | 16 | * @return array HTTP response headers, downloaded content |
13 | * | 17 | * |
@@ -37,7 +41,7 @@ function get_http_response($url, $timeout = 30, $maxBytes = 4194304, $curlWriteF | |||
37 | $cleanUrl = $urlObj->idnToAscii(); | 41 | $cleanUrl = $urlObj->idnToAscii(); |
38 | 42 | ||
39 | if (!filter_var($cleanUrl, FILTER_VALIDATE_URL) || !$urlObj->isHttp()) { | 43 | if (!filter_var($cleanUrl, FILTER_VALIDATE_URL) || !$urlObj->isHttp()) { |
40 | return array(array(0 => 'Invalid HTTP Url'), false); | 44 | return array(array(0 => 'Invalid HTTP UrlUtils'), false); |
41 | } | 45 | } |
42 | 46 | ||
43 | $userAgent = | 47 | $userAgent = |
@@ -64,29 +68,30 @@ function get_http_response($url, $timeout = 30, $maxBytes = 4194304, $curlWriteF | |||
64 | } | 68 | } |
65 | 69 | ||
66 | // General cURL settings | 70 | // General cURL settings |
67 | curl_setopt($ch, CURLOPT_AUTOREFERER, true); | 71 | curl_setopt($ch, CURLOPT_AUTOREFERER, true); |
68 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); | 72 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); |
69 | curl_setopt($ch, CURLOPT_HEADER, true); | 73 | curl_setopt($ch, CURLOPT_HEADER, true); |
70 | curl_setopt( | 74 | curl_setopt( |
71 | $ch, | 75 | $ch, |
72 | CURLOPT_HTTPHEADER, | 76 | CURLOPT_HTTPHEADER, |
73 | array('Accept-Language: ' . $acceptLanguage) | 77 | array('Accept-Language: ' . $acceptLanguage) |
74 | ); | 78 | ); |
75 | curl_setopt($ch, CURLOPT_MAXREDIRS, $maxRedirs); | 79 | curl_setopt($ch, CURLOPT_MAXREDIRS, $maxRedirs); |
76 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); | 80 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); |
77 | curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); | 81 | curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); |
78 | curl_setopt($ch, CURLOPT_USERAGENT, $userAgent); | 82 | curl_setopt($ch, CURLOPT_USERAGENT, $userAgent); |
79 | 83 | ||
80 | if (is_callable($curlWriteFunction)) { | 84 | if (is_callable($curlWriteFunction)) { |
81 | curl_setopt($ch, CURLOPT_WRITEFUNCTION, $curlWriteFunction); | 85 | curl_setopt($ch, CURLOPT_WRITEFUNCTION, $curlWriteFunction); |
82 | } | 86 | } |
83 | 87 | ||
84 | // Max download size management | 88 | // Max download size management |
85 | curl_setopt($ch, CURLOPT_BUFFERSIZE, 1024*16); | 89 | curl_setopt($ch, CURLOPT_BUFFERSIZE, 1024*16); |
86 | curl_setopt($ch, CURLOPT_NOPROGRESS, false); | 90 | curl_setopt($ch, CURLOPT_NOPROGRESS, false); |
87 | curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, | 91 | curl_setopt( |
88 | function($arg0, $arg1, $arg2, $arg3, $arg4 = 0) use ($maxBytes) | 92 | $ch, |
89 | { | 93 | CURLOPT_PROGRESSFUNCTION, |
94 | function ($arg0, $arg1, $arg2, $arg3, $arg4 = 0) use ($maxBytes) { | ||
90 | if (version_compare(phpversion(), '5.5', '<')) { | 95 | if (version_compare(phpversion(), '5.5', '<')) { |
91 | // PHP version lower than 5.5 | 96 | // PHP version lower than 5.5 |
92 | // Callback has 4 arguments | 97 | // Callback has 4 arguments |
@@ -232,7 +237,6 @@ function get_redirected_headers($url, $redirectionLimit = 3) | |||
232 | && !empty($headers) | 237 | && !empty($headers) |
233 | && (strpos($headers[0], '301') !== false || strpos($headers[0], '302') !== false) | 238 | && (strpos($headers[0], '301') !== false || strpos($headers[0], '302') !== false) |
234 | && !empty($headers['Location'])) { | 239 | && !empty($headers['Location'])) { |
235 | |||
236 | $redirection = is_array($headers['Location']) ? end($headers['Location']) : $headers['Location']; | 240 | $redirection = is_array($headers['Location']) ? end($headers['Location']) : $headers['Location']; |
237 | if ($redirection != $url) { | 241 | if ($redirection != $url) { |
238 | $redirection = getAbsoluteUrl($url, $redirection); | 242 | $redirection = getAbsoluteUrl($url, $redirection); |
diff --git a/application/Url.php b/application/http/Url.php index 6b9870f0..90444a2f 100644 --- a/application/Url.php +++ b/application/http/Url.php | |||
@@ -1,91 +1,6 @@ | |||
1 | <?php | 1 | <?php |
2 | /** | ||
3 | * Converts an array-represented URL to a string | ||
4 | * | ||
5 | * Source: http://php.net/manual/en/function.parse-url.php#106731 | ||
6 | * | ||
7 | * @see http://php.net/manual/en/function.parse-url.php | ||
8 | * | ||
9 | * @param array $parsedUrl an array-represented URL | ||
10 | * | ||
11 | * @return string the string representation of the URL | ||
12 | */ | ||
13 | function unparse_url($parsedUrl) | ||
14 | { | ||
15 | $scheme = isset($parsedUrl['scheme']) ? $parsedUrl['scheme'].'://' : ''; | ||
16 | $host = isset($parsedUrl['host']) ? $parsedUrl['host'] : ''; | ||
17 | $port = isset($parsedUrl['port']) ? ':'.$parsedUrl['port'] : ''; | ||
18 | $user = isset($parsedUrl['user']) ? $parsedUrl['user'] : ''; | ||
19 | $pass = isset($parsedUrl['pass']) ? ':'.$parsedUrl['pass'] : ''; | ||
20 | $pass = ($user || $pass) ? "$pass@" : ''; | ||
21 | $path = isset($parsedUrl['path']) ? $parsedUrl['path'] : ''; | ||
22 | $query = isset($parsedUrl['query']) ? '?'.$parsedUrl['query'] : ''; | ||
23 | $fragment = isset($parsedUrl['fragment']) ? '#'.$parsedUrl['fragment'] : ''; | ||
24 | |||
25 | return "$scheme$user$pass$host$port$path$query$fragment"; | ||
26 | } | ||
27 | |||
28 | /** | ||
29 | * Removes undesired query parameters and fragments | ||
30 | * | ||
31 | * @param string url Url to be cleaned | ||
32 | * | ||
33 | * @return string the string representation of this URL after cleanup | ||
34 | */ | ||
35 | function cleanup_url($url) | ||
36 | { | ||
37 | $obj_url = new Url($url); | ||
38 | return $obj_url->cleanup(); | ||
39 | } | ||
40 | 2 | ||
41 | /** | 3 | namespace Shaarli\Http; |
42 | * Get URL scheme. | ||
43 | * | ||
44 | * @param string url Url for which the scheme is requested | ||
45 | * | ||
46 | * @return mixed the URL scheme or false if none is provided. | ||
47 | */ | ||
48 | function get_url_scheme($url) | ||
49 | { | ||
50 | $obj_url = new Url($url); | ||
51 | return $obj_url->getScheme(); | ||
52 | } | ||
53 | |||
54 | /** | ||
55 | * Adds a trailing slash at the end of URL if necessary. | ||
56 | * | ||
57 | * @param string $url URL to check/edit. | ||
58 | * | ||
59 | * @return string $url URL with a end trailing slash. | ||
60 | */ | ||
61 | function add_trailing_slash($url) | ||
62 | { | ||
63 | return $url . (!endsWith($url, '/') ? '/' : ''); | ||
64 | } | ||
65 | |||
66 | /** | ||
67 | * Replace not whitelisted protocols by 'http://' from given URL. | ||
68 | * | ||
69 | * @param string $url URL to clean | ||
70 | * @param array $protocols List of allowed protocols (aside from http(s)). | ||
71 | * | ||
72 | * @return string URL with allowed protocol | ||
73 | */ | ||
74 | function whitelist_protocols($url, $protocols) | ||
75 | { | ||
76 | if (startsWith($url, '?') || startsWith($url, '/')) { | ||
77 | return $url; | ||
78 | } | ||
79 | $protocols = array_merge(['http', 'https'], $protocols); | ||
80 | $protocol = preg_match('#^(\w+):/?/?#', $url, $match); | ||
81 | // Protocol not allowed: we remove it and replace it with http | ||
82 | if ($protocol === 1 && ! in_array($match[1], $protocols)) { | ||
83 | $url = str_replace($match[0], 'http://', $url); | ||
84 | } elseif ($protocol !== 1) { | ||
85 | $url = 'http://' . $url; | ||
86 | } | ||
87 | return $url; | ||
88 | } | ||
89 | 4 | ||
90 | /** | 5 | /** |
91 | * URL representation and cleanup utilities | 6 | * URL representation and cleanup utilities |
@@ -182,7 +97,7 @@ class Url | |||
182 | } | 97 | } |
183 | return $input; | 98 | return $input; |
184 | } | 99 | } |
185 | 100 | ||
186 | /** | 101 | /** |
187 | * Returns a string representation of this URL | 102 | * Returns a string representation of this URL |
188 | */ | 103 | */ |
@@ -196,7 +111,7 @@ class Url | |||
196 | */ | 111 | */ |
197 | protected function cleanupQuery() | 112 | protected function cleanupQuery() |
198 | { | 113 | { |
199 | if (! isset($this->parts['query'])) { | 114 | if (!isset($this->parts['query'])) { |
200 | return; | 115 | return; |
201 | } | 116 | } |
202 | 117 | ||
@@ -217,14 +132,14 @@ class Url | |||
217 | } | 132 | } |
218 | 133 | ||
219 | $this->parts['query'] = implode('&', $queryParams); | 134 | $this->parts['query'] = implode('&', $queryParams); |
220 | } | 135 | } |
221 | 136 | ||
222 | /** | 137 | /** |
223 | * Removes undesired fragments | 138 | * Removes undesired fragments |
224 | */ | 139 | */ |
225 | protected function cleanupFragment() | 140 | protected function cleanupFragment() |
226 | { | 141 | { |
227 | if (! isset($this->parts['fragment'])) { | 142 | if (!isset($this->parts['fragment'])) { |
228 | return; | 143 | return; |
229 | } | 144 | } |
230 | 145 | ||
@@ -257,7 +172,7 @@ class Url | |||
257 | public function idnToAscii() | 172 | public function idnToAscii() |
258 | { | 173 | { |
259 | $out = $this->cleanup(); | 174 | $out = $this->cleanup(); |
260 | if (! function_exists('idn_to_ascii') || ! isset($this->parts['host'])) { | 175 | if (!function_exists('idn_to_ascii') || !isset($this->parts['host'])) { |
261 | return $out; | 176 | return $out; |
262 | } | 177 | } |
263 | $asciiHost = idn_to_ascii($this->parts['host'], 0, INTL_IDNA_VARIANT_UTS46); | 178 | $asciiHost = idn_to_ascii($this->parts['host'], 0, INTL_IDNA_VARIANT_UTS46); |
@@ -269,7 +184,8 @@ class Url | |||
269 | * | 184 | * |
270 | * @return string the URL scheme or false if none is provided. | 185 | * @return string the URL scheme or false if none is provided. |
271 | */ | 186 | */ |
272 | public function getScheme() { | 187 | public function getScheme() |
188 | { | ||
273 | if (!isset($this->parts['scheme'])) { | 189 | if (!isset($this->parts['scheme'])) { |
274 | return false; | 190 | return false; |
275 | } | 191 | } |
@@ -281,7 +197,8 @@ class Url | |||
281 | * | 197 | * |
282 | * @return string the URL host or false if none is provided. | 198 | * @return string the URL host or false if none is provided. |
283 | */ | 199 | */ |
284 | public function getHost() { | 200 | public function getHost() |
201 | { | ||
285 | if (empty($this->parts['host'])) { | 202 | if (empty($this->parts['host'])) { |
286 | return false; | 203 | return false; |
287 | } | 204 | } |
@@ -289,11 +206,12 @@ class Url | |||
289 | } | 206 | } |
290 | 207 | ||
291 | /** | 208 | /** |
292 | * Test if the Url is an HTTP one. | 209 | * Test if the UrlUtils is an HTTP one. |
293 | * | 210 | * |
294 | * @return true is HTTP, false otherwise. | 211 | * @return true is HTTP, false otherwise. |
295 | */ | 212 | */ |
296 | public function isHttp() { | 213 | public function isHttp() |
214 | { | ||
297 | return strpos(strtolower($this->parts['scheme']), 'http') !== false; | 215 | return strpos(strtolower($this->parts['scheme']), 'http') !== false; |
298 | } | 216 | } |
299 | } | 217 | } |
diff --git a/application/http/UrlUtils.php b/application/http/UrlUtils.php new file mode 100644 index 00000000..4bc84b82 --- /dev/null +++ b/application/http/UrlUtils.php | |||
@@ -0,0 +1,88 @@ | |||
1 | <?php | ||
2 | /** | ||
3 | * Converts an array-represented URL to a string | ||
4 | * | ||
5 | * Source: http://php.net/manual/en/function.parse-url.php#106731 | ||
6 | * | ||
7 | * @see http://php.net/manual/en/function.parse-url.php | ||
8 | * | ||
9 | * @param array $parsedUrl an array-represented URL | ||
10 | * | ||
11 | * @return string the string representation of the URL | ||
12 | */ | ||
13 | function unparse_url($parsedUrl) | ||
14 | { | ||
15 | $scheme = isset($parsedUrl['scheme']) ? $parsedUrl['scheme'].'://' : ''; | ||
16 | $host = isset($parsedUrl['host']) ? $parsedUrl['host'] : ''; | ||
17 | $port = isset($parsedUrl['port']) ? ':'.$parsedUrl['port'] : ''; | ||
18 | $user = isset($parsedUrl['user']) ? $parsedUrl['user'] : ''; | ||
19 | $pass = isset($parsedUrl['pass']) ? ':'.$parsedUrl['pass'] : ''; | ||
20 | $pass = ($user || $pass) ? "$pass@" : ''; | ||
21 | $path = isset($parsedUrl['path']) ? $parsedUrl['path'] : ''; | ||
22 | $query = isset($parsedUrl['query']) ? '?'.$parsedUrl['query'] : ''; | ||
23 | $fragment = isset($parsedUrl['fragment']) ? '#'.$parsedUrl['fragment'] : ''; | ||
24 | |||
25 | return "$scheme$user$pass$host$port$path$query$fragment"; | ||
26 | } | ||
27 | |||
28 | /** | ||
29 | * Removes undesired query parameters and fragments | ||
30 | * | ||
31 | * @param string url UrlUtils to be cleaned | ||
32 | * | ||
33 | * @return string the string representation of this URL after cleanup | ||
34 | */ | ||
35 | function cleanup_url($url) | ||
36 | { | ||
37 | $obj_url = new \Shaarli\Http\Url($url); | ||
38 | return $obj_url->cleanup(); | ||
39 | } | ||
40 | |||
41 | /** | ||
42 | * Get URL scheme. | ||
43 | * | ||
44 | * @param string url UrlUtils for which the scheme is requested | ||
45 | * | ||
46 | * @return mixed the URL scheme or false if none is provided. | ||
47 | */ | ||
48 | function get_url_scheme($url) | ||
49 | { | ||
50 | $obj_url = new \Shaarli\Http\Url($url); | ||
51 | return $obj_url->getScheme(); | ||
52 | } | ||
53 | |||
54 | /** | ||
55 | * Adds a trailing slash at the end of URL if necessary. | ||
56 | * | ||
57 | * @param string $url URL to check/edit. | ||
58 | * | ||
59 | * @return string $url URL with a end trailing slash. | ||
60 | */ | ||
61 | function add_trailing_slash($url) | ||
62 | { | ||
63 | return $url . (!endsWith($url, '/') ? '/' : ''); | ||
64 | } | ||
65 | |||
66 | /** | ||
67 | * Replace not whitelisted protocols by 'http://' from given URL. | ||
68 | * | ||
69 | * @param string $url URL to clean | ||
70 | * @param array $protocols List of allowed protocols (aside from http(s)). | ||
71 | * | ||
72 | * @return string URL with allowed protocol | ||
73 | */ | ||
74 | function whitelist_protocols($url, $protocols) | ||
75 | { | ||
76 | if (startsWith($url, '?') || startsWith($url, '/')) { | ||
77 | return $url; | ||
78 | } | ||
79 | $protocols = array_merge(['http', 'https'], $protocols); | ||
80 | $protocol = preg_match('#^(\w+):/?/?#', $url, $match); | ||
81 | // Protocol not allowed: we remove it and replace it with http | ||
82 | if ($protocol === 1 && ! in_array($match[1], $protocols)) { | ||
83 | $url = str_replace($match[0], 'http://', $url); | ||
84 | } elseif ($protocol !== 1) { | ||
85 | $url = 'http://' . $url; | ||
86 | } | ||
87 | return $url; | ||
88 | } | ||
diff --git a/application/NetscapeBookmarkUtils.php b/application/netscape/NetscapeBookmarkUtils.php index b4d16d00..2fb1a4a6 100644 --- a/application/NetscapeBookmarkUtils.php +++ b/application/netscape/NetscapeBookmarkUtils.php | |||
@@ -1,9 +1,16 @@ | |||
1 | <?php | 1 | <?php |
2 | 2 | ||
3 | namespace Shaarli\Netscape; | ||
4 | |||
5 | use DateTime; | ||
6 | use DateTimeZone; | ||
7 | use Exception; | ||
8 | use Katzgrau\KLogger\Logger; | ||
3 | use Psr\Log\LogLevel; | 9 | use Psr\Log\LogLevel; |
10 | use Shaarli\Bookmark\LinkDB; | ||
4 | use Shaarli\Config\ConfigManager; | 11 | use Shaarli\Config\ConfigManager; |
12 | use Shaarli\History; | ||
5 | use Shaarli\NetscapeBookmarkParser\NetscapeBookmarkParser; | 13 | use Shaarli\NetscapeBookmarkParser\NetscapeBookmarkParser; |
6 | use Katzgrau\KLogger\Logger; | ||
7 | 14 | ||
8 | /** | 15 | /** |
9 | * Utilities to import and export bookmarks using the Netscape format | 16 | * Utilities to import and export bookmarks using the Netscape format |
@@ -31,8 +38,8 @@ class NetscapeBookmarkUtils | |||
31 | public static function filterAndFormat($linkDb, $selection, $prependNoteUrl, $indexUrl) | 38 | public static function filterAndFormat($linkDb, $selection, $prependNoteUrl, $indexUrl) |
32 | { | 39 | { |
33 | // see tpl/export.html for possible values | 40 | // see tpl/export.html for possible values |
34 | if (! in_array($selection, array('all', 'public', 'private'))) { | 41 | if (!in_array($selection, array('all', 'public', 'private'))) { |
35 | throw new Exception(t('Invalid export selection:') .' "'.$selection.'"'); | 42 | throw new Exception(t('Invalid export selection:') . ' "' . $selection . '"'); |
36 | } | 43 | } |
37 | 44 | ||
38 | $bookmarkLinks = array(); | 45 | $bookmarkLinks = array(); |
@@ -72,18 +79,20 @@ class NetscapeBookmarkUtils | |||
72 | private static function importStatus( | 79 | private static function importStatus( |
73 | $filename, | 80 | $filename, |
74 | $filesize, | 81 | $filesize, |
75 | $importCount=0, | 82 | $importCount = 0, |
76 | $overwriteCount=0, | 83 | $overwriteCount = 0, |
77 | $skipCount=0, | 84 | $skipCount = 0, |
78 | $duration=0 | 85 | $duration = 0 |
79 | ) | 86 | ) { |
80 | { | ||
81 | $status = sprintf(t('File %s (%d bytes) '), $filename, $filesize); | 87 | $status = sprintf(t('File %s (%d bytes) '), $filename, $filesize); |
82 | if ($importCount == 0 && $overwriteCount == 0 && $skipCount == 0) { | 88 | if ($importCount == 0 && $overwriteCount == 0 && $skipCount == 0) { |
83 | $status .= t('has an unknown file format. Nothing was imported.'); | 89 | $status .= t('has an unknown file format. Nothing was imported.'); |
84 | } else { | 90 | } else { |
85 | $status .= vsprintf( | 91 | $status .= vsprintf( |
86 | t('was successfully processed in %d seconds: %d links imported, %d links overwritten, %d links skipped.'), | 92 | t( |
93 | 'was successfully processed in %d seconds: ' | ||
94 | . '%d links imported, %d links overwritten, %d links skipped.' | ||
95 | ), | ||
87 | [$duration, $importCount, $overwriteCount, $skipCount] | 96 | [$duration, $importCount, $overwriteCount, $skipCount] |
88 | ); | 97 | ); |
89 | } | 98 | } |
@@ -93,11 +102,11 @@ class NetscapeBookmarkUtils | |||
93 | /** | 102 | /** |
94 | * Imports Web bookmarks from an uploaded Netscape bookmark dump | 103 | * Imports Web bookmarks from an uploaded Netscape bookmark dump |
95 | * | 104 | * |
96 | * @param array $post Server $_POST parameters | 105 | * @param array $post Server $_POST parameters |
97 | * @param array $files Server $_FILES parameters | 106 | * @param array $files Server $_FILES parameters |
98 | * @param LinkDB $linkDb Loaded LinkDB instance | 107 | * @param LinkDB $linkDb Loaded LinkDB instance |
99 | * @param ConfigManager $conf instance | 108 | * @param ConfigManager $conf instance |
100 | * @param History $history History instance | 109 | * @param History $history History instance |
101 | * | 110 | * |
102 | * @return string Summary of the bookmark import status | 111 | * @return string Summary of the bookmark import status |
103 | */ | 112 | */ |
@@ -113,7 +122,7 @@ class NetscapeBookmarkUtils | |||
113 | } | 122 | } |
114 | 123 | ||
115 | // Overwrite existing links? | 124 | // Overwrite existing links? |
116 | $overwrite = ! empty($post['overwrite']); | 125 | $overwrite = !empty($post['overwrite']); |
117 | 126 | ||
118 | // Add tags to all imported links? | 127 | // Add tags to all imported links? |
119 | if (empty($post['default_tags'])) { | 128 | if (empty($post['default_tags'])) { |
@@ -136,7 +145,7 @@ class NetscapeBookmarkUtils | |||
136 | ); | 145 | ); |
137 | $logger = new Logger( | 146 | $logger = new Logger( |
138 | $conf->get('resource.data_dir'), | 147 | $conf->get('resource.data_dir'), |
139 | ! $conf->get('dev.debug') ? LogLevel::INFO : LogLevel::DEBUG, | 148 | !$conf->get('dev.debug') ? LogLevel::INFO : LogLevel::DEBUG, |
140 | [ | 149 | [ |
141 | 'prefix' => 'import.', | 150 | 'prefix' => 'import.', |
142 | 'extension' => 'log', | 151 | 'extension' => 'log', |
@@ -191,7 +200,7 @@ class NetscapeBookmarkUtils | |||
191 | } | 200 | } |
192 | 201 | ||
193 | // Add a new link - @ used for UNIX timestamps | 202 | // Add a new link - @ used for UNIX timestamps |
194 | $newLinkDate = new DateTime('@'.strval($bkm['time'])); | 203 | $newLinkDate = new DateTime('@' . strval($bkm['time'])); |
195 | $newLinkDate->setTimezone(new DateTimeZone(date_default_timezone_get())); | 204 | $newLinkDate->setTimezone(new DateTimeZone(date_default_timezone_get())); |
196 | $newLink['created'] = $newLinkDate; | 205 | $newLink['created'] = $newLinkDate; |
197 | $newLink['id'] = $linkDb->getNextId(); | 206 | $newLink['id'] = $linkDb->getNextId(); |
diff --git a/application/PluginManager.php b/application/plugin/PluginManager.php index cf603845..f7b24a8e 100644 --- a/application/PluginManager.php +++ b/application/plugin/PluginManager.php | |||
@@ -1,4 +1,8 @@ | |||
1 | <?php | 1 | <?php |
2 | namespace Shaarli\Plugin; | ||
3 | |||
4 | use Shaarli\Config\ConfigManager; | ||
5 | use Shaarli\Plugin\Exception\PluginFileNotFoundException; | ||
2 | 6 | ||
3 | /** | 7 | /** |
4 | * Class PluginManager | 8 | * Class PluginManager |
@@ -9,12 +13,14 @@ class PluginManager | |||
9 | { | 13 | { |
10 | /** | 14 | /** |
11 | * List of authorized plugins from configuration file. | 15 | * List of authorized plugins from configuration file. |
16 | * | ||
12 | * @var array $authorizedPlugins | 17 | * @var array $authorizedPlugins |
13 | */ | 18 | */ |
14 | private $authorizedPlugins; | 19 | private $authorizedPlugins; |
15 | 20 | ||
16 | /** | 21 | /** |
17 | * List of loaded plugins. | 22 | * List of loaded plugins. |
23 | * | ||
18 | * @var array $loadedPlugins | 24 | * @var array $loadedPlugins |
19 | */ | 25 | */ |
20 | private $loadedPlugins = array(); | 26 | private $loadedPlugins = array(); |
@@ -31,12 +37,14 @@ class PluginManager | |||
31 | 37 | ||
32 | /** | 38 | /** |
33 | * Plugins subdirectory. | 39 | * Plugins subdirectory. |
40 | * | ||
34 | * @var string $PLUGINS_PATH | 41 | * @var string $PLUGINS_PATH |
35 | */ | 42 | */ |
36 | public static $PLUGINS_PATH = 'plugins'; | 43 | public static $PLUGINS_PATH = 'plugins'; |
37 | 44 | ||
38 | /** | 45 | /** |
39 | * Plugins meta files extension. | 46 | * Plugins meta files extension. |
47 | * | ||
40 | * @var string $META_EXT | 48 | * @var string $META_EXT |
41 | */ | 49 | */ |
42 | public static $META_EXT = 'meta'; | 50 | public static $META_EXT = 'meta'; |
@@ -75,8 +83,7 @@ class PluginManager | |||
75 | 83 | ||
76 | try { | 84 | try { |
77 | $this->loadPlugin($dirs[$index], $plugin); | 85 | $this->loadPlugin($dirs[$index], $plugin); |
78 | } | 86 | } catch (PluginFileNotFoundException $e) { |
79 | catch (PluginFileNotFoundException $e) { | ||
80 | error_log($e->getMessage()); | 87 | error_log($e->getMessage()); |
81 | } | 88 | } |
82 | } | 89 | } |
@@ -85,9 +92,9 @@ class PluginManager | |||
85 | /** | 92 | /** |
86 | * Execute all plugins registered hook. | 93 | * Execute all plugins registered hook. |
87 | * | 94 | * |
88 | * @param string $hook name of the hook to trigger. | 95 | * @param string $hook name of the hook to trigger. |
89 | * @param array $data list of data to manipulate passed by reference. | 96 | * @param array $data list of data to manipulate passed by reference. |
90 | * @param array $params additional parameters such as page target. | 97 | * @param array $params additional parameters such as page target. |
91 | * | 98 | * |
92 | * @return void | 99 | * @return void |
93 | */ | 100 | */ |
@@ -119,7 +126,7 @@ class PluginManager | |||
119 | * @param string $pluginName plugin's name. | 126 | * @param string $pluginName plugin's name. |
120 | * | 127 | * |
121 | * @return void | 128 | * @return void |
122 | * @throws PluginFileNotFoundException - plugin files not found. | 129 | * @throws \Shaarli\Plugin\Exception\PluginFileNotFoundException - plugin files not found. |
123 | */ | 130 | */ |
124 | private function loadPlugin($dir, $pluginName) | 131 | private function loadPlugin($dir, $pluginName) |
125 | { | 132 | { |
@@ -205,8 +212,8 @@ class PluginManager | |||
205 | 212 | ||
206 | $metaData[$plugin]['parameters'][$param]['value'] = ''; | 213 | $metaData[$plugin]['parameters'][$param]['value'] = ''; |
207 | // Optional parameter description in parameter.PARAM_NAME= | 214 | // Optional parameter description in parameter.PARAM_NAME= |
208 | if (isset($metaData[$plugin]['parameter.'. $param])) { | 215 | if (isset($metaData[$plugin]['parameter.' . $param])) { |
209 | $metaData[$plugin]['parameters'][$param]['desc'] = t($metaData[$plugin]['parameter.'. $param]); | 216 | $metaData[$plugin]['parameters'][$param]['desc'] = t($metaData[$plugin]['parameter.' . $param]); |
210 | } | 217 | } |
211 | } | 218 | } |
212 | } | 219 | } |
@@ -224,22 +231,3 @@ class PluginManager | |||
224 | return $this->errors; | 231 | return $this->errors; |
225 | } | 232 | } |
226 | } | 233 | } |
227 | |||
228 | /** | ||
229 | * Class PluginFileNotFoundException | ||
230 | * | ||
231 | * Raise when plugin files can't be found. | ||
232 | */ | ||
233 | class PluginFileNotFoundException extends Exception | ||
234 | { | ||
235 | /** | ||
236 | * Construct exception with plugin name. | ||
237 | * Generate message. | ||
238 | * | ||
239 | * @param string $pluginName name of the plugin not found | ||
240 | */ | ||
241 | public function __construct($pluginName) | ||
242 | { | ||
243 | $this->message = sprintf(t('Plugin "%s" files not found.'), $pluginName); | ||
244 | } | ||
245 | } | ||
diff --git a/application/plugin/exception/PluginFileNotFoundException.php b/application/plugin/exception/PluginFileNotFoundException.php new file mode 100644 index 00000000..e5386f02 --- /dev/null +++ b/application/plugin/exception/PluginFileNotFoundException.php | |||
@@ -0,0 +1,23 @@ | |||
1 | <?php | ||
2 | namespace Shaarli\Plugin\Exception; | ||
3 | |||
4 | use Exception; | ||
5 | |||
6 | /** | ||
7 | * Class PluginFileNotFoundException | ||
8 | * | ||
9 | * Raise when plugin files can't be found. | ||
10 | */ | ||
11 | class PluginFileNotFoundException extends Exception | ||
12 | { | ||
13 | /** | ||
14 | * Construct exception with plugin name. | ||
15 | * Generate message. | ||
16 | * | ||
17 | * @param string $pluginName name of the plugin not found | ||
18 | */ | ||
19 | public function __construct($pluginName) | ||
20 | { | ||
21 | $this->message = sprintf(t('Plugin "%s" files not found.'), $pluginName); | ||
22 | } | ||
23 | } | ||
diff --git a/application/PageBuilder.php b/application/render/PageBuilder.php index a4483870..0569b67f 100644 --- a/application/PageBuilder.php +++ b/application/render/PageBuilder.php | |||
@@ -1,6 +1,13 @@ | |||
1 | <?php | 1 | <?php |
2 | 2 | ||
3 | namespace Shaarli\Render; | ||
4 | |||
5 | use Exception; | ||
6 | use RainTPL; | ||
7 | use Shaarli\ApplicationUtils; | ||
8 | use Shaarli\Bookmark\LinkDB; | ||
3 | use Shaarli\Config\ConfigManager; | 9 | use Shaarli\Config\ConfigManager; |
10 | use Shaarli\Thumbnailer; | ||
4 | 11 | ||
5 | /** | 12 | /** |
6 | * This class is in charge of building the final page. | 13 | * This class is in charge of building the final page. |
@@ -22,25 +29,40 @@ class PageBuilder | |||
22 | protected $conf; | 29 | protected $conf; |
23 | 30 | ||
24 | /** | 31 | /** |
32 | * @var array $_SESSION | ||
33 | */ | ||
34 | protected $session; | ||
35 | |||
36 | /** | ||
25 | * @var LinkDB $linkDB instance. | 37 | * @var LinkDB $linkDB instance. |
26 | */ | 38 | */ |
27 | protected $linkDB; | 39 | protected $linkDB; |
28 | 40 | ||
29 | /** @var bool $isLoggedIn Whether the user is logged in **/ | 41 | /** |
42 | * @var null|string XSRF token | ||
43 | */ | ||
44 | protected $token; | ||
45 | |||
46 | /** | ||
47 | * @var bool $isLoggedIn Whether the user is logged in | ||
48 | */ | ||
30 | protected $isLoggedIn = false; | 49 | protected $isLoggedIn = false; |
31 | 50 | ||
32 | /** | 51 | /** |
33 | * PageBuilder constructor. | 52 | * PageBuilder constructor. |
34 | * $tpl is initialized at false for lazy loading. | 53 | * $tpl is initialized at false for lazy loading. |
35 | * | 54 | * |
36 | * @param ConfigManager $conf Configuration Manager instance (reference). | 55 | * @param ConfigManager $conf Configuration Manager instance (reference). |
37 | * @param LinkDB $linkDB instance. | 56 | * @param array $session $_SESSION array |
38 | * @param string $token Session token | 57 | * @param LinkDB $linkDB instance. |
58 | * @param string $token Session token | ||
59 | * @param bool $isLoggedIn | ||
39 | */ | 60 | */ |
40 | public function __construct(&$conf, $linkDB = null, $token = null, $isLoggedIn = false) | 61 | public function __construct(&$conf, $session, $linkDB = null, $token = null, $isLoggedIn = false) |
41 | { | 62 | { |
42 | $this->tpl = false; | 63 | $this->tpl = false; |
43 | $this->conf = $conf; | 64 | $this->conf = $conf; |
65 | $this->session = $session; | ||
44 | $this->linkDB = $linkDB; | 66 | $this->linkDB = $linkDB; |
45 | $this->token = $token; | 67 | $this->token = $token; |
46 | $this->isLoggedIn = $isLoggedIn; | 68 | $this->isLoggedIn = $isLoggedIn; |
@@ -64,7 +86,6 @@ class PageBuilder | |||
64 | ); | 86 | ); |
65 | $this->tpl->assign('newVersion', escape($version)); | 87 | $this->tpl->assign('newVersion', escape($version)); |
66 | $this->tpl->assign('versionError', ''); | 88 | $this->tpl->assign('versionError', ''); |
67 | |||
68 | } catch (Exception $exc) { | 89 | } catch (Exception $exc) { |
69 | logm($this->conf->get('resource.log'), $_SERVER['REMOTE_ADDR'], $exc->getMessage()); | 90 | logm($this->conf->get('resource.log'), $_SERVER['REMOTE_ADDR'], $exc->getMessage()); |
70 | $this->tpl->assign('newVersion', ''); | 91 | $this->tpl->assign('newVersion', ''); |
@@ -87,8 +108,8 @@ class PageBuilder | |||
87 | 'version_hash', | 108 | 'version_hash', |
88 | ApplicationUtils::getVersionHash(SHAARLI_VERSION, $this->conf->get('credentials.salt')) | 109 | ApplicationUtils::getVersionHash(SHAARLI_VERSION, $this->conf->get('credentials.salt')) |
89 | ); | 110 | ); |
90 | $this->tpl->assign('scripturl', index_url($_SERVER)); | 111 | $this->tpl->assign('index_url', index_url($_SERVER)); |
91 | $visibility = ! empty($_SESSION['visibility']) ? $_SESSION['visibility'] : ''; | 112 | $visibility = !empty($_SESSION['visibility']) ? $_SESSION['visibility'] : ''; |
92 | $this->tpl->assign('visibility', $visibility); | 113 | $this->tpl->assign('visibility', $visibility); |
93 | $this->tpl->assign('untaggedonly', !empty($_SESSION['untaggedonly'])); | 114 | $this->tpl->assign('untaggedonly', !empty($_SESSION['untaggedonly'])); |
94 | $this->tpl->assign('pagetitle', $this->conf->get('general.title', 'Shaarli')); | 115 | $this->tpl->assign('pagetitle', $this->conf->get('general.title', 'Shaarli')); |
@@ -105,6 +126,19 @@ class PageBuilder | |||
105 | if ($this->linkDB !== null) { | 126 | if ($this->linkDB !== null) { |
106 | $this->tpl->assign('tags', $this->linkDB->linksCountPerTag()); | 127 | $this->tpl->assign('tags', $this->linkDB->linksCountPerTag()); |
107 | } | 128 | } |
129 | |||
130 | $this->tpl->assign( | ||
131 | 'thumbnails_enabled', | ||
132 | $this->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE | ||
133 | ); | ||
134 | $this->tpl->assign('thumbnails_width', $this->conf->get('thumbnails.width')); | ||
135 | $this->tpl->assign('thumbnails_height', $this->conf->get('thumbnails.height')); | ||
136 | |||
137 | if (!empty($_SESSION['warnings'])) { | ||
138 | $this->tpl->assign('global_warnings', $_SESSION['warnings']); | ||
139 | unset($_SESSION['warnings']); | ||
140 | } | ||
141 | |||
108 | // To be removed with a proper theme configuration. | 142 | // To be removed with a proper theme configuration. |
109 | $this->tpl->assign('conf', $this->conf); | 143 | $this->tpl->assign('conf', $this->conf); |
110 | } | 144 | } |
@@ -136,7 +170,7 @@ class PageBuilder | |||
136 | $this->initialize(); | 170 | $this->initialize(); |
137 | } | 171 | } |
138 | 172 | ||
139 | if (empty($data) || !is_array($data)){ | 173 | if (empty($data) || !is_array($data)) { |
140 | return false; | 174 | return false; |
141 | } | 175 | } |
142 | 176 | ||
@@ -163,16 +197,16 @@ class PageBuilder | |||
163 | 197 | ||
164 | /** | 198 | /** |
165 | * Render a 404 page (uses the template : tpl/404.tpl) | 199 | * Render a 404 page (uses the template : tpl/404.tpl) |
166 | * usage : $PAGE->render404('The link was deleted') | 200 | * usage: $PAGE->render404('The link was deleted') |
167 | * | 201 | * |
168 | * @param string $message A messate to display what is not found | 202 | * @param string $message A message to display what is not found |
169 | */ | 203 | */ |
170 | public function render404($message = '') | 204 | public function render404($message = '') |
171 | { | 205 | { |
172 | if (empty($message)) { | 206 | if (empty($message)) { |
173 | $message = t('The page you are trying to reach does not exist or has been deleted.'); | 207 | $message = t('The page you are trying to reach does not exist or has been deleted.'); |
174 | } | 208 | } |
175 | header($_SERVER['SERVER_PROTOCOL'] .' '. t('404 Not Found')); | 209 | header($_SERVER['SERVER_PROTOCOL'] . ' ' . t('404 Not Found')); |
176 | $this->tpl->assign('error_message', $message); | 210 | $this->tpl->assign('error_message', $message); |
177 | $this->renderPage('404'); | 211 | $this->renderPage('404'); |
178 | } | 212 | } |
diff --git a/application/ThemeUtils.php b/application/render/ThemeUtils.php index 16f2f6a2..86096c64 100644 --- a/application/ThemeUtils.php +++ b/application/render/ThemeUtils.php | |||
@@ -1,6 +1,6 @@ | |||
1 | <?php | 1 | <?php |
2 | 2 | ||
3 | namespace Shaarli; | 3 | namespace Shaarli\Render; |
4 | 4 | ||
5 | /** | 5 | /** |
6 | * Class ThemeUtils | 6 | * Class ThemeUtils |
diff --git a/application/security/LoginManager.php b/application/security/LoginManager.php index 5a58926d..1ff3d0be 100644 --- a/application/security/LoginManager.php +++ b/application/security/LoginManager.php | |||
@@ -98,7 +98,6 @@ class LoginManager | |||
98 | // The user client has a valid stay-signed-in cookie | 98 | // The user client has a valid stay-signed-in cookie |
99 | // Session information is updated with the current client information | 99 | // Session information is updated with the current client information |
100 | $this->sessionManager->storeLoginInfo($clientIpId); | 100 | $this->sessionManager->storeLoginInfo($clientIpId); |
101 | |||
102 | } elseif ($this->sessionManager->hasSessionExpired() | 101 | } elseif ($this->sessionManager->hasSessionExpired() |
103 | || $this->sessionManager->hasClientIpChanged($clientIpId) | 102 | || $this->sessionManager->hasClientIpChanged($clientIpId) |
104 | ) { | 103 | ) { |
diff --git a/application/Updater.php b/application/updater/Updater.php index dece2c02..f12e3516 100644 --- a/application/Updater.php +++ b/application/updater/Updater.php | |||
@@ -1,10 +1,24 @@ | |||
1 | <?php | 1 | <?php |
2 | |||
3 | namespace Shaarli\Updater; | ||
4 | |||
5 | use Exception; | ||
6 | use RainTPL; | ||
7 | use ReflectionClass; | ||
8 | use ReflectionException; | ||
9 | use ReflectionMethod; | ||
10 | use Shaarli\ApplicationUtils; | ||
11 | use Shaarli\Bookmark\LinkDB; | ||
12 | use Shaarli\Bookmark\LinkFilter; | ||
2 | use Shaarli\Config\ConfigJson; | 13 | use Shaarli\Config\ConfigJson; |
3 | use Shaarli\Config\ConfigPhp; | ||
4 | use Shaarli\Config\ConfigManager; | 14 | use Shaarli\Config\ConfigManager; |
15 | use Shaarli\Config\ConfigPhp; | ||
16 | use Shaarli\Exceptions\IOException; | ||
17 | use Shaarli\Thumbnailer; | ||
18 | use Shaarli\Updater\Exception\UpdaterException; | ||
5 | 19 | ||
6 | /** | 20 | /** |
7 | * Class Updater. | 21 | * Class updater. |
8 | * Used to update stuff when a new Shaarli's version is reached. | 22 | * Used to update stuff when a new Shaarli's version is reached. |
9 | * Update methods are ran only once, and the stored in a JSON file. | 23 | * Update methods are ran only once, and the stored in a JSON file. |
10 | */ | 24 | */ |
@@ -31,6 +45,11 @@ class Updater | |||
31 | protected $isLoggedIn; | 45 | protected $isLoggedIn; |
32 | 46 | ||
33 | /** | 47 | /** |
48 | * @var array $_SESSION | ||
49 | */ | ||
50 | protected $session; | ||
51 | |||
52 | /** | ||
34 | * @var ReflectionMethod[] List of current class methods. | 53 | * @var ReflectionMethod[] List of current class methods. |
35 | */ | 54 | */ |
36 | protected $methods; | 55 | protected $methods; |
@@ -42,13 +61,17 @@ class Updater | |||
42 | * @param LinkDB $linkDB LinkDB instance. | 61 | * @param LinkDB $linkDB LinkDB instance. |
43 | * @param ConfigManager $conf Configuration Manager instance. | 62 | * @param ConfigManager $conf Configuration Manager instance. |
44 | * @param boolean $isLoggedIn True if the user is logged in. | 63 | * @param boolean $isLoggedIn True if the user is logged in. |
64 | * @param array $session $_SESSION (by reference) | ||
65 | * | ||
66 | * @throws ReflectionException | ||
45 | */ | 67 | */ |
46 | public function __construct($doneUpdates, $linkDB, $conf, $isLoggedIn) | 68 | public function __construct($doneUpdates, $linkDB, $conf, $isLoggedIn, &$session = []) |
47 | { | 69 | { |
48 | $this->doneUpdates = $doneUpdates; | 70 | $this->doneUpdates = $doneUpdates; |
49 | $this->linkDB = $linkDB; | 71 | $this->linkDB = $linkDB; |
50 | $this->conf = $conf; | 72 | $this->conf = $conf; |
51 | $this->isLoggedIn = $isLoggedIn; | 73 | $this->isLoggedIn = $isLoggedIn; |
74 | $this->session = &$session; | ||
52 | 75 | ||
53 | // Retrieve all update methods. | 76 | // Retrieve all update methods. |
54 | $class = new ReflectionClass($this); | 77 | $class = new ReflectionClass($this); |
@@ -73,12 +96,12 @@ class Updater | |||
73 | } | 96 | } |
74 | 97 | ||
75 | if ($this->methods === null) { | 98 | if ($this->methods === null) { |
76 | throw new UpdaterException(t('Couldn\'t retrieve Updater class methods.')); | 99 | throw new UpdaterException(t('Couldn\'t retrieve updater class methods.')); |
77 | } | 100 | } |
78 | 101 | ||
79 | foreach ($this->methods as $method) { | 102 | foreach ($this->methods as $method) { |
80 | // Not an update method or already done, pass. | 103 | // Not an update method or already done, pass. |
81 | if (! startsWith($method->getName(), 'updateMethod') | 104 | if (!startsWith($method->getName(), 'updateMethod') |
82 | || in_array($method->getName(), $this->doneUpdates) | 105 | || in_array($method->getName(), $this->doneUpdates) |
83 | ) { | 106 | ) { |
84 | continue; | 107 | continue; |
@@ -129,7 +152,7 @@ class Updater | |||
129 | } | 152 | } |
130 | } | 153 | } |
131 | $this->conf->write($this->isLoggedIn); | 154 | $this->conf->write($this->isLoggedIn); |
132 | unlink($this->conf->get('resource.data_dir').'/options.php'); | 155 | unlink($this->conf->get('resource.data_dir') . '/options.php'); |
133 | } | 156 | } |
134 | 157 | ||
135 | return true; | 158 | return true; |
@@ -164,16 +187,16 @@ class Updater | |||
164 | $subConfig = array('config', 'plugins'); | 187 | $subConfig = array('config', 'plugins'); |
165 | foreach ($subConfig as $sub) { | 188 | foreach ($subConfig as $sub) { |
166 | foreach ($oldConfig[$sub] as $key => $value) { | 189 | foreach ($oldConfig[$sub] as $key => $value) { |
167 | if (isset($legacyMap[$sub .'.'. $key])) { | 190 | if (isset($legacyMap[$sub . '.' . $key])) { |
168 | $configKey = $legacyMap[$sub .'.'. $key]; | 191 | $configKey = $legacyMap[$sub . '.' . $key]; |
169 | } else { | 192 | } else { |
170 | $configKey = $sub .'.'. $key; | 193 | $configKey = $sub . '.' . $key; |
171 | } | 194 | } |
172 | $this->conf->set($configKey, $value); | 195 | $this->conf->set($configKey, $value); |
173 | } | 196 | } |
174 | } | 197 | } |
175 | 198 | ||
176 | try{ | 199 | try { |
177 | $this->conf->write($this->isLoggedIn); | 200 | $this->conf->write($this->isLoggedIn); |
178 | return true; | 201 | return true; |
179 | } catch (IOException $e) { | 202 | } catch (IOException $e) { |
@@ -223,7 +246,7 @@ class Updater | |||
223 | return true; | 246 | return true; |
224 | } | 247 | } |
225 | 248 | ||
226 | $save = $this->conf->get('resource.data_dir') .'/datastore.'. date('YmdHis') .'.php'; | 249 | $save = $this->conf->get('resource.data_dir') . '/datastore.' . date('YmdHis') . '.php'; |
227 | copy($this->conf->get('resource.datastore'), $save); | 250 | copy($this->conf->get('resource.datastore'), $save); |
228 | 251 | ||
229 | $links = array(); | 252 | $links = array(); |
@@ -297,7 +320,7 @@ class Updater | |||
297 | // We run the update only if this folder still contains the template files. | 320 | // We run the update only if this folder still contains the template files. |
298 | $tplDir = $this->conf->get('resource.raintpl_tpl'); | 321 | $tplDir = $this->conf->get('resource.raintpl_tpl'); |
299 | $tplFile = $tplDir . '/linklist.html'; | 322 | $tplFile = $tplDir . '/linklist.html'; |
300 | if (! file_exists($tplFile)) { | 323 | if (!file_exists($tplFile)) { |
301 | return true; | 324 | return true; |
302 | } | 325 | } |
303 | 326 | ||
@@ -321,7 +344,7 @@ class Updater | |||
321 | */ | 344 | */ |
322 | public function updateMethodMoveUserCss() | 345 | public function updateMethodMoveUserCss() |
323 | { | 346 | { |
324 | if (! is_file('inc/user.css')) { | 347 | if (!is_file('inc/user.css')) { |
325 | return true; | 348 | return true; |
326 | } | 349 | } |
327 | 350 | ||
@@ -357,11 +380,11 @@ class Updater | |||
357 | */ | 380 | */ |
358 | public function updateMethodPiwikUrl() | 381 | public function updateMethodPiwikUrl() |
359 | { | 382 | { |
360 | if (! $this->conf->exists('plugins.PIWIK_URL') || startsWith($this->conf->get('plugins.PIWIK_URL'), 'http')) { | 383 | if (!$this->conf->exists('plugins.PIWIK_URL') || startsWith($this->conf->get('plugins.PIWIK_URL'), 'http')) { |
361 | return true; | 384 | return true; |
362 | } | 385 | } |
363 | 386 | ||
364 | $this->conf->set('plugins.PIWIK_URL', 'http://'. $this->conf->get('plugins.PIWIK_URL')); | 387 | $this->conf->set('plugins.PIWIK_URL', 'http://' . $this->conf->get('plugins.PIWIK_URL')); |
365 | $this->conf->write($this->isLoggedIn); | 388 | $this->conf->write($this->isLoggedIn); |
366 | 389 | ||
367 | return true; | 390 | return true; |
@@ -471,109 +494,60 @@ class Updater | |||
471 | return true; | 494 | return true; |
472 | } | 495 | } |
473 | 496 | ||
474 | if (! $this->conf->exists('general.download_max_size')) { | 497 | if (!$this->conf->exists('general.download_max_size')) { |
475 | $this->conf->set('general.download_max_size', 1024*1024*4); | 498 | $this->conf->set('general.download_max_size', 1024 * 1024 * 4); |
476 | } | 499 | } |
477 | 500 | ||
478 | if (! $this->conf->exists('general.download_timeout')) { | 501 | if (!$this->conf->exists('general.download_timeout')) { |
479 | $this->conf->set('general.download_timeout', 30); | 502 | $this->conf->set('general.download_timeout', 30); |
480 | } | 503 | } |
481 | 504 | ||
482 | $this->conf->write($this->isLoggedIn); | 505 | $this->conf->write($this->isLoggedIn); |
483 | |||
484 | return true; | 506 | return true; |
485 | } | 507 | } |
486 | } | ||
487 | |||
488 | /** | ||
489 | * Class UpdaterException. | ||
490 | */ | ||
491 | class UpdaterException extends Exception | ||
492 | { | ||
493 | /** | ||
494 | * @var string Method where the error occurred. | ||
495 | */ | ||
496 | protected $method; | ||
497 | |||
498 | /** | ||
499 | * @var Exception The parent exception. | ||
500 | */ | ||
501 | protected $previous; | ||
502 | |||
503 | /** | ||
504 | * Constructor. | ||
505 | * | ||
506 | * @param string $message Force the error message if set. | ||
507 | * @param string $method Method where the error occurred. | ||
508 | * @param Exception|bool $previous Parent exception. | ||
509 | */ | ||
510 | public function __construct($message = '', $method = '', $previous = false) | ||
511 | { | ||
512 | $this->method = $method; | ||
513 | $this->previous = $previous; | ||
514 | $this->message = $this->buildMessage($message); | ||
515 | } | ||
516 | 508 | ||
517 | /** | 509 | /** |
518 | * Build the exception error message. | 510 | * * Move thumbnails management to WebThumbnailer, coming with new settings. |
519 | * | ||
520 | * @param string $message Optional given error message. | ||
521 | * | ||
522 | * @return string The built error message. | ||
523 | */ | 511 | */ |
524 | private function buildMessage($message) | 512 | public function updateMethodWebThumbnailer() |
525 | { | 513 | { |
526 | $out = ''; | 514 | if ($this->conf->exists('thumbnails.mode')) { |
527 | if (! empty($message)) { | 515 | return true; |
528 | $out .= $message . PHP_EOL; | ||
529 | } | 516 | } |
530 | 517 | ||
531 | if (! empty($this->method)) { | 518 | $thumbnailsEnabled = extension_loaded('gd') && $this->conf->get('thumbnail.enable_thumbnails', true); |
532 | $out .= t('An error occurred while running the update ') . $this->method . PHP_EOL; | 519 | $this->conf->set('thumbnails.mode', $thumbnailsEnabled ? Thumbnailer::MODE_ALL : Thumbnailer::MODE_NONE); |
533 | } | 520 | $this->conf->set('thumbnails.width', 125); |
521 | $this->conf->set('thumbnails.height', 90); | ||
522 | $this->conf->remove('thumbnail'); | ||
523 | $this->conf->write(true); | ||
534 | 524 | ||
535 | if (! empty($this->previous)) { | 525 | if ($thumbnailsEnabled) { |
536 | $out .= ' '. $this->previous->getMessage(); | 526 | $this->session['warnings'][] = t( |
527 | 'You have enabled or changed thumbnails mode. <a href="?do=thumbs_update">Please synchronize them</a>.' | ||
528 | ); | ||
537 | } | 529 | } |
538 | 530 | ||
539 | return $out; | 531 | return true; |
540 | } | 532 | } |
541 | } | ||
542 | 533 | ||
543 | /** | 534 | /** |
544 | * Read the updates file, and return already done updates. | 535 | * Set sticky = false on all links |
545 | * | 536 | * |
546 | * @param string $updatesFilepath Updates file path. | 537 | * @return bool true if the update is successful, false otherwise. |
547 | * | 538 | */ |
548 | * @return array Already done update methods. | 539 | public function updateMethodSetSticky() |
549 | */ | 540 | { |
550 | function read_updates_file($updatesFilepath) | 541 | foreach ($this->linkDB as $key => $link) { |
551 | { | 542 | if (isset($link['sticky'])) { |
552 | if (! empty($updatesFilepath) && is_file($updatesFilepath)) { | 543 | return true; |
553 | $content = file_get_contents($updatesFilepath); | 544 | } |
554 | if (! empty($content)) { | 545 | $link['sticky'] = false; |
555 | return explode(';', $content); | 546 | $this->linkDB[$key] = $link; |
556 | } | 547 | } |
557 | } | ||
558 | return array(); | ||
559 | } | ||
560 | 548 | ||
561 | /** | 549 | $this->linkDB->save($this->conf->get('resource.page_cache')); |
562 | * Write updates file. | ||
563 | * | ||
564 | * @param string $updatesFilepath Updates file path. | ||
565 | * @param array $updates Updates array to write. | ||
566 | * | ||
567 | * @throws Exception Couldn't write version number. | ||
568 | */ | ||
569 | function write_updates_file($updatesFilepath, $updates) | ||
570 | { | ||
571 | if (empty($updatesFilepath)) { | ||
572 | throw new Exception(t('Updates file path is not set, can\'t write updates.')); | ||
573 | } | ||
574 | 550 | ||
575 | $res = file_put_contents($updatesFilepath, implode(';', $updates)); | 551 | return true; |
576 | if ($res === false) { | ||
577 | throw new Exception(t('Unable to write updates in '. $updatesFilepath . '.')); | ||
578 | } | 552 | } |
579 | } | 553 | } |
diff --git a/application/updater/UpdaterUtils.php b/application/updater/UpdaterUtils.php new file mode 100644 index 00000000..34d4f422 --- /dev/null +++ b/application/updater/UpdaterUtils.php | |||
@@ -0,0 +1,39 @@ | |||
1 | <?php | ||
2 | |||
3 | /** | ||
4 | * Read the updates file, and return already done updates. | ||
5 | * | ||
6 | * @param string $updatesFilepath Updates file path. | ||
7 | * | ||
8 | * @return array Already done update methods. | ||
9 | */ | ||
10 | function read_updates_file($updatesFilepath) | ||
11 | { | ||
12 | if (! empty($updatesFilepath) && is_file($updatesFilepath)) { | ||
13 | $content = file_get_contents($updatesFilepath); | ||
14 | if (! empty($content)) { | ||
15 | return explode(';', $content); | ||
16 | } | ||
17 | } | ||
18 | return array(); | ||
19 | } | ||
20 | |||
21 | /** | ||
22 | * Write updates file. | ||
23 | * | ||
24 | * @param string $updatesFilepath Updates file path. | ||
25 | * @param array $updates Updates array to write. | ||
26 | * | ||
27 | * @throws Exception Couldn't write version number. | ||
28 | */ | ||
29 | function write_updates_file($updatesFilepath, $updates) | ||
30 | { | ||
31 | if (empty($updatesFilepath)) { | ||
32 | throw new Exception(t('Updates file path is not set, can\'t write updates.')); | ||
33 | } | ||
34 | |||
35 | $res = file_put_contents($updatesFilepath, implode(';', $updates)); | ||
36 | if ($res === false) { | ||
37 | throw new Exception(t('Unable to write updates in '. $updatesFilepath . '.')); | ||
38 | } | ||
39 | } | ||
diff --git a/application/updater/exception/UpdaterException.php b/application/updater/exception/UpdaterException.php new file mode 100644 index 00000000..20aceccf --- /dev/null +++ b/application/updater/exception/UpdaterException.php | |||
@@ -0,0 +1,60 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Shaarli\Updater\Exception; | ||
4 | |||
5 | use Exception; | ||
6 | |||
7 | /** | ||
8 | * Class UpdaterException. | ||
9 | */ | ||
10 | class UpdaterException extends Exception | ||
11 | { | ||
12 | /** | ||
13 | * @var string Method where the error occurred. | ||
14 | */ | ||
15 | protected $method; | ||
16 | |||
17 | /** | ||
18 | * @var Exception The parent exception. | ||
19 | */ | ||
20 | protected $previous; | ||
21 | |||
22 | /** | ||
23 | * Constructor. | ||
24 | * | ||
25 | * @param string $message Force the error message if set. | ||
26 | * @param string $method Method where the error occurred. | ||
27 | * @param Exception|bool $previous Parent exception. | ||
28 | */ | ||
29 | public function __construct($message = '', $method = '', $previous = false) | ||
30 | { | ||
31 | $this->method = $method; | ||
32 | $this->previous = $previous; | ||
33 | $this->message = $this->buildMessage($message); | ||
34 | } | ||
35 | |||
36 | /** | ||
37 | * Build the exception error message. | ||
38 | * | ||
39 | * @param string $message Optional given error message. | ||
40 | * | ||
41 | * @return string The built error message. | ||
42 | */ | ||
43 | private function buildMessage($message) | ||
44 | { | ||
45 | $out = ''; | ||
46 | if (!empty($message)) { | ||
47 | $out .= $message . PHP_EOL; | ||
48 | } | ||
49 | |||
50 | if (!empty($this->method)) { | ||
51 | $out .= t('An error occurred while running the update ') . $this->method . PHP_EOL; | ||
52 | } | ||
53 | |||
54 | if (!empty($this->previous)) { | ||
55 | $out .= ' ' . $this->previous->getMessage(); | ||
56 | } | ||
57 | |||
58 | return $out; | ||
59 | } | ||
60 | } | ||