diff --git a/application/LinkDB.php b/application/LinkDB.php index 8ca0fab3..5e38e848 100644 --- a/application/LinkDB.php +++ b/application/LinkDB.php @@ -463,6 +463,39 @@ public function linksCountPerTag($filteringTags = [], $visibility = 'all') return $tags; } + /** + * Rename or delete a tag across all links. + * + * @param string $from Tag to rename + * @param string $to New tag. If none is provided, the from tag will be deleted + * + * @return array|bool List of altered links or false on error + */ + public function renameTag($from, $to) + { + if (empty($from)) { + return false; + } + $delete = empty($to); + // True for case-sensitive tag search. + $linksToAlter = $this->filterSearch(['searchtags' => $from], true); + foreach($linksToAlter as $key => &$value) + { + $tags = preg_split('/\s+/', trim($value['tags'])); + if (($pos = array_search($from, $tags)) !== false) { + if ($delete) { + unset($tags[$pos]); // Remove tag. + } else { + $tags[$pos] = trim($to); + } + $value['tags'] = trim(implode(' ', array_unique($tags))); + $this[$value['id']] = $value; + } + } + + return $linksToAlter; + } + /** * Returns the list of days containing articles (oldest first) * Output: An array containing days (in format YYYYMMDD). diff --git a/index.php b/index.php index eb6b17d9..891a2ece 100644 --- a/index.php +++ b/index.php @@ -685,6 +685,7 @@ function showLinkList($PAGE, $LINKSDB, $conf, $pluginManager) { * @param ConfigManager $conf Configuration Manager instance. * @param PluginManager $pluginManager Plugin Manager instance, * @param LinkDB $LINKSDB + * @param History $history instance */ function renderPage($conf, $pluginManager, $LINKSDB, $history) { @@ -1176,39 +1177,16 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history) die('Wrong token.'); } - if (isset($_POST['deletetag']) && !empty($_POST['fromtag'])) { - $delete = true; - } else if (isset($_POST['renametag']) && !empty($_POST['fromtag']) && !empty($_POST['totag'])) { - $delete = false; - } else { - $PAGE->renderPage('changetag'); - exit; - } - - $count = 0; - $needle = trim($_POST['fromtag']); - // True for case-sensitive tag search. - $linksToAlter = $LINKSDB->filterSearch(array('searchtags' => $needle), true); - foreach($linksToAlter as $key => $value) - { - $tags = explode(' ',trim($value['tags'])); - if (($pos = array_search($needle,$tags)) !== false) { - if ($delete) { - unset($tags[$pos]); // Remove tag. - } else { - $tags[$pos] = trim($_POST['totag']); - } - $value['tags'] = trim(implode(' ', array_unique($tags))); - $LINKSDB[$key]=$value; - $history->updateLink($LINKSDB[$key]); - ++$count; - } - } + $alteredLinks = $LINKSDB->renameTag(escape($_POST['fromtag']), escape($_POST['totag'])); $LINKSDB->save($conf->get('resource.page_cache')); + foreach ($alteredLinks as $link) { + $history->updateLink($link); + } + $delete = empty($_POST['totag']); $redirect = $delete ? 'do=changetag' : 'searchtags='. urlencode(escape($_POST['totag'])); $alert = $delete - ? sprintf(t('The tag was removed from %d links.'), $count) - : sprintf(t('The tag was renamed in %d links.'), $count); + ? sprintf(t('The tag was removed from %d links.'), count($alteredLinks)) + : sprintf(t('The tag was renamed in %d links.'), count($alteredLinks)); echo ''; exit; } @@ -2237,6 +2215,12 @@ function resizeImage($filepath) $_SESSION['LINKS_PER_PAGE'] = $conf->get('general.links_per_page', 20); } +try { + $history = new History($conf->get('resource.history')); +} catch(Exception $e) { + die($e->getMessage()); +} + $linkDb = new LinkDB( $conf->get('resource.datastore'), isLoggedIn(), @@ -2245,12 +2229,6 @@ function resizeImage($filepath) $conf->get('redirector.encode_url') ); -try { - $history = new History($conf->get('resource.history')); -} catch(Exception $e) { - die($e->getMessage()); -} - $container = new \Slim\Container(); $container['conf'] = $conf; $container['plugins'] = $pluginManager; diff --git a/tests/LinkDBTest.php b/tests/LinkDBTest.php index 25438277..5b2f3667 100644 --- a/tests/LinkDBTest.php +++ b/tests/LinkDBTest.php @@ -487,4 +487,59 @@ public function testReorderLinksDesc() $this->assertEquals($linkIds[$cpt++], $key); } } + + /** + * Test rename tag with a valid value present in multiple links + */ + public function testRenameTagMultiple() + { + self::$refDB->write(self::$testDatastore); + $linkDB = new LinkDB(self::$testDatastore, true, false); + + $res = $linkDB->renameTag('cartoon', 'Taz'); + $this->assertEquals(3, count($res)); + $this->assertContains(' Taz ', $linkDB[4]['tags']); + $this->assertContains(' Taz ', $linkDB[1]['tags']); + $this->assertContains(' Taz ', $linkDB[0]['tags']); + } + + /** + * Test rename tag with a valid value + */ + public function testRenameTagCaseSensitive() + { + self::$refDB->write(self::$testDatastore); + $linkDB = new LinkDB(self::$testDatastore, true, false, ''); + + $res = $linkDB->renameTag('sTuff', 'Taz'); + $this->assertEquals(1, count($res)); + $this->assertEquals('Taz', $linkDB[41]['tags']); + } + + /** + * Test rename tag with invalid values + */ + public function testRenameTagInvalid() + { + $linkDB = new LinkDB(self::$testDatastore, false, false); + + $this->assertFalse($linkDB->renameTag('', 'test')); + $this->assertFalse($linkDB->renameTag('', '')); + // tag non existent + $this->assertEquals([], $linkDB->renameTag('test', '')); + $this->assertEquals([], $linkDB->renameTag('test', 'retest')); + } + + /** + * Test delete tag with a valid value + */ + public function testDeleteTag() + { + self::$refDB->write(self::$testDatastore); + $linkDB = new LinkDB(self::$testDatastore, true, false); + + $res = $linkDB->renameTag('cartoon', null); + $this->assertEquals(3, count($res)); + $this->assertNotContains('cartoon', $linkDB[4]['tags']); + } }