aboutsummaryrefslogtreecommitdiffhomepage
path: root/index.php
diff options
context:
space:
mode:
authorArthurHoaro <arthur@hoa.ro>2020-01-18 10:01:06 +0100
committerGitHub <noreply@github.com>2020-01-18 10:01:06 +0100
commit3fb29fdda04ca86e04422d49b86cf646d53c4f9d (patch)
treeadf8512f93f5559ba87d0c9931969ae4ebea7133 /index.php
parent796c4c57d085ae4589b53dfe8369ae9ba30ffdaf (diff)
parente26e2060f5470ce8bf4c5973284bae07b8af170a (diff)
downloadShaarli-3fb29fdda04ca86e04422d49b86cf646d53c4f9d.tar.gz
Shaarli-3fb29fdda04ca86e04422d49b86cf646d53c4f9d.tar.zst
Shaarli-3fb29fdda04ca86e04422d49b86cf646d53c4f9d.zip
Store bookmarks as PHP objects and add a service layer to retriā€¦ (#1307)
Store bookmarks as PHP objects and add a service layer to retrieve them
Diffstat (limited to 'index.php')
-rw-r--r--index.php555
1 files changed, 278 insertions, 277 deletions
diff --git a/index.php b/index.php
index 9783539a..2dd003f0 100644
--- a/index.php
+++ b/index.php
@@ -35,9 +35,6 @@ ini_set('upload_max_filesize', '16M');
35 35
36// See all error except warnings 36// See all error except warnings
37error_reporting(E_ALL^E_WARNING); 37error_reporting(E_ALL^E_WARNING);
38// See all errors (for debugging only)
39//error_reporting(-1);
40
41 38
42// 3rd-party libraries 39// 3rd-party libraries
43if (! file_exists(__DIR__ . '/vendor/autoload.php')) { 40if (! file_exists(__DIR__ . '/vendor/autoload.php')) {
@@ -65,11 +62,15 @@ require_once 'application/TimeZone.php';
65require_once 'application/Utils.php'; 62require_once 'application/Utils.php';
66 63
67use \Shaarli\ApplicationUtils; 64use \Shaarli\ApplicationUtils;
68use \Shaarli\Bookmark\Exception\LinkNotFoundException; 65use Shaarli\Bookmark\BookmarkServiceInterface;
69use \Shaarli\Bookmark\LinkDB; 66use \Shaarli\Bookmark\Exception\BookmarkNotFoundException;
67use Shaarli\Bookmark\Bookmark;
68use Shaarli\Bookmark\BookmarkFilter;
69use Shaarli\Bookmark\BookmarkFileService;
70use \Shaarli\Config\ConfigManager; 70use \Shaarli\Config\ConfigManager;
71use \Shaarli\Feed\CachedPage; 71use \Shaarli\Feed\CachedPage;
72use \Shaarli\Feed\FeedBuilder; 72use \Shaarli\Feed\FeedBuilder;
73use Shaarli\Formatter\FormatterFactory;
73use \Shaarli\History; 74use \Shaarli\History;
74use \Shaarli\Languages; 75use \Shaarli\Languages;
75use \Shaarli\Netscape\NetscapeBookmarkUtils; 76use \Shaarli\Netscape\NetscapeBookmarkUtils;
@@ -81,6 +82,7 @@ use \Shaarli\Security\LoginManager;
81use \Shaarli\Security\SessionManager; 82use \Shaarli\Security\SessionManager;
82use \Shaarli\Thumbnailer; 83use \Shaarli\Thumbnailer;
83use \Shaarli\Updater\Updater; 84use \Shaarli\Updater\Updater;
85use \Shaarli\Updater\UpdaterUtils;
84 86
85// Ensure the PHP version is supported 87// Ensure the PHP version is supported
86try { 88try {
@@ -122,6 +124,17 @@ if (isset($_COOKIE['shaarli']) && !SessionManager::checkId($_COOKIE['shaarli']))
122} 124}
123 125
124$conf = new ConfigManager(); 126$conf = new ConfigManager();
127
128// In dev mode, throw exception on any warning
129if ($conf->get('dev.debug', false)) {
130 // See all errors (for debugging only)
131 error_reporting(-1);
132
133 set_error_handler(function($errno, $errstr, $errfile, $errline, array $errcontext) {
134 throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
135 });
136}
137
125$sessionManager = new SessionManager($_SESSION, $conf); 138$sessionManager = new SessionManager($_SESSION, $conf);
126$loginManager = new LoginManager($conf, $sessionManager); 139$loginManager = new LoginManager($conf, $sessionManager);
127$loginManager->generateStaySignedInToken($_SERVER['REMOTE_ADDR']); 140$loginManager->generateStaySignedInToken($_SERVER['REMOTE_ADDR']);
@@ -140,7 +153,7 @@ if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
140new Languages(setlocale(LC_MESSAGES, 0), $conf); 153new Languages(setlocale(LC_MESSAGES, 0), $conf);
141 154
142$conf->setEmpty('general.timezone', date_default_timezone_get()); 155$conf->setEmpty('general.timezone', date_default_timezone_get());
143$conf->setEmpty('general.title', t('Shared links on '). escape(index_url($_SERVER))); 156$conf->setEmpty('general.title', t('Shared bookmarks on '). escape(index_url($_SERVER)));
144RainTPL::$tpl_dir = $conf->get('resource.raintpl_tpl').'/'.$conf->get('resource.theme').'/'; // template directory 157RainTPL::$tpl_dir = $conf->get('resource.raintpl_tpl').'/'.$conf->get('resource.theme').'/'; // template directory
145RainTPL::$cache_dir = $conf->get('resource.raintpl_tmp'); // cache directory 158RainTPL::$cache_dir = $conf->get('resource.raintpl_tmp'); // cache directory
146 159
@@ -283,14 +296,15 @@ if (!isset($_SESSION['tokens'])) {
283} 296}
284 297
285/** 298/**
286 * Daily RSS feed: 1 RSS entry per day giving all the links on that day. 299 * Daily RSS feed: 1 RSS entry per day giving all the bookmarks on that day.
287 * Gives the last 7 days (which have links). 300 * Gives the last 7 days (which have bookmarks).
288 * This RSS feed cannot be filtered. 301 * This RSS feed cannot be filtered.
289 * 302 *
290 * @param ConfigManager $conf Configuration Manager instance 303 * @param BookmarkServiceInterface $bookmarkService
291 * @param LoginManager $loginManager LoginManager instance 304 * @param ConfigManager $conf Configuration Manager instance
305 * @param LoginManager $loginManager LoginManager instance
292 */ 306 */
293function showDailyRSS($conf, $loginManager) 307function showDailyRSS($bookmarkService, $conf, $loginManager)
294{ 308{
295 // Cache system 309 // Cache system
296 $query = $_SERVER['QUERY_STRING']; 310 $query = $_SERVER['QUERY_STRING'];
@@ -305,28 +319,20 @@ function showDailyRSS($conf, $loginManager)
305 exit; 319 exit;
306 } 320 }
307 321
308 // If cached was not found (or not usable), then read the database and build the response: 322 /* Some Shaarlies may have very few bookmarks, so we need to look
309 // Read links from database (and filter private links if used it not logged in).
310 $LINKSDB = new LinkDB(
311 $conf->get('resource.datastore'),
312 $loginManager->isLoggedIn(),
313 $conf->get('privacy.hide_public_links')
314 );
315
316 /* Some Shaarlies may have very few links, so we need to look
317 back in time until we have enough days ($nb_of_days). 323 back in time until we have enough days ($nb_of_days).
318 */ 324 */
319 $nb_of_days = 7; // We take 7 days. 325 $nb_of_days = 7; // We take 7 days.
320 $today = date('Ymd'); 326 $today = date('Ymd');
321 $days = array(); 327 $days = array();
322 328
323 foreach ($LINKSDB as $link) { 329 foreach ($bookmarkService->search() as $bookmark) {
324 $day = $link['created']->format('Ymd'); // Extract day (without time) 330 $day = $bookmark->getCreated()->format('Ymd'); // Extract day (without time)
325 if (strcmp($day, $today) < 0) { 331 if (strcmp($day, $today) < 0) {
326 if (empty($days[$day])) { 332 if (empty($days[$day])) {
327 $days[$day] = array(); 333 $days[$day] = array();
328 } 334 }
329 $days[$day][] = $link; 335 $days[$day][] = $bookmark;
330 } 336 }
331 337
332 if (count($days) > $nb_of_days) { 338 if (count($days) > $nb_of_days) {
@@ -341,30 +347,38 @@ function showDailyRSS($conf, $loginManager)
341 echo '<channel>'; 347 echo '<channel>';
342 echo '<title>Daily - '. $conf->get('general.title') . '</title>'; 348 echo '<title>Daily - '. $conf->get('general.title') . '</title>';
343 echo '<link>'. $pageaddr .'</link>'; 349 echo '<link>'. $pageaddr .'</link>';
344 echo '<description>Daily shared links</description>'; 350 echo '<description>Daily shared bookmarks</description>';
345 echo '<language>en-en</language>'; 351 echo '<language>en-en</language>';
346 echo '<copyright>'. $pageaddr .'</copyright>'. PHP_EOL; 352 echo '<copyright>'. $pageaddr .'</copyright>'. PHP_EOL;
347 353
354 $factory = new FormatterFactory($conf);
355 $formatter = $factory->getFormatter();
356 $formatter->addContextData('index_url', index_url($_SERVER));
348 // For each day. 357 // For each day.
349 foreach ($days as $day => $links) { 358 /** @var Bookmark[] $bookmarks */
350 $dayDate = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $day.'_000000'); 359 foreach ($days as $day => $bookmarks) {
360 $formattedBookmarks = [];
361 $dayDate = DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, $day.'_000000');
351 $absurl = escape(index_url($_SERVER).'?do=daily&day='.$day); // Absolute URL of the corresponding "Daily" page. 362 $absurl = escape(index_url($_SERVER).'?do=daily&day='.$day); // Absolute URL of the corresponding "Daily" page.
352 363
353 // We pre-format some fields for proper output. 364 // We pre-format some fields for proper output.
354 foreach ($links as &$link) { 365 foreach ($bookmarks as $key => $bookmark) {
355 $link['formatedDescription'] = format_description($link['description']); 366 $formattedBookmarks[$key] = $formatter->format($bookmark);
356 $link['timestamp'] = $link['created']->getTimestamp(); 367 // This page is a bit specific, we need raw description to calculate the length
357 if (is_note($link['url'])) { 368 $formattedBookmarks[$key]['formatedDescription'] = $formattedBookmarks[$key]['description'];
358 $link['url'] = index_url($_SERVER) . $link['url']; // make permalink URL absolute 369 $formattedBookmarks[$key]['description'] = $bookmark->getDescription();
370
371 if ($bookmark->isNote()) {
372 $link['url'] = index_url($_SERVER) . $bookmark->getUrl(); // make permalink URL absolute
359 } 373 }
360 } 374 }
361 375
362 // Then build the HTML for this day: 376 // Then build the HTML for this day:
363 $tpl = new RainTPL; 377 $tpl = new RainTPL();
364 $tpl->assign('title', $conf->get('general.title')); 378 $tpl->assign('title', $conf->get('general.title'));
365 $tpl->assign('daydate', $dayDate->getTimestamp()); 379 $tpl->assign('daydate', $dayDate->getTimestamp());
366 $tpl->assign('absurl', $absurl); 380 $tpl->assign('absurl', $absurl);
367 $tpl->assign('links', $links); 381 $tpl->assign('links', $formattedBookmarks);
368 $tpl->assign('rssdate', escape($dayDate->format(DateTime::RSS))); 382 $tpl->assign('rssdate', escape($dayDate->format(DateTime::RSS)));
369 $tpl->assign('hide_timestamps', $conf->get('privacy.hide_timestamps', false)); 383 $tpl->assign('hide_timestamps', $conf->get('privacy.hide_timestamps', false));
370 $tpl->assign('index_url', $pageaddr); 384 $tpl->assign('index_url', $pageaddr);
@@ -382,13 +396,13 @@ function showDailyRSS($conf, $loginManager)
382/** 396/**
383 * Show the 'Daily' page. 397 * Show the 'Daily' page.
384 * 398 *
385 * @param PageBuilder $pageBuilder Template engine wrapper. 399 * @param PageBuilder $pageBuilder Template engine wrapper.
386 * @param LinkDB $LINKSDB LinkDB instance. 400 * @param BookmarkServiceInterface $bookmarkService instance.
387 * @param ConfigManager $conf Configuration Manager instance. 401 * @param ConfigManager $conf Configuration Manager instance.
388 * @param PluginManager $pluginManager Plugin Manager instance. 402 * @param PluginManager $pluginManager Plugin Manager instance.
389 * @param LoginManager $loginManager Login Manager instance 403 * @param LoginManager $loginManager Login Manager instance
390 */ 404 */
391function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager, $loginManager) 405function showDaily($pageBuilder, $bookmarkService, $conf, $pluginManager, $loginManager)
392{ 406{
393 if (isset($_GET['day'])) { 407 if (isset($_GET['day'])) {
394 $day = $_GET['day']; 408 $day = $_GET['day'];
@@ -402,10 +416,10 @@ function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager, $loginManager)
402 $pageBuilder->assign('dayDesc', t('Today')); 416 $pageBuilder->assign('dayDesc', t('Today'));
403 } 417 }
404 418
405 $days = $LINKSDB->days(); 419 $days = $bookmarkService->days();
406 $i = array_search($day, $days); 420 $i = array_search($day, $days);
407 if ($i === false && count($days)) { 421 if ($i === false && count($days)) {
408 // no links for day, but at least one day with links 422 // no bookmarks for day, but at least one day with bookmarks
409 $i = count($days) - 1; 423 $i = count($days) - 1;
410 $day = $days[$i]; 424 $day = $days[$i];
411 } 425 }
@@ -414,29 +428,30 @@ function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager, $loginManager)
414 428
415 if ($i !== false) { 429 if ($i !== false) {
416 if ($i >= 1) { 430 if ($i >= 1) {
417 $previousday=$days[$i - 1]; 431 $previousday = $days[$i - 1];
418 } 432 }
419 if ($i < count($days) - 1) { 433 if ($i < count($days) - 1) {
420 $nextday = $days[$i + 1]; 434 $nextday = $days[$i + 1];
421 } 435 }
422 } 436 }
423 try { 437 try {
424 $linksToDisplay = $LINKSDB->filterDay($day); 438 $linksToDisplay = $bookmarkService->filterDay($day);
425 } catch (Exception $exc) { 439 } catch (Exception $exc) {
426 error_log($exc); 440 error_log($exc);
427 $linksToDisplay = array(); 441 $linksToDisplay = [];
428 } 442 }
429 443
444 $factory = new FormatterFactory($conf);
445 $formatter = $factory->getFormatter();
430 // We pre-format some fields for proper output. 446 // We pre-format some fields for proper output.
431 foreach ($linksToDisplay as $key => $link) { 447 foreach ($linksToDisplay as $key => $bookmark) {
432 $taglist = explode(' ', $link['tags']); 448 $linksToDisplay[$key] = $formatter->format($bookmark);
433 uasort($taglist, 'strcasecmp'); 449 // This page is a bit specific, we need raw description to calculate the length
434 $linksToDisplay[$key]['taglist']=$taglist; 450 $linksToDisplay[$key]['formatedDescription'] = $linksToDisplay[$key]['description'];
435 $linksToDisplay[$key]['formatedDescription'] = format_description($link['description']); 451 $linksToDisplay[$key]['description'] = $bookmark->getDescription();
436 $linksToDisplay[$key]['timestamp'] = $link['created']->getTimestamp();
437 } 452 }
438 453
439 $dayDate = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $day.'_000000'); 454 $dayDate = DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, $day.'_000000');
440 $data = array( 455 $data = array(
441 'pagetitle' => $conf->get('general.title') .' - '. format_date($dayDate, false), 456 'pagetitle' => $conf->get('general.title') .' - '. format_date($dayDate, false),
442 'linksToDisplay' => $linksToDisplay, 457 'linksToDisplay' => $linksToDisplay,
@@ -457,19 +472,19 @@ function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager, $loginManager)
457 */ 472 */
458 $columns = array(array(), array(), array()); // Entries to display, for each column. 473 $columns = array(array(), array(), array()); // Entries to display, for each column.
459 $fill = array(0, 0, 0); // Rough estimate of columns fill. 474 $fill = array(0, 0, 0); // Rough estimate of columns fill.
460 foreach ($data['linksToDisplay'] as $key => $link) { 475 foreach ($data['linksToDisplay'] as $key => $bookmark) {
461 // Roughly estimate length of entry (by counting characters) 476 // Roughly estimate length of entry (by counting characters)
462 // Title: 30 chars = 1 line. 1 line is 30 pixels height. 477 // Title: 30 chars = 1 line. 1 line is 30 pixels height.
463 // Description: 836 characters gives roughly 342 pixel height. 478 // Description: 836 characters gives roughly 342 pixel height.
464 // This is not perfect, but it's usually OK. 479 // This is not perfect, but it's usually OK.
465 $length = strlen($link['title']) + (342 * strlen($link['description'])) / 836; 480 $length = strlen($bookmark['title']) + (342 * strlen($bookmark['description'])) / 836;
466 if (! empty($link['thumbnail'])) { 481 if (! empty($bookmark['thumbnail'])) {
467 $length += 100; // 1 thumbnails roughly takes 100 pixels height. 482 $length += 100; // 1 thumbnails roughly takes 100 pixels height.
468 } 483 }
469 // Then put in column which is the less filled: 484 // Then put in column which is the less filled:
470 $smallest = min($fill); // find smallest value in array. 485 $smallest = min($fill); // find smallest value in array.
471 $index = array_search($smallest, $fill); // find index of this smallest value. 486 $index = array_search($smallest, $fill); // find index of this smallest value.
472 array_push($columns[$index], $link); // Put entry in this column. 487 array_push($columns[$index], $bookmark); // Put entry in this column.
473 $fill[$index] += $length; 488 $fill[$index] += $length;
474 } 489 }
475 490
@@ -487,40 +502,39 @@ function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager, $loginManager)
487/** 502/**
488 * Renders the linklist 503 * Renders the linklist
489 * 504 *
490 * @param pageBuilder $PAGE pageBuilder instance. 505 * @param pageBuilder $PAGE pageBuilder instance.
491 * @param LinkDB $LINKSDB LinkDB instance. 506 * @param BookmarkServiceInterface $linkDb instance.
492 * @param ConfigManager $conf Configuration Manager instance. 507 * @param ConfigManager $conf Configuration Manager instance.
493 * @param PluginManager $pluginManager Plugin Manager instance. 508 * @param PluginManager $pluginManager Plugin Manager instance.
494 */ 509 */
495function showLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager) 510function showLinkList($PAGE, $linkDb, $conf, $pluginManager, $loginManager)
496{ 511{
497 buildLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager); 512 buildLinkList($PAGE, $linkDb, $conf, $pluginManager, $loginManager);
498 $PAGE->renderPage('linklist'); 513 $PAGE->renderPage('linklist');
499} 514}
500 515
501/** 516/**
502 * Render HTML page (according to URL parameters and user rights) 517 * Render HTML page (according to URL parameters and user rights)
503 * 518 *
504 * @param ConfigManager $conf Configuration Manager instance. 519 * @param ConfigManager $conf Configuration Manager instance.
505 * @param PluginManager $pluginManager Plugin Manager instance, 520 * @param PluginManager $pluginManager Plugin Manager instance,
506 * @param LinkDB $LINKSDB 521 * @param BookmarkServiceInterface $bookmarkService
507 * @param History $history instance 522 * @param History $history instance
508 * @param SessionManager $sessionManager SessionManager instance 523 * @param SessionManager $sessionManager SessionManager instance
509 * @param LoginManager $loginManager LoginManager instance 524 * @param LoginManager $loginManager LoginManager instance
510 */ 525 */
511function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager, $loginManager) 526function renderPage($conf, $pluginManager, $bookmarkService, $history, $sessionManager, $loginManager)
512{ 527{
513 $updater = new Updater( 528 $updater = new Updater(
514 read_updates_file($conf->get('resource.updates')), 529 UpdaterUtils::read_updates_file($conf->get('resource.updates')),
515 $LINKSDB, 530 $bookmarkService,
516 $conf, 531 $conf,
517 $loginManager->isLoggedIn(), 532 $loginManager->isLoggedIn()
518 $_SESSION
519 ); 533 );
520 try { 534 try {
521 $newUpdates = $updater->update(); 535 $newUpdates = $updater->update();
522 if (! empty($newUpdates)) { 536 if (! empty($newUpdates)) {
523 write_updates_file( 537 UpdaterUtils::write_updates_file(
524 $conf->get('resource.updates'), 538 $conf->get('resource.updates'),
525 $updater->getDoneUpdates() 539 $updater->getDoneUpdates()
526 ); 540 );
@@ -529,9 +543,9 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
529 die($e->getMessage()); 543 die($e->getMessage());
530 } 544 }
531 545
532 $PAGE = new PageBuilder($conf, $_SESSION, $LINKSDB, $sessionManager->generateToken(), $loginManager->isLoggedIn()); 546 $PAGE = new PageBuilder($conf, $_SESSION, $bookmarkService, $sessionManager->generateToken(), $loginManager->isLoggedIn());
533 $PAGE->assign('linkcount', count($LINKSDB)); 547 $PAGE->assign('linkcount', $bookmarkService->count(BookmarkFilter::$ALL));
534 $PAGE->assign('privateLinkcount', count_private($LINKSDB)); 548 $PAGE->assign('privateLinkcount', $bookmarkService->count(BookmarkFilter::$PRIVATE));
535 $PAGE->assign('plugin_errors', $pluginManager->getErrors()); 549 $PAGE->assign('plugin_errors', $pluginManager->getErrors());
536 550
537 // Determine which page will be rendered. 551 // Determine which page will be rendered.
@@ -611,27 +625,28 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
611 } 625 }
612 626
613 // Optionally filter the results: 627 // Optionally filter the results:
614 $links = $LINKSDB->filterSearch($_GET); 628 $links = $bookmarkService->search($_GET);
615 $linksToDisplay = array(); 629 $linksToDisplay = [];
616 630
617 // Get only links which have a thumbnail. 631 // Get only bookmarks which have a thumbnail.
618 // Note: we do not retrieve thumbnails here, the request is too heavy. 632 // Note: we do not retrieve thumbnails here, the request is too heavy.
633 $factory = new FormatterFactory($conf);
634 $formatter = $factory->getFormatter();
619 foreach ($links as $key => $link) { 635 foreach ($links as $key => $link) {
620 if (isset($link['thumbnail']) && $link['thumbnail'] !== false) { 636 if ($link->getThumbnail() !== false) {
621 $linksToDisplay[] = $link; // Add to array. 637 $linksToDisplay[] = $formatter->format($link);
622 } 638 }
623 } 639 }
624 640
625 $data = array( 641 $data = [
626 'linksToDisplay' => $linksToDisplay, 642 'linksToDisplay' => $linksToDisplay,
627 ); 643 ];
628 $pluginManager->executeHooks('render_picwall', $data, array('loggedin' => $loginManager->isLoggedIn())); 644 $pluginManager->executeHooks('render_picwall', $data, ['loggedin' => $loginManager->isLoggedIn()]);
629 645
630 foreach ($data as $key => $value) { 646 foreach ($data as $key => $value) {
631 $PAGE->assign($key, $value); 647 $PAGE->assign($key, $value);
632 } 648 }
633 649
634
635 $PAGE->renderPage('picwall'); 650 $PAGE->renderPage('picwall');
636 exit; 651 exit;
637 } 652 }
@@ -640,7 +655,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
640 if ($targetPage == Router::$PAGE_TAGCLOUD) { 655 if ($targetPage == Router::$PAGE_TAGCLOUD) {
641 $visibility = ! empty($_SESSION['visibility']) ? $_SESSION['visibility'] : ''; 656 $visibility = ! empty($_SESSION['visibility']) ? $_SESSION['visibility'] : '';
642 $filteringTags = isset($_GET['searchtags']) ? explode(' ', $_GET['searchtags']) : []; 657 $filteringTags = isset($_GET['searchtags']) ? explode(' ', $_GET['searchtags']) : [];
643 $tags = $LINKSDB->linksCountPerTag($filteringTags, $visibility); 658 $tags = $bookmarkService->bookmarksCountPerTag($filteringTags, $visibility);
644 659
645 // We sort tags alphabetically, then choose a font size according to count. 660 // We sort tags alphabetically, then choose a font size according to count.
646 // First, find max value. 661 // First, find max value.
@@ -687,7 +702,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
687 if ($targetPage == Router::$PAGE_TAGLIST) { 702 if ($targetPage == Router::$PAGE_TAGLIST) {
688 $visibility = ! empty($_SESSION['visibility']) ? $_SESSION['visibility'] : ''; 703 $visibility = ! empty($_SESSION['visibility']) ? $_SESSION['visibility'] : '';
689 $filteringTags = isset($_GET['searchtags']) ? explode(' ', $_GET['searchtags']) : []; 704 $filteringTags = isset($_GET['searchtags']) ? explode(' ', $_GET['searchtags']) : [];
690 $tags = $LINKSDB->linksCountPerTag($filteringTags, $visibility); 705 $tags = $bookmarkService->bookmarksCountPerTag($filteringTags, $visibility);
691 foreach ($filteringTags as $tag) { 706 foreach ($filteringTags as $tag) {
692 if (array_key_exists($tag, $tags)) { 707 if (array_key_exists($tag, $tags)) {
693 unset($tags[$tag]); 708 unset($tags[$tag]);
@@ -717,7 +732,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
717 732
718 // Daily page. 733 // Daily page.
719 if ($targetPage == Router::$PAGE_DAILY) { 734 if ($targetPage == Router::$PAGE_DAILY) {
720 showDaily($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager); 735 showDaily($PAGE, $bookmarkService, $conf, $pluginManager, $loginManager);
721 } 736 }
722 737
723 // ATOM and RSS feed. 738 // ATOM and RSS feed.
@@ -738,8 +753,16 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
738 exit; 753 exit;
739 } 754 }
740 755
756 $factory = new FormatterFactory($conf);
741 // Generate data. 757 // Generate data.
742 $feedGenerator = new FeedBuilder($LINKSDB, $feedType, $_SERVER, $_GET, $loginManager->isLoggedIn()); 758 $feedGenerator = new FeedBuilder(
759 $bookmarkService,
760 $factory->getFormatter(),
761 $feedType,
762 $_SERVER,
763 $_GET,
764 $loginManager->isLoggedIn()
765 );
743 $feedGenerator->setLocale(strtolower(setlocale(LC_COLLATE, 0))); 766 $feedGenerator->setLocale(strtolower(setlocale(LC_COLLATE, 0)));
744 $feedGenerator->setHideDates($conf->get('privacy.hide_timestamps') && !$loginManager->isLoggedIn()); 767 $feedGenerator->setHideDates($conf->get('privacy.hide_timestamps') && !$loginManager->isLoggedIn());
745 $feedGenerator->setUsePermalinks(isset($_GET['permalinks']) || !$conf->get('feed.rss_permalinks')); 768 $feedGenerator->setUsePermalinks(isset($_GET['permalinks']) || !$conf->get('feed.rss_permalinks'));
@@ -845,7 +868,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
845 exit; 868 exit;
846 } 869 }
847 870
848 // -------- User wants to change the number of links per page (linksperpage=...) 871 // -------- User wants to change the number of bookmarks per page (linksperpage=...)
849 if (isset($_GET['linksperpage'])) { 872 if (isset($_GET['linksperpage'])) {
850 if (is_numeric($_GET['linksperpage'])) { 873 if (is_numeric($_GET['linksperpage'])) {
851 $_SESSION['LINKS_PER_PAGE']=abs(intval($_GET['linksperpage'])); 874 $_SESSION['LINKS_PER_PAGE']=abs(intval($_GET['linksperpage']));
@@ -860,19 +883,19 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
860 exit; 883 exit;
861 } 884 }
862 885
863 // -------- User wants to see only private links (toggle) 886 // -------- User wants to see only private bookmarks (toggle)
864 if (isset($_GET['visibility'])) { 887 if (isset($_GET['visibility'])) {
865 if ($_GET['visibility'] === 'private') { 888 if ($_GET['visibility'] === 'private') {
866 // Visibility not set or not already private, set private, otherwise reset it 889 // Visibility not set or not already private, set private, otherwise reset it
867 if (empty($_SESSION['visibility']) || $_SESSION['visibility'] !== 'private') { 890 if (empty($_SESSION['visibility']) || $_SESSION['visibility'] !== 'private') {
868 // See only private links 891 // See only private bookmarks
869 $_SESSION['visibility'] = 'private'; 892 $_SESSION['visibility'] = 'private';
870 } else { 893 } else {
871 unset($_SESSION['visibility']); 894 unset($_SESSION['visibility']);
872 } 895 }
873 } elseif ($_GET['visibility'] === 'public') { 896 } elseif ($_GET['visibility'] === 'public') {
874 if (empty($_SESSION['visibility']) || $_SESSION['visibility'] !== 'public') { 897 if (empty($_SESSION['visibility']) || $_SESSION['visibility'] !== 'public') {
875 // See only public links 898 // See only public bookmarks
876 $_SESSION['visibility'] = 'public'; 899 $_SESSION['visibility'] = 'public';
877 } else { 900 } else {
878 unset($_SESSION['visibility']); 901 unset($_SESSION['visibility']);
@@ -888,7 +911,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
888 exit; 911 exit;
889 } 912 }
890 913
891 // -------- User wants to see only untagged links (toggle) 914 // -------- User wants to see only untagged bookmarks (toggle)
892 if (isset($_GET['untaggedonly'])) { 915 if (isset($_GET['untaggedonly'])) {
893 $_SESSION['untaggedonly'] = empty($_SESSION['untaggedonly']); 916 $_SESSION['untaggedonly'] = empty($_SESSION['untaggedonly']);
894 917
@@ -916,7 +939,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
916 exit; 939 exit;
917 } 940 }
918 941
919 showLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager); 942 showLinkList($PAGE, $bookmarkService, $conf, $pluginManager, $loginManager);
920 if (isset($_GET['edit_link'])) { 943 if (isset($_GET['edit_link'])) {
921 header('Location: ?do=login&edit_link='. escape($_GET['edit_link'])); 944 header('Location: ?do=login&edit_link='. escape($_GET['edit_link']));
922 exit; 945 exit;
@@ -1022,7 +1045,11 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1022 $conf->set('privacy.hide_public_links', !empty($_POST['hidePublicLinks'])); 1045 $conf->set('privacy.hide_public_links', !empty($_POST['hidePublicLinks']));
1023 $conf->set('api.enabled', !empty($_POST['enableApi'])); 1046 $conf->set('api.enabled', !empty($_POST['enableApi']));
1024 $conf->set('api.secret', escape($_POST['apiSecret'])); 1047 $conf->set('api.secret', escape($_POST['apiSecret']));
1025 $conf->set('translation.language', escape($_POST['language'])); 1048 $conf->set('formatter', escape($_POST['formatter']));
1049
1050 if (! empty($_POST['language'])) {
1051 $conf->set('translation.language', escape($_POST['language']));
1052 }
1026 1053
1027 $thumbnailsMode = extension_loaded('gd') ? $_POST['enableThumbnails'] : Thumbnailer::MODE_NONE; 1054 $thumbnailsMode = extension_loaded('gd') ? $_POST['enableThumbnails'] : Thumbnailer::MODE_NONE;
1028 if ($thumbnailsMode !== Thumbnailer::MODE_NONE 1055 if ($thumbnailsMode !== Thumbnailer::MODE_NONE
@@ -1056,6 +1083,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1056 $PAGE->assign('title', $conf->get('general.title')); 1083 $PAGE->assign('title', $conf->get('general.title'));
1057 $PAGE->assign('theme', $conf->get('resource.theme')); 1084 $PAGE->assign('theme', $conf->get('resource.theme'));
1058 $PAGE->assign('theme_available', ThemeUtils::getThemes($conf->get('resource.raintpl_tpl'))); 1085 $PAGE->assign('theme_available', ThemeUtils::getThemes($conf->get('resource.raintpl_tpl')));
1086 $PAGE->assign('formatter_available', ['default', 'markdown']);
1059 list($continents, $cities) = generateTimeZoneData( 1087 list($continents, $cities) = generateTimeZoneData(
1060 timezone_identifiers_list(), 1088 timezone_identifiers_list(),
1061 $conf->get('general.timezone') 1089 $conf->get('general.timezone')
@@ -1093,17 +1121,25 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1093 } 1121 }
1094 1122
1095 $toTag = isset($_POST['totag']) ? escape($_POST['totag']) : null; 1123 $toTag = isset($_POST['totag']) ? escape($_POST['totag']) : null;
1096 $alteredLinks = $LINKSDB->renameTag(escape($_POST['fromtag']), $toTag); 1124 $fromTag = escape($_POST['fromtag']);
1097 $LINKSDB->save($conf->get('resource.page_cache')); 1125 $count = 0;
1098 foreach ($alteredLinks as $link) { 1126 $bookmarks = $bookmarkService->search(['searchtags' => $fromTag], BookmarkFilter::$ALL, true);
1099 $history->updateLink($link); 1127 foreach ($bookmarks as $bookmark) {
1128 if ($toTag) {
1129 $bookmark->renameTag($fromTag, $toTag);
1130 } else {
1131 $bookmark->deleteTag($fromTag);
1132 }
1133 $bookmarkService->set($bookmark, false);
1134 $history->updateLink($bookmark);
1135 $count++;
1100 } 1136 }
1137 $bookmarkService->save();
1101 $delete = empty($_POST['totag']); 1138 $delete = empty($_POST['totag']);
1102 $redirect = $delete ? 'do=changetag' : 'searchtags='. urlencode(escape($_POST['totag'])); 1139 $redirect = $delete ? 'do=changetag' : 'searchtags='. urlencode(escape($_POST['totag']));
1103 $count = count($alteredLinks);
1104 $alert = $delete 1140 $alert = $delete
1105 ? sprintf(t('The tag was removed from %d link.', 'The tag was removed from %d links.', $count), $count) 1141 ? sprintf(t('The tag was removed from %d link.', 'The tag was removed from %d bookmarks.', $count), $count)
1106 : sprintf(t('The tag was renamed in %d link.', 'The tag was renamed in %d links.', $count), $count); 1142 : sprintf(t('The tag was renamed in %d link.', 'The tag was renamed in %d bookmarks.', $count), $count);
1107 echo '<script>alert("'. $alert .'");document.location=\'?'. $redirect .'\';</script>'; 1143 echo '<script>alert("'. $alert .'");document.location=\'?'. $redirect .'\';</script>';
1108 exit; 1144 exit;
1109 } 1145 }
@@ -1123,69 +1159,37 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1123 } 1159 }
1124 1160
1125 // lf_id should only be present if the link exists. 1161 // lf_id should only be present if the link exists.
1126 $id = isset($_POST['lf_id']) ? intval(escape($_POST['lf_id'])) : $LINKSDB->getNextId(); 1162 $id = isset($_POST['lf_id']) ? intval(escape($_POST['lf_id'])) : null;
1127 $link['id'] = $id; 1163 if ($id && $bookmarkService->exists($id)) {
1128 // Linkdate is kept here to:
1129 // - use the same permalink for notes as they're displayed when creating them
1130 // - let users hack creation date of their posts
1131 // See: https://shaarli.readthedocs.io/en/master/guides/various-hacks/#changing-the-timestamp-for-a-shaare
1132 $linkdate = escape($_POST['lf_linkdate']);
1133 $link['created'] = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $linkdate);
1134 if (isset($LINKSDB[$id])) {
1135 // Edit 1164 // Edit
1136 $link['updated'] = new DateTime(); 1165 $bookmark = $bookmarkService->get($id);
1137 $link['shorturl'] = $LINKSDB[$id]['shorturl'];
1138 $link['sticky'] = isset($LINKSDB[$id]['sticky']) ? $LINKSDB[$id]['sticky'] : false;
1139 $new = false;
1140 } else { 1166 } else {
1141 // New link 1167 // New link
1142 $link['updated'] = null; 1168 $bookmark = new Bookmark();
1143 $link['shorturl'] = link_small_hash($link['created'], $id);
1144 $link['sticky'] = false;
1145 $new = true;
1146 }
1147
1148 // Remove multiple spaces.
1149 $tags = trim(preg_replace('/\s\s+/', ' ', $_POST['lf_tags']));
1150 // Remove first '-' char in tags.
1151 $tags = preg_replace('/(^| )\-/', '$1', $tags);
1152 // Remove duplicates.
1153 $tags = implode(' ', array_unique(explode(' ', $tags)));
1154
1155 if (empty(trim($_POST['lf_url']))) {
1156 $_POST['lf_url'] = '?' . smallHash($linkdate . $id);
1157 } 1169 }
1158 $url = whitelist_protocols(trim($_POST['lf_url']), $conf->get('security.allowed_protocols'));
1159 1170
1160 $link = array_merge($link, [ 1171 $bookmark->setTitle($_POST['lf_title']);
1161 'title' => trim($_POST['lf_title']), 1172 $bookmark->setDescription($_POST['lf_description']);
1162 'url' => $url, 1173 $bookmark->setUrl($_POST['lf_url'], $conf->get('security.allowed_protocols'));
1163 'description' => $_POST['lf_description'], 1174 $bookmark->setPrivate(isset($_POST['lf_private']));
1164 'private' => (isset($_POST['lf_private']) ? 1 : 0), 1175 $bookmark->setTagsString($_POST['lf_tags']);
1165 'tags' => str_replace(',', ' ', $tags),
1166 ]);
1167
1168 // If title is empty, use the URL as title.
1169 if ($link['title'] == '') {
1170 $link['title'] = $link['url'];
1171 }
1172 1176
1173 if ($conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE 1177 if ($conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE
1174 && ! is_note($link['url']) 1178 && ! $bookmark->isNote()
1175 ) { 1179 ) {
1176 $thumbnailer = new Thumbnailer($conf); 1180 $thumbnailer = new Thumbnailer($conf);
1177 $link['thumbnail'] = $thumbnailer->get($url); 1181 $bookmark->setThumbnail($thumbnailer->get($bookmark->getUrl()));
1178 } 1182 }
1183 $bookmarkService->addOrSet($bookmark, false);
1179 1184
1180 $pluginManager->executeHooks('save_link', $link); 1185 // To preserve backward compatibility with 3rd parties, plugins still use arrays
1186 $factory = new FormatterFactory($conf);
1187 $formatter = $factory->getFormatter('raw');
1188 $data = $formatter->format($bookmark);
1189 $pluginManager->executeHooks('save_link', $data);
1181 1190
1182 $LINKSDB[$id] = $link; 1191 $bookmark->fromArray($data);
1183 $LINKSDB->save($conf->get('resource.page_cache')); 1192 $bookmarkService->set($bookmark);
1184 if ($new) {
1185 $history->addLink($link);
1186 } else {
1187 $history->updateLink($link);
1188 }
1189 1193
1190 // If we are called from the bookmarklet, we must close the popup: 1194 // If we are called from the bookmarklet, we must close the popup:
1191 if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { 1195 if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) {
@@ -1196,32 +1200,12 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1196 $returnurl = !empty($_POST['returnurl']) ? $_POST['returnurl'] : '?'; 1200 $returnurl = !empty($_POST['returnurl']) ? $_POST['returnurl'] : '?';
1197 $location = generateLocation($returnurl, $_SERVER['HTTP_HOST'], array('addlink', 'post', 'edit_link')); 1201 $location = generateLocation($returnurl, $_SERVER['HTTP_HOST'], array('addlink', 'post', 'edit_link'));
1198 // Scroll to the link which has been edited. 1202 // Scroll to the link which has been edited.
1199 $location .= '#' . $link['shorturl']; 1203 $location .= '#' . $bookmark->getShortUrl();
1200 // After saving the link, redirect to the page the user was on. 1204 // After saving the link, redirect to the page the user was on.
1201 header('Location: '. $location); 1205 header('Location: '. $location);
1202 exit; 1206 exit;
1203 } 1207 }
1204 1208
1205 // -------- User clicked the "Cancel" button when editing a link.
1206 if (isset($_POST['cancel_edit'])) {
1207 $id = isset($_POST['lf_id']) ? (int) escape($_POST['lf_id']) : false;
1208 if (! isset($LINKSDB[$id])) {
1209 header('Location: ?');
1210 }
1211 // If we are called from the bookmarklet, we must close the popup:
1212 if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) {
1213 echo '<script>self.close();</script>';
1214 exit;
1215 }
1216 $link = $LINKSDB[$id];
1217 $returnurl = ( isset($_POST['returnurl']) ? $_POST['returnurl'] : '?' );
1218 // Scroll to the link which has been edited.
1219 $returnurl .= '#'. $link['shorturl'];
1220 $returnurl = generateLocation($returnurl, $_SERVER['HTTP_HOST'], array('addlink', 'post', 'edit_link'));
1221 header('Location: '.$returnurl); // After canceling, redirect to the page the user was on.
1222 exit;
1223 }
1224
1225 // -------- User clicked the "Delete" button when editing a link: Delete link from database. 1209 // -------- User clicked the "Delete" button when editing a link: Delete link from database.
1226 if ($targetPage == Router::$PAGE_DELETELINK) { 1210 if ($targetPage == Router::$PAGE_DELETELINK) {
1227 if (! $sessionManager->checkToken($_GET['token'])) { 1211 if (! $sessionManager->checkToken($_GET['token'])) {
@@ -1231,23 +1215,31 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1231 $ids = trim($_GET['lf_linkdate']); 1215 $ids = trim($_GET['lf_linkdate']);
1232 if (strpos($ids, ' ') !== false) { 1216 if (strpos($ids, ' ') !== false) {
1233 // multiple, space-separated ids provided 1217 // multiple, space-separated ids provided
1234 $ids = array_values(array_filter(preg_split('/\s+/', escape($ids)))); 1218 $ids = array_values(array_filter(
1219 preg_split('/\s+/', escape($ids)),
1220 function ($item) {
1221 return $item !== '';
1222 }
1223 ));
1235 } else { 1224 } else {
1236 // only a single id provided 1225 // only a single id provided
1226 $shortUrl = $bookmarkService->get($ids)->getShortUrl();
1237 $ids = [$ids]; 1227 $ids = [$ids];
1238 } 1228 }
1239 // assert at least one id is given 1229 // assert at least one id is given
1240 if (!count($ids)) { 1230 if (!count($ids)) {
1241 die('no id provided'); 1231 die('no id provided');
1242 } 1232 }
1233 $factory = new FormatterFactory($conf);
1234 $formatter = $factory->getFormatter('raw');
1243 foreach ($ids as $id) { 1235 foreach ($ids as $id) {
1244 $id = (int) escape($id); 1236 $id = (int) escape($id);
1245 $link = $LINKSDB[$id]; 1237 $bookmark = $bookmarkService->get($id);
1246 $pluginManager->executeHooks('delete_link', $link); 1238 $data = $formatter->format($bookmark);
1247 $history->deleteLink($link); 1239 $pluginManager->executeHooks('delete_link', $data);
1248 unset($LINKSDB[$id]); 1240 $bookmarkService->remove($bookmark, false);
1249 } 1241 }
1250 $LINKSDB->save($conf->get('resource.page_cache')); // save to disk 1242 $bookmarkService->save();
1251 1243
1252 // If we are called from the bookmarklet, we must close the popup: 1244 // If we are called from the bookmarklet, we must close the popup:
1253 if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { 1245 if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) {
@@ -1261,7 +1253,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1261 $location = generateLocation( 1253 $location = generateLocation(
1262 $_SERVER['HTTP_REFERER'], 1254 $_SERVER['HTTP_REFERER'],
1263 $_SERVER['HTTP_HOST'], 1255 $_SERVER['HTTP_HOST'],
1264 ['delete_link', 'edit_link', $link['shorturl']] 1256 ['delete_link', 'edit_link', ! empty($shortUrl) ? $shortUrl : null]
1265 ); 1257 );
1266 } 1258 }
1267 1259
@@ -1294,14 +1286,21 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1294 } else { 1286 } else {
1295 $private = $_GET['newVisibility'] === 'private'; 1287 $private = $_GET['newVisibility'] === 'private';
1296 } 1288 }
1289 $factory = new FormatterFactory($conf);
1290 $formatter = $factory->getFormatter('raw');
1297 foreach ($ids as $id) { 1291 foreach ($ids as $id) {
1298 $id = (int) escape($id); 1292 $id = (int) escape($id);
1299 $link = $LINKSDB[$id]; 1293 $bookmark = $bookmarkService->get($id);
1300 $link['private'] = $private; 1294 $bookmark->setPrivate($private);
1301 $pluginManager->executeHooks('save_link', $link); 1295
1302 $LINKSDB[$id] = $link; 1296 // To preserve backward compatibility with 3rd parties, plugins still use arrays
1297 $data = $formatter->format($bookmark);
1298 $pluginManager->executeHooks('save_link', $data);
1299 $bookmark->fromArray($data);
1300
1301 $bookmarkService->set($bookmark);
1303 } 1302 }
1304 $LINKSDB->save($conf->get('resource.page_cache')); // save to disk 1303 $bookmarkService->save();
1305 1304
1306 $location = '?'; 1305 $location = '?';
1307 if (isset($_SERVER['HTTP_REFERER'])) { 1306 if (isset($_SERVER['HTTP_REFERER'])) {
@@ -1317,17 +1316,22 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1317 // -------- User clicked the "EDIT" button on a link: Display link edit form. 1316 // -------- User clicked the "EDIT" button on a link: Display link edit form.
1318 if (isset($_GET['edit_link'])) { 1317 if (isset($_GET['edit_link'])) {
1319 $id = (int) escape($_GET['edit_link']); 1318 $id = (int) escape($_GET['edit_link']);
1320 $link = $LINKSDB[$id]; // Read database 1319 try {
1321 if (!$link) { 1320 $link = $bookmarkService->get($id); // Read database
1321 } catch (BookmarkNotFoundException $e) {
1322 // Link not found in database.
1322 header('Location: ?'); 1323 header('Location: ?');
1323 exit; 1324 exit;
1324 } // Link not found in database. 1325 }
1325 $link['linkdate'] = $link['created']->format(LinkDB::LINK_DATE_FORMAT); 1326
1327 $factory = new FormatterFactory($conf);
1328 $formatter = $factory->getFormatter('raw');
1329 $formattedLink = $formatter->format($link);
1326 $data = array( 1330 $data = array(
1327 'link' => $link, 1331 'link' => $formattedLink,
1328 'link_is_new' => false, 1332 'link_is_new' => false,
1329 'http_referer' => (isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']) : ''), 1333 'http_referer' => (isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']) : ''),
1330 'tags' => $LINKSDB->linksCountPerTag(), 1334 'tags' => $bookmarkService->bookmarksCountPerTag(),
1331 ); 1335 );
1332 $pluginManager->executeHooks('render_editlink', $data); 1336 $pluginManager->executeHooks('render_editlink', $data);
1333 1337
@@ -1346,10 +1350,9 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1346 1350
1347 $link_is_new = false; 1351 $link_is_new = false;
1348 // Check if URL is not already in database (in this case, we will edit the existing link) 1352 // Check if URL is not already in database (in this case, we will edit the existing link)
1349 $link = $LINKSDB->getLinkFromUrl($url); 1353 $bookmark = $bookmarkService->findByUrl($url);
1350 if (! $link) { 1354 if (! $bookmark) {
1351 $link_is_new = true; 1355 $link_is_new = true;
1352 $linkdate = strval(date(LinkDB::LINK_DATE_FORMAT));
1353 // Get title if it was provided in URL (by the bookmarklet). 1356 // Get title if it was provided in URL (by the bookmarklet).
1354 $title = empty($_GET['title']) ? '' : escape($_GET['title']); 1357 $title = empty($_GET['title']) ? '' : escape($_GET['title']);
1355 // Get description if it was provided in URL (by the bookmarklet). [Bronco added that] 1358 // Get description if it was provided in URL (by the bookmarklet). [Bronco added that]
@@ -1375,32 +1378,32 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1375 } 1378 }
1376 1379
1377 if ($url == '') { 1380 if ($url == '') {
1378 $url = '?' . smallHash($linkdate . $LINKSDB->getNextId());
1379 $title = $conf->get('general.default_note_title', t('Note: ')); 1381 $title = $conf->get('general.default_note_title', t('Note: '));
1380 } 1382 }
1381 $url = escape($url); 1383 $url = escape($url);
1382 $title = escape($title); 1384 $title = escape($title);
1383 1385
1384 $link = array( 1386 $link = [
1385 'linkdate' => $linkdate,
1386 'title' => $title, 1387 'title' => $title,
1387 'url' => $url, 1388 'url' => $url,
1388 'description' => $description, 1389 'description' => $description,
1389 'tags' => $tags, 1390 'tags' => $tags,
1390 'private' => $private, 1391 'private' => $private,
1391 ); 1392 ];
1392 } else { 1393 } else {
1393 $link['linkdate'] = $link['created']->format(LinkDB::LINK_DATE_FORMAT); 1394 $factory = new FormatterFactory($conf);
1395 $formatter = $factory->getFormatter('raw');
1396 $link = $formatter->format($bookmark);
1394 } 1397 }
1395 1398
1396 $data = array( 1399 $data = [
1397 'link' => $link, 1400 'link' => $link,
1398 'link_is_new' => $link_is_new, 1401 'link_is_new' => $link_is_new,
1399 'http_referer' => (isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']) : ''), 1402 'http_referer' => (isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']) : ''),
1400 'source' => (isset($_GET['source']) ? $_GET['source'] : ''), 1403 'source' => (isset($_GET['source']) ? $_GET['source'] : ''),
1401 'tags' => $LINKSDB->linksCountPerTag(), 1404 'tags' => $bookmarkService->bookmarksCountPerTag(),
1402 'default_private_links' => $conf->get('privacy.default_private_links', false), 1405 'default_private_links' => $conf->get('privacy.default_private_links', false),
1403 ); 1406 ];
1404 $pluginManager->executeHooks('render_editlink', $data); 1407 $pluginManager->executeHooks('render_editlink', $data);
1405 1408
1406 foreach ($data as $key => $value) { 1409 foreach ($data as $key => $value) {
@@ -1413,7 +1416,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1413 } 1416 }
1414 1417
1415 if ($targetPage == Router::$PAGE_PINLINK) { 1418 if ($targetPage == Router::$PAGE_PINLINK) {
1416 if (! isset($_GET['id']) || empty($LINKSDB[$_GET['id']])) { 1419 if (! isset($_GET['id']) || !$bookmarkService->exists($_GET['id'])) {
1417 // FIXME! Use a proper error system. 1420 // FIXME! Use a proper error system.
1418 $msg = t('Invalid link ID provided'); 1421 $msg = t('Invalid link ID provided');
1419 echo '<script>alert("'. $msg .'");document.location=\''. index_url($_SERVER) .'\';</script>'; 1422 echo '<script>alert("'. $msg .'");document.location=\''. index_url($_SERVER) .'\';</script>';
@@ -1423,16 +1426,15 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1423 die('Wrong token.'); 1426 die('Wrong token.');
1424 } 1427 }
1425 1428
1426 $link = $LINKSDB[$_GET['id']]; 1429 $link = $bookmarkService->get($_GET['id']);
1427 $link['sticky'] = ! $link['sticky']; 1430 $link->setSticky(! $link->isSticky());
1428 $LINKSDB[(int) $_GET['id']] = $link; 1431 $bookmarkService->set($link);
1429 $LINKSDB->save($conf->get('resource.page_cache'));
1430 header('Location: '.index_url($_SERVER)); 1432 header('Location: '.index_url($_SERVER));
1431 exit; 1433 exit;
1432 } 1434 }
1433 1435
1434 if ($targetPage == Router::$PAGE_EXPORT) { 1436 if ($targetPage == Router::$PAGE_EXPORT) {
1435 // Export links as a Netscape Bookmarks file 1437 // Export bookmarks as a Netscape Bookmarks file
1436 1438
1437 if (empty($_GET['selection'])) { 1439 if (empty($_GET['selection'])) {
1438 $PAGE->assign('pagetitle', t('Export') .' - '. $conf->get('general.title', 'Shaarli')); 1440 $PAGE->assign('pagetitle', t('Export') .' - '. $conf->get('general.title', 'Shaarli'));
@@ -1449,10 +1451,13 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1449 } 1451 }
1450 1452
1451 try { 1453 try {
1454 $factory = new FormatterFactory($conf);
1455 $formatter = $factory->getFormatter('raw');
1452 $PAGE->assign( 1456 $PAGE->assign(
1453 'links', 1457 'links',
1454 NetscapeBookmarkUtils::filterAndFormat( 1458 NetscapeBookmarkUtils::filterAndFormat(
1455 $LINKSDB, 1459 $bookmarkService,
1460 $formatter,
1456 $selection, 1461 $selection,
1457 $prependNoteUrl, 1462 $prependNoteUrl,
1458 index_url($_SERVER) 1463 index_url($_SERVER)
@@ -1467,7 +1472,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1467 header('Content-Type: text/html; charset=utf-8'); 1472 header('Content-Type: text/html; charset=utf-8');
1468 header( 1473 header(
1469 'Content-disposition: attachment; filename=bookmarks_' 1474 'Content-disposition: attachment; filename=bookmarks_'
1470 .$selection.'_'.$now->format(LinkDB::LINK_DATE_FORMAT).'.html' 1475 .$selection.'_'.$now->format(Bookmark::LINK_DATE_FORMAT).'.html'
1471 ); 1476 );
1472 $PAGE->assign('date', $now->format(DateTime::RFC822)); 1477 $PAGE->assign('date', $now->format(DateTime::RFC822));
1473 $PAGE->assign('eol', PHP_EOL); 1478 $PAGE->assign('eol', PHP_EOL);
@@ -1521,7 +1526,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1521 $status = NetscapeBookmarkUtils::import( 1526 $status = NetscapeBookmarkUtils::import(
1522 $_POST, 1527 $_POST,
1523 $_FILES, 1528 $_FILES,
1524 $LINKSDB, 1529 $bookmarkService,
1525 $conf, 1530 $conf,
1526 $history 1531 $history
1527 ); 1532 );
@@ -1592,19 +1597,19 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1592 // Get a fresh token 1597 // Get a fresh token
1593 if ($targetPage == Router::$GET_TOKEN) { 1598 if ($targetPage == Router::$GET_TOKEN) {
1594 header('Content-Type:text/plain'); 1599 header('Content-Type:text/plain');
1595 echo $sessionManager->generateToken($conf); 1600 echo $sessionManager->generateToken();
1596 exit; 1601 exit;
1597 } 1602 }
1598 1603
1599 // -------- Thumbnails Update 1604 // -------- Thumbnails Update
1600 if ($targetPage == Router::$PAGE_THUMBS_UPDATE) { 1605 if ($targetPage == Router::$PAGE_THUMBS_UPDATE) {
1601 $ids = []; 1606 $ids = [];
1602 foreach ($LINKSDB as $link) { 1607 foreach ($bookmarkService->search() as $bookmark) {
1603 // A note or not HTTP(S) 1608 // A note or not HTTP(S)
1604 if (is_note($link['url']) || ! startsWith(strtolower($link['url']), 'http')) { 1609 if ($bookmark->isNote() || ! startsWith(strtolower($bookmark->getUrl()), 'http')) {
1605 continue; 1610 continue;
1606 } 1611 }
1607 $ids[] = $link['id']; 1612 $ids[] = $bookmark->getId();
1608 } 1613 }
1609 $PAGE->assign('ids', $ids); 1614 $PAGE->assign('ids', $ids);
1610 $PAGE->assign('pagetitle', t('Thumbnails update') .' - '. $conf->get('general.title', 'Shaarli')); 1615 $PAGE->assign('pagetitle', t('Thumbnails update') .' - '. $conf->get('general.title', 'Shaarli'));
@@ -1619,37 +1624,40 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1619 exit; 1624 exit;
1620 } 1625 }
1621 $id = (int) $_POST['id']; 1626 $id = (int) $_POST['id'];
1622 if (empty($LINKSDB[$id])) { 1627 if (! $bookmarkService->exists($id)) {
1623 http_response_code(404); 1628 http_response_code(404);
1624 exit; 1629 exit;
1625 } 1630 }
1626 $thumbnailer = new Thumbnailer($conf); 1631 $thumbnailer = new Thumbnailer($conf);
1627 $link = $LINKSDB[$id]; 1632 $bookmark = $bookmarkService->get($id);
1628 $link['thumbnail'] = $thumbnailer->get($link['url']); 1633 $bookmark->setThumbnail($thumbnailer->get($bookmark->getUrl()));
1629 $LINKSDB[$id] = $link; 1634 $bookmarkService->set($bookmark);
1630 $LINKSDB->save($conf->get('resource.page_cache'));
1631 1635
1632 echo json_encode($link); 1636 $factory = new FormatterFactory($conf);
1637 echo json_encode($factory->getFormatter('raw')->format($bookmark));
1633 exit; 1638 exit;
1634 } 1639 }
1635 1640
1636 // -------- Otherwise, simply display search form and links: 1641 // -------- Otherwise, simply display search form and bookmarks:
1637 showLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager); 1642 showLinkList($PAGE, $bookmarkService, $conf, $pluginManager, $loginManager);
1638 exit; 1643 exit;
1639} 1644}
1640 1645
1641/** 1646/**
1642 * Template for the list of links (<div id="linklist">) 1647 * Template for the list of bookmarks (<div id="linklist">)
1643 * This function fills all the necessary fields in the $PAGE for the template 'linklist.html' 1648 * This function fills all the necessary fields in the $PAGE for the template 'linklist.html'
1644 * 1649 *
1645 * @param pageBuilder $PAGE pageBuilder instance. 1650 * @param pageBuilder $PAGE pageBuilder instance.
1646 * @param LinkDB $LINKSDB LinkDB instance. 1651 * @param BookmarkServiceInterface $linkDb LinkDB instance.
1647 * @param ConfigManager $conf Configuration Manager instance. 1652 * @param ConfigManager $conf Configuration Manager instance.
1648 * @param PluginManager $pluginManager Plugin Manager instance. 1653 * @param PluginManager $pluginManager Plugin Manager instance.
1649 * @param LoginManager $loginManager LoginManager instance 1654 * @param LoginManager $loginManager LoginManager instance
1650 */ 1655 */
1651function buildLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager) 1656function buildLinkList($PAGE, $linkDb, $conf, $pluginManager, $loginManager)
1652{ 1657{
1658 $factory = new FormatterFactory($conf);
1659 $formatter = $factory->getFormatter();
1660
1653 // Used in templates 1661 // Used in templates
1654 if (isset($_GET['searchtags'])) { 1662 if (isset($_GET['searchtags'])) {
1655 if (! empty($_GET['searchtags'])) { 1663 if (! empty($_GET['searchtags'])) {
@@ -1666,19 +1674,19 @@ function buildLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager)
1666 if (! empty($_SERVER['QUERY_STRING']) 1674 if (! empty($_SERVER['QUERY_STRING'])
1667 && preg_match('/^[a-zA-Z0-9-_@]{6}($|&|#)/', $_SERVER['QUERY_STRING'])) { 1675 && preg_match('/^[a-zA-Z0-9-_@]{6}($|&|#)/', $_SERVER['QUERY_STRING'])) {
1668 try { 1676 try {
1669 $linksToDisplay = $LINKSDB->filterHash($_SERVER['QUERY_STRING']); 1677 $linksToDisplay = $linkDb->findByHash($_SERVER['QUERY_STRING']);
1670 } catch (LinkNotFoundException $e) { 1678 } catch (BookmarkNotFoundException $e) {
1671 $PAGE->render404($e->getMessage()); 1679 $PAGE->render404($e->getMessage());
1672 exit; 1680 exit;
1673 } 1681 }
1674 } else { 1682 } else {
1675 // Filter links according search parameters. 1683 // Filter bookmarks according search parameters.
1676 $visibility = ! empty($_SESSION['visibility']) ? $_SESSION['visibility'] : ''; 1684 $visibility = ! empty($_SESSION['visibility']) ? $_SESSION['visibility'] : '';
1677 $request = [ 1685 $request = [
1678 'searchtags' => $searchtags, 1686 'searchtags' => $searchtags,
1679 'searchterm' => $searchterm, 1687 'searchterm' => $searchterm,
1680 ]; 1688 ];
1681 $linksToDisplay = $LINKSDB->filterSearch($request, false, $visibility, !empty($_SESSION['untaggedonly'])); 1689 $linksToDisplay = $linkDb->search($request, $visibility, false, !empty($_SESSION['untaggedonly']));
1682 } 1690 }
1683 1691
1684 // ---- Handle paging. 1692 // ---- Handle paging.
@@ -1704,36 +1712,26 @@ function buildLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager)
1704 1712
1705 $linkDisp = array(); 1713 $linkDisp = array();
1706 while ($i<$end && $i<count($keys)) { 1714 while ($i<$end && $i<count($keys)) {
1707 $link = $linksToDisplay[$keys[$i]]; 1715 $link = $formatter->format($linksToDisplay[$keys[$i]]);
1708 $link['description'] = format_description($link['description']);
1709 $classLi = ($i % 2) != 0 ? '' : 'publicLinkHightLight';
1710 $link['class'] = $link['private'] == 0 ? $classLi : 'private';
1711 $link['timestamp'] = $link['created']->getTimestamp();
1712 if (! empty($link['updated'])) {
1713 $link['updated_timestamp'] = $link['updated']->getTimestamp();
1714 } else {
1715 $link['updated_timestamp'] = '';
1716 }
1717 $taglist = preg_split('/\s+/', $link['tags'], -1, PREG_SPLIT_NO_EMPTY);
1718 uasort($taglist, 'strcasecmp');
1719 $link['taglist'] = $taglist;
1720 1716
1721 // Logged in, thumbnails enabled, not a note, 1717 // Logged in, thumbnails enabled, not a note,
1722 // and (never retrieved yet or no valid cache file) 1718 // and (never retrieved yet or no valid cache file)
1723 if ($loginManager->isLoggedIn() && $thumbnailsEnabled && $link['url'][0] != '?' 1719 if ($loginManager->isLoggedIn()
1724 && (! isset($link['thumbnail']) || ($link['thumbnail'] !== false && ! is_file($link['thumbnail']))) 1720 && $thumbnailsEnabled
1721 && !$linksToDisplay[$keys[$i]]->isNote()
1722 && $linksToDisplay[$keys[$i]]->getThumbnail() !== false
1723 && ! is_file($linksToDisplay[$keys[$i]]->getThumbnail())
1725 ) { 1724 ) {
1726 $elem = $LINKSDB[$keys[$i]]; 1725 $linksToDisplay[$keys[$i]]->setThumbnail($thumbnailer->get($link['url']));
1727 $elem['thumbnail'] = $thumbnailer->get($link['url']); 1726 $linkDb->set($linksToDisplay[$keys[$i]], false);
1728 $LINKSDB[$keys[$i]] = $elem;
1729 $updateDB = true; 1727 $updateDB = true;
1730 $link['thumbnail'] = $elem['thumbnail']; 1728 $link['thumbnail'] = $linksToDisplay[$keys[$i]]->getThumbnail();
1731 } 1729 }
1732 1730
1733 // Check for both signs of a note: starting with ? and 7 chars long. 1731 // Check for both signs of a note: starting with ? and 7 chars long.
1734 if ($link['url'][0] === '?' && strlen($link['url']) === 7) { 1732// if ($link['url'][0] === '?' && strlen($link['url']) === 7) {
1735 $link['url'] = index_url($_SERVER) . $link['url']; 1733// $link['url'] = index_url($_SERVER) . $link['url'];
1736 } 1734// }
1737 1735
1738 $linkDisp[$keys[$i]] = $link; 1736 $linkDisp[$keys[$i]] = $link;
1739 $i++; 1737 $i++;
@@ -1741,7 +1739,7 @@ function buildLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager)
1741 1739
1742 // If we retrieved new thumbnails, we update the database. 1740 // If we retrieved new thumbnails, we update the database.
1743 if (!empty($updateDB)) { 1741 if (!empty($updateDB)) {
1744 $LINKSDB->save($conf->get('resource.page_cache')); 1742 $linkDb->save();
1745 } 1743 }
1746 1744
1747 // Compute paging navigation 1745 // Compute paging navigation
@@ -1771,7 +1769,7 @@ function buildLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager)
1771 1769
1772 // If there is only a single link, we change on-the-fly the title of the page. 1770 // If there is only a single link, we change on-the-fly the title of the page.
1773 if (count($linksToDisplay) == 1) { 1771 if (count($linksToDisplay) == 1) {
1774 $data['pagetitle'] = $linksToDisplay[$keys[0]]['title'] .' - '. $conf->get('general.title'); 1772 $data['pagetitle'] = $linksToDisplay[$keys[0]]->getTitle() .' - '. $conf->get('general.title');
1775 } elseif (! empty($searchterm) || ! empty($searchtags)) { 1773 } elseif (! empty($searchterm) || ! empty($searchtags)) {
1776 $data['pagetitle'] = t('Search: '); 1774 $data['pagetitle'] = t('Search: ');
1777 $data['pagetitle'] .= ! empty($searchterm) ? $searchterm .' ' : ''; 1775 $data['pagetitle'] .= ! empty($searchterm) ? $searchterm .' ' : '';
@@ -1856,7 +1854,7 @@ function install($conf, $sessionManager, $loginManager)
1856 if (!empty($_POST['title'])) { 1854 if (!empty($_POST['title'])) {
1857 $conf->set('general.title', escape($_POST['title'])); 1855 $conf->set('general.title', escape($_POST['title']));
1858 } else { 1856 } else {
1859 $conf->set('general.title', 'Shared links on '.escape(index_url($_SERVER))); 1857 $conf->set('general.title', 'Shared bookmarks on '.escape(index_url($_SERVER)));
1860 } 1858 }
1861 $conf->set('translation.language', escape($_POST['language'])); 1859 $conf->set('translation.language', escape($_POST['language']));
1862 $conf->set('updates.check_updates', !empty($_POST['updateCheck'])); 1860 $conf->set('updates.check_updates', !empty($_POST['updateCheck']));
@@ -1881,9 +1879,16 @@ function install($conf, $sessionManager, $loginManager)
1881 echo '<script>alert("'. $e->getMessage() .'");document.location=\'?\';</script>'; 1879 echo '<script>alert("'. $e->getMessage() .'");document.location=\'?\';</script>';
1882 exit; 1880 exit;
1883 } 1881 }
1882
1883 $history = new History($conf->get('resource.history'));
1884 $bookmarkService = new BookmarkFileService($conf, $history, true);
1885 if ($bookmarkService->count() === 0) {
1886 $bookmarkService->initialize();
1887 }
1888
1884 echo '<script>alert(' 1889 echo '<script>alert('
1885 .'"Shaarli is now configured. ' 1890 .'"Shaarli is now configured. '
1886 .'Please enter your login/password and start shaaring your links!"' 1891 .'Please enter your login/password and start shaaring your bookmarks!"'
1887 .');document.location=\'?do=login\';</script>'; 1892 .');document.location=\'?do=login\';</script>';
1888 exit; 1893 exit;
1889 } 1894 }
@@ -1897,11 +1902,6 @@ function install($conf, $sessionManager, $loginManager)
1897 exit; 1902 exit;
1898} 1903}
1899 1904
1900if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=dailyrss')) {
1901 showDailyRSS($conf, $loginManager);
1902 exit;
1903}
1904
1905if (!isset($_SESSION['LINKS_PER_PAGE'])) { 1905if (!isset($_SESSION['LINKS_PER_PAGE'])) {
1906 $_SESSION['LINKS_PER_PAGE'] = $conf->get('general.links_per_page', 20); 1906 $_SESSION['LINKS_PER_PAGE'] = $conf->get('general.links_per_page', 20);
1907} 1907}
@@ -1912,11 +1912,12 @@ try {
1912 die($e->getMessage()); 1912 die($e->getMessage());
1913} 1913}
1914 1914
1915$linkDb = new LinkDB( 1915$linkDb = new BookmarkFileService($conf, $history, $loginManager->isLoggedIn());
1916 $conf->get('resource.datastore'), 1916
1917 $loginManager->isLoggedIn(), 1917if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=dailyrss')) {
1918 $conf->get('privacy.hide_public_links') 1918 showDailyRSS($linkDb, $conf, $loginManager);
1919); 1919 exit;
1920}
1920 1921
1921$container = new \Slim\Container(); 1922$container = new \Slim\Container();
1922$container['conf'] = $conf; 1923$container['conf'] = $conf;
@@ -1927,11 +1928,11 @@ $app = new \Slim\App($container);
1927// REST API routes 1928// REST API routes
1928$app->group('/api/v1', function () { 1929$app->group('/api/v1', function () {
1929 $this->get('/info', '\Shaarli\Api\Controllers\Info:getInfo')->setName('getInfo'); 1930 $this->get('/info', '\Shaarli\Api\Controllers\Info:getInfo')->setName('getInfo');
1930 $this->get('/links', '\Shaarli\Api\Controllers\Links:getLinks')->setName('getLinks'); 1931 $this->get('/bookmarks', '\Shaarli\Api\Controllers\Links:getLinks')->setName('getLinks');
1931 $this->get('/links/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:getLink')->setName('getLink'); 1932 $this->get('/bookmarks/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:getLink')->setName('getLink');
1932 $this->post('/links', '\Shaarli\Api\Controllers\Links:postLink')->setName('postLink'); 1933 $this->post('/bookmarks', '\Shaarli\Api\Controllers\Links:postLink')->setName('postLink');
1933 $this->put('/links/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:putLink')->setName('putLink'); 1934 $this->put('/bookmarks/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:putLink')->setName('putLink');
1934 $this->delete('/links/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:deleteLink')->setName('deleteLink'); 1935 $this->delete('/bookmarks/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:deleteLink')->setName('deleteLink');
1935 1936
1936 $this->get('/tags', '\Shaarli\Api\Controllers\Tags:getTags')->setName('getTags'); 1937 $this->get('/tags', '\Shaarli\Api\Controllers\Tags:getTags')->setName('getTags');
1937 $this->get('/tags/{tagName:[\w]+}', '\Shaarli\Api\Controllers\Tags:getTag')->setName('getTag'); 1938 $this->get('/tags/{tagName:[\w]+}', '\Shaarli\Api\Controllers\Tags:getTag')->setName('getTag');