Use multi-level routes for existing controllers instead of 1 level everywhere

Also prefix most admin routes with /admin/
This commit is contained in:
ArthurHoaro 2020-06-13 13:08:01 +02:00
parent 818b3193ff
commit 9c75f87793
42 changed files with 191 additions and 227 deletions

View file

@ -38,19 +38,17 @@ class ContainerBuilder
/** @var LoginManager */ /** @var LoginManager */
protected $login; protected $login;
/** @var string */ /** @var string|null */
protected $webPath; protected $basePath = null;
public function __construct( public function __construct(
ConfigManager $conf, ConfigManager $conf,
SessionManager $session, SessionManager $session,
LoginManager $login, LoginManager $login
string $webPath
) { ) {
$this->conf = $conf; $this->conf = $conf;
$this->session = $session; $this->session = $session;
$this->login = $login; $this->login = $login;
$this->webPath = $webPath;
} }
public function build(): ShaarliContainer public function build(): ShaarliContainer
@ -60,7 +58,7 @@ public function build(): ShaarliContainer
$container['conf'] = $this->conf; $container['conf'] = $this->conf;
$container['sessionManager'] = $this->session; $container['sessionManager'] = $this->session;
$container['loginManager'] = $this->login; $container['loginManager'] = $this->login;
$container['webPath'] = $this->webPath; $container['basePath'] = $this->basePath;
$container['plugins'] = function (ShaarliContainer $container): PluginManager { $container['plugins'] = function (ShaarliContainer $container): PluginManager {
return new PluginManager($container->conf); return new PluginManager($container->conf);

View file

@ -21,21 +21,20 @@
/** /**
* Extension of Slim container to document the injected objects. * Extension of Slim container to document the injected objects.
* *
* @property mixed[] $environment $_SERVER automatically injected by Slim
* @property string $basePath Shaarli's instance base path (e.g. `/shaarli/`) * @property string $basePath Shaarli's instance base path (e.g. `/shaarli/`)
* @property ConfigManager $conf
* @property SessionManager $sessionManager
* @property LoginManager $loginManager
* @property string $webPath
* @property History $history
* @property BookmarkServiceInterface $bookmarkService * @property BookmarkServiceInterface $bookmarkService
* @property PageBuilder $pageBuilder * @property ConfigManager $conf
* @property PluginManager $pluginManager * @property mixed[] $environment $_SERVER automatically injected by Slim
* @property FormatterFactory $formatterFactory
* @property PageCacheManager $pageCacheManager
* @property FeedBuilder $feedBuilder * @property FeedBuilder $feedBuilder
* @property Thumbnailer $thumbnailer * @property FormatterFactory $formatterFactory
* @property History $history
* @property HttpAccess $httpAccess * @property HttpAccess $httpAccess
* @property LoginManager $loginManager
* @property PageBuilder $pageBuilder
* @property PageCacheManager $pageCacheManager
* @property PluginManager $pluginManager
* @property SessionManager $sessionManager
* @property Thumbnailer $thumbnailer
*/ */
class ShaarliContainer extends Container class ShaarliContainer extends Container
{ {

View file

@ -38,9 +38,9 @@ public function __construct(ShaarliContainer $container)
*/ */
public function __invoke(Request $request, Response $response, callable $next) public function __invoke(Request $request, Response $response, callable $next)
{ {
try { $this->container->basePath = rtrim($request->getUri()->getBasePath(), '/');
$this->container->basePath = rtrim($request->getUri()->getBasePath(), '/');
try {
$response = $next($request, $response); $response = $next($request, $response);
} catch (ShaarliFrontException $e) { } catch (ShaarliFrontException $e) {
$this->container->pageBuilder->assign('message', $e->getMessage()); $this->container->pageBuilder->assign('message', $e->getMessage());
@ -54,7 +54,7 @@ public function __invoke(Request $request, Response $response, callable $next)
$response = $response->withStatus($e->getCode()); $response = $response->withStatus($e->getCode());
$response = $response->write($this->container->pageBuilder->render('error')); $response = $response->write($this->container->pageBuilder->render('error'));
} catch (UnauthorizedException $e) { } catch (UnauthorizedException $e) {
return $response->withRedirect($request->getUri()->getBasePath() . '/login'); return $response->withRedirect($this->container->basePath . '/login');
} }
return $response; return $response;

View file

@ -19,7 +19,7 @@
class ConfigureController extends ShaarliAdminController class ConfigureController extends ShaarliAdminController
{ {
/** /**
* GET /configure - Displays the configuration page * GET /admin/configure - Displays the configuration page
*/ */
public function index(Request $request, Response $response): Response public function index(Request $request, Response $response): Response
{ {
@ -56,7 +56,7 @@ public function index(Request $request, Response $response): Response
} }
/** /**
* POST /configure - Update Shaarli's configuration * POST /admin/configure - Update Shaarli's configuration
*/ */
public function save(Request $request, Response $response): Response public function save(Request $request, Response $response): Response
{ {
@ -115,6 +115,6 @@ public function save(Request $request, Response $response): Response
$this->saveSuccessMessage(t('Configuration was saved.')); $this->saveSuccessMessage(t('Configuration was saved.'));
return $response->withRedirect('./configure'); return $this->redirect($response, '/admin/configure');
} }
} }

View file

@ -22,8 +22,8 @@ public function index(Request $request, Response $response): Response
$this->container->sessionManager->logout(); $this->container->sessionManager->logout();
// TODO: switch to a simple Cookie manager allowing to check the session, and create mocks. // TODO: switch to a simple Cookie manager allowing to check the session, and create mocks.
setcookie(LoginManager::$STAY_SIGNED_IN_COOKIE, 'false', 0, $this->container->webPath); setcookie(LoginManager::$STAY_SIGNED_IN_COOKIE, 'false', 0, $this->container->basePath . '/');
return $response->withRedirect('./'); return $this->redirect($response, '/');
} }
} }

View file

@ -16,7 +16,7 @@
class ManageTagController extends ShaarliAdminController class ManageTagController extends ShaarliAdminController
{ {
/** /**
* GET /manage-tags - Displays the manage tags page * GET /admin/tags - Displays the manage tags page
*/ */
public function index(Request $request, Response $response): Response public function index(Request $request, Response $response): Response
{ {
@ -32,7 +32,7 @@ public function index(Request $request, Response $response): Response
} }
/** /**
* POST /manage-tags - Update or delete provided tag * POST /admin/tags - Update or delete provided tag
*/ */
public function save(Request $request, Response $response): Response public function save(Request $request, Response $response): Response
{ {
@ -46,7 +46,7 @@ public function save(Request $request, Response $response): Response
if (0 === strlen($fromTag) || false === $isDelete && 0 === strlen($toTag)) { if (0 === strlen($fromTag) || false === $isDelete && 0 === strlen($toTag)) {
$this->saveWarningMessage(t('Invalid tags provided.')); $this->saveWarningMessage(t('Invalid tags provided.'));
return $response->withRedirect('./manage-tags'); return $this->redirect($response, '/admin/tags');
} }
// TODO: move this to bookmark service // TODO: move this to bookmark service
@ -80,8 +80,8 @@ public function save(Request $request, Response $response): Response
$this->saveSuccessMessage($alert); $this->saveSuccessMessage($alert);
$redirect = true === $isDelete ? './manage-tags' : './?searchtags='. urlencode($toTag); $redirect = true === $isDelete ? '/admin/tags' : '/?searchtags='. urlencode($toTag);
return $response->withRedirect($redirect); return $this->redirect($response, $redirect);
} }
} }

View file

@ -29,7 +29,7 @@ public function __construct(ShaarliContainer $container)
} }
/** /**
* GET /password - Displays the change password template * GET /admin/password - Displays the change password template
*/ */
public function index(Request $request, Response $response): Response public function index(Request $request, Response $response): Response
{ {
@ -37,7 +37,7 @@ public function index(Request $request, Response $response): Response
} }
/** /**
* POST /password - Change admin password - existing and new passwords need to be provided. * POST /admin/password - Change admin password - existing and new passwords need to be provided.
*/ */
public function change(Request $request, Response $response): Response public function change(Request $request, Response $response): Response
{ {

View file

@ -19,7 +19,7 @@
class PostBookmarkController extends ShaarliAdminController class PostBookmarkController extends ShaarliAdminController
{ {
/** /**
* GET /add-shaare - Displays the form used to create a new bookmark from an URL * GET /admin/add-shaare - Displays the form used to create a new bookmark from an URL
*/ */
public function addShaare(Request $request, Response $response): Response public function addShaare(Request $request, Response $response): Response
{ {
@ -32,7 +32,7 @@ public function addShaare(Request $request, Response $response): Response
} }
/** /**
* GET /shaare - Displays the bookmark form for creation. * GET /admin/shaare - Displays the bookmark form for creation.
* Note that if the URL is found in existing bookmarks, then it will be in edit mode. * Note that if the URL is found in existing bookmarks, then it will be in edit mode.
*/ */
public function displayCreateForm(Request $request, Response $response): Response public function displayCreateForm(Request $request, Response $response): Response
@ -93,7 +93,7 @@ public function displayCreateForm(Request $request, Response $response): Respons
} }
/** /**
* GET /shaare-{id} - Displays the bookmark form in edition mode. * GET /admin/shaare/{id} - Displays the bookmark form in edition mode.
*/ */
public function displayEditForm(Request $request, Response $response, array $args): Response public function displayEditForm(Request $request, Response $response, array $args): Response
{ {
@ -106,7 +106,7 @@ public function displayEditForm(Request $request, Response $response, array $arg
} catch (BookmarkNotFoundException $e) { } catch (BookmarkNotFoundException $e) {
$this->saveErrorMessage(t('Bookmark not found')); $this->saveErrorMessage(t('Bookmark not found'));
return $response->withRedirect('./'); return $this->redirect($response, '/');
} }
$formatter = $this->container->formatterFactory->getFormatter('raw'); $formatter = $this->container->formatterFactory->getFormatter('raw');
@ -116,7 +116,7 @@ public function displayEditForm(Request $request, Response $response, array $arg
} }
/** /**
* POST /shaare * POST /admin/shaare
*/ */
public function save(Request $request, Response $response): Response public function save(Request $request, Response $response): Response
{ {
@ -170,11 +170,14 @@ public function save(Request $request, Response $response): Response
); );
} }
/**
* GET /admin/shaare/delete
*/
public function deleteBookmark(Request $request, Response $response): Response public function deleteBookmark(Request $request, Response $response): Response
{ {
$this->checkToken($request); $this->checkToken($request);
$ids = escape(trim($request->getParam('lf_linkdate'))); $ids = escape(trim($request->getParam('id')));
if (strpos($ids, ' ') !== false) { if (strpos($ids, ' ') !== false) {
// multiple, space-separated ids provided // multiple, space-separated ids provided
$ids = array_values(array_filter(preg_split('/\s+/', $ids), 'strlen')); $ids = array_values(array_filter(preg_split('/\s+/', $ids), 'strlen'));
@ -207,7 +210,7 @@ public function deleteBookmark(Request $request, Response $response): Response
} }
// Don't redirect to where we were previously because the datastore has changed. // Don't redirect to where we were previously because the datastore has changed.
return $response->withRedirect('./'); return $this->redirect($response, '/');
} }
protected function displayForm(array $link, bool $isNew, Request $request, Response $response): Response protected function displayForm(array $link, bool $isNew, Request $request, Response $response): Response

View file

@ -23,7 +23,7 @@ public function index(Request $request, Response $response): Response
if ($this->container->loginManager->isLoggedIn() if ($this->container->loginManager->isLoggedIn()
|| $this->container->conf->get('security.open_shaarli', false) || $this->container->conf->get('security.open_shaarli', false)
) { ) {
return $response->withRedirect('./'); return $this->redirect($response, '/');
} }
$userCanLogin = $this->container->loginManager->canLogin($request->getServerParams()); $userCanLogin = $this->container->loginManager->canLogin($request->getServerParams());

View file

@ -104,6 +104,19 @@ protected function executeDefaultHooks(string $template): void
} }
} }
/**
* Simple helper which prepend the base path to redirect path.
*
* @param Response $response
* @param string $path Absolute path, e.g.: `/`, or `/admin/shaare/123` regardless of install directory
*
* @return Response updated
*/
protected function redirect(Response $response, string $path): Response
{
return $response->withRedirect($this->container->basePath . $path);
}
/** /**
* Generates a redirection to the previous page, based on the HTTP_REFERER. * Generates a redirection to the previous page, based on the HTTP_REFERER.
* It fails back to the home page. * It fails back to the home page.

View file

@ -11,6 +11,8 @@
* Class TagController * Class TagController
* *
* Slim controller handle tags. * Slim controller handle tags.
*
* TODO: check redirections with new helper
*/ */
class TagController extends ShaarliVisitorController class TagController extends ShaarliVisitorController
{ {
@ -27,10 +29,10 @@ public function addTag(Request $request, Response $response, array $args): Respo
// In case browser does not send HTTP_REFERER, we search a single tag // In case browser does not send HTTP_REFERER, we search a single tag
if (null === $referer) { if (null === $referer) {
if (null !== $newTag) { if (null !== $newTag) {
return $response->withRedirect('./?searchtags='. urlencode($newTag)); return $this->redirect($response, '/?searchtags='. urlencode($newTag));
} }
return $response->withRedirect('./'); return $this->redirect($response, '/');
} }
$currentUrl = parse_url($referer); $currentUrl = parse_url($referer);
@ -81,7 +83,7 @@ public function removeTag(Request $request, Response $response, array $args): Re
// If the referrer is not provided, we can update the search, so we failback on the bookmark list // If the referrer is not provided, we can update the search, so we failback on the bookmark list
if (empty($referer)) { if (empty($referer)) {
return $response->withRedirect('./'); return $this->redirect($response, '/');
} }
$tagToRemove = $args['tag'] ?? null; $tagToRemove = $args['tag'] ?? null;

View file

@ -463,7 +463,7 @@ function init(description) {
}); });
if (window.confirm(message)) { if (window.confirm(message)) {
window.location = `${basePath}/?delete_link&lf_linkdate=${ids.join('+')}&token=${token.value}`; window.location = `${basePath}/admin/shaare/delete?id=${ids.join('+')}&token=${token.value}`;
} }
}); });
} }
@ -549,7 +549,7 @@ function init(description) {
const refreshedToken = document.getElementById('token').value; const refreshedToken = document.getElementById('token').value;
const fromtag = block.getAttribute('data-tag'); const fromtag = block.getAttribute('data-tag');
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open('POST', `${basePath}/manage-tags`); xhr.open('POST', `${basePath}/admin/tags`);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onload = () => { xhr.onload = () => {
if (xhr.status !== 200) { if (xhr.status !== 200) {
@ -566,7 +566,7 @@ function init(description) {
.setAttribute('href', `${basePath}/?searchtags=${encodeURIComponent(totag)}`); .setAttribute('href', `${basePath}/?searchtags=${encodeURIComponent(totag)}`);
block block
.querySelector('a.rename-tag') .querySelector('a.rename-tag')
.setAttribute('href', `${basePath}/manage-tags?fromtag=${encodeURIComponent(totag)}`); .setAttribute('href', `${basePath}/admin/tags?fromtag=${encodeURIComponent(totag)}`);
// Refresh awesomplete values // Refresh awesomplete values
existingTags = existingTags.map(tag => (tag === fromtag ? totag : tag)); existingTags = existingTags.map(tag => (tag === fromtag ? totag : tag));
@ -600,7 +600,7 @@ function init(description) {
if (confirm(`Are you sure you want to delete the tag "${tag}"?`)) { if (confirm(`Are you sure you want to delete the tag "${tag}"?`)) {
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open('POST', `${basePath}/manage-tags`); xhr.open('POST', `${basePath}/admin/tags`);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onload = () => { xhr.onload = () => {
block.remove(); block.remove();

View file

@ -1,14 +1,14 @@
### Feeds options ### Feeds options
Feeds are available in ATOM with `/feed-atom` and RSS with `/feed-rss`. Feeds are available in ATOM with `/feed/atom` and RSS with `/feed/rss`.
Options: Options:
- You can use `permalinks` in the feed URL to get permalink to Shaares instead of direct link to shaared URL. - You can use `permalinks` in the feed URL to get permalink to Shaares instead of direct link to shaared URL.
- E.G. `https://my.shaarli.domain/feed-atom?permalinks`. - E.G. `https://my.shaarli.domain/feed/atom?permalinks`.
- You can use `nb` parameter in the feed URL to specify the number of Shaares you want in a feed (default if not specified: `50`). The keyword `all` is available if you want everything. - You can use `nb` parameter in the feed URL to specify the number of Shaares you want in a feed (default if not specified: `50`). The keyword `all` is available if you want everything.
- `https://my.shaarli.domain/feed-atom?permalinks&nb=42` - `https://my.shaarli.domain/feed/atom?permalinks&nb=42`
- `https://my.shaarli.domain/feed-atom?permalinks&nb=all` - `https://my.shaarli.domain/feed/atom?permalinks&nb=all`
### RSS Feeds or Picture Wall for a specific search/tag ### RSS Feeds or Picture Wall for a specific search/tag

View file

@ -32,11 +32,11 @@ Here is a list :
``` ```
http://<replace_domain>/ http://<replace_domain>/
http://<replace_domain>/?nonope http://<replace_domain>/?nonope
http://<replace_domain>/add-shaare http://<replace_domain>/admin/add-shaare
http://<replace_domain>/?do=changepasswd http://<replace_domain>/admin/password
http://<replace_domain>/?do=changetag http://<replace_domain>/?do=changetag
http://<replace_domain>/configure http://<replace_domain>/admin/configure
http://<replace_domain>/tools http://<replace_domain>/admin/tools
http://<replace_domain>/daily http://<replace_domain>/daily
http://<replace_domain>/?post http://<replace_domain>/?post
http://<replace_domain>/?do=export http://<replace_domain>/?do=export
@ -44,8 +44,8 @@ http://<replace_domain>/?do=import
http://<replace_domain>/login http://<replace_domain>/login
http://<replace_domain>/picture-wall http://<replace_domain>/picture-wall
http://<replace_domain>/?do=pluginadmin http://<replace_domain>/?do=pluginadmin
http://<replace_domain>/tag-cloud http://<replace_domain>/tags/cloud
http://<replace_domain>/tag-list http://<replace_domain>/tags/list
``` ```
#### Improve existing translation #### Improve existing translation

142
index.php
View file

@ -412,13 +412,13 @@ function renderPage($conf, $pluginManager, $bookmarkService, $history, $sessionM
// -------- Tag cloud // -------- Tag cloud
if ($targetPage == Router::$PAGE_TAGCLOUD) { if ($targetPage == Router::$PAGE_TAGCLOUD) {
header('Location: ./tag-cloud'); header('Location: ./tags/cloud');
exit; exit;
} }
// -------- Tag list // -------- Tag list
if ($targetPage == Router::$PAGE_TAGLIST) { if ($targetPage == Router::$PAGE_TAGLIST) {
header('Location: ./tag-list'); header('Location: ./tags/list');
exit; exit;
} }
@ -433,7 +433,7 @@ function renderPage($conf, $pluginManager, $bookmarkService, $history, $sessionM
if ($targetPage == Router::$PAGE_FEED_ATOM || $targetPage == Router::$PAGE_FEED_RSS) { if ($targetPage == Router::$PAGE_FEED_ATOM || $targetPage == Router::$PAGE_FEED_RSS) {
$feedType = $targetPage == Router::$PAGE_FEED_RSS ? FeedBuilder::$FEED_RSS : FeedBuilder::$FEED_ATOM; $feedType = $targetPage == Router::$PAGE_FEED_RSS ? FeedBuilder::$FEED_RSS : FeedBuilder::$FEED_ATOM;
header('Location: ./feed-'. $feedType .'?'. http_build_query($_GET)); header('Location: ./feed/'. $feedType .'?'. http_build_query($_GET));
exit; exit;
} }
@ -501,31 +501,31 @@ function renderPage($conf, $pluginManager, $bookmarkService, $history, $sessionM
// -------- Display the Tools menu if requested (import/export/bookmarklet...) // -------- Display the Tools menu if requested (import/export/bookmarklet...)
if ($targetPage == Router::$PAGE_TOOLS) { if ($targetPage == Router::$PAGE_TOOLS) {
header('Location: ./tools'); header('Location: ./admin/tools');
exit; exit;
} }
// -------- User wants to change his/her password. // -------- User wants to change his/her password.
if ($targetPage == Router::$PAGE_CHANGEPASSWORD) { if ($targetPage == Router::$PAGE_CHANGEPASSWORD) {
header('Location: ./password'); header('Location: ./admin/password');
exit; exit;
} }
// -------- User wants to change configuration // -------- User wants to change configuration
if ($targetPage == Router::$PAGE_CONFIGURE) { if ($targetPage == Router::$PAGE_CONFIGURE) {
header('Location: ./configure'); header('Location: ./admin/configure');
exit; exit;
} }
// -------- User wants to rename a tag or delete it // -------- User wants to rename a tag or delete it
if ($targetPage == Router::$PAGE_CHANGETAG) { if ($targetPage == Router::$PAGE_CHANGETAG) {
header('Location: ./manage-tags'); header('Location: ./admin/tags');
exit; exit;
} }
// -------- User wants to add a link without using the bookmarklet: Show form. // -------- User wants to add a link without using the bookmarklet: Show form.
if ($targetPage == Router::$PAGE_ADDLINK) { if ($targetPage == Router::$PAGE_ADDLINK) {
header('Location: ./shaare'); header('Location: ./admin/shaare');
exit; exit;
} }
@ -538,56 +538,10 @@ function renderPage($conf, $pluginManager, $bookmarkService, $history, $sessionM
// -------- User clicked the "Delete" button when editing a link: Delete link from database. // -------- User clicked the "Delete" button when editing a link: Delete link from database.
if ($targetPage == Router::$PAGE_DELETELINK) { if ($targetPage == Router::$PAGE_DELETELINK) {
if (! $sessionManager->checkToken($_GET['token'])) { $ids = $_GET['lf_linkdate'] ?? '';
die(t('Wrong token.')); $token = $_GET['token'] ?? '';
}
$ids = trim($_GET['lf_linkdate']); header('Location: ./admin/shaare/delete?id=' . $ids . '&token=' . $token);
if (strpos($ids, ' ') !== false) {
// multiple, space-separated ids provided
$ids = array_values(array_filter(
preg_split('/\s+/', escape($ids)),
function ($item) {
return $item !== '';
}
));
} else {
// only a single id provided
$shortUrl = $bookmarkService->get($ids)->getShortUrl();
$ids = [$ids];
}
// assert at least one id is given
if (!count($ids)) {
die('no id provided');
}
$factory = new FormatterFactory($conf, $loginManager->isLoggedIn());
$formatter = $factory->getFormatter('raw');
foreach ($ids as $id) {
$id = (int) escape($id);
$bookmark = $bookmarkService->get($id);
$data = $formatter->format($bookmark);
$pluginManager->executeHooks('delete_link', $data);
$bookmarkService->remove($bookmark, false);
}
$bookmarkService->save();
// If we are called from the bookmarklet, we must close the popup:
if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) {
echo '<script>self.close();</script>';
exit;
}
$location = '?';
if (isset($_SERVER['HTTP_REFERER'])) {
// Don't redirect to where we were previously if it was a permalink or an edit_link, because it would 404.
$location = generateLocation(
$_SERVER['HTTP_REFERER'],
$_SERVER['HTTP_HOST'],
['delete_link', 'edit_link', ! empty($shortUrl) ? $shortUrl : null]
);
}
header('Location: ' . $location); // After deleting the link, redirect to appropriate location
exit; exit;
} }
@ -646,13 +600,13 @@ function ($item) {
// -------- User clicked the "EDIT" button on a link: Display link edit form. // -------- User clicked the "EDIT" button on a link: Display link edit form.
if (isset($_GET['edit_link'])) { if (isset($_GET['edit_link'])) {
$id = (int) escape($_GET['edit_link']); $id = (int) escape($_GET['edit_link']);
header('Location: ./shaare-' . $id); header('Location: ./admin/shaare/' . $id);
exit; exit;
} }
// -------- User want to post a new link: Display link edit form. // -------- User want to post a new link: Display link edit form.
if (isset($_GET['post'])) { if (isset($_GET['post'])) {
header('Location: ./shaare?' . http_build_query($_GET)); header('Location: ./admin/shaare?' . http_build_query($_GET));
exit; exit;
} }
@ -1160,7 +1114,7 @@ function install($conf, $sessionManager, $loginManager)
exit; exit;
} }
$containerBuilder = new ContainerBuilder($conf, $sessionManager, $loginManager, WEB_PATH); $containerBuilder = new ContainerBuilder($conf, $sessionManager, $loginManager);
$container = $containerBuilder->build(); $container = $containerBuilder->build();
$app = new App($container); $app = new App($container);
@ -1183,51 +1137,37 @@ function install($conf, $sessionManager, $loginManager)
$app->group('', function () { $app->group('', function () {
/* -- PUBLIC --*/ /* -- PUBLIC --*/
$this->get('/login', '\Shaarli\Front\Controller\Visitor\LoginController:index')->setName('login'); $this->get('/login', '\Shaarli\Front\Controller\Visitor\LoginController:index');
$this->get('/picture-wall', '\Shaarli\Front\Controller\Visitor\PictureWallController:index')->setName('picwall'); $this->get('/picture-wall', '\Shaarli\Front\Controller\Visitor\PictureWallController:index');
$this->get('/tag-cloud', '\Shaarli\Front\Controller\Visitor\TagCloudController:cloud')->setName('tagcloud'); $this->get('/tags/cloud', '\Shaarli\Front\Controller\Visitor\TagCloudController:cloud');
$this->get('/tag-list', '\Shaarli\Front\Controller\Visitor\TagCloudController:list')->setName('taglist'); $this->get('/tags/list', '\Shaarli\Front\Controller\Visitor\TagCloudController:list');
$this->get('/daily', '\Shaarli\Front\Controller\Visitor\DailyController:index')->setName('daily'); $this->get('/daily', '\Shaarli\Front\Controller\Visitor\DailyController:index');
$this->get('/daily-rss', '\Shaarli\Front\Controller\Visitor\DailyController:rss')->setName('dailyrss'); $this->get('/daily-rss', '\Shaarli\Front\Controller\Visitor\DailyController:rss');
$this->get('/feed-atom', '\Shaarli\Front\Controller\Visitor\FeedController:atom')->setName('feedatom'); $this->get('/feed/atom', '\Shaarli\Front\Controller\Visitor\FeedController:atom');
$this->get('/feed-rss', '\Shaarli\Front\Controller\Visitor\FeedController:rss')->setName('feedrss'); $this->get('/feed/rss', '\Shaarli\Front\Controller\Visitor\FeedController:rss');
$this->get('/open-search', '\Shaarli\Front\Controller\Visitor\OpenSearchController:index')->setName('opensearch'); $this->get('/open-search', '\Shaarli\Front\Controller\Visitor\OpenSearchController:index');
$this->get('/add-tag/{newTag}', '\Shaarli\Front\Controller\Visitor\TagController:addTag')->setName('add-tag'); $this->get('/add-tag/{newTag}', '\Shaarli\Front\Controller\Visitor\TagController:addTag');
$this->get('/remove-tag/{tag}', '\Shaarli\Front\Controller\Visitor\TagController:removeTag')->setName('remove-tag'); $this->get('/remove-tag/{tag}', '\Shaarli\Front\Controller\Visitor\TagController:removeTag');
/* -- LOGGED IN -- */ /* -- LOGGED IN -- */
$this->get('/logout', '\Shaarli\Front\Controller\Admin\LogoutController:index')->setName('logout'); $this->get('/logout', '\Shaarli\Front\Controller\Admin\LogoutController:index');
$this->get('/tools', '\Shaarli\Front\Controller\Admin\ToolsController:index')->setName('tools'); $this->get('/admin/tools', '\Shaarli\Front\Controller\Admin\ToolsController:index');
$this->get('/password', '\Shaarli\Front\Controller\Admin\PasswordController:index')->setName('password'); $this->get('/admin/password', '\Shaarli\Front\Controller\Admin\PasswordController:index');
$this->post('/password', '\Shaarli\Front\Controller\Admin\PasswordController:change')->setName('changePassword'); $this->post('/admin/password', '\Shaarli\Front\Controller\Admin\PasswordController:change');
$this->get('/configure', '\Shaarli\Front\Controller\Admin\ConfigureController:index')->setName('configure'); $this->get('/admin/configure', '\Shaarli\Front\Controller\Admin\ConfigureController:index');
$this->post('/configure', '\Shaarli\Front\Controller\Admin\ConfigureController:save')->setName('saveConfigure'); $this->post('/admin/configure', '\Shaarli\Front\Controller\Admin\ConfigureController:save');
$this->get('/manage-tags', '\Shaarli\Front\Controller\Admin\ManageTagController:index')->setName('manageTag'); $this->get('/admin/tags', '\Shaarli\Front\Controller\Admin\ManageTagController:index');
$this->post('/manage-tags', '\Shaarli\Front\Controller\Admin\ManageTagController:save')->setName('saveManageTag'); $this->post('/admin/tags', '\Shaarli\Front\Controller\Admin\ManageTagController:save');
$this->get('/add-shaare', '\Shaarli\Front\Controller\Admin\PostBookmarkController:addShaare')->setName('addShaare'); $this->get('/admin/add-shaare', '\Shaarli\Front\Controller\Admin\PostBookmarkController:addShaare');
$this $this->get('/admin/shaare', '\Shaarli\Front\Controller\Admin\PostBookmarkController:displayCreateForm');
->get('/shaare', '\Shaarli\Front\Controller\Admin\PostBookmarkController:displayCreateForm') $this->get('/admin/shaare/{id:[0-9]+}', '\Shaarli\Front\Controller\Admin\PostBookmarkController:displayEditForm');
->setName('newShaare'); $this->post('/admin/shaare', '\Shaarli\Front\Controller\Admin\PostBookmarkController:save');
$this $this->get('/admin/shaare/delete', '\Shaarli\Front\Controller\Admin\PostBookmarkController:deleteBookmark');
->get('/shaare-{id}', '\Shaarli\Front\Controller\Admin\PostBookmarkController:displayEditForm')
->setName('editShaare');
$this
->post('/shaare', '\Shaarli\Front\Controller\Admin\PostBookmarkController:save')
->setName('saveShaare');
$this
->get('/delete-shaare', '\Shaarli\Front\Controller\Admin\PostBookmarkController:deleteBookmark')
->setName('deleteShaare');
$this $this->get('/links-per-page', '\Shaarli\Front\Controller\Admin\SessionFilterController:linksPerPage');
->get('/links-per-page', '\Shaarli\Front\Controller\Admin\SessionFilterController:linksPerPage') $this->get('/visibility/{visibility}', '\Shaarli\Front\Controller\Admin\SessionFilterController:visibility');
->setName('filter-links-per-page'); $this->get('/untagged-only', '\Shaarli\Front\Controller\Admin\SessionFilterController:untaggedOnly');
$this
->get('/visibility/{visibility}', '\Shaarli\Front\Controller\Admin\SessionFilterController:visibility')
->setName('visibility');
$this
->get('/untagged-only', '\Shaarli\Front\Controller\Admin\SessionFilterController:untaggedOnly')
->setName('untagged-only');
})->add('\Shaarli\Front\ShaarliMiddleware'); })->add('\Shaarli\Front\ShaarliMiddleware');
$response = $app->run(true); $response = $app->run(true);

View file

@ -60,8 +60,8 @@ function hook_pubsubhubbub_render_feed($data, $conf)
function hook_pubsubhubbub_save_link($data, $conf) function hook_pubsubhubbub_save_link($data, $conf)
{ {
$feeds = array( $feeds = array(
index_url($_SERVER) .'feed-atom', index_url($_SERVER) .'feed/atom',
index_url($_SERVER) .'feed-rss', index_url($_SERVER) .'feed/rss',
); );
$httpPost = function_exists('curl_version') ? false : 'nocurl_http_post'; $httpPost = function_exists('curl_version') ? false : 'nocurl_http_post';

View file

@ -7,12 +7,16 @@
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Shaarli\Bookmark\BookmarkServiceInterface; use Shaarli\Bookmark\BookmarkServiceInterface;
use Shaarli\Config\ConfigManager; use Shaarli\Config\ConfigManager;
use Shaarli\Feed\FeedBuilder;
use Shaarli\Formatter\FormatterFactory; use Shaarli\Formatter\FormatterFactory;
use Shaarli\History; use Shaarli\History;
use Shaarli\Http\HttpAccess;
use Shaarli\Plugin\PluginManager;
use Shaarli\Render\PageBuilder; use Shaarli\Render\PageBuilder;
use Shaarli\Render\PageCacheManager; use Shaarli\Render\PageCacheManager;
use Shaarli\Security\LoginManager; use Shaarli\Security\LoginManager;
use Shaarli\Security\SessionManager; use Shaarli\Security\SessionManager;
use Shaarli\Thumbnailer;
class ContainerBuilderTest extends TestCase class ContainerBuilderTest extends TestCase
{ {
@ -39,8 +43,7 @@ public function setUp(): void
$this->containerBuilder = new ContainerBuilder( $this->containerBuilder = new ContainerBuilder(
$this->conf, $this->conf,
$this->sessionManager, $this->sessionManager,
$this->loginManager, $this->loginManager
'UT web path'
); );
} }
@ -51,11 +54,17 @@ public function testBuildContainer(): void
static::assertInstanceOf(ConfigManager::class, $container->conf); static::assertInstanceOf(ConfigManager::class, $container->conf);
static::assertInstanceOf(SessionManager::class, $container->sessionManager); static::assertInstanceOf(SessionManager::class, $container->sessionManager);
static::assertInstanceOf(LoginManager::class, $container->loginManager); static::assertInstanceOf(LoginManager::class, $container->loginManager);
static::assertSame('UT web path', $container->webPath);
static::assertInstanceOf(History::class, $container->history); static::assertInstanceOf(History::class, $container->history);
static::assertInstanceOf(BookmarkServiceInterface::class, $container->bookmarkService); static::assertInstanceOf(BookmarkServiceInterface::class, $container->bookmarkService);
static::assertInstanceOf(PageBuilder::class, $container->pageBuilder); static::assertInstanceOf(PageBuilder::class, $container->pageBuilder);
static::assertInstanceOf(PluginManager::class, $container->pluginManager);
static::assertInstanceOf(FormatterFactory::class, $container->formatterFactory); static::assertInstanceOf(FormatterFactory::class, $container->formatterFactory);
static::assertInstanceOf(PageCacheManager::class, $container->pageCacheManager); static::assertInstanceOf(PageCacheManager::class, $container->pageCacheManager);
static::assertInstanceOf(FeedBuilder::class, $container->feedBuilder);
static::assertInstanceOf(Thumbnailer::class, $container->thumbnailer);
static::assertInstanceOf(HttpAccess::class, $container->httpAccess);
// Set by the middleware
static::assertNull($container->basePath);
} }
} }

View file

@ -11,7 +11,7 @@ class CachedPageTest extends \PHPUnit\Framework\TestCase
{ {
// test cache directory // test cache directory
protected static $testCacheDir = 'sandbox/pagecache'; protected static $testCacheDir = 'sandbox/pagecache';
protected static $url = 'http://shaar.li/feed-atom'; protected static $url = 'http://shaar.li/feed/atom';
protected static $filename; protected static $filename;
/** /**
@ -42,8 +42,8 @@ public function testConstruct()
{ {
new CachedPage(self::$testCacheDir, '', true); new CachedPage(self::$testCacheDir, '', true);
new CachedPage(self::$testCacheDir, '', false); new CachedPage(self::$testCacheDir, '', false);
new CachedPage(self::$testCacheDir, 'http://shaar.li/feed-rss', true); new CachedPage(self::$testCacheDir, 'http://shaar.li/feed/rss', true);
new CachedPage(self::$testCacheDir, 'http://shaar.li/feed-atom', false); new CachedPage(self::$testCacheDir, 'http://shaar.li/feed/atom', false);
$this->addToAssertionCount(1); $this->addToAssertionCount(1);
} }

View file

@ -142,7 +142,7 @@ public function testSaveNewConfig(): void
$result = $this->controller->save($request, $response); $result = $this->controller->save($request, $response);
static::assertSame(302, $result->getStatusCode()); static::assertSame(302, $result->getStatusCode());
static::assertSame(['./configure'], $result->getHeader('Location')); static::assertSame(['/subfolder/admin/configure'], $result->getHeader('Location'));
static::assertArrayNotHasKey(SessionManager::KEY_WARNING_MESSAGES, $session); static::assertArrayNotHasKey(SessionManager::KEY_WARNING_MESSAGES, $session);
static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session); static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session);
@ -193,7 +193,7 @@ public function testSaveNewConfigThumbnailsActivation(): void
$result = $this->controller->save($request, $response); $result = $this->controller->save($request, $response);
static::assertSame(302, $result->getStatusCode()); static::assertSame(302, $result->getStatusCode());
static::assertSame(['./configure'], $result->getHeader('Location')); static::assertSame(['/subfolder/admin/configure'], $result->getHeader('Location'));
static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session); static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session);
static::assertArrayHasKey(SessionManager::KEY_WARNING_MESSAGES, $session); static::assertArrayHasKey(SessionManager::KEY_WARNING_MESSAGES, $session);
@ -242,7 +242,7 @@ public function testSaveNewConfigThumbnailsAlreadyActive(): void
$result = $this->controller->save($request, $response); $result = $this->controller->save($request, $response);
static::assertSame(302, $result->getStatusCode()); static::assertSame(302, $result->getStatusCode());
static::assertSame(['./configure'], $result->getHeader('Location')); static::assertSame(['/subfolder/admin/configure'], $result->getHeader('Location'));
static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session); static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session);
static::assertArrayNotHasKey(SessionManager::KEY_WARNING_MESSAGES, $session); static::assertArrayNotHasKey(SessionManager::KEY_WARNING_MESSAGES, $session);

View file

@ -49,7 +49,7 @@ public function testValidControllerInvoke(): void
static::assertInstanceOf(Response::class, $result); static::assertInstanceOf(Response::class, $result);
static::assertSame(302, $result->getStatusCode()); static::assertSame(302, $result->getStatusCode());
static::assertContains('./', $result->getHeader('Location')); static::assertSame(['/subfolder/'], $result->getHeader('location'));
static::assertSame('false', $_COOKIE[LoginManager::$STAY_SIGNED_IN_COOKIE]); static::assertSame('false', $_COOKIE[LoginManager::$STAY_SIGNED_IN_COOKIE]);
} }
} }

View file

@ -93,7 +93,7 @@ public function testSaveRenameTagValid(): void
$result = $this->controller->save($request, $response); $result = $this->controller->save($request, $response);
static::assertSame(302, $result->getStatusCode()); static::assertSame(302, $result->getStatusCode());
static::assertSame(['./?searchtags=new-tag'], $result->getHeader('location')); static::assertSame(['/subfolder/?searchtags=new-tag'], $result->getHeader('location'));
static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session); static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session);
static::assertArrayNotHasKey(SessionManager::KEY_WARNING_MESSAGES, $session); static::assertArrayNotHasKey(SessionManager::KEY_WARNING_MESSAGES, $session);
@ -146,7 +146,7 @@ public function testSaveDeleteTagValid(): void
$result = $this->controller->save($request, $response); $result = $this->controller->save($request, $response);
static::assertSame(302, $result->getStatusCode()); static::assertSame(302, $result->getStatusCode());
static::assertSame(['./manage-tags'], $result->getHeader('location')); static::assertSame(['/subfolder/admin/tags'], $result->getHeader('location'));
static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session); static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session);
static::assertArrayNotHasKey(SessionManager::KEY_WARNING_MESSAGES, $session); static::assertArrayNotHasKey(SessionManager::KEY_WARNING_MESSAGES, $session);
@ -197,7 +197,7 @@ public function testSaveRenameTagMissingFrom(): void
$result = $this->controller->save($request, $response); $result = $this->controller->save($request, $response);
static::assertSame(302, $result->getStatusCode()); static::assertSame(302, $result->getStatusCode());
static::assertSame(['./manage-tags'], $result->getHeader('location')); static::assertSame(['/subfolder/admin/tags'], $result->getHeader('location'));
static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session); static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session);
static::assertArrayHasKey(SessionManager::KEY_WARNING_MESSAGES, $session); static::assertArrayHasKey(SessionManager::KEY_WARNING_MESSAGES, $session);
@ -229,7 +229,7 @@ public function testSaveDeleteTagMissingFrom(): void
$result = $this->controller->save($request, $response); $result = $this->controller->save($request, $response);
static::assertSame(302, $result->getStatusCode()); static::assertSame(302, $result->getStatusCode());
static::assertSame(['./manage-tags'], $result->getHeader('location')); static::assertSame(['/subfolder/admin/tags'], $result->getHeader('location'));
static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session); static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session);
static::assertArrayHasKey(SessionManager::KEY_WARNING_MESSAGES, $session); static::assertArrayHasKey(SessionManager::KEY_WARNING_MESSAGES, $session);
@ -262,7 +262,7 @@ public function testSaveRenameTagMissingTo(): void
$result = $this->controller->save($request, $response); $result = $this->controller->save($request, $response);
static::assertSame(302, $result->getStatusCode()); static::assertSame(302, $result->getStatusCode());
static::assertSame(['./manage-tags'], $result->getHeader('location')); static::assertSame(['/subfolder/admin/tags'], $result->getHeader('location'));
static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session); static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session);
static::assertArrayHasKey(SessionManager::KEY_WARNING_MESSAGES, $session); static::assertArrayHasKey(SessionManager::KEY_WARNING_MESSAGES, $session);

View file

@ -395,7 +395,7 @@ public function testSaveBookmark(): void
'lf_description' => 'Provided description.', 'lf_description' => 'Provided description.',
'lf_tags' => 'abc def', 'lf_tags' => 'abc def',
'lf_private' => '1', 'lf_private' => '1',
'returnurl' => 'http://shaarli.tld/subfolder/add-shaare' 'returnurl' => 'http://shaarli.tld/subfolder/admin/add-shaare'
]; ];
$request = $this->createMock(Request::class); $request = $this->createMock(Request::class);
@ -459,7 +459,7 @@ public function testSaveBookmark(): void
$result = $this->controller->save($request, $response); $result = $this->controller->save($request, $response);
static::assertSame(302, $result->getStatusCode()); static::assertSame(302, $result->getStatusCode());
static::assertRegExp('@/subfolder/#\w{6}@', $result->getHeader('location')[0]); static::assertRegExp('@/subfolder/#[\w\-]{6}@', $result->getHeader('location')[0]);
} }
@ -545,7 +545,7 @@ public function testSaveExistingBookmark(): void
$result = $this->controller->save($request, $response); $result = $this->controller->save($request, $response);
static::assertSame(302, $result->getStatusCode()); static::assertSame(302, $result->getStatusCode());
static::assertRegExp('@/subfolder/\?page=2#\w{6}@', $result->getHeader('location')[0]); static::assertRegExp('@/subfolder/\?page=2#[\w\-]{6}@', $result->getHeader('location')[0]);
} }
/** /**

View file

@ -95,7 +95,7 @@ public function testLoginControllerWhileLoggedIn(): void
static::assertInstanceOf(Response::class, $result); static::assertInstanceOf(Response::class, $result);
static::assertSame(302, $result->getStatusCode()); static::assertSame(302, $result->getStatusCode());
static::assertSame(['./'], $result->getHeader('Location')); static::assertSame(['/subfolder/'], $result->getHeader('Location'));
} }
public function testLoginControllerOpenShaarli(): void public function testLoginControllerOpenShaarli(): void
@ -116,7 +116,7 @@ public function testLoginControllerOpenShaarli(): void
static::assertInstanceOf(Response::class, $result); static::assertInstanceOf(Response::class, $result);
static::assertSame(302, $result->getStatusCode()); static::assertSame(302, $result->getStatusCode());
static::assertSame(['./'], $result->getHeader('Location')); static::assertSame(['/subfolder/'], $result->getHeader('Location'));
} }
public function testLoginControllerWhileBanned(): void public function testLoginControllerWhileBanned(): void

View file

@ -64,7 +64,7 @@ public function testAddTagWithoutRefererAndExistingSearch(): void
static::assertInstanceOf(Response::class, $result); static::assertInstanceOf(Response::class, $result);
static::assertSame(302, $result->getStatusCode()); static::assertSame(302, $result->getStatusCode());
static::assertSame(['./?searchtags=abc'], $result->getHeader('location')); static::assertSame(['/subfolder/?searchtags=abc'], $result->getHeader('location'));
} }
public function testAddTagRemoveLegacyQueryParam(): void public function testAddTagRemoveLegacyQueryParam(): void
@ -138,7 +138,7 @@ public function testAddTagWithoutNewTagWithoutReferer(): void
static::assertInstanceOf(Response::class, $result); static::assertInstanceOf(Response::class, $result);
static::assertSame(302, $result->getStatusCode()); static::assertSame(302, $result->getStatusCode());
static::assertSame(['./'], $result->getHeader('location')); static::assertSame(['/subfolder/'], $result->getHeader('location'));
} }
public function testRemoveTagWithoutMatchingTag(): void public function testRemoveTagWithoutMatchingTag(): void
@ -184,7 +184,7 @@ public function testRemoveTagWithoutReferer(): void
static::assertInstanceOf(Response::class, $result); static::assertInstanceOf(Response::class, $result);
static::assertSame(302, $result->getStatusCode()); static::assertSame(302, $result->getStatusCode());
static::assertSame(['./'], $result->getHeader('location')); static::assertSame(['/subfolder/'], $result->getHeader('location'));
} }
public function testRemoveTagWithoutTag(): void public function testRemoveTagWithoutTag(): void
@ -210,6 +210,6 @@ public function testRemoveTagWithoutTagWithoutReferer(): void
static::assertInstanceOf(Response::class, $result); static::assertInstanceOf(Response::class, $result);
static::assertSame(302, $result->getStatusCode()); static::assertSame(302, $result->getStatusCode());
static::assertSame(['./'], $result->getHeader('location')); static::assertSame(['/subfolder/'], $result->getHeader('location'));
} }
} }

View file

@ -9,7 +9,7 @@
<div class="pure-u-lg-1-3 pure-u-1-24"></div> <div class="pure-u-lg-1-3 pure-u-1-24"></div>
<div id="addlink-form" class="page-form page-form-light pure-u-lg-1-3 pure-u-22-24"> <div id="addlink-form" class="page-form page-form-light pure-u-lg-1-3 pure-u-22-24">
<h2 class="window-title">{"Shaare a new link"|t}</h2> <h2 class="window-title">{"Shaare a new link"|t}</h2>
<form method="GET" action="{$base_path}/shaare" name="addform" class="addform"> <form method="GET" action="{$base_path}/admin/shaare" name="addform" class="addform">
<div> <div>
<label for="shaare">{'URL or leave empty to post a note'|t}</label> <label for="shaare">{'URL or leave empty to post a note'|t}</label>
<input type="text" name="post" id="shaare" class="autofocus"> <input type="text" name="post" id="shaare" class="autofocus">

View file

@ -9,7 +9,7 @@
<div class="pure-u-lg-1-3 pure-u-1-24"></div> <div class="pure-u-lg-1-3 pure-u-1-24"></div>
<div id="addlink-form" class="page-form page-form-light pure-u-lg-1-3 pure-u-22-24"> <div id="addlink-form" class="page-form page-form-light pure-u-lg-1-3 pure-u-22-24">
<h2 class="window-title">{"Change password"|t}</h2> <h2 class="window-title">{"Change password"|t}</h2>
<form method="POST" action="{$base_path}/password" name="changepasswordform" id="changepasswordform"> <form method="POST" action="{$base_path}/admin/password" name="changepasswordform" id="changepasswordform">
<div> <div>
<input type="password" name="oldpassword" aria-label="{'Current password'|t}" placeholder="{'Current password'|t}" class="autofocus"> <input type="password" name="oldpassword" aria-label="{'Current password'|t}" placeholder="{'Current password'|t}" class="autofocus">
</div> </div>

View file

@ -9,7 +9,7 @@
<div class="pure-u-lg-1-3 pure-u-1-24"></div> <div class="pure-u-lg-1-3 pure-u-1-24"></div>
<div id="addlink-form" class="page-form page-form-light pure-u-lg-1-3 pure-u-22-24"> <div id="addlink-form" class="page-form page-form-light pure-u-lg-1-3 pure-u-22-24">
<h2 class="window-title">{"Manage tags"|t}</h2> <h2 class="window-title">{"Manage tags"|t}</h2>
<form method="POST" action="{$base_path}/manage-tags" name="changetag" id="changetag"> <form method="POST" action="{$base_path}/admin/tags" name="changetag" id="changetag">
<div> <div>
<input type="text" name="fromtag" aria-label="{'Tag'|t}" placeholder="{'Tag'|t}" value="{$fromtag}" <input type="text" name="fromtag" aria-label="{'Tag'|t}" placeholder="{'Tag'|t}" value="{$fromtag}"
list="tagsList" autocomplete="off" class="awesomplete autofocus" data-minChars="1"> list="tagsList" autocomplete="off" class="awesomplete autofocus" data-minChars="1">
@ -32,7 +32,7 @@ <h2 class="window-title">{"Manage tags"|t}</h2>
</div> </div>
</form> </form>
<p>{'You can also edit tags in the'|t} <a href="{$base_path}/tag-list?sort=usage">{'tag list'|t}</a>.</p> <p>{'You can also edit tags in the'|t} <a href="{$base_path}/tags/list?sort=usage">{'tag list'|t}</a>.</p>
</div> </div>
</div> </div>
{include="page.footer"} {include="page.footer"}

View file

@ -11,7 +11,7 @@
{$ratioInput='7-12'} {$ratioInput='7-12'}
{$ratioInputMobile='1-8'} {$ratioInputMobile='1-8'}
<form method="POST" action="{$base_path}/configure" name="configform" id="configform"> <form method="POST" action="{$base_path}/admin/configure" name="configform" id="configform">
<div class="pure-g"> <div class="pure-g">
<div class="pure-u-lg-1-8 pure-u-1-24"></div> <div class="pure-u-lg-1-8 pure-u-1-24"></div>
<div class="pure-u-lg-3-4 pure-u-22-24 page-form page-form-complete"> <div class="pure-u-lg-3-4 pure-u-22-24 page-form page-form-complete">

View file

@ -9,7 +9,7 @@
<div class="pure-u-lg-1-5 pure-u-1-24"></div> <div class="pure-u-lg-1-5 pure-u-1-24"></div>
<form method="post" <form method="post"
name="linkform" name="linkform"
action="{$base_path}/shaare" action="{$base_path}/admin/shaare"
class="page-form pure-u-lg-3-5 pure-u-22-24 page-form page-form-light" class="page-form pure-u-lg-3-5 pure-u-22-24 page-form page-form-light"
> >
<h2 class="window-title"> <h2 class="window-title">
@ -73,7 +73,7 @@ <h2 class="window-title">
<input type="submit" name="save_edit" class="" id="button-save-edit" <input type="submit" name="save_edit" class="" id="button-save-edit"
value="{if="$link_is_new"}{'Save'|t}{else}{'Apply Changes'|t}{/if}"> value="{if="$link_is_new"}{'Save'|t}{else}{'Apply Changes'|t}{/if}">
{if="!$link_is_new"} {if="!$link_is_new"}
<a href="{$base_path}/?delete_link&amp;lf_linkdate={$link.id}&amp;token={$token}" <a href="{$base_path}/admin/shaare/delete?id={$link.id}&amp;token={$token}"
title="" name="delete_link" class="button button-red confirm-delete"> title="" name="delete_link" class="button button-red confirm-delete">
{'Delete'|t} {'Delete'|t}
</a> </a>

View file

@ -3,8 +3,8 @@
<meta name="format-detection" content="telephone=no" /> <meta name="format-detection" content="telephone=no" />
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="referrer" content="same-origin"> <meta name="referrer" content="same-origin">
<link rel="alternate" type="application/atom+xml" href="{$feedurl}feed-atom?{$searchcrits}#" title="ATOM Feed" /> <link rel="alternate" type="application/atom+xml" href="{$feedurl}feed/atom?{$searchcrits}#" title="ATOM Feed" />
<link rel="alternate" type="application/rss+xml" href="{$feedurl}feed-rss?{$searchcrits}#" title="RSS Feed" /> <link rel="alternate" type="application/rss+xml" href="{$feedurl}feed/rss?{$searchcrits}#" title="RSS Feed" />
<link href="{$asset_path}/img/favicon.png#" rel="shortcut icon" type="image/png" /> <link href="{$asset_path}/img/favicon.png#" rel="shortcut icon" type="image/png" />
<link href="{$asset_path}/img/apple-touch-icon.png#" rel="apple-touch-icon" sizes="180x180" /> <link href="{$asset_path}/img/apple-touch-icon.png#" rel="apple-touch-icon" sizes="180x180" />
<link type="text/css" rel="stylesheet" href="{$asset_path}/css/shaarli.min.css?v={$version_hash}#" /> <link type="text/css" rel="stylesheet" href="{$asset_path}/css/shaarli.min.css?v={$version_hash}#" />

View file

@ -198,10 +198,10 @@ <h2>
<input type="checkbox" class="link-checkbox" value="{$value.id}"> <input type="checkbox" class="link-checkbox" value="{$value.id}">
</span> </span>
<span class="linklist-item-infos-controls-item ctrl-edit"> <span class="linklist-item-infos-controls-item ctrl-edit">
<a href="{$base_path}/?edit_link={$value.id}" aria-label="{$strEdit}" title="{$strEdit}"><i class="fa fa-pencil-square-o edit-link" aria-hidden="true"></i></a> <a href="{$base_path}/admin/shaare/{$value.id}" aria-label="{$strEdit}" title="{$strEdit}"><i class="fa fa-pencil-square-o edit-link" aria-hidden="true"></i></a>
</span> </span>
<span class="linklist-item-infos-controls-item ctrl-delete"> <span class="linklist-item-infos-controls-item ctrl-delete">
<a href="{$base_path}/?delete_link&amp;lf_linkdate={$value.id}&amp;token={$token}" aria-label="{$strDelete}" <a href="{$base_path}/admin/shaare/delete?id={$value.id}&amp;token={$token}" aria-label="{$strDelete}"
title="{$strDelete}" class="delete-link pure-u-0 pure-u-lg-visible confirm-delete"> title="{$strDelete}" class="delete-link pure-u-0 pure-u-lg-visible confirm-delete">
<i class="fa fa-trash" aria-hidden="true"></i> <i class="fa fa-trash" aria-hidden="true"></i>
</a> </a>
@ -267,12 +267,12 @@ <h2>
{/if} {/if}
{if="$is_logged_in"} {if="$is_logged_in"}
&middot; &middot;
<a href="{$base_path}/?delete_link&amp;lf_linkdate={$value.id}&amp;token={$token}" aria-label="{$strDelete}" <a href="{$base_path}/admin/shaare/delete?id={$value.id}&amp;token={$token}" aria-label="{$strDelete}"
title="{$strDelete}" class="delete-link confirm-delete"> title="{$strDelete}" class="delete-link confirm-delete">
<i class="fa fa-trash" aria-hidden="true"></i> <i class="fa fa-trash" aria-hidden="true"></i>
</a> </a>
&middot; &middot;
<a href="{$base_path}/?edit_link={$value.id}" aria-label="{$strEdit}" title="{$strEdit}"><i class="fa fa-pencil-square-o edit-link" aria-hidden="true"></i></a> <a href="{$base_path}/admin/shaare/{$value.id}" aria-label="{$strEdit}" title="{$strEdit}"><i class="fa fa-pencil-square-o edit-link" aria-hidden="true"></i></a>
{/if} {/if}
</div> </div>
</div> </div>

View file

@ -3,8 +3,8 @@
<ShortName>Shaarli search - {$pagetitle}</ShortName> <ShortName>Shaarli search - {$pagetitle}</ShortName>
<Description>Shaarli search - {$pagetitle}</Description> <Description>Shaarli search - {$pagetitle}</Description>
<Url type="text/html" template="{$serverurl}?searchterm={searchTerms}" /> <Url type="text/html" template="{$serverurl}?searchterm={searchTerms}" />
<Url type="application/atom+xml" template="{$serverurl}feed-atom?searchterm={searchTerms}"/> <Url type="application/atom+xml" template="{$serverurl}feed/atom?searchterm={searchTerms}"/>
<Url type="application/rss+xml" template="{$serverurl}feed-rss?searchterm={searchTerms}"/> <Url type="application/rss+xml" template="{$serverurl}feed/rss?searchterm={searchTerms}"/>
<InputEncoding>UTF-8</InputEncoding> <InputEncoding>UTF-8</InputEncoding>
<Developer>Shaarli Community - https://github.com/shaarli/Shaarli/</Developer> <Developer>Shaarli Community - https://github.com/shaarli/Shaarli/</Developer>
<Image width="16" height="16">data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAHRklE <Image width="16" height="16">data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAHRklE

View file

@ -21,16 +21,16 @@
</li> </li>
{if="$is_logged_in || $openshaarli"} {if="$is_logged_in || $openshaarli"}
<li class="pure-menu-item"> <li class="pure-menu-item">
<a href="{$base_path}/add-shaare" class="pure-menu-link" id="shaarli-menu-shaare"> <a href="{$base_path}/admin/add-shaare" class="pure-menu-link" id="shaarli-menu-shaare">
<i class="fa fa-plus" aria-hidden="true"></i> {'Shaare'|t} <i class="fa fa-plus" aria-hidden="true"></i> {'Shaare'|t}
</a> </a>
</li> </li>
<li class="pure-menu-item" id="shaarli-menu-tools"> <li class="pure-menu-item" id="shaarli-menu-tools">
<a href="{$base_path}/tools" class="pure-menu-link">{'Tools'|t}</a> <a href="{$base_path}/admin/tools" class="pure-menu-link">{'Tools'|t}</a>
</li> </li>
{/if} {/if}
<li class="pure-menu-item" id="shaarli-menu-tags"> <li class="pure-menu-item" id="shaarli-menu-tags">
<a href="{$base_path}/tag-cloud" class="pure-menu-link">{'Tag cloud'|t}</a> <a href="{$base_path}/tags/cloud" class="pure-menu-link">{'Tag cloud'|t}</a>
</li> </li>
{if="$thumbnails_enabled"} {if="$thumbnails_enabled"}
<li class="pure-menu-item" id="shaarli-menu-picwall"> <li class="pure-menu-item" id="shaarli-menu-picwall">
@ -52,7 +52,7 @@
</li> </li>
{/loop} {/loop}
<li class="pure-menu-item pure-u-lg-0 shaarli-menu-mobile" id="shaarli-menu-mobile-rss"> <li class="pure-menu-item pure-u-lg-0 shaarli-menu-mobile" id="shaarli-menu-mobile-rss">
<a href="{$base_path}/feed-{$feed_type}?{$searchcrits}" class="pure-menu-link">{'RSS Feed'|t}</a> <a href="{$base_path}/feed/{$feed_type}?{$searchcrits}" class="pure-menu-link">{'RSS Feed'|t}</a>
</li> </li>
{if="$is_logged_in"} {if="$is_logged_in"}
<li class="pure-menu-item pure-u-lg-0 shaarli-menu-mobile" id="shaarli-menu-mobile-logout"> <li class="pure-menu-item pure-u-lg-0 shaarli-menu-mobile" id="shaarli-menu-mobile-logout">
@ -74,7 +74,7 @@
</a> </a>
</li> </li>
<li class="pure-menu-item" id="shaarli-menu-desktop-rss"> <li class="pure-menu-item" id="shaarli-menu-desktop-rss">
<a href="{$base_path}/feed-{$feed_type}?{$searchcrits}" class="pure-menu-link" title="{'RSS Feed'|t}" aria-label="{'RSS Feed'|t}"> <a href="{$base_path}/feed/{$feed_type}?{$searchcrits}" class="pure-menu-link" title="{'RSS Feed'|t}" aria-label="{'RSS Feed'|t}">
<i class="fa fa-rss" aria-hidden="true"></i> <i class="fa fa-rss" aria-hidden="true"></i>
</a> </a>
</li> </li>

View file

@ -51,7 +51,7 @@ <h2 class="window-title">{'Tag list'|t} - {$countTags} {'tags'|t}</h2>
<div class="pure-u-1"> <div class="pure-u-1">
{if="$is_logged_in===true"} {if="$is_logged_in===true"}
<a href="#" class="delete-tag" aria-label="{'Delete'|t}"><i class="fa fa-trash" aria-hidden="true"></i></a>&nbsp;&nbsp; <a href="#" class="delete-tag" aria-label="{'Delete'|t}"><i class="fa fa-trash" aria-hidden="true"></i></a>&nbsp;&nbsp;
<a href="{$base_path}/manage-tags?fromtag={$key|urlencode}" class="rename-tag" aria-label="{'Rename tag'|t}"> <a href="{$base_path}/admin/tags?fromtag={$key|urlencode}" class="rename-tag" aria-label="{'Rename tag'|t}">
<i class="fa fa-pencil-square-o {$key}" aria-hidden="true"></i> <i class="fa fa-pencil-square-o {$key}" aria-hidden="true"></i>
</a> </a>
{/if} {/if}

View file

@ -1,8 +1,8 @@
<div class="pure-g"> <div class="pure-g">
<div class="pure-u-1 pure-alert pure-alert-success tag-sort"> <div class="pure-u-1 pure-alert pure-alert-success tag-sort">
{'Sort by:'|t} {'Sort by:'|t}
<a href="{$base_path}/tag-cloud">{'Cloud'|t}</a> &middot; <a href="{$base_path}/tags/cloud">{'Cloud'|t}</a> &middot;
<a href="{$base_path}/tag-list?sort=usage">{'Most used'|t}</a> &middot; <a href="{$base_path}/tags/list?sort=usage">{'Most used'|t}</a> &middot;
<a href="{$base_path}/tag-list?sort=alpha">{'Alphabetical'|t}</a> <a href="{$base_path}/tags/list?sort=alpha">{'Alphabetical'|t}</a>
</div> </div>
</div> </div>

View file

@ -11,7 +11,7 @@
<div class="pure-u-lg-1-3 pure-u-22-24 page-form page-form-light"> <div class="pure-u-lg-1-3 pure-u-22-24 page-form page-form-light">
<h2 class="window-title">{'Settings'|t}</h2> <h2 class="window-title">{'Settings'|t}</h2>
<div class="tools-item"> <div class="tools-item">
<a href="{$base_path}/configure" title="{'Change Shaarli settings: title, timezone, etc.'|t}"> <a href="{$base_path}/admin/configure" title="{'Change Shaarli settings: title, timezone, etc.'|t}">
<span class="pure-button pure-u-lg-2-3 pure-u-3-4">{'Configure your Shaarli'|t}</span> <span class="pure-button pure-u-lg-2-3 pure-u-3-4">{'Configure your Shaarli'|t}</span>
</a> </a>
</div> </div>
@ -22,13 +22,13 @@ <h2 class="window-title">{'Settings'|t}</h2>
</div> </div>
{if="!$openshaarli"} {if="!$openshaarli"}
<div class="tools-item"> <div class="tools-item">
<a href="{$base_path}/?do=changepasswd" title="{'Change your password'|t}"> <a href="{$base_path}/admin/password" title="{'Change your password'|t}">
<span class="pure-button pure-u-lg-2-3 pure-u-3-4">{'Change password'|t}</span> <span class="pure-button pure-u-lg-2-3 pure-u-3-4">{'Change password'|t}</span>
</a> </a>
</div> </div>
{/if} {/if}
<div class="tools-item"> <div class="tools-item">
<a href="{$base_path}/manage-tags" title="{'Rename or delete a tag in all links'|t}"> <a href="{$base_path}/admin/tags" title="{'Rename or delete a tag in all links'|t}">
<span class="pure-button pure-u-lg-2-3 pure-u-3-4">{'Manage tags'|t}</span> <span class="pure-button pure-u-lg-2-3 pure-u-3-4">{'Manage tags'|t}</span>
</a> </a>
</div> </div>

View file

@ -5,7 +5,7 @@
<div id="pageheader"> <div id="pageheader">
{include="page.header"} {include="page.header"}
<div id="headerform"> <div id="headerform">
<form method="GET" action="{$base_path}/shaare" name="addform" class="addform"> <form method="GET" action="{$base_path}/admin/shaare" name="addform" class="addform">
<input type="text" name="post" class="linkurl"> <input type="text" name="post" class="linkurl">
<input type="submit" value="Add link" class="bigbutton"> <input type="submit" value="Add link" class="bigbutton">
</form> </form>

View file

@ -48,7 +48,7 @@
{/if} {/if}
<input type="submit" value="Save" name="save_edit" class="bigbutton"> <input type="submit" value="Save" name="save_edit" class="bigbutton">
{if="!$link_is_new && isset($link.id)"} {if="!$link_is_new && isset($link.id)"}
<a href="{$base_path}/?delete_link&amp;lf_linkdate={$link.id}&amp;token={$token}" <a href="{$base_path}/admin/shaare/delete?id={$link.id}&amp;token={$token}"
name="delete_link" class="bigbutton" name="delete_link" class="bigbutton"
onClick="return confirmDeleteLink();"> onClick="return confirmDeleteLink();">
{'Delete'|t} {'Delete'|t}

View file

@ -3,8 +3,8 @@
<meta name="format-detection" content="telephone=no" /> <meta name="format-detection" content="telephone=no" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" /> <meta name="viewport" content="width=device-width,initial-scale=1.0" />
<meta name="referrer" content="same-origin"> <meta name="referrer" content="same-origin">
<link rel="alternate" type="application/rss+xml" href="{$feedurl}feed-rss?{$searchcrits}#" title="RSS Feed" /> <link rel="alternate" type="application/rss+xml" href="{$feedurl}feed/rss?{$searchcrits}#" title="RSS Feed" />
<link rel="alternate" type="application/atom+xml" href="{$feedurl}feed-atom?{$searchcrits}#" title="ATOM Feed" /> <link rel="alternate" type="application/atom+xml" href="{$feedurl}feed/atom?{$searchcrits}#" title="ATOM Feed" />
<link href="img/favicon.ico" rel="shortcut icon" type="image/x-icon" /> <link href="img/favicon.ico" rel="shortcut icon" type="image/x-icon" />
<link type="text/css" rel="stylesheet" href="css/shaarli.min.css" /> <link type="text/css" rel="stylesheet" href="css/shaarli.min.css" />
{if="$formatter==='markdown'"} {if="$formatter==='markdown'"}

View file

@ -3,8 +3,8 @@
<ShortName>Shaarli search - {$pagetitle}</ShortName> <ShortName>Shaarli search - {$pagetitle}</ShortName>
<Description>Shaarli search - {$pagetitle}</Description> <Description>Shaarli search - {$pagetitle}</Description>
<Url type="text/html" template="{$serverurl}?searchterm={searchTerms}" /> <Url type="text/html" template="{$serverurl}?searchterm={searchTerms}" />
<Url type="application/atom+xml" template="{$serverurl}feed-atom?searchterm={searchTerms}"/> <Url type="application/atom+xml" template="{$serverurl}feed/atom?searchterm={searchTerms}"/>
<Url type="application/rss+xml" template="{$serverurl}feed-rss?searchterm={searchTerms}"/> <Url type="application/rss+xml" template="{$serverurl}feed/rss?searchterm={searchTerms}"/>
<InputEncoding>UTF-8</InputEncoding> <InputEncoding>UTF-8</InputEncoding>
<Developer>Shaarli Community - https://github.com/shaarli/Shaarli/</Developer> <Developer>Shaarli Community - https://github.com/shaarli/Shaarli/</Developer>
<Image width="16" height="16">data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAHRklE <Image width="16" height="16">data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAHRklE

View file

@ -19,19 +19,19 @@
<li><a href="{$titleLink}" class="nomobile">Home</a></li> <li><a href="{$titleLink}" class="nomobile">Home</a></li>
{if="$is_logged_in"} {if="$is_logged_in"}
<li><a href="{$base_path}/logout">Logout</a></li> <li><a href="{$base_path}/logout">Logout</a></li>
<li><a href="{$base_path}/tools">Tools</a></li> <li><a href="{$base_path}/admin/tools">Tools</a></li>
<li><a href="{$base_path}/add-shaare">Add link</a></li> <li><a href="{$base_path}/admin/add-shaare">Add link</a></li>
{elseif="$openshaarli"} {elseif="$openshaarli"}
<li><a href="{$base_path}/tools">Tools</a></li> <li><a href="{$base_path}/admin/tools">Tools</a></li>
<li><a href="{$base_path}/add-shaare">Add link</a></li> <li><a href="{$base_path}/admin/add-shaare">Add link</a></li>
{else} {else}
<li><a href="{$base_path}/login">Login</a></li> <li><a href="{$base_path}/login">Login</a></li>
{/if} {/if}
<li><a href="{$feedurl}/feed-rss?{$searchcrits}" class="nomobile">RSS Feed</a></li> <li><a href="{$feedurl}/feed/rss?{$searchcrits}" class="nomobile">RSS Feed</a></li>
{if="$showatom"} {if="$showatom"}
<li><a href="{$feedurl}/feed-atom?{$searchcrits}" class="nomobile">ATOM Feed</a></li> <li><a href="{$feedurl}/feed/atom?{$searchcrits}" class="nomobile">ATOM Feed</a></li>
{/if} {/if}
<li><a href="{$base_path}/tag-cloud">Tag cloud</a></li> <li><a href="{$base_path}/tags/cloud">Tag cloud</a></li>
<li><a href="{$base_path}/picture-wall{function="ltrim($searchcrits, '&')"}">Picture wall</a></li> <li><a href="{$base_path}/picture-wall{function="ltrim($searchcrits, '&')"}">Picture wall</a></li>
<li><a href="{$base_path}/daily">Daily</a></li> <li><a href="{$base_path}/daily">Daily</a></li>
{loop="$plugins_header.buttons_toolbar"} {loop="$plugins_header.buttons_toolbar"}

View file

@ -5,13 +5,13 @@
<div id="pageheader"> <div id="pageheader">
{include="page.header"} {include="page.header"}
<div id="toolsdiv"> <div id="toolsdiv">
<a href="{$base_path}/configure"><b>Configure your Shaarli</b><span>: Change Title, timezone...</span></a> <a href="{$base_path}/admin/configure"><b>Configure your Shaarli</b><span>: Change Title, timezone...</span></a>
<br><br> <br><br>
<a href="{$base_path}/?do=pluginadmin"><b>Plugin administration</b><span>: Enable, disable and configure plugins.</span></a> <a href="{$base_path}/?do=pluginadmin"><b>Plugin administration</b><span>: Enable, disable and configure plugins.</span></a>
<br><br> <br><br>
{if="!$openshaarli"}<a href="{$base_path}/?do=changepasswd"><b>Change password</b><span>: Change your password.</span></a> {if="!$openshaarli"}<a href="{$base_path}/admin/password"><b>Change password</b><span>: Change your password.</span></a>
<br><br>{/if} <br><br>{/if}
<a href="{$base_path}/manage-tags"><b>Rename/delete tags</b><span>: Rename or delete a tag in all links</span></a> <a href="{$base_path}/admin/tags"><b>Rename/delete tags</b><span>: Rename or delete a tag in all links</span></a>
<br><br> <br><br>
<a href="{$base_path}/?do=import"><b>Import</b><span>: Import Netscape html bookmarks (as exported from Firefox, Chrome, Opera, delicious...)</span></a> <a href="{$base_path}/?do=import"><b>Import</b><span>: Import Netscape html bookmarks (as exported from Firefox, Chrome, Opera, delicious...)</span></a>
<br><br> <br><br>