From cf92b4dd1521241eefc58eaf6dcd202cd83969d8 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Sat, 25 May 2019 15:52:27 +0200 Subject: Apply the new system (Bookmark + Service) to the whole code base See https://github.com/shaarli/Shaarli/issues/1307 --- application/History.php | 17 +- application/Utils.php | 2 +- application/api/ApiMiddleware.php | 11 +- application/api/ApiUtils.php | 79 ++-- application/api/controllers/ApiController.php | 8 +- application/api/controllers/HistoryController.php | 2 +- application/api/controllers/Info.php | 5 +- application/api/controllers/Links.php | 79 ++-- application/api/controllers/Tags.php | 44 +- application/bookmark/BookmarkArray.php | 2 +- application/config/ConfigManager.php | 2 + application/feed/FeedBuilder.php | 78 ++-- .../formatter/BookmarkMarkdownFormatter.php | 6 + application/netscape/NetscapeBookmarkUtils.php | 117 +++-- application/render/PageBuilder.php | 24 +- application/updater/Updater.php | 480 +-------------------- application/updater/UpdaterUtils.php | 65 +-- 17 files changed, 287 insertions(+), 734 deletions(-) (limited to 'application') diff --git a/application/History.php b/application/History.php index a5846652..4fd2f294 100644 --- a/application/History.php +++ b/application/History.php @@ -3,6 +3,7 @@ namespace Shaarli; use DateTime; use Exception; +use Shaarli\Bookmark\Bookmark; /** * Class History @@ -20,7 +21,7 @@ use Exception; * - UPDATED: link updated * - DELETED: link deleted * - SETTINGS: the settings have been updated through the UI. - * - IMPORT: bulk links import + * - IMPORT: bulk bookmarks import * * Note: new events are put at the beginning of the file and history array. */ @@ -96,31 +97,31 @@ class History /** * Add Event: new link. * - * @param array $link Link data. + * @param Bookmark $link Link data. */ public function addLink($link) { - $this->addEvent(self::CREATED, $link['id']); + $this->addEvent(self::CREATED, $link->getId()); } /** * Add Event: update existing link. * - * @param array $link Link data. + * @param Bookmark $link Link data. */ public function updateLink($link) { - $this->addEvent(self::UPDATED, $link['id']); + $this->addEvent(self::UPDATED, $link->getId()); } /** * Add Event: delete existing link. * - * @param array $link Link data. + * @param Bookmark $link Link data. */ public function deleteLink($link) { - $this->addEvent(self::DELETED, $link['id']); + $this->addEvent(self::DELETED, $link->getId()); } /** @@ -134,7 +135,7 @@ class History /** * Add Event: bulk import. * - * Note: we don't store links add/update one by one since it can have a huge impact on performances. + * Note: we don't store bookmarks add/update one by one since it can have a huge impact on performances. */ public function importLinks() { diff --git a/application/Utils.php b/application/Utils.php index 925e1a22..56f5b9a2 100644 --- a/application/Utils.php +++ b/application/Utils.php @@ -162,7 +162,7 @@ function generateLocation($referer, $host, $loopTerms = array()) $finalReferer = '?'; // No referer if it contains any value in $loopCriteria. - foreach ($loopTerms as $value) { + foreach (array_filter($loopTerms) as $value) { if (strpos($referer, $value) !== false) { return $finalReferer; } diff --git a/application/api/ApiMiddleware.php b/application/api/ApiMiddleware.php index 2d55bda6..4745ac94 100644 --- a/application/api/ApiMiddleware.php +++ b/application/api/ApiMiddleware.php @@ -3,6 +3,7 @@ namespace Shaarli\Api; use Shaarli\Api\Exceptions\ApiAuthorizationException; use Shaarli\Api\Exceptions\ApiException; +use Shaarli\Bookmark\BookmarkFileService; use Shaarli\Config\ConfigManager; use Slim\Container; use Slim\Http\Request; @@ -117,7 +118,7 @@ class ApiMiddleware } /** - * Instantiate a new LinkDB including private links, + * Instantiate a new LinkDB including private bookmarks, * and load in the Slim container. * * FIXME! LinkDB could use a refactoring to avoid this trick. @@ -126,10 +127,10 @@ class ApiMiddleware */ protected function setLinkDb($conf) { - $linkDb = new \Shaarli\Bookmark\LinkDB( - $conf->get('resource.datastore'), - true, - $conf->get('privacy.hide_public_links') + $linkDb = new BookmarkFileService( + $conf, + $this->container->get('history'), + true ); $this->container['db'] = $linkDb; } diff --git a/application/api/ApiUtils.php b/application/api/ApiUtils.php index 5ac07c4d..5156a5f7 100644 --- a/application/api/ApiUtils.php +++ b/application/api/ApiUtils.php @@ -2,6 +2,7 @@ namespace Shaarli\Api; use Shaarli\Api\Exceptions\ApiAuthorizationException; +use Shaarli\Bookmark\Bookmark; use Shaarli\Http\Base64Url; /** @@ -54,28 +55,28 @@ class ApiUtils /** * Format a Link for the REST API. * - * @param array $link Link data read from the datastore. - * @param string $indexUrl Shaarli's index URL (used for relative URL). + * @param Bookmark $bookmark Bookmark data read from the datastore. + * @param string $indexUrl Shaarli's index URL (used for relative URL). * * @return array Link data formatted for the REST API. */ - public static function formatLink($link, $indexUrl) + public static function formatLink($bookmark, $indexUrl) { - $out['id'] = $link['id']; + $out['id'] = $bookmark->getId(); // Not an internal link - if (! is_note($link['url'])) { - $out['url'] = $link['url']; + if (! $bookmark->isNote()) { + $out['url'] = $bookmark->getUrl(); } else { - $out['url'] = $indexUrl . $link['url']; + $out['url'] = $indexUrl . $bookmark->getUrl(); } - $out['shorturl'] = $link['shorturl']; - $out['title'] = $link['title']; - $out['description'] = $link['description']; - $out['tags'] = preg_split('/\s+/', $link['tags'], -1, PREG_SPLIT_NO_EMPTY); - $out['private'] = $link['private'] == true; - $out['created'] = $link['created']->format(\DateTime::ATOM); - if (! empty($link['updated'])) { - $out['updated'] = $link['updated']->format(\DateTime::ATOM); + $out['shorturl'] = $bookmark->getShortUrl(); + $out['title'] = $bookmark->getTitle(); + $out['description'] = $bookmark->getDescription(); + $out['tags'] = $bookmark->getTags(); + $out['private'] = $bookmark->isPrivate(); + $out['created'] = $bookmark->getCreated()->format(\DateTime::ATOM); + if (! empty($bookmark->getUpdated())) { + $out['updated'] = $bookmark->getUpdated()->format(\DateTime::ATOM); } else { $out['updated'] = ''; } @@ -83,7 +84,7 @@ class ApiUtils } /** - * Convert a link given through a request, to a valid link for LinkDB. + * Convert a link given through a request, to a valid Bookmark for the datastore. * * If no URL is provided, it will generate a local note URL. * If no title is provided, it will use the URL as title. @@ -91,50 +92,42 @@ class ApiUtils * @param array $input Request Link. * @param bool $defaultPrivate Request Link. * - * @return array Formatted link. + * @return Bookmark instance. */ public static function buildLinkFromRequest($input, $defaultPrivate) { - $input['url'] = ! empty($input['url']) ? cleanup_url($input['url']) : ''; + $bookmark = new Bookmark(); + $url = ! empty($input['url']) ? cleanup_url($input['url']) : ''; if (isset($input['private'])) { $private = filter_var($input['private'], FILTER_VALIDATE_BOOLEAN); } else { $private = $defaultPrivate; } - $link = [ - 'title' => ! empty($input['title']) ? $input['title'] : $input['url'], - 'url' => $input['url'], - 'description' => ! empty($input['description']) ? $input['description'] : '', - 'tags' => ! empty($input['tags']) ? implode(' ', $input['tags']) : '', - 'private' => $private, - 'created' => new \DateTime(), - ]; - return $link; + $bookmark->setTitle(! empty($input['title']) ? $input['title'] : ''); + $bookmark->setUrl($url); + $bookmark->setDescription(! empty($input['description']) ? $input['description'] : ''); + $bookmark->setTags(! empty($input['tags']) ? $input['tags'] : []); + $bookmark->setPrivate($private); + + return $bookmark; } /** * Update link fields using an updated link object. * - * @param array $oldLink data - * @param array $newLink data + * @param Bookmark $oldLink data + * @param Bookmark $newLink data * - * @return array $oldLink updated with $newLink values + * @return Bookmark $oldLink updated with $newLink values */ public static function updateLink($oldLink, $newLink) { - foreach (['title', 'url', 'description', 'tags', 'private'] as $field) { - $oldLink[$field] = $newLink[$field]; - } - $oldLink['updated'] = new \DateTime(); - - if (empty($oldLink['url'])) { - $oldLink['url'] = '?' . $oldLink['shorturl']; - } - - if (empty($oldLink['title'])) { - $oldLink['title'] = $oldLink['url']; - } + $oldLink->setTitle($newLink->getTitle()); + $oldLink->setUrl($newLink->getUrl()); + $oldLink->setDescription($newLink->getDescription()); + $oldLink->setTags($newLink->getTags()); + $oldLink->setPrivate($newLink->isPrivate()); return $oldLink; } @@ -143,7 +136,7 @@ class ApiUtils * Format a Tag for the REST API. * * @param string $tag Tag name - * @param int $occurrences Number of links using this tag + * @param int $occurrences Number of bookmarks using this tag * * @return array Link data formatted for the REST API. */ diff --git a/application/api/controllers/ApiController.php b/application/api/controllers/ApiController.php index a6e7cbab..c4b3d0c3 100644 --- a/application/api/controllers/ApiController.php +++ b/application/api/controllers/ApiController.php @@ -2,7 +2,7 @@ namespace Shaarli\Api\Controllers; -use Shaarli\Bookmark\LinkDB; +use Shaarli\Bookmark\BookmarkServiceInterface; use Shaarli\Config\ConfigManager; use Slim\Container; @@ -26,9 +26,9 @@ abstract class ApiController protected $conf; /** - * @var LinkDB + * @var BookmarkServiceInterface */ - protected $linkDb; + protected $bookmarkService; /** * @var HistoryController @@ -51,7 +51,7 @@ abstract class ApiController { $this->ci = $ci; $this->conf = $ci->get('conf'); - $this->linkDb = $ci->get('db'); + $this->bookmarkService = $ci->get('db'); $this->history = $ci->get('history'); if ($this->conf->get('dev.debug', false)) { $this->jsonStyle = JSON_PRETTY_PRINT; diff --git a/application/api/controllers/HistoryController.php b/application/api/controllers/HistoryController.php index 9afcfa26..505647a9 100644 --- a/application/api/controllers/HistoryController.php +++ b/application/api/controllers/HistoryController.php @@ -41,7 +41,7 @@ class HistoryController extends ApiController throw new ApiBadParametersException('Invalid offset'); } - // limit parameter is either a number of links or 'all' for everything. + // limit parameter is either a number of bookmarks or 'all' for everything. $limit = $request->getParam('limit'); if (empty($limit)) { $limit = count($history); diff --git a/application/api/controllers/Info.php b/application/api/controllers/Info.php index f37dcae5..12f6b2f0 100644 --- a/application/api/controllers/Info.php +++ b/application/api/controllers/Info.php @@ -2,6 +2,7 @@ namespace Shaarli\Api\Controllers; +use Shaarli\Bookmark\BookmarkFilter; use Slim\Http\Request; use Slim\Http\Response; @@ -26,8 +27,8 @@ class Info extends ApiController public function getInfo($request, $response) { $info = [ - 'global_counter' => count($this->linkDb), - 'private_counter' => count_private($this->linkDb), + 'global_counter' => $this->bookmarkService->count(), + 'private_counter' => $this->bookmarkService->count(BookmarkFilter::$PRIVATE), 'settings' => array( 'title' => $this->conf->get('general.title', 'Shaarli'), 'header_link' => $this->conf->get('general.header_link', '?'), diff --git a/application/api/controllers/Links.php b/application/api/controllers/Links.php index ffcfd4c7..29247950 100644 --- a/application/api/controllers/Links.php +++ b/application/api/controllers/Links.php @@ -11,7 +11,7 @@ use Slim\Http\Response; /** * Class Links * - * REST API Controller: all services related to links collection. + * REST API Controller: all services related to bookmarks collection. * * @package Api\Controllers * @see http://shaarli.github.io/api-documentation/#links-links-collection @@ -19,12 +19,12 @@ use Slim\Http\Response; class Links extends ApiController { /** - * @var int Number of links returned if no limit is provided. + * @var int Number of bookmarks returned if no limit is provided. */ public static $DEFAULT_LIMIT = 20; /** - * Retrieve a list of links, allowing different filters. + * Retrieve a list of bookmarks, allowing different filters. * * @param Request $request Slim request. * @param Response $response Slim response. @@ -36,33 +36,32 @@ class Links extends ApiController public function getLinks($request, $response) { $private = $request->getParam('visibility'); - $links = $this->linkDb->filterSearch( + $bookmarks = $this->bookmarkService->search( [ 'searchtags' => $request->getParam('searchtags', ''), 'searchterm' => $request->getParam('searchterm', ''), ], - false, $private ); - // Return links from the {offset}th link, starting from 0. + // Return bookmarks from the {offset}th link, starting from 0. $offset = $request->getParam('offset'); if (! empty($offset) && ! ctype_digit($offset)) { throw new ApiBadParametersException('Invalid offset'); } $offset = ! empty($offset) ? intval($offset) : 0; - if ($offset > count($links)) { + if ($offset > count($bookmarks)) { return $response->withJson([], 200, $this->jsonStyle); } - // limit parameter is either a number of links or 'all' for everything. + // limit parameter is either a number of bookmarks or 'all' for everything. $limit = $request->getParam('limit'); if (empty($limit)) { $limit = self::$DEFAULT_LIMIT; } elseif (ctype_digit($limit)) { $limit = intval($limit); } elseif ($limit === 'all') { - $limit = count($links); + $limit = count($bookmarks); } else { throw new ApiBadParametersException('Invalid limit'); } @@ -72,12 +71,12 @@ class Links extends ApiController $out = []; $index = 0; - foreach ($links as $link) { + foreach ($bookmarks as $bookmark) { if (count($out) >= $limit) { break; } if ($index++ >= $offset) { - $out[] = ApiUtils::formatLink($link, $indexUrl); + $out[] = ApiUtils::formatLink($bookmark, $indexUrl); } } @@ -97,11 +96,11 @@ class Links extends ApiController */ public function getLink($request, $response, $args) { - if (!isset($this->linkDb[$args['id']])) { + if (!$this->bookmarkService->exists($args['id'])) { throw new ApiLinkNotFoundException(); } $index = index_url($this->ci['environment']); - $out = ApiUtils::formatLink($this->linkDb[$args['id']], $index); + $out = ApiUtils::formatLink($this->bookmarkService->get($args['id']), $index); return $response->withJson($out, 200, $this->jsonStyle); } @@ -117,9 +116,11 @@ class Links extends ApiController public function postLink($request, $response) { $data = $request->getParsedBody(); - $link = ApiUtils::buildLinkFromRequest($data, $this->conf->get('privacy.default_private_links')); + $bookmark = ApiUtils::buildLinkFromRequest($data, $this->conf->get('privacy.default_private_links')); // duplicate by URL, return 409 Conflict - if (! empty($link['url']) && ! empty($dup = $this->linkDb->getLinkFromUrl($link['url']))) { + if (! empty($bookmark->getUrl()) + && ! empty($dup = $this->bookmarkService->findByUrl($bookmark->getUrl())) + ) { return $response->withJson( ApiUtils::formatLink($dup, index_url($this->ci['environment'])), 409, @@ -127,23 +128,9 @@ class Links extends ApiController ); } - $link['id'] = $this->linkDb->getNextId(); - $link['shorturl'] = link_small_hash($link['created'], $link['id']); - - // note: general relative URL - if (empty($link['url'])) { - $link['url'] = '?' . $link['shorturl']; - } - - if (empty($link['title'])) { - $link['title'] = $link['url']; - } - - $this->linkDb[$link['id']] = $link; - $this->linkDb->save($this->conf->get('resource.page_cache')); - $this->history->addLink($link); - $out = ApiUtils::formatLink($link, index_url($this->ci['environment'])); - $redirect = $this->ci->router->relativePathFor('getLink', ['id' => $link['id']]); + $this->bookmarkService->add($bookmark); + $out = ApiUtils::formatLink($bookmark, index_url($this->ci['environment'])); + $redirect = $this->ci->router->relativePathFor('getLink', ['id' => $bookmark->getId()]); return $response->withAddedHeader('Location', $redirect) ->withJson($out, 201, $this->jsonStyle); } @@ -161,18 +148,18 @@ class Links extends ApiController */ public function putLink($request, $response, $args) { - if (! isset($this->linkDb[$args['id']])) { + if (! $this->bookmarkService->exists($args['id'])) { throw new ApiLinkNotFoundException(); } $index = index_url($this->ci['environment']); $data = $request->getParsedBody(); - $requestLink = ApiUtils::buildLinkFromRequest($data, $this->conf->get('privacy.default_private_links')); + $requestBookmark = ApiUtils::buildLinkFromRequest($data, $this->conf->get('privacy.default_private_links')); // duplicate URL on a different link, return 409 Conflict - if (! empty($requestLink['url']) - && ! empty($dup = $this->linkDb->getLinkFromUrl($requestLink['url'])) - && $dup['id'] != $args['id'] + if (! empty($requestBookmark->getUrl()) + && ! empty($dup = $this->bookmarkService->findByUrl($requestBookmark->getUrl())) + && $dup->getId() != $args['id'] ) { return $response->withJson( ApiUtils::formatLink($dup, $index), @@ -181,13 +168,11 @@ class Links extends ApiController ); } - $responseLink = $this->linkDb[$args['id']]; - $responseLink = ApiUtils::updateLink($responseLink, $requestLink); - $this->linkDb[$responseLink['id']] = $responseLink; - $this->linkDb->save($this->conf->get('resource.page_cache')); - $this->history->updateLink($responseLink); + $responseBookmark = $this->bookmarkService->get($args['id']); + $responseBookmark = ApiUtils::updateLink($responseBookmark, $requestBookmark); + $this->bookmarkService->set($responseBookmark); - $out = ApiUtils::formatLink($responseLink, $index); + $out = ApiUtils::formatLink($responseBookmark, $index); return $response->withJson($out, 200, $this->jsonStyle); } @@ -204,13 +189,11 @@ class Links extends ApiController */ public function deleteLink($request, $response, $args) { - if (! isset($this->linkDb[$args['id']])) { + if (! $this->bookmarkService->exists($args['id'])) { throw new ApiLinkNotFoundException(); } - $link = $this->linkDb[$args['id']]; - unset($this->linkDb[(int) $args['id']]); - $this->linkDb->save($this->conf->get('resource.page_cache')); - $this->history->deleteLink($link); + $bookmark = $this->bookmarkService->get($args['id']); + $this->bookmarkService->remove($bookmark); return $response->withStatus(204); } diff --git a/application/api/controllers/Tags.php b/application/api/controllers/Tags.php index 82f3ef74..e60e00a7 100644 --- a/application/api/controllers/Tags.php +++ b/application/api/controllers/Tags.php @@ -5,6 +5,7 @@ namespace Shaarli\Api\Controllers; use Shaarli\Api\ApiUtils; use Shaarli\Api\Exceptions\ApiBadParametersException; use Shaarli\Api\Exceptions\ApiTagNotFoundException; +use Shaarli\Bookmark\BookmarkFilter; use Slim\Http\Request; use Slim\Http\Response; @@ -18,7 +19,7 @@ use Slim\Http\Response; class Tags extends ApiController { /** - * @var int Number of links returned if no limit is provided. + * @var int Number of bookmarks returned if no limit is provided. */ public static $DEFAULT_LIMIT = 'all'; @@ -35,7 +36,7 @@ class Tags extends ApiController public function getTags($request, $response) { $visibility = $request->getParam('visibility'); - $tags = $this->linkDb->linksCountPerTag([], $visibility); + $tags = $this->bookmarkService->bookmarksCountPerTag([], $visibility); // Return tags from the {offset}th tag, starting from 0. $offset = $request->getParam('offset'); @@ -47,7 +48,7 @@ class Tags extends ApiController return $response->withJson([], 200, $this->jsonStyle); } - // limit parameter is either a number of links or 'all' for everything. + // limit parameter is either a number of bookmarks or 'all' for everything. $limit = $request->getParam('limit'); if (empty($limit)) { $limit = self::$DEFAULT_LIMIT; @@ -87,7 +88,7 @@ class Tags extends ApiController */ public function getTag($request, $response, $args) { - $tags = $this->linkDb->linksCountPerTag(); + $tags = $this->bookmarkService->bookmarksCountPerTag(); if (!isset($tags[$args['tagName']])) { throw new ApiTagNotFoundException(); } @@ -111,7 +112,7 @@ class Tags extends ApiController */ public function putTag($request, $response, $args) { - $tags = $this->linkDb->linksCountPerTag(); + $tags = $this->bookmarkService->bookmarksCountPerTag(); if (! isset($tags[$args['tagName']])) { throw new ApiTagNotFoundException(); } @@ -121,13 +122,19 @@ class Tags extends ApiController throw new ApiBadParametersException('New tag name is required in the request body'); } - $updated = $this->linkDb->renameTag($args['tagName'], $data['name']); - $this->linkDb->save($this->conf->get('resource.page_cache')); - foreach ($updated as $link) { - $this->history->updateLink($link); + $bookmarks = $this->bookmarkService->search( + ['searchtags' => $args['tagName']], + BookmarkFilter::$ALL, + true + ); + foreach ($bookmarks as $bookmark) { + $bookmark->renameTag($args['tagName'], $data['name']); + $this->bookmarkService->set($bookmark, false); + $this->history->updateLink($bookmark); } + $this->bookmarkService->save(); - $tags = $this->linkDb->linksCountPerTag(); + $tags = $this->bookmarkService->bookmarksCountPerTag(); $out = ApiUtils::formatTag($data['name'], $tags[$data['name']]); return $response->withJson($out, 200, $this->jsonStyle); } @@ -145,15 +152,22 @@ class Tags extends ApiController */ public function deleteTag($request, $response, $args) { - $tags = $this->linkDb->linksCountPerTag(); + $tags = $this->bookmarkService->bookmarksCountPerTag(); if (! isset($tags[$args['tagName']])) { throw new ApiTagNotFoundException(); } - $updated = $this->linkDb->renameTag($args['tagName'], null); - $this->linkDb->save($this->conf->get('resource.page_cache')); - foreach ($updated as $link) { - $this->history->updateLink($link); + + $bookmarks = $this->bookmarkService->search( + ['searchtags' => $args['tagName']], + BookmarkFilter::$ALL, + true + ); + foreach ($bookmarks as $bookmark) { + $bookmark->deleteTag($args['tagName']); + $this->bookmarkService->set($bookmark, false); + $this->history->updateLink($bookmark); } + $this->bookmarkService->save(); return $response->withStatus(204); } diff --git a/application/bookmark/BookmarkArray.php b/application/bookmark/BookmarkArray.php index b427c91a..d87d43b4 100644 --- a/application/bookmark/BookmarkArray.php +++ b/application/bookmark/BookmarkArray.php @@ -118,7 +118,7 @@ class BookmarkArray implements \Iterator, \Countable, \ArrayAccess $realOffset = $this->getBookmarkOffset($offset); $url = $this->bookmarks[$realOffset]->getUrl(); unset($this->urls[$url]); - unset($this->ids[$realOffset]); + unset($this->ids[$offset]); unset($this->bookmarks[$realOffset]); } diff --git a/application/config/ConfigManager.php b/application/config/ConfigManager.php index c95e6800..e45bb4c3 100644 --- a/application/config/ConfigManager.php +++ b/application/config/ConfigManager.php @@ -389,6 +389,8 @@ class ConfigManager $this->setEmpty('translation.extensions', []); $this->setEmpty('plugins', array()); + + $this->setEmpty('formatter', 'markdown'); } /** diff --git a/application/feed/FeedBuilder.php b/application/feed/FeedBuilder.php index 957c8273..40bd4f15 100644 --- a/application/feed/FeedBuilder.php +++ b/application/feed/FeedBuilder.php @@ -2,6 +2,9 @@ namespace Shaarli\Feed; use DateTime; +use Shaarli\Bookmark\Bookmark; +use Shaarli\Bookmark\BookmarkServiceInterface; +use Shaarli\Formatter\BookmarkFormatter; /** * FeedBuilder class. @@ -26,15 +29,20 @@ class FeedBuilder public static $DEFAULT_LANGUAGE = 'en-en'; /** - * @var int Number of links to display in a feed by default. + * @var int Number of bookmarks to display in a feed by default. */ public static $DEFAULT_NB_LINKS = 50; /** - * @var \Shaarli\Bookmark\LinkDB instance. + * @var BookmarkServiceInterface instance. */ protected $linkDB; + /** + * @var BookmarkFormatter instance. + */ + protected $formatter; + /** * @var string RSS or ATOM feed. */ @@ -56,7 +64,7 @@ class FeedBuilder protected $isLoggedIn; /** - * @var boolean Use permalinks instead of direct links if true. + * @var boolean Use permalinks instead of direct bookmarks if true. */ protected $usePermalinks; @@ -78,16 +86,17 @@ class FeedBuilder /** * Feed constructor. * - * @param \Shaarli\Bookmark\LinkDB $linkDB LinkDB instance. + * @param BookmarkServiceInterface $linkDB LinkDB instance. + * @param BookmarkFormatter $formatter instance. * @param string $feedType Type of feed. * @param array $serverInfo $_SERVER. * @param array $userInput $_GET. - * @param boolean $isLoggedIn True if the user is currently logged in, - * false otherwise. + * @param boolean $isLoggedIn True if the user is currently logged in, false otherwise. */ - public function __construct($linkDB, $feedType, $serverInfo, $userInput, $isLoggedIn) + public function __construct($linkDB, $formatter, $feedType, $serverInfo, $userInput, $isLoggedIn) { $this->linkDB = $linkDB; + $this->formatter = $formatter; $this->feedType = $feedType; $this->serverInfo = $serverInfo; $this->userInput = $userInput; @@ -101,13 +110,13 @@ class FeedBuilder */ public function buildData() { - // Search for untagged links + // Search for untagged bookmarks if (isset($this->userInput['searchtags']) && empty($this->userInput['searchtags'])) { $this->userInput['searchtags'] = false; } // Optionally filter the results: - $linksToDisplay = $this->linkDB->filterSearch($this->userInput); + $linksToDisplay = $this->linkDB->search($this->userInput); $nblinksToDisplay = $this->getNbLinks(count($linksToDisplay)); @@ -118,6 +127,7 @@ class FeedBuilder } $pageaddr = escape(index_url($this->serverInfo)); + $this->formatter->addContextData('index_url', $pageaddr); $linkDisplayed = array(); for ($i = 0; $i < $nblinksToDisplay && $i < count($keys); $i++) { $linkDisplayed[$keys[$i]] = $this->buildItem($linksToDisplay[$keys[$i]], $pageaddr); @@ -139,54 +149,44 @@ class FeedBuilder /** * Build a feed item (one per shaare). * - * @param array $link Single link array extracted from LinkDB. - * @param string $pageaddr Index URL. + * @param Bookmark $link Single link array extracted from LinkDB. + * @param string $pageaddr Index URL. * * @return array Link array with feed attributes. */ protected function buildItem($link, $pageaddr) { - $link['guid'] = $pageaddr . '?' . $link['shorturl']; - // Prepend the root URL for notes - if (is_note($link['url'])) { - $link['url'] = $pageaddr . $link['url']; - } + $data = $this->formatter->format($link); + $data['guid'] = $pageaddr . '?' . $data['shorturl']; if ($this->usePermalinks === true) { - $permalink = '' . t('Direct link') . ''; + $permalink = ''. t('Direct link') .''; } else { - $permalink = '' . t('Permalink') . ''; + $permalink = ''. t('Permalink') .''; } - $link['description'] = format_description($link['description'], $pageaddr); - $link['description'] .= PHP_EOL . PHP_EOL . '
— ' . $permalink; + $data['description'] .= PHP_EOL . PHP_EOL . '
— ' . $permalink; - $pubDate = $link['created']; - $link['pub_iso_date'] = $this->getIsoDate($pubDate); + $data['pub_iso_date'] = $this->getIsoDate($data['created']); // atom:entry elements MUST contain exactly one atom:updated element. - if (!empty($link['updated'])) { - $upDate = $link['updated']; - $link['up_iso_date'] = $this->getIsoDate($upDate, DateTime::ATOM); + if (!empty($link->getUpdated())) { + $data['up_iso_date'] = $this->getIsoDate($data['updated'], DateTime::ATOM); } else { - $link['up_iso_date'] = $this->getIsoDate($pubDate, DateTime::ATOM); + $data['up_iso_date'] = $this->getIsoDate($data['created'], DateTime::ATOM); } // Save the more recent item. - if (empty($this->latestDate) || $this->latestDate < $pubDate) { - $this->latestDate = $pubDate; + if (empty($this->latestDate) || $this->latestDate < $data['created']) { + $this->latestDate = $data['created']; } - if (!empty($upDate) && $this->latestDate < $upDate) { - $this->latestDate = $upDate; + if (!empty($data['updated']) && $this->latestDate < $data['updated']) { + $this->latestDate = $data['updated']; } - $taglist = array_filter(explode(' ', $link['tags']), 'strlen'); - uasort($taglist, 'strcasecmp'); - $link['taglist'] = $taglist; - - return $link; + return $data; } /** - * Set this to true to use permalinks instead of direct links. + * Set this to true to use permalinks instead of direct bookmarks. * * @param boolean $usePermalinks true to force permalinks. */ @@ -273,11 +273,11 @@ class FeedBuilder * Returns the number of link to display according to 'nb' user input parameter. * * If 'nb' not set or invalid, default value: $DEFAULT_NB_LINKS. - * If 'nb' is set to 'all', display all filtered links (max parameter). + * If 'nb' is set to 'all', display all filtered bookmarks (max parameter). * - * @param int $max maximum number of links to display. + * @param int $max maximum number of bookmarks to display. * - * @return int number of links to display. + * @return int number of bookmarks to display. */ public function getNbLinks($max) { diff --git a/application/formatter/BookmarkMarkdownFormatter.php b/application/formatter/BookmarkMarkdownFormatter.php index f60c61f4..7797bfbf 100644 --- a/application/formatter/BookmarkMarkdownFormatter.php +++ b/application/formatter/BookmarkMarkdownFormatter.php @@ -57,6 +57,7 @@ class BookmarkMarkdownFormatter extends BookmarkDefaultFormatter $processedDescription = $bookmark->getDescription(); $processedDescription = $this->filterProtocols($processedDescription); $processedDescription = $this->formatHashTags($processedDescription); + $processedDescription = $this->reverseEscapedHtml($processedDescription); $processedDescription = $this->parsedown ->setMarkupEscaped($this->escape) ->setBreaksEnabled(true) @@ -195,4 +196,9 @@ class BookmarkMarkdownFormatter extends BookmarkDefaultFormatter ); return $description; } + + protected function reverseEscapedHtml($description) + { + return unescape($description); + } } diff --git a/application/netscape/NetscapeBookmarkUtils.php b/application/netscape/NetscapeBookmarkUtils.php index 28665941..d64eef7f 100644 --- a/application/netscape/NetscapeBookmarkUtils.php +++ b/application/netscape/NetscapeBookmarkUtils.php @@ -7,8 +7,10 @@ use DateTimeZone; use Exception; use Katzgrau\KLogger\Logger; use Psr\Log\LogLevel; -use Shaarli\Bookmark\LinkDB; +use Shaarli\Bookmark\Bookmark; +use Shaarli\Bookmark\BookmarkServiceInterface; use Shaarli\Config\ConfigManager; +use Shaarli\Formatter\BookmarkFormatter; use Shaarli\History; use Shaarli\NetscapeBookmarkParser\NetscapeBookmarkParser; @@ -20,41 +22,39 @@ class NetscapeBookmarkUtils { /** - * Filters links and adds Netscape-formatted fields + * Filters bookmarks and adds Netscape-formatted fields * * Added fields: * - timestamp link addition date, using the Unix epoch format * - taglist comma-separated tag list * - * @param LinkDB $linkDb Link datastore - * @param string $selection Which links to export: (all|private|public) - * @param bool $prependNoteUrl Prepend note permalinks with the server's URL - * @param string $indexUrl Absolute URL of the Shaarli index page + * @param BookmarkServiceInterface $bookmarkService Link datastore + * @param BookmarkFormatter $formatter instance + * @param string $selection Which bookmarks to export: (all|private|public) + * @param bool $prependNoteUrl Prepend note permalinks with the server's URL + * @param string $indexUrl Absolute URL of the Shaarli index page * - * @throws Exception Invalid export selection + * @return array The bookmarks to be exported, with additional fields + *@throws Exception Invalid export selection * - * @return array The links to be exported, with additional fields */ - public static function filterAndFormat($linkDb, $selection, $prependNoteUrl, $indexUrl) - { + public static function filterAndFormat( + $bookmarkService, + $formatter, + $selection, + $prependNoteUrl, + $indexUrl + ) { // see tpl/export.html for possible values if (!in_array($selection, array('all', 'public', 'private'))) { throw new Exception(t('Invalid export selection:') . ' "' . $selection . '"'); } $bookmarkLinks = array(); - foreach ($linkDb as $link) { - if ($link['private'] != 0 && $selection == 'public') { - continue; - } - if ($link['private'] == 0 && $selection == 'private') { - continue; - } - $date = $link['created']; - $link['timestamp'] = $date->getTimestamp(); - $link['taglist'] = str_replace(' ', ',', $link['tags']); - - if (is_note($link['url']) && $prependNoteUrl) { + foreach ($bookmarkService->search([], $selection) as $bookmark) { + $link = $formatter->format($bookmark); + $link['taglist'] = implode(',', $bookmark->getTags()); + if ($bookmark->isNote() && $prependNoteUrl) { $link['url'] = $indexUrl . $link['url']; } @@ -69,9 +69,9 @@ class NetscapeBookmarkUtils * * @param string $filename name of the file to import * @param int $filesize size of the file to import - * @param int $importCount how many links were imported - * @param int $overwriteCount how many links were overwritten - * @param int $skipCount how many links were skipped + * @param int $importCount how many bookmarks were imported + * @param int $overwriteCount how many bookmarks were overwritten + * @param int $skipCount how many bookmarks were skipped * @param int $duration how many seconds did the import take * * @return string Summary of the bookmark import status @@ -91,7 +91,7 @@ class NetscapeBookmarkUtils $status .= vsprintf( t( 'was successfully processed in %d seconds: ' - . '%d links imported, %d links overwritten, %d links skipped.' + . '%d bookmarks imported, %d bookmarks overwritten, %d bookmarks skipped.' ), [$duration, $importCount, $overwriteCount, $skipCount] ); @@ -102,15 +102,15 @@ class NetscapeBookmarkUtils /** * Imports Web bookmarks from an uploaded Netscape bookmark dump * - * @param array $post Server $_POST parameters - * @param array $files Server $_FILES parameters - * @param LinkDB $linkDb Loaded LinkDB instance - * @param ConfigManager $conf instance - * @param History $history History instance + * @param array $post Server $_POST parameters + * @param array $files Server $_FILES parameters + * @param BookmarkServiceInterface $bookmarkService Loaded LinkDB instance + * @param ConfigManager $conf instance + * @param History $history History instance * * @return string Summary of the bookmark import status */ - public static function import($post, $files, $linkDb, $conf, $history) + public static function import($post, $files, $bookmarkService, $conf, $history) { $start = time(); $filename = $files['filetoupload']['name']; @@ -121,10 +121,10 @@ class NetscapeBookmarkUtils return self::importStatus($filename, $filesize); } - // Overwrite existing links? + // Overwrite existing bookmarks? $overwrite = !empty($post['overwrite']); - // Add tags to all imported links? + // Add tags to all imported bookmarks? if (empty($post['default_tags'])) { $defaultTags = array(); } else { @@ -134,7 +134,7 @@ class NetscapeBookmarkUtils ); } - // links are imported as public by default + // bookmarks are imported as public by default $defaultPrivacy = 0; $parser = new NetscapeBookmarkParser( @@ -164,22 +164,18 @@ class NetscapeBookmarkUtils // use value from the imported file $private = $bkm['pub'] == '1' ? 0 : 1; } elseif ($post['privacy'] == 'private') { - // all imported links are private + // all imported bookmarks are private $private = 1; } elseif ($post['privacy'] == 'public') { - // all imported links are public + // all imported bookmarks are public $private = 0; } - $newLink = array( - 'title' => $bkm['title'], - 'url' => $bkm['uri'], - 'description' => $bkm['note'], - 'private' => $private, - 'tags' => $bkm['tags'] - ); - - $existingLink = $linkDb->getLinkFromUrl($bkm['uri']); + $link = $bookmarkService->findByUrl($bkm['uri']); + $existingLink = $link !== null; + if (! $existingLink) { + $link = new Bookmark(); + } if ($existingLink !== false) { if ($overwrite === false) { @@ -188,28 +184,25 @@ class NetscapeBookmarkUtils continue; } - // Overwrite an existing link, keep its date - $newLink['id'] = $existingLink['id']; - $newLink['created'] = $existingLink['created']; - $newLink['updated'] = new DateTime(); - $newLink['shorturl'] = $existingLink['shorturl']; - $linkDb[$existingLink['id']] = $newLink; - $importCount++; + $link->setUpdated(new DateTime()); $overwriteCount++; - continue; + } else { + $newLinkDate = new DateTime('@' . strval($bkm['time'])); + $newLinkDate->setTimezone(new DateTimeZone(date_default_timezone_get())); + $link->setCreated($newLinkDate); } - // Add a new link - @ used for UNIX timestamps - $newLinkDate = new DateTime('@' . strval($bkm['time'])); - $newLinkDate->setTimezone(new DateTimeZone(date_default_timezone_get())); - $newLink['created'] = $newLinkDate; - $newLink['id'] = $linkDb->getNextId(); - $newLink['shorturl'] = link_small_hash($newLink['created'], $newLink['id']); - $linkDb[$newLink['id']] = $newLink; + $link->setTitle($bkm['title']); + $link->setUrl($bkm['uri'], $conf->get('security.allowed_protocols')); + $link->setDescription($bkm['note']); + $link->setPrivate($private); + $link->setTagsString($bkm['tags']); + + $bookmarkService->addOrSet($link, false); $importCount++; } - $linkDb->save($conf->get('resource.page_cache')); + $bookmarkService->save(); $history->importLinks(); $duration = time() - $start; diff --git a/application/render/PageBuilder.php b/application/render/PageBuilder.php index 3f86fc26..65e85aaf 100644 --- a/application/render/PageBuilder.php +++ b/application/render/PageBuilder.php @@ -5,7 +5,7 @@ namespace Shaarli\Render; use Exception; use RainTPL; use Shaarli\ApplicationUtils; -use Shaarli\Bookmark\LinkDB; +use Shaarli\Bookmark\BookmarkServiceInterface; use Shaarli\Config\ConfigManager; use Shaarli\Thumbnailer; @@ -34,9 +34,9 @@ class PageBuilder protected $session; /** - * @var LinkDB $linkDB instance. + * @var BookmarkServiceInterface $bookmarkService instance. */ - protected $linkDB; + protected $bookmarkService; /** * @var null|string XSRF token @@ -52,18 +52,18 @@ class PageBuilder * PageBuilder constructor. * $tpl is initialized at false for lazy loading. * - * @param ConfigManager $conf Configuration Manager instance (reference). - * @param array $session $_SESSION array - * @param LinkDB $linkDB instance. - * @param string $token Session token - * @param bool $isLoggedIn + * @param ConfigManager $conf Configuration Manager instance (reference). + * @param array $session $_SESSION array + * @param BookmarkServiceInterface $linkDB instance. + * @param string $token Session token + * @param bool $isLoggedIn */ public function __construct(&$conf, $session, $linkDB = null, $token = null, $isLoggedIn = false) { $this->tpl = false; $this->conf = $conf; $this->session = $session; - $this->linkDB = $linkDB; + $this->bookmarkService = $linkDB; $this->token = $token; $this->isLoggedIn = $isLoggedIn; } @@ -125,8 +125,8 @@ class PageBuilder $this->tpl->assign('language', $this->conf->get('translation.language')); - if ($this->linkDB !== null) { - $this->tpl->assign('tags', $this->linkDB->linksCountPerTag()); + if ($this->bookmarkService !== null) { + $this->tpl->assign('tags', $this->bookmarkService->bookmarksCountPerTag()); } $this->tpl->assign( @@ -141,6 +141,8 @@ class PageBuilder unset($_SESSION['warnings']); } + $this->tpl->assign('formatter', $this->conf->get('formatter', 'default')); + // To be removed with a proper theme configuration. $this->tpl->assign('conf', $this->conf); } diff --git a/application/updater/Updater.php b/application/updater/Updater.php index beb9ea9b..95654d81 100644 --- a/application/updater/Updater.php +++ b/application/updater/Updater.php @@ -2,25 +2,14 @@ namespace Shaarli\Updater; -use Exception; -use RainTPL; -use ReflectionClass; -use ReflectionException; -use ReflectionMethod; -use Shaarli\ApplicationUtils; -use Shaarli\Bookmark\LinkDB; -use Shaarli\Bookmark\LinkFilter; -use Shaarli\Config\ConfigJson; use Shaarli\Config\ConfigManager; -use Shaarli\Config\ConfigPhp; -use Shaarli\Exceptions\IOException; -use Shaarli\Thumbnailer; +use Shaarli\Bookmark\BookmarkServiceInterface; use Shaarli\Updater\Exception\UpdaterException; /** - * Class updater. + * Class Updater. * Used to update stuff when a new Shaarli's version is reached. - * Update methods are ran only once, and the stored in a JSON file. + * Update methods are ran only once, and the stored in a TXT file. */ class Updater { @@ -30,9 +19,9 @@ class Updater protected $doneUpdates; /** - * @var LinkDB instance. + * @var BookmarkServiceInterface instance. */ - protected $linkDB; + protected $linkServices; /** * @var ConfigManager $conf Configuration Manager instance. @@ -45,36 +34,27 @@ class Updater protected $isLoggedIn; /** - * @var array $_SESSION - */ - protected $session; - - /** - * @var ReflectionMethod[] List of current class methods. + * @var \ReflectionMethod[] List of current class methods. */ protected $methods; /** * Object constructor. * - * @param array $doneUpdates Updates which are already done. - * @param LinkDB $linkDB LinkDB instance. - * @param ConfigManager $conf Configuration Manager instance. - * @param boolean $isLoggedIn True if the user is logged in. - * @param array $session $_SESSION (by reference) - * - * @throws ReflectionException + * @param array $doneUpdates Updates which are already done. + * @param BookmarkServiceInterface $linkDB LinksService instance. + * @param ConfigManager $conf Configuration Manager instance. + * @param boolean $isLoggedIn True if the user is logged in. */ - public function __construct($doneUpdates, $linkDB, $conf, $isLoggedIn, &$session = []) + public function __construct($doneUpdates, $linkDB, $conf, $isLoggedIn) { $this->doneUpdates = $doneUpdates; - $this->linkDB = $linkDB; + $this->linkServices = $linkDB; $this->conf = $conf; $this->isLoggedIn = $isLoggedIn; - $this->session = &$session; // Retrieve all update methods. - $class = new ReflectionClass($this); + $class = new \ReflectionClass($this); $this->methods = $class->getMethods(); } @@ -96,12 +76,12 @@ class Updater } if ($this->methods === null) { - throw new UpdaterException(t('Couldn\'t retrieve updater class methods.')); + throw new UpdaterException('Couldn\'t retrieve LegacyUpdater class methods.'); } foreach ($this->methods as $method) { // Not an update method or already done, pass. - if (!startsWith($method->getName(), 'updateMethod') + if (! startsWith($method->getName(), 'updateMethod') || in_array($method->getName(), $this->doneUpdates) ) { continue; @@ -114,7 +94,7 @@ class Updater if ($res === true) { $updatesRan[] = $method->getName(); } - } catch (Exception $e) { + } catch (\Exception $e) { throw new UpdaterException($method, $e); } } @@ -131,432 +111,4 @@ class Updater { return $this->doneUpdates; } - - /** - * Move deprecated options.php to config.php. - * - * Milestone 0.9 (old versioning) - shaarli/Shaarli#41: - * options.php is not supported anymore. - */ - public function updateMethodMergeDeprecatedConfigFile() - { - if (is_file($this->conf->get('resource.data_dir') . '/options.php')) { - include $this->conf->get('resource.data_dir') . '/options.php'; - - // Load GLOBALS into config - $allowedKeys = array_merge(ConfigPhp::$ROOT_KEYS); - $allowedKeys[] = 'config'; - foreach ($GLOBALS as $key => $value) { - if (in_array($key, $allowedKeys)) { - $this->conf->set($key, $value); - } - } - $this->conf->write($this->isLoggedIn); - unlink($this->conf->get('resource.data_dir') . '/options.php'); - } - - return true; - } - - /** - * Move old configuration in PHP to the new config system in JSON format. - * - * Will rename 'config.php' into 'config.save.php' and create 'config.json.php'. - * It will also convert legacy setting keys to the new ones. - */ - public function updateMethodConfigToJson() - { - // JSON config already exists, nothing to do. - if ($this->conf->getConfigIO() instanceof ConfigJson) { - return true; - } - - $configPhp = new ConfigPhp(); - $configJson = new ConfigJson(); - $oldConfig = $configPhp->read($this->conf->getConfigFile() . '.php'); - rename($this->conf->getConfigFileExt(), $this->conf->getConfigFile() . '.save.php'); - $this->conf->setConfigIO($configJson); - $this->conf->reload(); - - $legacyMap = array_flip(ConfigPhp::$LEGACY_KEYS_MAPPING); - foreach (ConfigPhp::$ROOT_KEYS as $key) { - $this->conf->set($legacyMap[$key], $oldConfig[$key]); - } - - // Set sub config keys (config and plugins) - $subConfig = array('config', 'plugins'); - foreach ($subConfig as $sub) { - foreach ($oldConfig[$sub] as $key => $value) { - if (isset($legacyMap[$sub . '.' . $key])) { - $configKey = $legacyMap[$sub . '.' . $key]; - } else { - $configKey = $sub . '.' . $key; - } - $this->conf->set($configKey, $value); - } - } - - try { - $this->conf->write($this->isLoggedIn); - return true; - } catch (IOException $e) { - error_log($e->getMessage()); - return false; - } - } - - /** - * Escape settings which have been manually escaped in every request in previous versions: - * - general.title - * - general.header_link - * - redirector.url - * - * @return bool true if the update is successful, false otherwise. - */ - public function updateMethodEscapeUnescapedConfig() - { - try { - $this->conf->set('general.title', escape($this->conf->get('general.title'))); - $this->conf->set('general.header_link', escape($this->conf->get('general.header_link'))); - $this->conf->write($this->isLoggedIn); - } catch (Exception $e) { - error_log($e->getMessage()); - return false; - } - return true; - } - - /** - * Update the database to use the new ID system, which replaces linkdate primary keys. - * Also, creation and update dates are now DateTime objects (done by LinkDB). - * - * Since this update is very sensitve (changing the whole database), the datastore will be - * automatically backed up into the file datastore..php. - * - * LinkDB also adds the field 'shorturl' with the precedent format (linkdate smallhash), - * which will be saved by this method. - * - * @return bool true if the update is successful, false otherwise. - */ - public function updateMethodDatastoreIds() - { - // up to date database - if (isset($this->linkDB[0])) { - return true; - } - - $save = $this->conf->get('resource.data_dir') . '/datastore.' . date('YmdHis') . '.php'; - copy($this->conf->get('resource.datastore'), $save); - - $links = array(); - foreach ($this->linkDB as $offset => $value) { - $links[] = $value; - unset($this->linkDB[$offset]); - } - $links = array_reverse($links); - $cpt = 0; - foreach ($links as $l) { - unset($l['linkdate']); - $l['id'] = $cpt; - $this->linkDB[$cpt++] = $l; - } - - $this->linkDB->save($this->conf->get('resource.page_cache')); - $this->linkDB->reorder(); - - return true; - } - - /** - * Rename tags starting with a '-' to work with tag exclusion search. - */ - public function updateMethodRenameDashTags() - { - $linklist = $this->linkDB->filterSearch(); - foreach ($linklist as $key => $link) { - $link['tags'] = preg_replace('/(^| )\-/', '$1', $link['tags']); - $link['tags'] = implode(' ', array_unique(LinkFilter::tagsStrToArray($link['tags'], true))); - $this->linkDB[$key] = $link; - } - $this->linkDB->save($this->conf->get('resource.page_cache')); - return true; - } - - /** - * Initialize API settings: - * - api.enabled: true - * - api.secret: generated secret - */ - public function updateMethodApiSettings() - { - if ($this->conf->exists('api.secret')) { - return true; - } - - $this->conf->set('api.enabled', true); - $this->conf->set( - 'api.secret', - generate_api_secret( - $this->conf->get('credentials.login'), - $this->conf->get('credentials.salt') - ) - ); - $this->conf->write($this->isLoggedIn); - return true; - } - - /** - * New setting: theme name. If the default theme is used, nothing to do. - * - * If the user uses a custom theme, raintpl_tpl dir is updated to the parent directory, - * and the current theme is set as default in the theme setting. - * - * @return bool true if the update is successful, false otherwise. - */ - public function updateMethodDefaultTheme() - { - // raintpl_tpl isn't the root template directory anymore. - // We run the update only if this folder still contains the template files. - $tplDir = $this->conf->get('resource.raintpl_tpl'); - $tplFile = $tplDir . '/linklist.html'; - if (!file_exists($tplFile)) { - return true; - } - - $parent = dirname($tplDir); - $this->conf->set('resource.raintpl_tpl', $parent); - $this->conf->set('resource.theme', trim(str_replace($parent, '', $tplDir), '/')); - $this->conf->write($this->isLoggedIn); - - // Dependency injection gore - RainTPL::$tpl_dir = $tplDir; - - return true; - } - - /** - * Move the file to inc/user.css to data/user.css. - * - * Note: Due to hardcoded paths, it's not unit testable. But one line of code should be fine. - * - * @return bool true if the update is successful, false otherwise. - */ - public function updateMethodMoveUserCss() - { - if (!is_file('inc/user.css')) { - return true; - } - - return rename('inc/user.css', 'data/user.css'); - } - - /** - * * `markdown_escape` is a new setting, set to true as default. - * - * If the markdown plugin was already enabled, escaping is disabled to avoid - * breaking existing entries. - */ - public function updateMethodEscapeMarkdown() - { - if ($this->conf->exists('security.markdown_escape')) { - return true; - } - - if (in_array('markdown', $this->conf->get('general.enabled_plugins'))) { - $this->conf->set('security.markdown_escape', false); - } else { - $this->conf->set('security.markdown_escape', true); - } - $this->conf->write($this->isLoggedIn); - - return true; - } - - /** - * Add 'http://' to Piwik URL the setting is set. - * - * @return bool true if the update is successful, false otherwise. - */ - public function updateMethodPiwikUrl() - { - if (!$this->conf->exists('plugins.PIWIK_URL') || startsWith($this->conf->get('plugins.PIWIK_URL'), 'http')) { - return true; - } - - $this->conf->set('plugins.PIWIK_URL', 'http://' . $this->conf->get('plugins.PIWIK_URL')); - $this->conf->write($this->isLoggedIn); - - return true; - } - - /** - * Use ATOM feed as default. - */ - public function updateMethodAtomDefault() - { - if (!$this->conf->exists('feed.show_atom') || $this->conf->get('feed.show_atom') === true) { - return true; - } - - $this->conf->set('feed.show_atom', true); - $this->conf->write($this->isLoggedIn); - - return true; - } - - /** - * Update updates.check_updates_branch setting. - * - * If the current major version digit matches the latest branch - * major version digit, we set the branch to `latest`, - * otherwise we'll check updates on the `stable` branch. - * - * No update required for the dev version. - * - * Note: due to hardcoded URL and lack of dependency injection, this is not unit testable. - * - * FIXME! This needs to be removed when we switch to first digit major version - * instead of the second one since the versionning process will change. - */ - public function updateMethodCheckUpdateRemoteBranch() - { - if (SHAARLI_VERSION === 'dev' || $this->conf->get('updates.check_updates_branch') === 'latest') { - return true; - } - - // Get latest branch major version digit - $latestVersion = ApplicationUtils::getLatestGitVersionCode( - 'https://raw.githubusercontent.com/shaarli/Shaarli/latest/shaarli_version.php', - 5 - ); - if (preg_match('/(\d+)\.\d+$/', $latestVersion, $matches) === false) { - return false; - } - $latestMajor = $matches[1]; - - // Get current major version digit - preg_match('/(\d+)\.\d+$/', SHAARLI_VERSION, $matches); - $currentMajor = $matches[1]; - - if ($currentMajor === $latestMajor) { - $branch = 'latest'; - } else { - $branch = 'stable'; - } - $this->conf->set('updates.check_updates_branch', $branch); - $this->conf->write($this->isLoggedIn); - return true; - } - - /** - * Reset history store file due to date format change. - */ - public function updateMethodResetHistoryFile() - { - if (is_file($this->conf->get('resource.history'))) { - unlink($this->conf->get('resource.history')); - } - return true; - } - - /** - * Save the datastore -> the link order is now applied when links are saved. - */ - public function updateMethodReorderDatastore() - { - $this->linkDB->save($this->conf->get('resource.page_cache')); - return true; - } - - /** - * Change privateonly session key to visibility. - */ - public function updateMethodVisibilitySession() - { - if (isset($_SESSION['privateonly'])) { - unset($_SESSION['privateonly']); - $_SESSION['visibility'] = 'private'; - } - return true; - } - - /** - * Add download size and timeout to the configuration file - * - * @return bool true if the update is successful, false otherwise. - */ - public function updateMethodDownloadSizeAndTimeoutConf() - { - if ($this->conf->exists('general.download_max_size') - && $this->conf->exists('general.download_timeout') - ) { - return true; - } - - if (!$this->conf->exists('general.download_max_size')) { - $this->conf->set('general.download_max_size', 1024 * 1024 * 4); - } - - if (!$this->conf->exists('general.download_timeout')) { - $this->conf->set('general.download_timeout', 30); - } - - $this->conf->write($this->isLoggedIn); - return true; - } - - /** - * * Move thumbnails management to WebThumbnailer, coming with new settings. - */ - public function updateMethodWebThumbnailer() - { - if ($this->conf->exists('thumbnails.mode')) { - return true; - } - - $thumbnailsEnabled = extension_loaded('gd') && $this->conf->get('thumbnail.enable_thumbnails', true); - $this->conf->set('thumbnails.mode', $thumbnailsEnabled ? Thumbnailer::MODE_ALL : Thumbnailer::MODE_NONE); - $this->conf->set('thumbnails.width', 125); - $this->conf->set('thumbnails.height', 90); - $this->conf->remove('thumbnail'); - $this->conf->write(true); - - if ($thumbnailsEnabled) { - $this->session['warnings'][] = t( - 'You have enabled or changed thumbnails mode. Please synchronize them.' - ); - } - - return true; - } - - /** - * Set sticky = false on all links - * - * @return bool true if the update is successful, false otherwise. - */ - public function updateMethodSetSticky() - { - foreach ($this->linkDB as $key => $link) { - if (isset($link['sticky'])) { - return true; - } - $link['sticky'] = false; - $this->linkDB[$key] = $link; - } - - $this->linkDB->save($this->conf->get('resource.page_cache')); - - return true; - } - - /** - * Remove redirector settings. - */ - public function updateMethodRemoveRedirector() - { - $this->conf->remove('redirector'); - $this->conf->write(true); - return true; - } } diff --git a/application/updater/UpdaterUtils.php b/application/updater/UpdaterUtils.php index 34d4f422..828a49fc 100644 --- a/application/updater/UpdaterUtils.php +++ b/application/updater/UpdaterUtils.php @@ -1,39 +1,44 @@