diff options
-rw-r--r-- | application/Config.php | 27 | ||||
-rw-r--r-- | application/LinkDB.php | 10 | ||||
-rw-r--r-- | application/LinkFilter.php | 1 | ||||
-rw-r--r-- | application/Updater.php | 228 | ||||
-rw-r--r-- | index.php | 80 | ||||
-rw-r--r-- | tests/ConfigTest.php | 42 | ||||
-rw-r--r-- | tests/Updater/DummyUpdater.php | 68 | ||||
-rw-r--r-- | tests/Updater/UpdaterTest.php | 227 |
8 files changed, 584 insertions, 99 deletions
diff --git a/application/Config.php b/application/Config.php index 9af5a535..05a59452 100644 --- a/application/Config.php +++ b/application/Config.php | |||
@@ -174,33 +174,6 @@ function load_plugin_parameter_values($plugins, $config) | |||
174 | } | 174 | } |
175 | 175 | ||
176 | /** | 176 | /** |
177 | * Milestone 0.9 - shaarli/Shaarli#41: options.php is not supported anymore. | ||
178 | * ==> if user is loggedIn, merge its content with config.php, then delete options.php. | ||
179 | * | ||
180 | * @param array $config contains all configuration fields. | ||
181 | * @param bool $isLoggedIn true if user is logged in. | ||
182 | * | ||
183 | * @return void | ||
184 | */ | ||
185 | function mergeDeprecatedConfig($config, $isLoggedIn) | ||
186 | { | ||
187 | $config_file = $config['config']['CONFIG_FILE']; | ||
188 | |||
189 | if (is_file($config['config']['DATADIR'].'/options.php') && $isLoggedIn) { | ||
190 | include $config['config']['DATADIR'].'/options.php'; | ||
191 | |||
192 | // Load GLOBALS into config | ||
193 | foreach ($GLOBALS as $key => $value) { | ||
194 | $config[$key] = $value; | ||
195 | } | ||
196 | $config['config']['CONFIG_FILE'] = $config_file; | ||
197 | writeConfig($config, $isLoggedIn); | ||
198 | |||
199 | unlink($config['config']['DATADIR'].'/options.php'); | ||
200 | } | ||
201 | } | ||
202 | |||
203 | /** | ||
204 | * Exception used if a mandatory field is missing in given configuration. | 177 | * Exception used if a mandatory field is missing in given configuration. |
205 | */ | 178 | */ |
206 | class MissingFieldConfigException extends Exception | 179 | class MissingFieldConfigException extends Exception |
diff --git a/application/LinkDB.php b/application/LinkDB.php index 19ca6435..a95b3f36 100644 --- a/application/LinkDB.php +++ b/application/LinkDB.php | |||
@@ -260,14 +260,11 @@ You use the community supported version of the original Shaarli project, by Seba | |||
260 | } | 260 | } |
261 | } | 261 | } |
262 | 262 | ||
263 | // Keep the list of the mapping URLs-->linkdate up-to-date. | ||
264 | $this->_urls = array(); | 263 | $this->_urls = array(); |
265 | foreach ($this->_links as $link) { | 264 | foreach ($this->_links as &$link) { |
265 | // Keep the list of the mapping URLs-->linkdate up-to-date. | ||
266 | $this->_urls[$link['url']] = $link['linkdate']; | 266 | $this->_urls[$link['url']] = $link['linkdate']; |
267 | } | 267 | // Sanitize data fields. |
268 | |||
269 | // Escape links data | ||
270 | foreach($this->_links as &$link) { | ||
271 | sanitizeLink($link); | 268 | sanitizeLink($link); |
272 | // Do not use the redirector for internal links (Shaarli note URL starting with a '?'). | 269 | // Do not use the redirector for internal links (Shaarli note URL starting with a '?'). |
273 | if (!empty($this->_redirector) && !startsWith($link['url'], '?')) { | 270 | if (!empty($this->_redirector) && !startsWith($link['url'], '?')) { |
@@ -381,6 +378,7 @@ You use the community supported version of the original Shaarli project, by Seba | |||
381 | } | 378 | } |
382 | $linkDays = array_keys($linkDays); | 379 | $linkDays = array_keys($linkDays); |
383 | sort($linkDays); | 380 | sort($linkDays); |
381 | |||
384 | return $linkDays; | 382 | return $linkDays; |
385 | } | 383 | } |
386 | } | 384 | } |
diff --git a/application/LinkFilter.php b/application/LinkFilter.php index b2e6530f..096d3b04 100644 --- a/application/LinkFilter.php +++ b/application/LinkFilter.php | |||
@@ -260,7 +260,6 @@ class LinkFilter | |||
260 | * Convert a list of tags (str) to an array. Also | 260 | * Convert a list of tags (str) to an array. Also |
261 | * - handle case sensitivity. | 261 | * - handle case sensitivity. |
262 | * - accepts spaces commas as separator. | 262 | * - accepts spaces commas as separator. |
263 | * - remove private tags for loggedout users. | ||
264 | * | 263 | * |
265 | * @param string $tags string containing a list of tags. | 264 | * @param string $tags string containing a list of tags. |
266 | * @param bool $casesensitive will convert everything to lowercase if false. | 265 | * @param bool $casesensitive will convert everything to lowercase if false. |
diff --git a/application/Updater.php b/application/Updater.php new file mode 100644 index 00000000..20ae0c4d --- /dev/null +++ b/application/Updater.php | |||
@@ -0,0 +1,228 @@ | |||
1 | <?php | ||
2 | |||
3 | /** | ||
4 | * Class Updater. | ||
5 | * Used to update stuff when a new Shaarli's version is reached. | ||
6 | * Update methods are ran only once, and the stored in a JSON file. | ||
7 | */ | ||
8 | class Updater | ||
9 | { | ||
10 | /** | ||
11 | * @var array Updates which are already done. | ||
12 | */ | ||
13 | protected $doneUpdates; | ||
14 | |||
15 | /** | ||
16 | * @var array Shaarli's configuration array. | ||
17 | */ | ||
18 | protected $config; | ||
19 | |||
20 | /** | ||
21 | * @var LinkDB instance. | ||
22 | */ | ||
23 | protected $linkDB; | ||
24 | |||
25 | /** | ||
26 | * @var bool True if the user is logged in, false otherwise. | ||
27 | */ | ||
28 | protected $isLoggedIn; | ||
29 | |||
30 | /** | ||
31 | * @var ReflectionMethod[] List of current class methods. | ||
32 | */ | ||
33 | protected $methods; | ||
34 | |||
35 | /** | ||
36 | * Object constructor. | ||
37 | * | ||
38 | * @param array $doneUpdates Updates which are already done. | ||
39 | * @param array $config Shaarli's configuration array. | ||
40 | * @param LinkDB $linkDB LinkDB instance. | ||
41 | * @param boolean $isLoggedIn True if the user is logged in. | ||
42 | */ | ||
43 | public function __construct($doneUpdates, $config, $linkDB, $isLoggedIn) | ||
44 | { | ||
45 | $this->doneUpdates = $doneUpdates; | ||
46 | $this->config = $config; | ||
47 | $this->linkDB = $linkDB; | ||
48 | $this->isLoggedIn = $isLoggedIn; | ||
49 | |||
50 | // Retrieve all update methods. | ||
51 | $class = new ReflectionClass($this); | ||
52 | $this->methods = $class->getMethods(); | ||
53 | } | ||
54 | |||
55 | /** | ||
56 | * Run all new updates. | ||
57 | * Update methods have to start with 'updateMethod' and return true (on success). | ||
58 | * | ||
59 | * @return array An array containing ran updates. | ||
60 | * | ||
61 | * @throws UpdaterException If something went wrong. | ||
62 | */ | ||
63 | public function update() | ||
64 | { | ||
65 | $updatesRan = array(); | ||
66 | |||
67 | // If the user isn't logged in, exit without updating. | ||
68 | if ($this->isLoggedIn !== true) { | ||
69 | return $updatesRan; | ||
70 | } | ||
71 | |||
72 | if ($this->methods == null) { | ||
73 | throw new UpdaterException('Couldn\'t retrieve Updater class methods.'); | ||
74 | } | ||
75 | |||
76 | foreach ($this->methods as $method) { | ||
77 | // Not an update method or already done, pass. | ||
78 | if (! startsWith($method->getName(), 'updateMethod') | ||
79 | || in_array($method->getName(), $this->doneUpdates) | ||
80 | ) { | ||
81 | continue; | ||
82 | } | ||
83 | |||
84 | try { | ||
85 | $method->setAccessible(true); | ||
86 | $res = $method->invoke($this); | ||
87 | // Update method must return true to be considered processed. | ||
88 | if ($res === true) { | ||
89 | $updatesRan[] = $method->getName(); | ||
90 | } | ||
91 | } catch (Exception $e) { | ||
92 | throw new UpdaterException($method, $e); | ||
93 | } | ||
94 | } | ||
95 | |||
96 | $this->doneUpdates = array_merge($this->doneUpdates, $updatesRan); | ||
97 | |||
98 | return $updatesRan; | ||
99 | } | ||
100 | |||
101 | /** | ||
102 | * @return array Updates methods already processed. | ||
103 | */ | ||
104 | public function getDoneUpdates() | ||
105 | { | ||
106 | return $this->doneUpdates; | ||
107 | } | ||
108 | |||
109 | /** | ||
110 | * Move deprecated options.php to config.php. | ||
111 | * | ||
112 | * Milestone 0.9 (old versioning) - shaarli/Shaarli#41: | ||
113 | * options.php is not supported anymore. | ||
114 | */ | ||
115 | public function updateMethodMergeDeprecatedConfigFile() | ||
116 | { | ||
117 | $config_file = $this->config['config']['CONFIG_FILE']; | ||
118 | |||
119 | if (is_file($this->config['config']['DATADIR'].'/options.php')) { | ||
120 | include $this->config['config']['DATADIR'].'/options.php'; | ||
121 | |||
122 | // Load GLOBALS into config | ||
123 | foreach ($GLOBALS as $key => $value) { | ||
124 | $this->config[$key] = $value; | ||
125 | } | ||
126 | $this->config['config']['CONFIG_FILE'] = $config_file; | ||
127 | writeConfig($this->config, $this->isLoggedIn); | ||
128 | |||
129 | unlink($this->config['config']['DATADIR'].'/options.php'); | ||
130 | } | ||
131 | |||
132 | return true; | ||
133 | } | ||
134 | } | ||
135 | |||
136 | /** | ||
137 | * Class UpdaterException. | ||
138 | */ | ||
139 | class UpdaterException extends Exception | ||
140 | { | ||
141 | /** | ||
142 | * @var string Method where the error occurred. | ||
143 | */ | ||
144 | protected $method; | ||
145 | |||
146 | /** | ||
147 | * @var Exception The parent exception. | ||
148 | */ | ||
149 | protected $previous; | ||
150 | |||
151 | /** | ||
152 | * Constructor. | ||
153 | * | ||
154 | * @param string $message Force the error message if set. | ||
155 | * @param string $method Method where the error occurred. | ||
156 | * @param Exception|bool $previous Parent exception. | ||
157 | */ | ||
158 | public function __construct($message = '', $method = '', $previous = false) | ||
159 | { | ||
160 | $this->method = $method; | ||
161 | $this->previous = $previous; | ||
162 | $this->message = $this->buildMessage($message); | ||
163 | } | ||
164 | |||
165 | /** | ||
166 | * Build the exception error message. | ||
167 | * | ||
168 | * @param string $message Optional given error message. | ||
169 | * | ||
170 | * @return string The built error message. | ||
171 | */ | ||
172 | private function buildMessage($message) | ||
173 | { | ||
174 | $out = ''; | ||
175 | if (! empty($message)) { | ||
176 | $out .= $message . PHP_EOL; | ||
177 | } | ||
178 | |||
179 | if (! empty($this->method)) { | ||
180 | $out .= 'An error occurred while running the update '. $this->method . PHP_EOL; | ||
181 | } | ||
182 | |||
183 | if (! empty($this->previous)) { | ||
184 | $out .= ' '. $this->previous->getMessage(); | ||
185 | } | ||
186 | |||
187 | return $out; | ||
188 | } | ||
189 | } | ||
190 | |||
191 | |||
192 | /** | ||
193 | * Read the updates file, and return already done updates. | ||
194 | * | ||
195 | * @param string $updatesFilepath Updates file path. | ||
196 | * | ||
197 | * @return array Already done update methods. | ||
198 | */ | ||
199 | function read_updates_file($updatesFilepath) | ||
200 | { | ||
201 | if (! empty($updatesFilepath) && is_file($updatesFilepath)) { | ||
202 | $content = file_get_contents($updatesFilepath); | ||
203 | if (! empty($content)) { | ||
204 | return explode(';', $content); | ||
205 | } | ||
206 | } | ||
207 | return array(); | ||
208 | } | ||
209 | |||
210 | /** | ||
211 | * Write updates file. | ||
212 | * | ||
213 | * @param string $updatesFilepath Updates file path. | ||
214 | * @param array $updates Updates array to write. | ||
215 | * | ||
216 | * @throws Exception Couldn't write version number. | ||
217 | */ | ||
218 | function write_updates_file($updatesFilepath, $updates) | ||
219 | { | ||
220 | if (empty($updatesFilepath)) { | ||
221 | throw new Exception('Updates file path is not set, can\'t write updates.'); | ||
222 | } | ||
223 | |||
224 | $res = file_put_contents($updatesFilepath, implode(';', $updates)); | ||
225 | if ($res === false) { | ||
226 | throw new Exception('Unable to write updates in '. $updatesFilepath . '.'); | ||
227 | } | ||
228 | } | ||
@@ -44,6 +44,9 @@ $GLOBALS['config']['DATASTORE'] = $GLOBALS['config']['DATADIR'].'/datastore.php' | |||
44 | // Banned IPs | 44 | // Banned IPs |
45 | $GLOBALS['config']['IPBANS_FILENAME'] = $GLOBALS['config']['DATADIR'].'/ipbans.php'; | 45 | $GLOBALS['config']['IPBANS_FILENAME'] = $GLOBALS['config']['DATADIR'].'/ipbans.php'; |
46 | 46 | ||
47 | // Processed updates file. | ||
48 | $GLOBALS['config']['UPDATES_FILE'] = $GLOBALS['config']['DATADIR'].'/updates.txt'; | ||
49 | |||
47 | // Access log | 50 | // Access log |
48 | $GLOBALS['config']['LOG_FILE'] = $GLOBALS['config']['DATADIR'].'/log.txt'; | 51 | $GLOBALS['config']['LOG_FILE'] = $GLOBALS['config']['DATADIR'].'/log.txt'; |
49 | 52 | ||
@@ -64,7 +67,6 @@ $GLOBALS['config']['CACHEDIR'] = 'cache'; | |||
64 | // Atom & RSS feed cache directory | 67 | // Atom & RSS feed cache directory |
65 | $GLOBALS['config']['PAGECACHE'] = 'pagecache'; | 68 | $GLOBALS['config']['PAGECACHE'] = 'pagecache'; |
66 | 69 | ||
67 | |||
68 | /* | 70 | /* |
69 | * Global configuration | 71 | * Global configuration |
70 | */ | 72 | */ |
@@ -163,6 +165,7 @@ require_once 'application/Utils.php'; | |||
163 | require_once 'application/Config.php'; | 165 | require_once 'application/Config.php'; |
164 | require_once 'application/PluginManager.php'; | 166 | require_once 'application/PluginManager.php'; |
165 | require_once 'application/Router.php'; | 167 | require_once 'application/Router.php'; |
168 | require_once 'application/Updater.php'; | ||
166 | 169 | ||
167 | // Ensure the PHP version is supported | 170 | // Ensure the PHP version is supported |
168 | try { | 171 | try { |
@@ -1114,6 +1117,25 @@ function renderPage() | |||
1114 | $GLOBALS['redirector'] | 1117 | $GLOBALS['redirector'] |
1115 | ); | 1118 | ); |
1116 | 1119 | ||
1120 | $updater = new Updater( | ||
1121 | read_updates_file($GLOBALS['config']['UPDATES_FILE']), | ||
1122 | $GLOBALS, | ||
1123 | $LINKSDB, | ||
1124 | isLoggedIn() | ||
1125 | ); | ||
1126 | try { | ||
1127 | $newUpdates = $updater->update(); | ||
1128 | if (! empty($newUpdates)) { | ||
1129 | write_updates_file( | ||
1130 | $GLOBALS['config']['UPDATES_FILE'], | ||
1131 | $updater->getDoneUpdates() | ||
1132 | ); | ||
1133 | } | ||
1134 | } | ||
1135 | catch(Exception $e) { | ||
1136 | die($e->getMessage()); | ||
1137 | } | ||
1138 | |||
1117 | $PAGE = new pageBuilder; | 1139 | $PAGE = new pageBuilder; |
1118 | 1140 | ||
1119 | // Determine which page will be rendered. | 1141 | // Determine which page will be rendered. |
@@ -1530,21 +1552,40 @@ function renderPage() | |||
1530 | // -------- User clicked the "Save" button when editing a link: Save link to database. | 1552 | // -------- User clicked the "Save" button when editing a link: Save link to database. |
1531 | if (isset($_POST['save_edit'])) | 1553 | if (isset($_POST['save_edit'])) |
1532 | { | 1554 | { |
1533 | if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away! | 1555 | // Go away! |
1534 | $tags = trim(preg_replace('/\s\s+/',' ', $_POST['lf_tags'])); // Remove multiple spaces. | 1556 | if (! tokenOk($_POST['token'])) { |
1535 | $tags = implode(' ', array_unique(explode(' ', $tags))); // Remove duplicates. | 1557 | die('Wrong token.'); |
1536 | $linkdate=$_POST['lf_linkdate']; | 1558 | } |
1559 | // Remove multiple spaces. | ||
1560 | $tags = trim(preg_replace('/\s\s+/', ' ', $_POST['lf_tags'])); | ||
1561 | // Remove duplicates. | ||
1562 | $tags = implode(' ', array_unique(explode(' ', $tags))); | ||
1563 | $linkdate = $_POST['lf_linkdate']; | ||
1537 | $url = trim($_POST['lf_url']); | 1564 | $url = trim($_POST['lf_url']); |
1538 | if (!startsWith($url,'http:') && !startsWith($url,'https:') && !startsWith($url,'ftp:') && !startsWith($url,'magnet:') && !startsWith($url,'?') && !startsWith($url,'javascript:')) | 1565 | if (! startsWith($url, 'http:') && ! startsWith($url, 'https:') |
1539 | $url = 'http://'.$url; | 1566 | && ! startsWith($url, 'ftp:') && ! startsWith($url, 'magnet:') |
1540 | $link = array('title'=>trim($_POST['lf_title']),'url'=>$url,'description'=>trim($_POST['lf_description']),'private'=>(isset($_POST['lf_private']) ? 1 : 0), | 1567 | && ! startsWith($url, '?') && ! startsWith($url, 'javascript:') |
1541 | 'linkdate'=>$linkdate,'tags'=>str_replace(',',' ',$tags)); | 1568 | ) { |
1542 | if ($link['title']=='') $link['title']=$link['url']; // If title is empty, use the URL as title. | 1569 | $url = 'http://' . $url; |
1570 | } | ||
1571 | |||
1572 | $link = array( | ||
1573 | 'title' => trim($_POST['lf_title']), | ||
1574 | 'url' => $url, | ||
1575 | 'description' => trim($_POST['lf_description']), | ||
1576 | 'private' => (isset($_POST['lf_private']) ? 1 : 0), | ||
1577 | 'linkdate' => $linkdate, | ||
1578 | 'tags' => str_replace(',', ' ', $tags) | ||
1579 | ); | ||
1580 | // If title is empty, use the URL as title. | ||
1581 | if ($link['title'] == '') { | ||
1582 | $link['title'] = $link['url']; | ||
1583 | } | ||
1543 | 1584 | ||
1544 | $pluginManager->executeHooks('save_link', $link); | 1585 | $pluginManager->executeHooks('save_link', $link); |
1545 | 1586 | ||
1546 | $LINKSDB[$linkdate] = $link; | 1587 | $LINKSDB[$linkdate] = $link; |
1547 | $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']); // Save to disk. | 1588 | $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']); |
1548 | pubsubhub(); | 1589 | pubsubhub(); |
1549 | 1590 | ||
1550 | // If we are called from the bookmarklet, we must close the popup: | 1591 | // If we are called from the bookmarklet, we must close the popup: |
@@ -1553,10 +1594,12 @@ function renderPage() | |||
1553 | exit; | 1594 | exit; |
1554 | } | 1595 | } |
1555 | 1596 | ||
1556 | $returnurl = !empty($_POST['returnurl']) ? escape($_POST['returnurl']): '?'; | 1597 | $returnurl = !empty($_POST['returnurl']) ? $_POST['returnurl'] : '?'; |
1557 | $location = generateLocation($returnurl, $_SERVER['HTTP_HOST'], array('addlink', 'post', 'edit_link')); | 1598 | $location = generateLocation($returnurl, $_SERVER['HTTP_HOST'], array('addlink', 'post', 'edit_link')); |
1558 | $location .= '#'.smallHash($_POST['lf_linkdate']); // Scroll to the link which has been edited. | 1599 | // Scroll to the link which has been edited. |
1559 | header('Location: '. $location); // After saving the link, redirect to the page the user was on. | 1600 | $location .= '#' . smallHash($_POST['lf_linkdate']); |
1601 | // After saving the link, redirect to the page the user was on. | ||
1602 | header('Location: '. $location); | ||
1560 | exit; | 1603 | exit; |
1561 | } | 1604 | } |
1562 | 1605 | ||
@@ -2519,15 +2562,6 @@ function resizeImage($filepath) | |||
2519 | return true; | 2562 | return true; |
2520 | } | 2563 | } |
2521 | 2564 | ||
2522 | try { | ||
2523 | mergeDeprecatedConfig($GLOBALS, isLoggedIn()); | ||
2524 | } catch(Exception $e) { | ||
2525 | error_log( | ||
2526 | 'ERROR while merging deprecated options.php file.' . PHP_EOL . | ||
2527 | $e->getMessage() | ||
2528 | ); | ||
2529 | } | ||
2530 | |||
2531 | if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=genthumbnail')) { genThumbnail(); exit; } // Thumbnail generation/cache does not need the link database. | 2565 | if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=genthumbnail')) { genThumbnail(); exit; } // Thumbnail generation/cache does not need the link database. |
2532 | if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=rss')) { showRSS(); exit; } | 2566 | if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=rss')) { showRSS(); exit; } |
2533 | if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=atom')) { showATOM(); exit; } | 2567 | if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=atom')) { showATOM(); exit; } |
diff --git a/tests/ConfigTest.php b/tests/ConfigTest.php index 492ddd3b..7200aae6 100644 --- a/tests/ConfigTest.php +++ b/tests/ConfigTest.php | |||
@@ -134,48 +134,6 @@ class ConfigTest extends PHPUnit_Framework_TestCase | |||
134 | } | 134 | } |
135 | 135 | ||
136 | /** | 136 | /** |
137 | * Test mergeDeprecatedConfig while being logged in: | ||
138 | * 1. init a config file. | ||
139 | * 2. init a options.php file with update value. | ||
140 | * 3. merge. | ||
141 | * 4. check updated value in config file. | ||
142 | */ | ||
143 | public function testMergeDeprecatedConfig() | ||
144 | { | ||
145 | // init | ||
146 | writeConfig(self::$configFields, true); | ||
147 | $configCopy = self::$configFields; | ||
148 | $invert = !$configCopy['privateLinkByDefault']; | ||
149 | $configCopy['privateLinkByDefault'] = $invert; | ||
150 | |||
151 | // Use writeConfig to create a options.php | ||
152 | $configCopy['config']['CONFIG_FILE'] = 'tests/options.php'; | ||
153 | writeConfig($configCopy, true); | ||
154 | |||
155 | $this->assertTrue(is_file($configCopy['config']['CONFIG_FILE'])); | ||
156 | |||
157 | // merge configs | ||
158 | mergeDeprecatedConfig(self::$configFields, true); | ||
159 | |||
160 | // make sure updated field is changed | ||
161 | include self::$configFields['config']['CONFIG_FILE']; | ||
162 | $this->assertEquals($invert, $GLOBALS['privateLinkByDefault']); | ||
163 | $this->assertFalse(is_file($configCopy['config']['CONFIG_FILE'])); | ||
164 | } | ||
165 | |||
166 | /** | ||
167 | * Test mergeDeprecatedConfig while being logged in without options file. | ||
168 | */ | ||
169 | public function testMergeDeprecatedConfigNoFile() | ||
170 | { | ||
171 | writeConfig(self::$configFields, true); | ||
172 | mergeDeprecatedConfig(self::$configFields, true); | ||
173 | |||
174 | include self::$configFields['config']['CONFIG_FILE']; | ||
175 | $this->assertEquals(self::$configFields['login'], $GLOBALS['login']); | ||
176 | } | ||
177 | |||
178 | /** | ||
179 | * Test save_plugin_config with valid data. | 137 | * Test save_plugin_config with valid data. |
180 | * | 138 | * |
181 | * @throws PluginConfigOrderException | 139 | * @throws PluginConfigOrderException |
diff --git a/tests/Updater/DummyUpdater.php b/tests/Updater/DummyUpdater.php new file mode 100644 index 00000000..e9ef2aaa --- /dev/null +++ b/tests/Updater/DummyUpdater.php | |||
@@ -0,0 +1,68 @@ | |||
1 | <?php | ||
2 | |||
3 | require_once 'application/Updater.php'; | ||
4 | |||
5 | /** | ||
6 | * Class DummyUpdater. | ||
7 | * Extends Updater to add update method designed for unit tests. | ||
8 | */ | ||
9 | class DummyUpdater extends Updater | ||
10 | { | ||
11 | /** | ||
12 | * Object constructor. | ||
13 | * | ||
14 | * @param array $doneUpdates Updates which are already done. | ||
15 | * @param array $config Shaarli's configuration array. | ||
16 | * @param LinkDB $linkDB LinkDB instance. | ||
17 | * @param boolean $isLoggedIn True if the user is logged in. | ||
18 | */ | ||
19 | public function __construct($doneUpdates, $config, $linkDB, $isLoggedIn) | ||
20 | { | ||
21 | parent::__construct($doneUpdates, $config, $linkDB, $isLoggedIn); | ||
22 | |||
23 | // Retrieve all update methods. | ||
24 | // For unit test, only retrieve final methods, | ||
25 | $class = new ReflectionClass($this); | ||
26 | $this->methods = $class->getMethods(ReflectionMethod::IS_FINAL); | ||
27 | } | ||
28 | |||
29 | /** | ||
30 | * Update method 1. | ||
31 | * | ||
32 | * @return bool true. | ||
33 | */ | ||
34 | private final function updateMethodDummy1() | ||
35 | { | ||
36 | return true; | ||
37 | } | ||
38 | |||
39 | /** | ||
40 | * Update method 2. | ||
41 | * | ||
42 | * @return bool true. | ||
43 | */ | ||
44 | private final function updateMethodDummy2() | ||
45 | { | ||
46 | return true; | ||
47 | } | ||
48 | |||
49 | /** | ||
50 | * Update method 3. | ||
51 | * | ||
52 | * @return bool true. | ||
53 | */ | ||
54 | private final function updateMethodDummy3() | ||
55 | { | ||
56 | return true; | ||
57 | } | ||
58 | |||
59 | /** | ||
60 | * Update method 4, raise an exception. | ||
61 | * | ||
62 | * @throws Exception error. | ||
63 | */ | ||
64 | private final function updateMethodException() | ||
65 | { | ||
66 | throw new Exception('whatever'); | ||
67 | } | ||
68 | } | ||
diff --git a/tests/Updater/UpdaterTest.php b/tests/Updater/UpdaterTest.php new file mode 100644 index 00000000..63ed5e03 --- /dev/null +++ b/tests/Updater/UpdaterTest.php | |||
@@ -0,0 +1,227 @@ | |||
1 | <?php | ||
2 | |||
3 | require_once 'tests/Updater/DummyUpdater.php'; | ||
4 | |||
5 | /** | ||
6 | * Class UpdaterTest. | ||
7 | * Runs unit tests against the Updater class. | ||
8 | */ | ||
9 | class UpdaterTest extends PHPUnit_Framework_TestCase | ||
10 | { | ||
11 | /** | ||
12 | * @var array Configuration input set. | ||
13 | */ | ||
14 | private static $configFields; | ||
15 | |||
16 | /** | ||
17 | * Executed before each test. | ||
18 | */ | ||
19 | public function setUp() | ||
20 | { | ||
21 | self::$configFields = array( | ||
22 | 'login' => 'login', | ||
23 | 'hash' => 'hash', | ||
24 | 'salt' => 'salt', | ||
25 | 'timezone' => 'Europe/Paris', | ||
26 | 'title' => 'title', | ||
27 | 'titleLink' => 'titleLink', | ||
28 | 'redirector' => '', | ||
29 | 'disablesessionprotection' => false, | ||
30 | 'privateLinkByDefault' => false, | ||
31 | 'config' => array( | ||
32 | 'CONFIG_FILE' => 'tests/Updater/config.php', | ||
33 | 'DATADIR' => 'tests/Updater', | ||
34 | 'config1' => 'config1data', | ||
35 | 'config2' => 'config2data', | ||
36 | ) | ||
37 | ); | ||
38 | } | ||
39 | |||
40 | /** | ||
41 | * Executed after each test. | ||
42 | * | ||
43 | * @return void | ||
44 | */ | ||
45 | public function tearDown() | ||
46 | { | ||
47 | if (is_file(self::$configFields['config']['CONFIG_FILE'])) { | ||
48 | unlink(self::$configFields['config']['CONFIG_FILE']); | ||
49 | } | ||
50 | |||
51 | if (is_file(self::$configFields['config']['DATADIR'] . '/options.php')) { | ||
52 | unlink(self::$configFields['config']['DATADIR'] . '/options.php'); | ||
53 | } | ||
54 | |||
55 | if (is_file(self::$configFields['config']['DATADIR'] . '/updates.json')) { | ||
56 | unlink(self::$configFields['config']['DATADIR'] . '/updates.json'); | ||
57 | } | ||
58 | } | ||
59 | |||
60 | /** | ||
61 | * Test read_updates_file with an empty/missing file. | ||
62 | */ | ||
63 | public function testReadEmptyUpdatesFile() | ||
64 | { | ||
65 | $this->assertEquals(array(), read_updates_file('')); | ||
66 | $updatesFile = self::$configFields['config']['DATADIR'] . '/updates.json'; | ||
67 | touch($updatesFile); | ||
68 | $this->assertEquals(array(), read_updates_file($updatesFile)); | ||
69 | } | ||
70 | |||
71 | /** | ||
72 | * Test read/write updates file. | ||
73 | */ | ||
74 | public function testReadWriteUpdatesFile() | ||
75 | { | ||
76 | $updatesFile = self::$configFields['config']['DATADIR'] . '/updates.json'; | ||
77 | $updatesMethods = array('m1', 'm2', 'm3'); | ||
78 | |||
79 | write_updates_file($updatesFile, $updatesMethods); | ||
80 | $readMethods = read_updates_file($updatesFile); | ||
81 | $this->assertEquals($readMethods, $updatesMethods); | ||
82 | |||
83 | // Update | ||
84 | $updatesMethods[] = 'm4'; | ||
85 | write_updates_file($updatesFile, $updatesMethods); | ||
86 | $readMethods = read_updates_file($updatesFile); | ||
87 | $this->assertEquals($readMethods, $updatesMethods); | ||
88 | } | ||
89 | |||
90 | /** | ||
91 | * Test errors in write_updates_file(): empty updates file. | ||
92 | * | ||
93 | * @expectedException Exception | ||
94 | * @expectedExceptionMessageRegExp /Updates file path is not set(.*)/ | ||
95 | */ | ||
96 | public function testWriteEmptyUpdatesFile() | ||
97 | { | ||
98 | write_updates_file('', array('test')); | ||
99 | } | ||
100 | |||
101 | /** | ||
102 | * Test errors in write_updates_file(): not writable updates file. | ||
103 | * | ||
104 | * @expectedException Exception | ||
105 | * @expectedExceptionMessageRegExp /Unable to write(.*)/ | ||
106 | */ | ||
107 | public function testWriteUpdatesFileNotWritable() | ||
108 | { | ||
109 | $updatesFile = self::$configFields['config']['DATADIR'] . '/updates.json'; | ||
110 | touch($updatesFile); | ||
111 | chmod($updatesFile, 0444); | ||
112 | @write_updates_file($updatesFile, array('test')); | ||
113 | } | ||
114 | |||
115 | /** | ||
116 | * Test the update() method, with no update to run. | ||
117 | * 1. Everything already run. | ||
118 | * 2. User is logged out. | ||
119 | */ | ||
120 | public function testNoUpdates() | ||
121 | { | ||
122 | $updates = array( | ||
123 | 'updateMethodDummy1', | ||
124 | 'updateMethodDummy2', | ||
125 | 'updateMethodDummy3', | ||
126 | 'updateMethodException', | ||
127 | ); | ||
128 | $updater = new DummyUpdater($updates, array(), array(), true); | ||
129 | $this->assertEquals(array(), $updater->update()); | ||
130 | |||
131 | $updater = new DummyUpdater(array(), array(), array(), false); | ||
132 | $this->assertEquals(array(), $updater->update()); | ||
133 | } | ||
134 | |||
135 | /** | ||
136 | * Test the update() method, with all updates to run (except the failing one). | ||
137 | */ | ||
138 | public function testUpdatesFirstTime() | ||
139 | { | ||
140 | $updates = array('updateMethodException',); | ||
141 | $expectedUpdates = array( | ||
142 | 'updateMethodDummy1', | ||
143 | 'updateMethodDummy2', | ||
144 | 'updateMethodDummy3', | ||
145 | ); | ||
146 | $updater = new DummyUpdater($updates, array(), array(), true); | ||
147 | $this->assertEquals($expectedUpdates, $updater->update()); | ||
148 | } | ||
149 | |||
150 | /** | ||
151 | * Test the update() method, only one update to run. | ||
152 | */ | ||
153 | public function testOneUpdate() | ||
154 | { | ||
155 | $updates = array( | ||
156 | 'updateMethodDummy1', | ||
157 | 'updateMethodDummy3', | ||
158 | 'updateMethodException', | ||
159 | ); | ||
160 | $expectedUpdate = array('updateMethodDummy2'); | ||
161 | |||
162 | $updater = new DummyUpdater($updates, array(), array(), true); | ||
163 | $this->assertEquals($expectedUpdate, $updater->update()); | ||
164 | } | ||
165 | |||
166 | /** | ||
167 | * Test Update failed. | ||
168 | * | ||
169 | * @expectedException UpdaterException | ||
170 | */ | ||
171 | public function testUpdateFailed() | ||
172 | { | ||
173 | $updates = array( | ||
174 | 'updateMethodDummy1', | ||
175 | 'updateMethodDummy2', | ||
176 | 'updateMethodDummy3', | ||
177 | ); | ||
178 | |||
179 | $updater = new DummyUpdater($updates, array(), array(), true); | ||
180 | $updater->update(); | ||
181 | } | ||
182 | |||
183 | /** | ||
184 | * Test update mergeDeprecatedConfig: | ||
185 | * 1. init a config file. | ||
186 | * 2. init a options.php file with update value. | ||
187 | * 3. merge. | ||
188 | * 4. check updated value in config file. | ||
189 | */ | ||
190 | public function testUpdateMergeDeprecatedConfig() | ||
191 | { | ||
192 | // init | ||
193 | writeConfig(self::$configFields, true); | ||
194 | $configCopy = self::$configFields; | ||
195 | $invert = !$configCopy['privateLinkByDefault']; | ||
196 | $configCopy['privateLinkByDefault'] = $invert; | ||
197 | |||
198 | // Use writeConfig to create a options.php | ||
199 | $configCopy['config']['CONFIG_FILE'] = 'tests/Updater/options.php'; | ||
200 | writeConfig($configCopy, true); | ||
201 | |||
202 | $this->assertTrue(is_file($configCopy['config']['CONFIG_FILE'])); | ||
203 | |||
204 | // merge configs | ||
205 | $updater = new Updater(array(), self::$configFields, array(), true); | ||
206 | $updater->updateMethodMergeDeprecatedConfigFile(); | ||
207 | |||
208 | // make sure updated field is changed | ||
209 | include self::$configFields['config']['CONFIG_FILE']; | ||
210 | $this->assertEquals($invert, $GLOBALS['privateLinkByDefault']); | ||
211 | $this->assertFalse(is_file($configCopy['config']['CONFIG_FILE'])); | ||
212 | } | ||
213 | |||
214 | /** | ||
215 | * Test mergeDeprecatedConfig in without options file. | ||
216 | */ | ||
217 | public function testMergeDeprecatedConfigNoFile() | ||
218 | { | ||
219 | writeConfig(self::$configFields, true); | ||
220 | |||
221 | $updater = new Updater(array(), self::$configFields, array(), true); | ||
222 | $updater->updateMethodMergeDeprecatedConfigFile(); | ||
223 | |||
224 | include self::$configFields['config']['CONFIG_FILE']; | ||
225 | $this->assertEquals(self::$configFields['login'], $GLOBALS['login']); | ||
226 | } | ||
227 | } | ||