Bulk action: add or delete tag to multiple bookmarks (#1898)
This commit is contained in:
parent
cd618bd8be
commit
00cce1f8c7
6 changed files with 543 additions and 0 deletions
|
@ -517,6 +517,16 @@ public function renameTag(string $fromTag, string $toTag): void
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a tag in tags list.
|
||||||
|
*
|
||||||
|
* @param string $tag
|
||||||
|
*/
|
||||||
|
public function addTag(string $tag): self
|
||||||
|
{
|
||||||
|
return $this->setTags(array_merge($this->getTags(), [$tag]));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a tag from tags list.
|
* Delete a tag from tags list.
|
||||||
*
|
*
|
||||||
|
|
|
@ -203,4 +203,85 @@ public function sharePrivate(Request $request, Response $response, array $args):
|
||||||
'/shaare/' . $hash . '?key=' . $bookmark->getAdditionalContentEntry('private_key')
|
'/shaare/' . $hash . '?key=' . $bookmark->getAdditionalContentEntry('private_key')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST /admin/shaare/update-tags
|
||||||
|
*
|
||||||
|
* Bulk add or delete a tags on one or multiple bookmarks.
|
||||||
|
*/
|
||||||
|
public function addOrDeleteTags(Request $request, Response $response): Response
|
||||||
|
{
|
||||||
|
$this->checkToken($request);
|
||||||
|
|
||||||
|
$ids = trim(escape($request->getParam('id') ?? ''));
|
||||||
|
if (empty($ids) || strpos($ids, ' ') !== false) {
|
||||||
|
// multiple, space-separated ids provided
|
||||||
|
$ids = array_values(array_filter(preg_split('/\s+/', $ids), 'ctype_digit'));
|
||||||
|
} else {
|
||||||
|
// only a single id provided
|
||||||
|
$ids = [$ids];
|
||||||
|
}
|
||||||
|
|
||||||
|
// assert at least one id is given
|
||||||
|
if (0 === count($ids)) {
|
||||||
|
$this->saveErrorMessage(t('Invalid bookmark ID provided.'));
|
||||||
|
|
||||||
|
return $this->redirectFromReferer($request, $response, ['/updateTag'], []);
|
||||||
|
}
|
||||||
|
|
||||||
|
// assert that the action is valid
|
||||||
|
$action = $request->getParam('action');
|
||||||
|
if (!in_array($action, ['add', 'delete'], true)) {
|
||||||
|
$this->saveErrorMessage(t('Invalid action provided.'));
|
||||||
|
|
||||||
|
return $this->redirectFromReferer($request, $response, ['/updateTag'], []);
|
||||||
|
}
|
||||||
|
|
||||||
|
// assert that the tag name is valid
|
||||||
|
$tagString = trim($request->getParam('tag'));
|
||||||
|
if (empty($tagString)) {
|
||||||
|
$this->saveErrorMessage(t('Invalid tag name provided.'));
|
||||||
|
|
||||||
|
return $this->redirectFromReferer($request, $response, ['/updateTag'], []);
|
||||||
|
}
|
||||||
|
|
||||||
|
$tags = tags_str2array($tagString, $this->container->conf->get('general.tags_separator', ' '));
|
||||||
|
$formatter = $this->container->formatterFactory->getFormatter('raw');
|
||||||
|
$count = 0;
|
||||||
|
|
||||||
|
foreach ($ids as $id) {
|
||||||
|
try {
|
||||||
|
$bookmark = $this->container->bookmarkService->get((int) $id);
|
||||||
|
} catch (BookmarkNotFoundException $e) {
|
||||||
|
$this->saveErrorMessage(sprintf(
|
||||||
|
t('Bookmark with identifier %s could not be found.'),
|
||||||
|
$id
|
||||||
|
));
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($tags as $tag) {
|
||||||
|
if ($action === 'add') {
|
||||||
|
$bookmark->addTag($tag);
|
||||||
|
} else {
|
||||||
|
$bookmark->deleteTag($tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// To preserve backward compatibility with 3rd parties, plugins still use arrays
|
||||||
|
$data = $formatter->format($bookmark);
|
||||||
|
$this->executePageHooks('save_link', $data);
|
||||||
|
$bookmark->fromArray($data, $this->container->conf->get('general.tags_separator', ' '));
|
||||||
|
|
||||||
|
$this->container->bookmarkService->set($bookmark, false);
|
||||||
|
++$count;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($count > 0) {
|
||||||
|
$this->container->bookmarkService->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->redirectFromReferer($request, $response, ['/updateTag'], []);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -383,6 +383,10 @@ function init(description) {
|
||||||
});
|
});
|
||||||
|
|
||||||
sub.classList.toggle('open');
|
sub.classList.toggle('open');
|
||||||
|
const autofocus = sub.querySelector('.autofocus');
|
||||||
|
if (autofocus) {
|
||||||
|
autofocus.focus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -507,6 +511,37 @@ function init(description) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
['add', 'delete'].forEach((action) => {
|
||||||
|
const subHeader = document.getElementById(`bulk-tag-action-${action}`);
|
||||||
|
|
||||||
|
if (subHeader) {
|
||||||
|
subHeader.querySelectorAll('a.button').forEach((link) => {
|
||||||
|
if (!link.classList.contains('action')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
subHeader.querySelector('input[name="tag"]').addEventListener('keypress', (event) => {
|
||||||
|
if (event.keyCode === 13) { // enter
|
||||||
|
link.click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
link.addEventListener('click', (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const ids = [];
|
||||||
|
const linkCheckedCheckboxes = document.querySelectorAll('.link-checkbox:checked');
|
||||||
|
[...linkCheckedCheckboxes].forEach((checkbox) => {
|
||||||
|
ids.push(checkbox.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
subHeader.querySelector('input[name="id"]').value = ids.join(' ');
|
||||||
|
subHeader.querySelector('form').submit();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select all button
|
* Select all button
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -151,6 +151,7 @@
|
||||||
$this->post('/shaare', '\Shaarli\Front\Controller\Admin\ShaarePublishController:save');
|
$this->post('/shaare', '\Shaarli\Front\Controller\Admin\ShaarePublishController:save');
|
||||||
$this->get('/shaare/delete', '\Shaarli\Front\Controller\Admin\ShaareManageController:deleteBookmark');
|
$this->get('/shaare/delete', '\Shaarli\Front\Controller\Admin\ShaareManageController:deleteBookmark');
|
||||||
$this->get('/shaare/visibility', '\Shaarli\Front\Controller\Admin\ShaareManageController:changeVisibility');
|
$this->get('/shaare/visibility', '\Shaarli\Front\Controller\Admin\ShaareManageController:changeVisibility');
|
||||||
|
$this->post('/shaare/update-tags', '\Shaarli\Front\Controller\Admin\ShaareManageController:addOrDeleteSingleTag');
|
||||||
$this->get('/shaare/{id:[0-9]+}/pin', '\Shaarli\Front\Controller\Admin\ShaareManageController:pinBookmark');
|
$this->get('/shaare/{id:[0-9]+}/pin', '\Shaarli\Front\Controller\Admin\ShaareManageController:pinBookmark');
|
||||||
$this->patch(
|
$this->patch(
|
||||||
'/shaare/{id:[0-9]+}/update-thumbnail',
|
'/shaare/{id:[0-9]+}/update-thumbnail',
|
||||||
|
|
|
@ -0,0 +1,380 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Shaarli\Front\Controller\Admin\ShaareManageControllerTest;
|
||||||
|
|
||||||
|
use Shaarli\Bookmark\Bookmark;
|
||||||
|
use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
|
||||||
|
use Shaarli\Formatter\BookmarkFormatter;
|
||||||
|
use Shaarli\Formatter\BookmarkRawFormatter;
|
||||||
|
use Shaarli\Formatter\FormatterFactory;
|
||||||
|
use Shaarli\Front\Controller\Admin\FrontAdminControllerMockHelper;
|
||||||
|
use Shaarli\Front\Controller\Admin\ShaareManageController;
|
||||||
|
use Shaarli\Http\HttpAccess;
|
||||||
|
use Shaarli\Security\SessionManager;
|
||||||
|
use Shaarli\TestCase;
|
||||||
|
use Slim\Http\Request;
|
||||||
|
use Slim\Http\Response;
|
||||||
|
|
||||||
|
class AddOrDeleteTagTest extends TestCase
|
||||||
|
{
|
||||||
|
use FrontAdminControllerMockHelper;
|
||||||
|
|
||||||
|
/** @var ShaareManageController */
|
||||||
|
protected $controller;
|
||||||
|
|
||||||
|
public function setUp(): void
|
||||||
|
{
|
||||||
|
$this->createContainer();
|
||||||
|
|
||||||
|
$this->container->httpAccess = $this->createMock(HttpAccess::class);
|
||||||
|
$this->controller = new ShaareManageController($this->container);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add 1 tag to 1 bookmark
|
||||||
|
*/
|
||||||
|
public function testAddOneTagOnOneBookmark(): void
|
||||||
|
{
|
||||||
|
$parameters = ['id' => '123', 'tag' => 'newtag', 'action' => 'add'];
|
||||||
|
|
||||||
|
$request = $this->createMock(Request::class);
|
||||||
|
$request
|
||||||
|
->method('getParam')
|
||||||
|
->willReturnCallback(function (string $key) use ($parameters): ?string {
|
||||||
|
return $parameters[$key] ?? null;
|
||||||
|
})
|
||||||
|
;
|
||||||
|
$response = new Response();
|
||||||
|
$bookmark = (new Bookmark())
|
||||||
|
->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123')
|
||||||
|
->setTagsString('first second');
|
||||||
|
|
||||||
|
static::assertSame(['first', 'second'], $bookmark->getTags());
|
||||||
|
|
||||||
|
$this->container->bookmarkService->expects(static::once())->method('get')->with(123)->willReturn($bookmark);
|
||||||
|
$this->container->bookmarkService->expects(static::once())->method('set')->with($bookmark, false);
|
||||||
|
$this->container->bookmarkService->expects(static::once())->method('save');
|
||||||
|
$this->container->formatterFactory = $this->createMock(FormatterFactory::class);
|
||||||
|
$this->container->formatterFactory
|
||||||
|
->expects(static::once())
|
||||||
|
->method('getFormatter')
|
||||||
|
->with('raw')
|
||||||
|
->willReturnCallback(function () use ($bookmark): BookmarkFormatter {
|
||||||
|
return new BookmarkRawFormatter($this->container->conf, true);
|
||||||
|
})
|
||||||
|
;
|
||||||
|
|
||||||
|
// Make sure that PluginManager hook is triggered
|
||||||
|
$this->container->pluginManager
|
||||||
|
->expects(static::once())
|
||||||
|
->method('executeHooks')
|
||||||
|
->with('save_link')
|
||||||
|
;
|
||||||
|
|
||||||
|
$result = $this->controller->addOrDeleteTags($request, $response);
|
||||||
|
|
||||||
|
static::assertSame(['first', 'second', 'newtag'], $bookmark->getTags());
|
||||||
|
|
||||||
|
static::assertSame(302, $result->getStatusCode());
|
||||||
|
static::assertSame(['/subfolder/'], $result->getHeader('location'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add 2 tags to 2 bookmarks
|
||||||
|
*/
|
||||||
|
public function testAddTwoTagsOnTwoBookmarks(): void
|
||||||
|
{
|
||||||
|
$parameters = ['id' => '123 456', 'tag' => 'newtag@othertag', 'action' => 'add'];
|
||||||
|
|
||||||
|
$request = $this->createMock(Request::class);
|
||||||
|
$request
|
||||||
|
->method('getParam')
|
||||||
|
->willReturnCallback(function (string $key) use ($parameters): ?string {
|
||||||
|
return $parameters[$key] ?? null;
|
||||||
|
})
|
||||||
|
;
|
||||||
|
$response = new Response();
|
||||||
|
$bookmark1 = (new Bookmark())
|
||||||
|
->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123')
|
||||||
|
->setTagsString('first second');
|
||||||
|
$bookmark2 = (new Bookmark())
|
||||||
|
->setId(456)->setUrl('http://domain.tld')->setTitle('Title 123');
|
||||||
|
|
||||||
|
static::assertSame(['first', 'second'], $bookmark1->getTags());
|
||||||
|
static::assertSame([], $bookmark2->getTags());
|
||||||
|
|
||||||
|
$this->container->bookmarkService->expects(static::exactly(2))->method('get')
|
||||||
|
->withConsecutive([123], [456])
|
||||||
|
->willReturnOnConsecutiveCalls($bookmark1, $bookmark2);
|
||||||
|
$this->container->bookmarkService->expects(static::exactly(2))->method('set')
|
||||||
|
->withConsecutive([$bookmark1, false], [$bookmark2, false]);
|
||||||
|
$this->container->bookmarkService->expects(static::once())->method('save');
|
||||||
|
$this->container->formatterFactory = $this->createMock(FormatterFactory::class);
|
||||||
|
$this->container->formatterFactory
|
||||||
|
->expects(static::once())
|
||||||
|
->method('getFormatter')
|
||||||
|
->with('raw')
|
||||||
|
->willReturnCallback(function (): BookmarkFormatter {
|
||||||
|
return new BookmarkRawFormatter($this->container->conf, true);
|
||||||
|
})
|
||||||
|
;
|
||||||
|
|
||||||
|
// Make sure that PluginManager hook is triggered
|
||||||
|
$this->container->pluginManager
|
||||||
|
->expects(static::exactly(2))
|
||||||
|
->method('executeHooks')
|
||||||
|
->with('save_link')
|
||||||
|
;
|
||||||
|
|
||||||
|
$result = $this->controller->addOrDeleteTags($request, $response);
|
||||||
|
|
||||||
|
static::assertSame(['first', 'second', 'newtag', 'othertag'], $bookmark1->getTags());
|
||||||
|
static::assertSame(['newtag', 'othertag'], $bookmark2->getTags());
|
||||||
|
|
||||||
|
static::assertSame(302, $result->getStatusCode());
|
||||||
|
static::assertSame(['/subfolder/'], $result->getHeader('location'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete 1 tag to 1 bookmark
|
||||||
|
*/
|
||||||
|
public function testDeleteOneTagOnOneBookmark(): void
|
||||||
|
{
|
||||||
|
$parameters = ['id' => '123', 'tag' => 'second', 'action' => 'delete'];
|
||||||
|
|
||||||
|
$request = $this->createMock(Request::class);
|
||||||
|
$request
|
||||||
|
->method('getParam')
|
||||||
|
->willReturnCallback(function (string $key) use ($parameters): ?string {
|
||||||
|
return $parameters[$key] ?? null;
|
||||||
|
})
|
||||||
|
;
|
||||||
|
$response = new Response();
|
||||||
|
$bookmark = (new Bookmark())
|
||||||
|
->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123')
|
||||||
|
->setTagsString('first second third');
|
||||||
|
|
||||||
|
static::assertSame(['first', 'second', 'third'], $bookmark->getTags());
|
||||||
|
|
||||||
|
$this->container->bookmarkService->expects(static::once())->method('get')->with(123)->willReturn($bookmark);
|
||||||
|
$this->container->bookmarkService->expects(static::once())->method('set')->with($bookmark, false);
|
||||||
|
$this->container->bookmarkService->expects(static::once())->method('save');
|
||||||
|
$this->container->formatterFactory = $this->createMock(FormatterFactory::class);
|
||||||
|
$this->container->formatterFactory
|
||||||
|
->expects(static::once())
|
||||||
|
->method('getFormatter')
|
||||||
|
->with('raw')
|
||||||
|
->willReturnCallback(function () use ($bookmark): BookmarkFormatter {
|
||||||
|
return new BookmarkRawFormatter($this->container->conf, true);
|
||||||
|
})
|
||||||
|
;
|
||||||
|
|
||||||
|
// Make sure that PluginManager hook is triggered
|
||||||
|
$this->container->pluginManager
|
||||||
|
->expects(static::once())
|
||||||
|
->method('executeHooks')
|
||||||
|
->with('save_link')
|
||||||
|
;
|
||||||
|
|
||||||
|
$result = $this->controller->addOrDeleteTags($request, $response);
|
||||||
|
|
||||||
|
static::assertSame(['first', 'third'], $bookmark->getTags());
|
||||||
|
|
||||||
|
static::assertSame(302, $result->getStatusCode());
|
||||||
|
static::assertSame(['/subfolder/'], $result->getHeader('location'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete 2 tags to 2 bookmarks
|
||||||
|
*/
|
||||||
|
public function testDeleteTwoTagOnTwoBookmarks(): void
|
||||||
|
{
|
||||||
|
$parameters = ['id' => '123 456', 'tag' => 'second@first', 'action' => 'delete'];
|
||||||
|
|
||||||
|
$request = $this->createMock(Request::class);
|
||||||
|
$request
|
||||||
|
->method('getParam')
|
||||||
|
->willReturnCallback(function (string $key) use ($parameters): ?string {
|
||||||
|
return $parameters[$key] ?? null;
|
||||||
|
})
|
||||||
|
;
|
||||||
|
$response = new Response();
|
||||||
|
$bookmark1 = (new Bookmark())
|
||||||
|
->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123')
|
||||||
|
->setTagsString('first second third other');
|
||||||
|
$bookmark2 = (new Bookmark())
|
||||||
|
->setId(456)->setUrl('http://domain.tld')->setTitle('Title 123')
|
||||||
|
->setTagsString('first second');
|
||||||
|
|
||||||
|
static::assertSame(['first', 'second', 'third', 'other'], $bookmark1->getTags());
|
||||||
|
static::assertSame(['first', 'second'], $bookmark2->getTags());
|
||||||
|
|
||||||
|
$this->container->bookmarkService->expects(static::exactly(2))->method('get')
|
||||||
|
->withConsecutive([123], [456])
|
||||||
|
->willReturnOnConsecutiveCalls($bookmark1, $bookmark2);
|
||||||
|
$this->container->bookmarkService->expects(static::exactly(2))->method('set')
|
||||||
|
->withConsecutive([$bookmark1, false], [$bookmark2, false]);
|
||||||
|
$this->container->bookmarkService->expects(static::once())->method('save');
|
||||||
|
$this->container->formatterFactory = $this->createMock(FormatterFactory::class);
|
||||||
|
$this->container->formatterFactory
|
||||||
|
->expects(static::once())
|
||||||
|
->method('getFormatter')
|
||||||
|
->with('raw')
|
||||||
|
->willReturnCallback(function (): BookmarkFormatter {
|
||||||
|
return new BookmarkRawFormatter($this->container->conf, true);
|
||||||
|
})
|
||||||
|
;
|
||||||
|
|
||||||
|
// Make sure that PluginManager hook is triggered
|
||||||
|
$this->container->pluginManager
|
||||||
|
->expects(static::exactly(2))
|
||||||
|
->method('executeHooks')
|
||||||
|
->with('save_link')
|
||||||
|
;
|
||||||
|
|
||||||
|
$result = $this->controller->addOrDeleteTags($request, $response);
|
||||||
|
|
||||||
|
static::assertSame(['third', 'other'], $bookmark1->getTags());
|
||||||
|
static::assertSame([], $bookmark2->getTags());
|
||||||
|
|
||||||
|
static::assertSame(302, $result->getStatusCode());
|
||||||
|
static::assertSame(['/subfolder/'], $result->getHeader('location'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test add a tag without passing an ID.
|
||||||
|
*/
|
||||||
|
public function testAddTagWithoutId(): void
|
||||||
|
{
|
||||||
|
$parameters = ['tag' => 'newtag', 'action' => 'add'];
|
||||||
|
$request = $this->createMock(Request::class);
|
||||||
|
$request
|
||||||
|
->method('getParam')
|
||||||
|
->willReturnCallback(function (string $key) use ($parameters): ?string {
|
||||||
|
return $parameters[$key] ?? null;
|
||||||
|
})
|
||||||
|
;
|
||||||
|
$response = new Response();
|
||||||
|
|
||||||
|
$this->container->sessionManager
|
||||||
|
->expects(static::once())
|
||||||
|
->method('setSessionParameter')
|
||||||
|
->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid bookmark ID provided.'])
|
||||||
|
;
|
||||||
|
|
||||||
|
$result = $this->controller->addOrDeleteTags($request, $response);
|
||||||
|
|
||||||
|
static::assertSame(302, $result->getStatusCode());
|
||||||
|
static::assertSame(['/subfolder/'], $result->getHeader('location'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test add a tag without passing an ID.
|
||||||
|
*/
|
||||||
|
public function testDeleteTagWithoutId(): void
|
||||||
|
{
|
||||||
|
$parameters = ['tag' => 'newtag', 'action' => 'delete'];
|
||||||
|
$request = $this->createMock(Request::class);
|
||||||
|
$request
|
||||||
|
->method('getParam')
|
||||||
|
->willReturnCallback(function (string $key) use ($parameters): ?string {
|
||||||
|
return $parameters[$key] ?? null;
|
||||||
|
})
|
||||||
|
;
|
||||||
|
$response = new Response();
|
||||||
|
|
||||||
|
$this->container->sessionManager
|
||||||
|
->expects(static::once())
|
||||||
|
->method('setSessionParameter')
|
||||||
|
->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid bookmark ID provided.'])
|
||||||
|
;
|
||||||
|
|
||||||
|
$result = $this->controller->addOrDeleteTags($request, $response);
|
||||||
|
|
||||||
|
static::assertSame(302, $result->getStatusCode());
|
||||||
|
static::assertSame(['/subfolder/'], $result->getHeader('location'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test add a tag without passing an action.
|
||||||
|
*/
|
||||||
|
public function testAddTagWithoutAction(): void
|
||||||
|
{
|
||||||
|
$parameters = ['id' => '123', 'tag' => 'newtag'];
|
||||||
|
$request = $this->createMock(Request::class);
|
||||||
|
$request
|
||||||
|
->method('getParam')
|
||||||
|
->willReturnCallback(function (string $key) use ($parameters): ?string {
|
||||||
|
return $parameters[$key] ?? null;
|
||||||
|
})
|
||||||
|
;
|
||||||
|
$response = new Response();
|
||||||
|
|
||||||
|
$this->container->sessionManager
|
||||||
|
->expects(static::once())
|
||||||
|
->method('setSessionParameter')
|
||||||
|
->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid action provided.'])
|
||||||
|
;
|
||||||
|
|
||||||
|
$result = $this->controller->addOrDeleteTags($request, $response);
|
||||||
|
|
||||||
|
static::assertSame(302, $result->getStatusCode());
|
||||||
|
static::assertSame(['/subfolder/'], $result->getHeader('location'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test add a tag without passing a tag string value.
|
||||||
|
*/
|
||||||
|
public function testAddTagWithoutValue(): void
|
||||||
|
{
|
||||||
|
$parameters = ['id' => '123', 'tag' => '', 'action' => 'add'];
|
||||||
|
$request = $this->createMock(Request::class);
|
||||||
|
$request
|
||||||
|
->method('getParam')
|
||||||
|
->willReturnCallback(function (string $key) use ($parameters): ?string {
|
||||||
|
return $parameters[$key] ?? null;
|
||||||
|
})
|
||||||
|
;
|
||||||
|
$response = new Response();
|
||||||
|
|
||||||
|
$this->container->sessionManager
|
||||||
|
->expects(static::once())
|
||||||
|
->method('setSessionParameter')
|
||||||
|
->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid tag name provided.'])
|
||||||
|
;
|
||||||
|
|
||||||
|
$result = $this->controller->addOrDeleteTags($request, $response);
|
||||||
|
|
||||||
|
static::assertSame(302, $result->getStatusCode());
|
||||||
|
static::assertSame(['/subfolder/'], $result->getHeader('location'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test delete a tag without passing a tag string value.
|
||||||
|
*/
|
||||||
|
public function testDeleteTagWithoutValue(): void
|
||||||
|
{
|
||||||
|
$parameters = ['id' => '123', 'tag' => '', 'action' => 'delete'];
|
||||||
|
$request = $this->createMock(Request::class);
|
||||||
|
$request
|
||||||
|
->method('getParam')
|
||||||
|
->willReturnCallback(function (string $key) use ($parameters): ?string {
|
||||||
|
return $parameters[$key] ?? null;
|
||||||
|
})
|
||||||
|
;
|
||||||
|
$response = new Response();
|
||||||
|
|
||||||
|
$this->container->sessionManager
|
||||||
|
->expects(static::once())
|
||||||
|
->method('setSessionParameter')
|
||||||
|
->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid tag name provided.'])
|
||||||
|
;
|
||||||
|
|
||||||
|
$result = $this->controller->addOrDeleteTags($request, $response);
|
||||||
|
|
||||||
|
static::assertSame(302, $result->getStatusCode());
|
||||||
|
static::assertSame(['/subfolder/'], $result->getHeader('location'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -131,10 +131,46 @@
|
||||||
<a href="" class="actions-change-visibility button" data-visibility="private">
|
<a href="" class="actions-change-visibility button" data-visibility="private">
|
||||||
<i class="fa fa-user-secret" aria-hidden="true"></i>
|
<i class="fa fa-user-secret" aria-hidden="true"></i>
|
||||||
{'Set private'|t}
|
{'Set private'|t}
|
||||||
|
</a>
|
||||||
|
<a href="" class="subheader-opener button" data-open-id="bulk-tag-action-add">
|
||||||
|
<i class="fa fa-tag" aria-hidden="true"></i>
|
||||||
|
{'Add tag'|t}
|
||||||
|
</a>
|
||||||
|
<a href="" class="subheader-opener button" data-open-id="bulk-tag-action-delete">
|
||||||
|
<i class="fa fa-window-close" aria-hidden="true"></i>
|
||||||
|
{'Delete tag'|t}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{$addDelete=['add', 'delete']}
|
||||||
|
{loop="$addDelete"}
|
||||||
|
<div id="bulk-tag-action-{$value}" class="subheader-form">
|
||||||
|
<form class="pure-g" action="{$base_path}/admin/shaare/update-tags" method="post">
|
||||||
|
<div class="pure-u-1">
|
||||||
|
<span>
|
||||||
|
<input
|
||||||
|
type="text" name="tag" class="autofocus"
|
||||||
|
aria-label="{$value === 'add' ? t('Tag to add') : t('Tag to delete')}"
|
||||||
|
placeholder="{$value === 'add' ? t('Tag to add') : t('Tag to delete')}"
|
||||||
|
autocomplete="off" data-multiple data-autofirst data-minChars="1"
|
||||||
|
data-list="{loop="$tags"}{$key}, {/loop}"
|
||||||
|
>
|
||||||
|
<input type="hidden" name="action" value="{$value}" />
|
||||||
|
<input type="hidden" name="id" value="" />
|
||||||
|
<input type="hidden" name="token" value="{$token}" />
|
||||||
|
</span>
|
||||||
|
<a href="" class="button action">
|
||||||
|
<i class="fa fa-tag" aria-hidden="true"></i>
|
||||||
|
{$value === 'add' ? t('Add tag') : t('Delete tag')}
|
||||||
|
</a>
|
||||||
|
<a href="" class="subheader-opener button cancel" data-open-id="actions">{'Cancel'|t}</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{/loop}
|
||||||
|
|
||||||
{if="!$is_logged_in"}
|
{if="!$is_logged_in"}
|
||||||
<form method="post" name="loginform">
|
<form method="post" name="loginform">
|
||||||
<div class="subheader-form header-login-form" id="header-login-form">
|
<div class="subheader-form header-login-form" id="header-login-form">
|
||||||
|
|
Loading…
Reference in a new issue