aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--application/Utils.php33
-rw-r--r--application/bookmark/BookmarkFileService.php38
-rw-r--r--application/bookmark/BookmarkServiceInterface.php27
-rw-r--r--application/front/controller/visitor/DailyController.php105
-rw-r--r--application/helper/DailyPageHelper.php208
-rw-r--r--inc/languages/fr/LC_MESSAGES/shaarli.po256
-rw-r--r--tests/bookmark/BookmarkFileServiceTest.php124
-rw-r--r--tests/front/controller/visitor/DailyControllerTest.php412
-rw-r--r--tests/helper/DailyPageHelperTest.php262
-rw-r--r--tpl/default/daily.html32
-rw-r--r--tpl/default/dailyrss.html11
11 files changed, 1190 insertions, 318 deletions
diff --git a/application/Utils.php b/application/Utils.php
index bc1c9f5d..db046893 100644
--- a/application/Utils.php
+++ b/application/Utils.php
@@ -327,6 +327,23 @@ function format_date($date, $time = true, $intl = true)
327} 327}
328 328
329/** 329/**
330 * Format the date month according to the locale.
331 *
332 * @param DateTimeInterface $date to format.
333 *
334 * @return bool|string Formatted date, or false if the input is invalid.
335 */
336function format_month(DateTimeInterface $date)
337{
338 if (! $date instanceof DateTimeInterface) {
339 return false;
340 }
341
342 return strftime('%B', $date->getTimestamp());
343}
344
345
346/**
330 * Check if the input is an integer, no matter its real type. 347 * Check if the input is an integer, no matter its real type.
331 * 348 *
332 * PHP is a bit messy regarding this: 349 * PHP is a bit messy regarding this:
@@ -454,16 +471,20 @@ function alphabetical_sort(&$data, $reverse = false, $byKeys = false)
454 * Wrapper function for translation which match the API 471 * Wrapper function for translation which match the API
455 * of gettext()/_() and ngettext(). 472 * of gettext()/_() and ngettext().
456 * 473 *
457 * @param string $text Text to translate. 474 * @param string $text Text to translate.
458 * @param string $nText The plural message ID. 475 * @param string $nText The plural message ID.
459 * @param int $nb The number of items for plural forms. 476 * @param int $nb The number of items for plural forms.
460 * @param string $domain The domain where the translation is stored (default: shaarli). 477 * @param string $domain The domain where the translation is stored (default: shaarli).
478 * @param array $variables Associative array of variables to replace in translated text.
479 * @param bool $fixCase Apply `ucfirst` on the translated string, might be useful for strings with variables.
461 * 480 *
462 * @return string Text translated. 481 * @return string Text translated.
463 */ 482 */
464function t($text, $nText = '', $nb = 1, $domain = 'shaarli') 483function t($text, $nText = '', $nb = 1, $domain = 'shaarli', $variables = [], $fixCase = false)
465{ 484{
466 return dn__($domain, $text, $nText, $nb); 485 $postFunction = $fixCase ? 'ucfirst' : function ($input) { return $input; };
486
487 return $postFunction(dn__($domain, $text, $nText, $nb, $variables));
467} 488}
468 489
469/** 490/**
diff --git a/application/bookmark/BookmarkFileService.php b/application/bookmark/BookmarkFileService.php
index 14b3d620..0df2f47f 100644
--- a/application/bookmark/BookmarkFileService.php
+++ b/application/bookmark/BookmarkFileService.php
@@ -343,26 +343,42 @@ class BookmarkFileService implements BookmarkServiceInterface
343 /** 343 /**
344 * @inheritDoc 344 * @inheritDoc
345 */ 345 */
346 public function days(): array 346 public function findByDate(
347 { 347 \DateTimeInterface $from,
348 $bookmarkDays = []; 348 \DateTimeInterface $to,
349 foreach ($this->search() as $bookmark) { 349 ?\DateTimeInterface &$previous,
350 $bookmarkDays[$bookmark->getCreated()->format('Ymd')] = 0; 350 ?\DateTimeInterface &$next
351 ): array {
352 $out = [];
353 $previous = null;
354 $next = null;
355
356 foreach ($this->search([], null, false, false, true) as $bookmark) {
357 if ($to < $bookmark->getCreated()) {
358 $next = $bookmark->getCreated();
359 } else if ($from < $bookmark->getCreated() && $to > $bookmark->getCreated()) {
360 $out[] = $bookmark;
361 } else {
362 if ($previous !== null) {
363 break;
364 }
365 $previous = $bookmark->getCreated();
366 }
351 } 367 }
352 $bookmarkDays = array_keys($bookmarkDays);
353 sort($bookmarkDays);
354 368
355 return array_map('strval', $bookmarkDays); 369 return $out;
356 } 370 }
357 371
358 /** 372 /**
359 * @inheritDoc 373 * @inheritDoc
360 */ 374 */
361 public function filterDay(string $request) 375 public function getLatest(): ?Bookmark
362 { 376 {
363 $visibility = $this->isLoggedIn ? BookmarkFilter::$ALL : BookmarkFilter::$PUBLIC; 377 foreach ($this->search([], null, false, false, true) as $bookmark) {
378 return $bookmark;
379 }
364 380
365 return $this->bookmarkFilter->filter(BookmarkFilter::$FILTER_DAY, $request, false, $visibility); 381 return null;
366 } 382 }
367 383
368 /** 384 /**
diff --git a/application/bookmark/BookmarkServiceInterface.php b/application/bookmark/BookmarkServiceInterface.php
index 9fa61533..08cdbb4e 100644
--- a/application/bookmark/BookmarkServiceInterface.php
+++ b/application/bookmark/BookmarkServiceInterface.php
@@ -156,22 +156,29 @@ interface BookmarkServiceInterface
156 public function bookmarksCountPerTag(array $filteringTags = [], ?string $visibility = null): array; 156 public function bookmarksCountPerTag(array $filteringTags = [], ?string $visibility = null): array;
157 157
158 /** 158 /**
159 * Returns the list of days containing articles (oldest first) 159 * Return a list of bookmark matching provided period of time.
160 * It also update directly previous and next date outside of given period found in the datastore.
160 * 161 *
161 * @return array containing days (in format YYYYMMDD). 162 * @param \DateTimeInterface $from Starting date.
163 * @param \DateTimeInterface $to Ending date.
164 * @param \DateTimeInterface|null $previous (by reference) updated with first created date found before $from.
165 * @param \DateTimeInterface|null $next (by reference) updated with first created date found after $to.
166 *
167 * @return array List of bookmarks matching provided period of time.
162 */ 168 */
163 public function days(): array; 169 public function findByDate(
170 \DateTimeInterface $from,
171 \DateTimeInterface $to,
172 ?\DateTimeInterface &$previous,
173 ?\DateTimeInterface &$next
174 ): array;
164 175
165 /** 176 /**
166 * Returns the list of articles for a given day. 177 * Returns the latest bookmark by creation date.
167 *
168 * @param string $request day to filter. Format: YYYYMMDD.
169 * 178 *
170 * @return Bookmark[] list of shaare found. 179 * @return Bookmark|null Found Bookmark or null if the datastore is empty.
171 *
172 * @throws BookmarkNotFoundException
173 */ 180 */
174 public function filterDay(string $request); 181 public function getLatest(): ?Bookmark;
175 182
176 /** 183 /**
177 * Creates the default database after a fresh install. 184 * Creates the default database after a fresh install.
diff --git a/application/front/controller/visitor/DailyController.php b/application/front/controller/visitor/DailyController.php
index 07617cf1..728bc2d8 100644
--- a/application/front/controller/visitor/DailyController.php
+++ b/application/front/controller/visitor/DailyController.php
@@ -5,8 +5,8 @@ declare(strict_types=1);
5namespace Shaarli\Front\Controller\Visitor; 5namespace Shaarli\Front\Controller\Visitor;
6 6
7use DateTime; 7use DateTime;
8use DateTimeImmutable;
9use Shaarli\Bookmark\Bookmark; 8use Shaarli\Bookmark\Bookmark;
9use Shaarli\Helper\DailyPageHelper;
10use Shaarli\Render\TemplatePage; 10use Shaarli\Render\TemplatePage;
11use Slim\Http\Request; 11use Slim\Http\Request;
12use Slim\Http\Response; 12use Slim\Http\Response;
@@ -26,32 +26,20 @@ class DailyController extends ShaarliVisitorController
26 */ 26 */
27 public function index(Request $request, Response $response): Response 27 public function index(Request $request, Response $response): Response
28 { 28 {
29 $day = $request->getQueryParam('day') ?? date('Ymd'); 29 $type = DailyPageHelper::extractRequestedType($request);
30 30 $format = DailyPageHelper::getFormatByType($type);
31 $availableDates = $this->container->bookmarkService->days(); 31 $latestBookmark = $this->container->bookmarkService->getLatest();
32 $nbAvailableDates = count($availableDates); 32 $dateTime = DailyPageHelper::extractRequestedDateTime($type, $request->getQueryParam($type), $latestBookmark);
33 $index = array_search($day, $availableDates); 33 $start = DailyPageHelper::getStartDateTimeByType($type, $dateTime);
34 34 $end = DailyPageHelper::getEndDateTimeByType($type, $dateTime);
35 if ($index === false) { 35 $dailyDesc = DailyPageHelper::getDescriptionByType($type, $dateTime);
36 // no bookmarks for day, but at least one day with bookmarks 36
37 $day = $availableDates[$nbAvailableDates - 1] ?? $day; 37 $linksToDisplay = $this->container->bookmarkService->findByDate(
38 $previousDay = $availableDates[$nbAvailableDates - 2] ?? ''; 38 $start,
39 } else { 39 $end,
40 $previousDay = $availableDates[$index - 1] ?? ''; 40 $previousDay,
41 $nextDay = $availableDates[$index + 1] ?? ''; 41 $nextDay
42 } 42 );
43
44 if ($day === date('Ymd')) {
45 $this->assignView('dayDesc', t('Today'));
46 } elseif ($day === date('Ymd', strtotime('-1 days'))) {
47 $this->assignView('dayDesc', t('Yesterday'));
48 }
49
50 try {
51 $linksToDisplay = $this->container->bookmarkService->filterDay($day);
52 } catch (\Exception $exc) {
53 $linksToDisplay = [];
54 }
55 43
56 $formatter = $this->container->formatterFactory->getFormatter(); 44 $formatter = $this->container->formatterFactory->getFormatter();
57 $formatter->addContextData('base_path', $this->container->basePath); 45 $formatter->addContextData('base_path', $this->container->basePath);
@@ -63,13 +51,15 @@ class DailyController extends ShaarliVisitorController
63 $linksToDisplay[$key]['description'] = $bookmark->getDescription(); 51 $linksToDisplay[$key]['description'] = $bookmark->getDescription();
64 } 52 }
65 53
66 $dayDate = DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, $day.'_000000');
67 $data = [ 54 $data = [
68 'linksToDisplay' => $linksToDisplay, 55 'linksToDisplay' => $linksToDisplay,
69 'day' => $dayDate->getTimestamp(), 56 'dayDate' => $start,
70 'dayDate' => $dayDate, 57 'day' => $start->getTimestamp(),
71 'previousday' => $previousDay ?? '', 58 'previousday' => $previousDay ? $previousDay->format($format) : '',
72 'nextday' => $nextDay ?? '', 59 'nextday' => $nextDay ? $nextDay->format($format) : '',
60 'dayDesc' => $dailyDesc,
61 'type' => $type,
62 'localizedType' => $this->translateType($type),
73 ]; 63 ];
74 64
75 // Hooks are called before column construction so that plugins don't have to deal with columns. 65 // Hooks are called before column construction so that plugins don't have to deal with columns.
@@ -82,7 +72,7 @@ class DailyController extends ShaarliVisitorController
82 $mainTitle = $this->container->conf->get('general.title', 'Shaarli'); 72 $mainTitle = $this->container->conf->get('general.title', 'Shaarli');
83 $this->assignView( 73 $this->assignView(
84 'pagetitle', 74 'pagetitle',
85 t('Daily') .' - '. format_date($dayDate, false) . ' - ' . $mainTitle 75 $data['localizedType'] . ' - ' . $data['dayDesc'] . ' - ' . $mainTitle
86 ); 76 );
87 77
88 return $response->write($this->render(TemplatePage::DAILY)); 78 return $response->write($this->render(TemplatePage::DAILY));
@@ -106,11 +96,14 @@ class DailyController extends ShaarliVisitorController
106 } 96 }
107 97
108 $days = []; 98 $days = [];
99 $type = DailyPageHelper::extractRequestedType($request);
100 $format = DailyPageHelper::getFormatByType($type);
101 $length = DailyPageHelper::getRssLengthByType($type);
109 foreach ($this->container->bookmarkService->search() as $bookmark) { 102 foreach ($this->container->bookmarkService->search() as $bookmark) {
110 $day = $bookmark->getCreated()->format('Ymd'); 103 $day = $bookmark->getCreated()->format($format);
111 104
112 // Stop iterating after DAILY_RSS_NB_DAYS entries 105 // Stop iterating after DAILY_RSS_NB_DAYS entries
113 if (count($days) === static::$DAILY_RSS_NB_DAYS && !isset($days[$day])) { 106 if (count($days) === $length && !isset($days[$day])) {
114 break; 107 break;
115 } 108 }
116 109
@@ -127,12 +120,19 @@ class DailyController extends ShaarliVisitorController
127 120
128 /** @var Bookmark[] $bookmarks */ 121 /** @var Bookmark[] $bookmarks */
129 foreach ($days as $day => $bookmarks) { 122 foreach ($days as $day => $bookmarks) {
130 $dayDatetime = DateTimeImmutable::createFromFormat(Bookmark::LINK_DATE_FORMAT, $day.'_000000'); 123 $dayDateTime = DailyPageHelper::extractRequestedDateTime($type, (string) $day);
124 $endDateTime = DailyPageHelper::getEndDateTimeByType($type, $dayDateTime);
125
126 // We only want the RSS entry to be published when the period is over.
127 if (new DateTime() < $endDateTime) {
128 continue;
129 }
130
131 $dataPerDay[$day] = [ 131 $dataPerDay[$day] = [
132 'date' => $dayDatetime, 132 'date' => $endDateTime,
133 'date_rss' => $dayDatetime->format(DateTime::RSS), 133 'date_rss' => $endDateTime->format(DateTime::RSS),
134 'date_human' => format_date($dayDatetime, false, true), 134 'date_human' => DailyPageHelper::getDescriptionByType($type, $dayDateTime),
135 'absolute_url' => $indexUrl . 'daily?day=' . $day, 135 'absolute_url' => $indexUrl . 'daily?'. $type .'=' . $day,
136 'links' => [], 136 'links' => [],
137 ]; 137 ];
138 138
@@ -141,16 +141,20 @@ class DailyController extends ShaarliVisitorController
141 141
142 // Make permalink URL absolute 142 // Make permalink URL absolute
143 if ($bookmark->isNote()) { 143 if ($bookmark->isNote()) {
144 $dataPerDay[$day]['links'][$key]['url'] = $indexUrl . $bookmark->getUrl(); 144 $dataPerDay[$day]['links'][$key]['url'] = rtrim($indexUrl, '/') . $bookmark->getUrl();
145 } 145 }
146 } 146 }
147 } 147 }
148 148
149 $this->assignView('title', $this->container->conf->get('general.title', 'Shaarli')); 149 $this->assignAllView([
150 $this->assignView('index_url', $indexUrl); 150 'title' => $this->container->conf->get('general.title', 'Shaarli'),
151 $this->assignView('page_url', $pageUrl); 151 'index_url' => $indexUrl,
152 $this->assignView('hide_timestamps', $this->container->conf->get('privacy.hide_timestamps', false)); 152 'page_url' => $pageUrl,
153 $this->assignView('days', $dataPerDay); 153 'hide_timestamps' => $this->container->conf->get('privacy.hide_timestamps', false),
154 'days' => $dataPerDay,
155 'type' => $type,
156 'localizedType' => $this->translateType($type),
157 ]);
154 158
155 $rssContent = $this->render(TemplatePage::DAILY_RSS); 159 $rssContent = $this->render(TemplatePage::DAILY_RSS);
156 160
@@ -189,4 +193,13 @@ class DailyController extends ShaarliVisitorController
189 193
190 return $columns; 194 return $columns;
191 } 195 }
196
197 protected function translateType($type): string
198 {
199 return [
200 t('day') => t('Daily'),
201 t('week') => t('Weekly'),
202 t('month') => t('Monthly'),
203 ][t($type)] ?? t('Daily');
204 }
192} 205}
diff --git a/application/helper/DailyPageHelper.php b/application/helper/DailyPageHelper.php
new file mode 100644
index 00000000..5fabc907
--- /dev/null
+++ b/application/helper/DailyPageHelper.php
@@ -0,0 +1,208 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Helper;
6
7use Shaarli\Bookmark\Bookmark;
8use Slim\Http\Request;
9
10class DailyPageHelper
11{
12 public const MONTH = 'month';
13 public const WEEK = 'week';
14 public const DAY = 'day';
15
16 /**
17 * Extracts the type of the daily to display from the HTTP request parameters
18 *
19 * @param Request $request HTTP request
20 *
21 * @return string month/week/day
22 */
23 public static function extractRequestedType(Request $request): string
24 {
25 if ($request->getQueryParam(static::MONTH) !== null) {
26 return static::MONTH;
27 } elseif ($request->getQueryParam(static::WEEK) !== null) {
28 return static::WEEK;
29 }
30
31 return static::DAY;
32 }
33
34 /**
35 * Extracts a DateTimeImmutable from provided HTTP request.
36 * If no parameter is provided, we rely on the creation date of the latest provided created bookmark.
37 * If the datastore is empty or no bookmark is provided, we use the current date.
38 *
39 * @param string $type month/week/day
40 * @param string|null $requestedDate Input string extracted from the request
41 * @param Bookmark|null $latestBookmark Latest bookmark found in the datastore (by date)
42 *
43 * @return \DateTimeImmutable from input or latest bookmark.
44 *
45 * @throws \Exception Type not supported.
46 */
47 public static function extractRequestedDateTime(
48 string $type,
49 ?string $requestedDate,
50 Bookmark $latestBookmark = null
51 ): \DateTimeImmutable {
52 $format = static::getFormatByType($type);
53 if (empty($requestedDate)) {
54 return $latestBookmark instanceof Bookmark
55 ? new \DateTimeImmutable($latestBookmark->getCreated()->format(\DateTime::ATOM))
56 : new \DateTimeImmutable()
57 ;
58 }
59
60 // W is not supported by createFromFormat...
61 if ($type === static::WEEK) {
62 return (new \DateTimeImmutable())
63 ->setISODate((int) substr($requestedDate, 0, 4), (int) substr($requestedDate, 4, 2))
64 ;
65 }
66
67 return \DateTimeImmutable::createFromFormat($format, $requestedDate);
68 }
69
70 /**
71 * Get the DateTime format used by provided type
72 * Examples:
73 * - day: 20201016 (<year><month><day>)
74 * - week: 202041 (<year><week number>)
75 * - month: 202010 (<year><month>)
76 *
77 * @param string $type month/week/day
78 *
79 * @return string DateTime compatible format
80 *
81 * @see https://www.php.net/manual/en/datetime.format.php
82 *
83 * @throws \Exception Type not supported.
84 */
85 public static function getFormatByType(string $type): string
86 {
87 switch ($type) {
88 case static::MONTH:
89 return 'Ym';
90 case static::WEEK:
91 return 'YW';
92 case static::DAY:
93 return 'Ymd';
94 default:
95 throw new \Exception('Unsupported daily format type');
96 }
97 }
98
99 /**
100 * Get the first DateTime of the time period depending on given datetime and type.
101 * Note: DateTimeImmutable is required because we rely heavily on DateTime->modify() syntax
102 * and we don't want to alter original datetime.
103 *
104 * @param string $type month/week/day
105 * @param \DateTimeImmutable $requested DateTime extracted from request input
106 * (should come from extractRequestedDateTime)
107 *
108 * @return \DateTimeInterface First DateTime of the time period
109 *
110 * @throws \Exception Type not supported.
111 */
112 public static function getStartDateTimeByType(string $type, \DateTimeImmutable $requested): \DateTimeInterface
113 {
114 switch ($type) {
115 case static::MONTH:
116 return $requested->modify('first day of this month midnight');
117 case static::WEEK:
118 return $requested->modify('Monday this week midnight');
119 case static::DAY:
120 return $requested->modify('Today midnight');
121 default:
122 throw new \Exception('Unsupported daily format type');
123 }
124 }
125
126 /**
127 * Get the last DateTime of the time period depending on given datetime and type.
128 * Note: DateTimeImmutable is required because we rely heavily on DateTime->modify() syntax
129 * and we don't want to alter original datetime.
130 *
131 * @param string $type month/week/day
132 * @param \DateTimeImmutable $requested DateTime extracted from request input
133 * (should come from extractRequestedDateTime)
134 *
135 * @return \DateTimeInterface Last DateTime of the time period
136 *
137 * @throws \Exception Type not supported.
138 */
139 public static function getEndDateTimeByType(string $type, \DateTimeImmutable $requested): \DateTimeInterface
140 {
141 switch ($type) {
142 case static::MONTH:
143 return $requested->modify('last day of this month 23:59:59');
144 case static::WEEK:
145 return $requested->modify('Sunday this week 23:59:59');
146 case static::DAY:
147 return $requested->modify('Today 23:59:59');
148 default:
149 throw new \Exception('Unsupported daily format type');
150 }
151 }
152
153 /**
154 * Get localized description of the time period depending on given datetime and type.
155 * Example: for a month period, it returns `October, 2020`.
156 *
157 * @param string $type month/week/day
158 * @param \DateTimeImmutable $requested DateTime extracted from request input
159 * (should come from extractRequestedDateTime)
160 *
161 * @return string Localized time period description
162 *
163 * @throws \Exception Type not supported.
164 */
165 public static function getDescriptionByType(string $type, \DateTimeImmutable $requested): string
166 {
167 switch ($type) {
168 case static::MONTH:
169 return $requested->format('F') . ', ' . $requested->format('Y');
170 case static::WEEK:
171 $requested = $requested->modify('Monday this week');
172 return t('Week') . ' ' . $requested->format('W') . ' (' . format_date($requested, false) . ')';
173 case static::DAY:
174 $out = '';
175 if ($requested->format('Ymd') === date('Ymd')) {
176 $out = t('Today') . ' - ';
177 } elseif ($requested->format('Ymd') === date('Ymd', strtotime('-1 days'))) {
178 $out = t('Yesterday') . ' - ';
179 }
180 return $out . format_date($requested, false);
181 default:
182 throw new \Exception('Unsupported daily format type');
183 }
184 }
185
186 /**
187 * Get the number of items to display in the RSS feed depending on the given type.
188 *
189 * @param string $type month/week/day
190 *
191 * @return int number of elements
192 *
193 * @throws \Exception Type not supported.
194 */
195 public static function getRssLengthByType(string $type): int
196 {
197 switch ($type) {
198 case static::MONTH:
199 return 12; // 1 year
200 case static::WEEK:
201 return 26; // ~6 months
202 case static::DAY:
203 return 30; // ~1 month
204 default:
205 throw new \Exception('Unsupported daily format type');
206 }
207 }
208}
diff --git a/inc/languages/fr/LC_MESSAGES/shaarli.po b/inc/languages/fr/LC_MESSAGES/shaarli.po
index 3f14d22c..6d4ff0bd 100644
--- a/inc/languages/fr/LC_MESSAGES/shaarli.po
+++ b/inc/languages/fr/LC_MESSAGES/shaarli.po
@@ -1,8 +1,8 @@
1msgid "" 1msgid ""
2msgstr "" 2msgstr ""
3"Project-Id-Version: Shaarli\n" 3"Project-Id-Version: Shaarli\n"
4"POT-Creation-Date: 2020-10-27 19:32+0100\n" 4"POT-Creation-Date: 2020-10-27 19:44+0100\n"
5"PO-Revision-Date: 2020-10-27 19:32+0100\n" 5"PO-Revision-Date: 2020-10-27 19:44+0100\n"
6"Last-Translator: \n" 6"Last-Translator: \n"
7"Language-Team: Shaarli\n" 7"Language-Team: Shaarli\n"
8"Language: fr_FR\n" 8"Language: fr_FR\n"
@@ -20,78 +20,11 @@ msgstr ""
20"X-Poedit-SearchPath-3: init.php\n" 20"X-Poedit-SearchPath-3: init.php\n"
21"X-Poedit-SearchPath-4: plugins\n" 21"X-Poedit-SearchPath-4: plugins\n"
22 22
23#: application/ApplicationUtils.php:162 23#: application/History.php:180
24#, php-format
25msgid ""
26"Your PHP version is obsolete! Shaarli requires at least PHP %s, and thus "
27"cannot run. Your PHP version has known security vulnerabilities and should "
28"be updated as soon as possible."
29msgstr ""
30"Votre version de PHP est obsolète ! Shaarli nécessite au moins PHP %s, et ne "
31"peut donc pas fonctionner. Votre version de PHP a des failles de sécurités "
32"connues et devrait être mise à jour au plus tôt."
33
34#: application/ApplicationUtils.php:195 application/ApplicationUtils.php:215
35msgid "directory is not readable"
36msgstr "le répertoire n'est pas accessible en lecture"
37
38#: application/ApplicationUtils.php:218
39msgid "directory is not writable"
40msgstr "le répertoire n'est pas accessible en écriture"
41
42#: application/ApplicationUtils.php:240
43msgid "file is not readable"
44msgstr "le fichier n'est pas accessible en lecture"
45
46#: application/ApplicationUtils.php:243
47msgid "file is not writable"
48msgstr "le fichier n'est pas accessible en écriture"
49
50#: application/ApplicationUtils.php:277
51msgid "Configuration parsing"
52msgstr "Chargement de la configuration"
53
54#: application/ApplicationUtils.php:278
55msgid "Slim Framework (routing, etc.)"
56msgstr "Slim Framwork (routage, etc.)"
57
58#: application/ApplicationUtils.php:279
59msgid "Multibyte (Unicode) string support"
60msgstr "Support des chaînes de caractère multibytes (Unicode)"
61
62#: application/ApplicationUtils.php:280
63msgid "Required to use thumbnails"
64msgstr "Obligatoire pour utiliser les miniatures"
65
66#: application/ApplicationUtils.php:281
67msgid "Localized text sorting (e.g. e->è->f)"
68msgstr "Tri des textes traduits (ex : e->è->f)"
69
70#: application/ApplicationUtils.php:282
71msgid "Better retrieval of bookmark metadata and thumbnail"
72msgstr "Meilleure récupération des meta-données des marque-pages et minatures"
73
74#: application/ApplicationUtils.php:283
75msgid "Use the translation system in gettext mode"
76msgstr "Utiliser le système de traduction en mode gettext"
77
78#: application/ApplicationUtils.php:284
79msgid "Login using LDAP server"
80msgstr "Authentification via un serveur LDAP"
81
82#: application/FileUtils.php:100
83msgid "Provided path is not a directory."
84msgstr "Le chemin fourni n'est pas un dossier."
85
86#: application/FileUtils.php:104
87msgid "Trying to delete a folder outside of Shaarli path."
88msgstr "Tentative de supprimer un dossier en dehors du chemin de Shaarli."
89
90#: application/History.php:179
91msgid "History file isn't readable or writable" 24msgid "History file isn't readable or writable"
92msgstr "Le fichier d'historique n'est pas accessible en lecture ou en écriture" 25msgstr "Le fichier d'historique n'est pas accessible en lecture ou en écriture"
93 26
94#: application/History.php:190 27#: application/History.php:191
95msgid "Could not parse history file" 28msgid "Could not parse history file"
96msgstr "Format incorrect pour le fichier d'historique" 29msgstr "Format incorrect pour le fichier d'historique"
97 30
@@ -123,27 +56,27 @@ msgstr ""
123"l'extension php-gd doit être chargée pour utiliser les miniatures. Les " 56"l'extension php-gd doit être chargée pour utiliser les miniatures. Les "
124"miniatures sont désormais désactivées. Rechargez la page." 57"miniatures sont désormais désactivées. Rechargez la page."
125 58
126#: application/Utils.php:385 59#: application/Utils.php:402
127msgid "Setting not set" 60msgid "Setting not set"
128msgstr "Paramètre non défini" 61msgstr "Paramètre non défini"
129 62
130#: application/Utils.php:392 63#: application/Utils.php:409
131msgid "Unlimited" 64msgid "Unlimited"
132msgstr "Illimité" 65msgstr "Illimité"
133 66
134#: application/Utils.php:395 67#: application/Utils.php:412
135msgid "B" 68msgid "B"
136msgstr "o" 69msgstr "o"
137 70
138#: application/Utils.php:395 71#: application/Utils.php:412
139msgid "kiB" 72msgid "kiB"
140msgstr "ko" 73msgstr "ko"
141 74
142#: application/Utils.php:395 75#: application/Utils.php:412
143msgid "MiB" 76msgid "MiB"
144msgstr "Mo" 77msgstr "Mo"
145 78
146#: application/Utils.php:395 79#: application/Utils.php:412
147msgid "GiB" 80msgid "GiB"
148msgstr "Go" 81msgstr "Go"
149 82
@@ -156,7 +89,7 @@ msgstr "Vous n'êtes pas autorisé à modifier les données"
156 89
157#: application/bookmark/BookmarkFileService.php:208 90#: application/bookmark/BookmarkFileService.php:208
158msgid "This bookmarks already exists" 91msgid "This bookmarks already exists"
159msgstr "Ce marque-page existe déjà." 92msgstr "Ce marque-page existe déjà"
160 93
161#: application/bookmark/BookmarkInitializer.php:39 94#: application/bookmark/BookmarkInitializer.php:39
162msgid "(private bookmark with thumbnail demo)" 95msgid "(private bookmark with thumbnail demo)"
@@ -354,7 +287,8 @@ msgid "Direct link"
354msgstr "Liens directs" 287msgstr "Liens directs"
355 288
356#: application/feed/FeedBuilder.php:181 289#: application/feed/FeedBuilder.php:181
357#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:96 290#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:103
291#: tmp/dailyrss.b91ef64efc3688266305ea9b42e5017e.rtpl.php:26
358#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:179 292#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:179
359msgid "Permalink" 293msgid "Permalink"
360msgstr "Permalien" 294msgstr "Permalien"
@@ -537,20 +471,36 @@ msgstr "Outils"
537msgid "Search: " 471msgid "Search: "
538msgstr "Recherche : " 472msgstr "Recherche : "
539 473
540#: application/front/controller/visitor/DailyController.php:45 474#: application/front/controller/visitor/DailyController.php:200
541msgid "Today" 475msgid "day"
542msgstr "Aujourd'hui" 476msgstr "jour"
543
544#: application/front/controller/visitor/DailyController.php:47
545msgid "Yesterday"
546msgstr "Hier"
547 477
548#: application/front/controller/visitor/DailyController.php:85 478#: application/front/controller/visitor/DailyController.php:200
479#: application/front/controller/visitor/DailyController.php:203
480#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13
549#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:48 481#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:48
550#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:48 482#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:48
551msgid "Daily" 483msgid "Daily"
552msgstr "Quotidien" 484msgstr "Quotidien"
553 485
486#: application/front/controller/visitor/DailyController.php:201
487msgid "week"
488msgstr "semaine"
489
490#: application/front/controller/visitor/DailyController.php:201
491#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14
492msgid "Weekly"
493msgstr "Hebdomadaire"
494
495#: application/front/controller/visitor/DailyController.php:202
496msgid "month"
497msgstr "mois"
498
499#: application/front/controller/visitor/DailyController.php:202
500#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:15
501msgid "Monthly"
502msgstr "Mensuel"
503
554#: application/front/controller/visitor/ErrorController.php:33 504#: application/front/controller/visitor/ErrorController.php:33
555msgid "An unexpected error occurred." 505msgid "An unexpected error occurred."
556msgstr "Une erreur inattendue s'est produite." 506msgstr "Une erreur inattendue s'est produite."
@@ -616,7 +566,7 @@ msgstr "Mur d'images"
616 566
617#: application/front/controller/visitor/TagCloudController.php:88 567#: application/front/controller/visitor/TagCloudController.php:88
618msgid "Tag " 568msgid "Tag "
619msgstr "Tag" 569msgstr "Tag "
620 570
621#: application/front/exceptions/AlreadyInstalledException.php:11 571#: application/front/exceptions/AlreadyInstalledException.php:11
622msgid "Shaarli has already been installed. Login to edit the configuration." 572msgid "Shaarli has already been installed. Login to edit the configuration."
@@ -644,6 +594,86 @@ msgstr ""
644msgid "Wrong token." 594msgid "Wrong token."
645msgstr "Jeton invalide." 595msgstr "Jeton invalide."
646 596
597#: application/helper/ApplicationUtils.php:162
598#, php-format
599msgid ""
600"Your PHP version is obsolete! Shaarli requires at least PHP %s, and thus "
601"cannot run. Your PHP version has known security vulnerabilities and should "
602"be updated as soon as possible."
603msgstr ""
604"Votre version de PHP est obsolète ! Shaarli nécessite au moins PHP %s, et ne "
605"peut donc pas fonctionner. Votre version de PHP a des failles de sécurités "
606"connues et devrait être mise à jour au plus tôt."
607
608#: application/helper/ApplicationUtils.php:195
609#: application/helper/ApplicationUtils.php:215
610msgid "directory is not readable"
611msgstr "le répertoire n'est pas accessible en lecture"
612
613#: application/helper/ApplicationUtils.php:218
614msgid "directory is not writable"
615msgstr "le répertoire n'est pas accessible en écriture"
616
617#: application/helper/ApplicationUtils.php:240
618msgid "file is not readable"
619msgstr "le fichier n'est pas accessible en lecture"
620
621#: application/helper/ApplicationUtils.php:243
622msgid "file is not writable"
623msgstr "le fichier n'est pas accessible en écriture"
624
625#: application/helper/ApplicationUtils.php:277
626msgid "Configuration parsing"
627msgstr "Chargement de la configuration"
628
629#: application/helper/ApplicationUtils.php:278
630msgid "Slim Framework (routing, etc.)"
631msgstr "Slim Framwork (routage, etc.)"
632
633#: application/helper/ApplicationUtils.php:279
634msgid "Multibyte (Unicode) string support"
635msgstr "Support des chaînes de caractère multibytes (Unicode)"
636
637#: application/helper/ApplicationUtils.php:280
638msgid "Required to use thumbnails"
639msgstr "Obligatoire pour utiliser les miniatures"
640
641#: application/helper/ApplicationUtils.php:281
642msgid "Localized text sorting (e.g. e->è->f)"
643msgstr "Tri des textes traduits (ex : e->è->f)"
644
645#: application/helper/ApplicationUtils.php:282
646msgid "Better retrieval of bookmark metadata and thumbnail"
647msgstr "Meilleure récupération des meta-données des marque-pages et minatures"
648
649#: application/helper/ApplicationUtils.php:283
650msgid "Use the translation system in gettext mode"
651msgstr "Utiliser le système de traduction en mode gettext"
652
653#: application/helper/ApplicationUtils.php:284
654msgid "Login using LDAP server"
655msgstr "Authentification via un serveur LDAP"
656
657#: application/helper/DailyPageHelper.php:172
658msgid "Week"
659msgstr "Semaine"
660
661#: application/helper/DailyPageHelper.php:176
662msgid "Today"
663msgstr "Aujourd'hui"
664
665#: application/helper/DailyPageHelper.php:178
666msgid "Yesterday"
667msgstr "Hier"
668
669#: application/helper/FileUtils.php:100
670msgid "Provided path is not a directory."
671msgstr "Le chemin fourni n'est pas un dossier."
672
673#: application/helper/FileUtils.php:104
674msgid "Trying to delete a folder outside of Shaarli path."
675msgstr "Tentative de supprimer un dossier en dehors du chemin de Shaarli."
676
647#: application/legacy/LegacyLinkDB.php:131 677#: application/legacy/LegacyLinkDB.php:131
648msgid "You are not authorized to add a link." 678msgid "You are not authorized to add a link."
649msgstr "Vous n'êtes pas autorisé à ajouter un lien." 679msgstr "Vous n'êtes pas autorisé à ajouter un lien."
@@ -1103,25 +1133,30 @@ msgstr "Aucune"
1103msgid "Save" 1133msgid "Save"
1104msgstr "Enregistrer" 1134msgstr "Enregistrer"
1105 1135
1106#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:15 1136#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:26
1107msgid "The Daily Shaarli" 1137msgid "1 RSS entry per :type"
1108msgstr "Le Quotidien Shaarli" 1138msgid_plural ""
1109 1139msgstr[0] "1 entrée RSS par :type"
1110#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:17 1140msgstr[1] ""
1111msgid "1 RSS entry per day" 1141
1112msgstr "1 entrée RSS par jour" 1142#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:49
1113 1143msgid "Previous :type"
1114#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:37 1144msgid_plural ""
1115msgid "Previous day" 1145msgstr[0] ":type précédent"
1116msgstr "Jour précédent" 1146msgstr[1] "Jour précédent"
1117 1147
1118#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:44 1148#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:56
1119msgid "All links of one day in a single page." 1149#: tmp/dailyrss.b91ef64efc3688266305ea9b42e5017e.rtpl.php:7
1120msgstr "Tous les liens d'un jour sur une page." 1150msgid "All links of one :type in a single page."
1121 1151msgid_plural ""
1122#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:51 1152msgstr[0] "Tous les liens d'un :type sur une page."
1123msgid "Next day" 1153msgstr[1] "Tous les liens d'un jour sur une page."
1124msgstr "Jour suivant" 1154
1155#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:63
1156msgid "Next :type"
1157msgid_plural ""
1158msgstr[0] ":type suivant"
1159msgstr[1] ""
1125 1160
1126#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:21 1161#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:21
1127msgid "Edit Shaare" 1162msgid "Edit Shaare"
@@ -1821,8 +1856,11 @@ msgstr ""
1821"Glisser ce lien dans votre barre de favoris ou cliquer droit dessus et « " 1856"Glisser ce lien dans votre barre de favoris ou cliquer droit dessus et « "
1822"Ajouter aux favoris »" 1857"Ajouter aux favoris »"
1823 1858
1824#~ msgid "Rename" 1859#~ msgid "Display:"
1825#~ msgstr "Renommer" 1860#~ msgstr "Afficher :"
1861
1862#~ msgid "The Daily Shaarli"
1863#~ msgstr "Le Quotidien Shaarli"
1826 1864
1827#, fuzzy 1865#, fuzzy
1828#~| msgid "Selection" 1866#~| msgid "Selection"
diff --git a/tests/bookmark/BookmarkFileServiceTest.php b/tests/bookmark/BookmarkFileServiceTest.php
index 47970117..8e0ff8dd 100644
--- a/tests/bookmark/BookmarkFileServiceTest.php
+++ b/tests/bookmark/BookmarkFileServiceTest.php
@@ -686,22 +686,6 @@ class BookmarkFileServiceTest extends TestCase
686 } 686 }
687 687
688 /** 688 /**
689 * List the days for which bookmarks have been posted
690 */
691 public function testDays()
692 {
693 $this->assertSame(
694 ['20100309', '20100310', '20121206', '20121207', '20130614', '20150310'],
695 $this->publicLinkDB->days()
696 );
697
698 $this->assertSame(
699 ['20100309', '20100310', '20121206', '20121207', '20130614', '20141125', '20150310'],
700 $this->privateLinkDB->days()
701 );
702 }
703
704 /**
705 * The URL corresponds to an existing entry in the DB 689 * The URL corresponds to an existing entry in the DB
706 */ 690 */
707 public function testGetKnownLinkFromURL() 691 public function testGetKnownLinkFromURL()
@@ -1074,33 +1058,105 @@ class BookmarkFileServiceTest extends TestCase
1074 } 1058 }
1075 1059
1076 /** 1060 /**
1077 * Test filterDay while logged in 1061 * Test find by dates in the middle of the datastore (sorted by dates) with a single bookmark as a result.
1078 */ 1062 */
1079 public function testFilterDayLoggedIn(): void 1063 public function testFilterByDateMidTimePeriodSingleBookmark(): void
1080 { 1064 {
1081 $bookmarks = $this->privateLinkDB->filterDay('20121206'); 1065 $bookmarks = $this->privateLinkDB->findByDate(
1082 $expectedIds = [4, 9, 1, 0]; 1066 DateTime::createFromFormat('Ymd_His', '20121206_150000'),
1067 DateTime::createFromFormat('Ymd_His', '20121206_160000'),
1068 $before,
1069 $after
1070 );
1083 1071
1084 static::assertCount(4, $bookmarks); 1072 static::assertCount(1, $bookmarks);
1085 foreach ($bookmarks as $bookmark) { 1073
1086 $i = ($i ?? -1) + 1; 1074 static::assertSame(9, $bookmarks[0]->getId());
1087 static::assertSame($expectedIds[$i], $bookmark->getId()); 1075 static::assertEquals(DateTime::createFromFormat('Ymd_His', '20121206_142300'), $before);
1088 } 1076 static::assertEquals(DateTime::createFromFormat('Ymd_His', '20121206_172539'), $after);
1089 } 1077 }
1090 1078
1091 /** 1079 /**
1092 * Test filterDay while logged out 1080 * Test find by dates in the middle of the datastore (sorted by dates) with a multiple bookmarks as a result.
1093 */ 1081 */
1094 public function testFilterDayLoggedOut(): void 1082 public function testFilterByDateMidTimePeriodMultipleBookmarks(): void
1095 { 1083 {
1096 $bookmarks = $this->publicLinkDB->filterDay('20121206'); 1084 $bookmarks = $this->privateLinkDB->findByDate(
1097 $expectedIds = [4, 9, 1]; 1085 DateTime::createFromFormat('Ymd_His', '20121206_150000'),
1086 DateTime::createFromFormat('Ymd_His', '20121206_180000'),
1087 $before,
1088 $after
1089 );
1098 1090
1099 static::assertCount(3, $bookmarks); 1091 static::assertCount(2, $bookmarks);
1100 foreach ($bookmarks as $bookmark) { 1092
1101 $i = ($i ?? -1) + 1; 1093 static::assertSame(1, $bookmarks[0]->getId());
1102 static::assertSame($expectedIds[$i], $bookmark->getId()); 1094 static::assertSame(9, $bookmarks[1]->getId());
1103 } 1095 static::assertEquals(DateTime::createFromFormat('Ymd_His', '20121206_142300'), $before);
1096 static::assertEquals(DateTime::createFromFormat('Ymd_His', '20121206_182539'), $after);
1097 }
1098
1099 /**
1100 * Test find by dates at the end of the datastore (sorted by dates).
1101 */
1102 public function testFilterByDateLastTimePeriod(): void
1103 {
1104 $after = new DateTime();
1105 $bookmarks = $this->privateLinkDB->findByDate(
1106 DateTime::createFromFormat('Ymd_His', '20150310_114640'),
1107 DateTime::createFromFormat('Ymd_His', '20450101_010101'),
1108 $before,
1109 $after
1110 );
1111
1112 static::assertCount(1, $bookmarks);
1113
1114 static::assertSame(41, $bookmarks[0]->getId());
1115 static::assertEquals(DateTime::createFromFormat('Ymd_His', '20150310_114633'), $before);
1116 static::assertNull($after);
1117 }
1118
1119 /**
1120 * Test find by dates at the beginning of the datastore (sorted by dates).
1121 */
1122 public function testFilterByDateFirstTimePeriod(): void
1123 {
1124 $before = new DateTime();
1125 $bookmarks = $this->privateLinkDB->findByDate(
1126 DateTime::createFromFormat('Ymd_His', '20000101_101010'),
1127 DateTime::createFromFormat('Ymd_His', '20100309_110000'),
1128 $before,
1129 $after
1130 );
1131
1132 static::assertCount(1, $bookmarks);
1133
1134 static::assertSame(11, $bookmarks[0]->getId());
1135 static::assertNull($before);
1136 static::assertEquals(DateTime::createFromFormat('Ymd_His', '20100310_101010'), $after);
1137 }
1138
1139 /**
1140 * Test getLatest with a sticky bookmark: it should be ignored and return the latest by creation date instead.
1141 */
1142 public function testGetLatestWithSticky(): void
1143 {
1144 $bookmark = $this->publicLinkDB->getLatest();
1145
1146 static::assertSame(41, $bookmark->getId());
1147 }
1148
1149 /**
1150 * Test getLatest with a sticky bookmark: it should be ignored and return the latest by creation date instead.
1151 */
1152 public function testGetLatestEmptyDatastore(): void
1153 {
1154 unlink($this->conf->get('resource.datastore'));
1155 $this->publicLinkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, false);
1156
1157 $bookmark = $this->publicLinkDB->getLatest();
1158
1159 static::assertNull($bookmark);
1104 } 1160 }
1105 1161
1106 /** 1162 /**
diff --git a/tests/front/controller/visitor/DailyControllerTest.php b/tests/front/controller/visitor/DailyControllerTest.php
index fc78bc13..758e7219 100644
--- a/tests/front/controller/visitor/DailyControllerTest.php
+++ b/tests/front/controller/visitor/DailyControllerTest.php
@@ -28,52 +28,49 @@ class DailyControllerTest extends TestCase
28 public function testValidIndexControllerInvokeDefault(): void 28 public function testValidIndexControllerInvokeDefault(): void
29 { 29 {
30 $currentDay = new \DateTimeImmutable('2020-05-13'); 30 $currentDay = new \DateTimeImmutable('2020-05-13');
31 $previousDate = new \DateTime('2 days ago 00:00:00');
32 $nextDate = new \DateTime('today 00:00:00');
31 33
32 $request = $this->createMock(Request::class); 34 $request = $this->createMock(Request::class);
33 $request->method('getQueryParam')->willReturn($currentDay->format('Ymd')); 35 $request->method('getQueryParam')->willReturnCallback(function (string $key) use ($currentDay): ?string {
36 return $key === 'day' ? $currentDay->format('Ymd') : null;
37 });
34 $response = new Response(); 38 $response = new Response();
35 39
36 // Save RainTPL assigned variables 40 // Save RainTPL assigned variables
37 $assignedVariables = []; 41 $assignedVariables = [];
38 $this->assignTemplateVars($assignedVariables); 42 $this->assignTemplateVars($assignedVariables);
39 43
40 // Links dataset: 2 links with thumbnails
41 $this->container->bookmarkService
42 ->expects(static::once())
43 ->method('days')
44 ->willReturnCallback(function () use ($currentDay): array {
45 return [
46 '20200510',
47 $currentDay->format('Ymd'),
48 '20200516',
49 ];
50 })
51 ;
52 $this->container->bookmarkService 44 $this->container->bookmarkService
53 ->expects(static::once()) 45 ->expects(static::once())
54 ->method('filterDay') 46 ->method('findByDate')
55 ->willReturnCallback(function (): array { 47 ->willReturnCallback(
56 return [ 48 function ($from, $to, &$previous, &$next) use ($currentDay, $previousDate, $nextDate): array {
57 (new Bookmark()) 49 $previous = $previousDate;
58 ->setId(1) 50 $next = $nextDate;
59 ->setUrl('http://url.tld') 51
60 ->setTitle(static::generateString(50)) 52 return [
61 ->setDescription(static::generateString(500)) 53 (new Bookmark())
62 , 54 ->setId(1)
63 (new Bookmark()) 55 ->setUrl('http://url.tld')
64 ->setId(2) 56 ->setTitle(static::generateString(50))
65 ->setUrl('http://url2.tld') 57 ->setDescription(static::generateString(500))
66 ->setTitle(static::generateString(50)) 58 ,
67 ->setDescription(static::generateString(500)) 59 (new Bookmark())
68 , 60 ->setId(2)
69 (new Bookmark()) 61 ->setUrl('http://url2.tld')
70 ->setId(3) 62 ->setTitle(static::generateString(50))
71 ->setUrl('http://url3.tld') 63 ->setDescription(static::generateString(500))
72 ->setTitle(static::generateString(50)) 64 ,
73 ->setDescription(static::generateString(500)) 65 (new Bookmark())
74 , 66 ->setId(3)
75 ]; 67 ->setUrl('http://url3.tld')
76 }) 68 ->setTitle(static::generateString(50))
69 ->setDescription(static::generateString(500))
70 ,
71 ];
72 }
73 )
77 ; 74 ;
78 75
79 // Make sure that PluginManager hook is triggered 76 // Make sure that PluginManager hook is triggered
@@ -81,20 +78,22 @@ class DailyControllerTest extends TestCase
81 ->expects(static::atLeastOnce()) 78 ->expects(static::atLeastOnce())
82 ->method('executeHooks') 79 ->method('executeHooks')
83 ->withConsecutive(['render_daily']) 80 ->withConsecutive(['render_daily'])
84 ->willReturnCallback(function (string $hook, array $data, array $param) use ($currentDay): array { 81 ->willReturnCallback(
85 if ('render_daily' === $hook) { 82 function (string $hook, array $data, array $param) use ($currentDay, $previousDate, $nextDate): array {
86 static::assertArrayHasKey('linksToDisplay', $data); 83 if ('render_daily' === $hook) {
87 static::assertCount(3, $data['linksToDisplay']); 84 static::assertArrayHasKey('linksToDisplay', $data);
88 static::assertSame(1, $data['linksToDisplay'][0]['id']); 85 static::assertCount(3, $data['linksToDisplay']);
89 static::assertSame($currentDay->getTimestamp(), $data['day']); 86 static::assertSame(1, $data['linksToDisplay'][0]['id']);
90 static::assertSame('20200510', $data['previousday']); 87 static::assertSame($currentDay->getTimestamp(), $data['day']);
91 static::assertSame('20200516', $data['nextday']); 88 static::assertSame($previousDate->format('Ymd'), $data['previousday']);
92 89 static::assertSame($nextDate->format('Ymd'), $data['nextday']);
93 static::assertArrayHasKey('loggedin', $param); 90
91 static::assertArrayHasKey('loggedin', $param);
92 }
93
94 return $data;
94 } 95 }
95 96 )
96 return $data;
97 })
98 ; 97 ;
99 98
100 $result = $this->controller->index($request, $response); 99 $result = $this->controller->index($request, $response);
@@ -107,6 +106,11 @@ class DailyControllerTest extends TestCase
107 ); 106 );
108 static::assertEquals($currentDay, $assignedVariables['dayDate']); 107 static::assertEquals($currentDay, $assignedVariables['dayDate']);
109 static::assertEquals($currentDay->getTimestamp(), $assignedVariables['day']); 108 static::assertEquals($currentDay->getTimestamp(), $assignedVariables['day']);
109 static::assertSame($previousDate->format('Ymd'), $assignedVariables['previousday']);
110 static::assertSame($nextDate->format('Ymd'), $assignedVariables['nextday']);
111 static::assertSame('day', $assignedVariables['type']);
112 static::assertSame('May 13, 2020', $assignedVariables['dayDesc']);
113 static::assertSame('Daily', $assignedVariables['localizedType']);
110 static::assertCount(3, $assignedVariables['linksToDisplay']); 114 static::assertCount(3, $assignedVariables['linksToDisplay']);
111 115
112 $link = $assignedVariables['linksToDisplay'][0]; 116 $link = $assignedVariables['linksToDisplay'][0];
@@ -171,27 +175,20 @@ class DailyControllerTest extends TestCase
171 $currentDay = new \DateTimeImmutable('2020-05-13'); 175 $currentDay = new \DateTimeImmutable('2020-05-13');
172 176
173 $request = $this->createMock(Request::class); 177 $request = $this->createMock(Request::class);
178 $request->method('getQueryParam')->willReturnCallback(function (string $key) use ($currentDay): ?string {
179 return $key === 'day' ? $currentDay->format('Ymd') : null;
180 });
174 $response = new Response(); 181 $response = new Response();
175 182
176 // Save RainTPL assigned variables 183 // Save RainTPL assigned variables
177 $assignedVariables = []; 184 $assignedVariables = [];
178 $this->assignTemplateVars($assignedVariables); 185 $this->assignTemplateVars($assignedVariables);
179 186
180 // Links dataset: 2 links with thumbnails
181 $this->container->bookmarkService 187 $this->container->bookmarkService
182 ->expects(static::once()) 188 ->expects(static::once())
183 ->method('days') 189 ->method('findByDate')
184 ->willReturnCallback(function () use ($currentDay): array { 190 ->willReturnCallback(function () use ($currentDay): array {
185 return [ 191 return [
186 $currentDay->format($currentDay->format('Ymd')),
187 ];
188 })
189 ;
190 $this->container->bookmarkService
191 ->expects(static::once())
192 ->method('filterDay')
193 ->willReturnCallback(function (): array {
194 return [
195 (new Bookmark()) 192 (new Bookmark())
196 ->setId(1) 193 ->setId(1)
197 ->setUrl('http://url.tld') 194 ->setUrl('http://url.tld')
@@ -250,21 +247,11 @@ class DailyControllerTest extends TestCase
250 $assignedVariables = []; 247 $assignedVariables = [];
251 $this->assignTemplateVars($assignedVariables); 248 $this->assignTemplateVars($assignedVariables);
252 249
253 // Links dataset: 2 links with thumbnails
254 $this->container->bookmarkService 250 $this->container->bookmarkService
255 ->expects(static::once()) 251 ->expects(static::once())
256 ->method('days') 252 ->method('findByDate')
257 ->willReturnCallback(function () use ($currentDay): array { 253 ->willReturnCallback(function () use ($currentDay): array {
258 return [ 254 return [
259 $currentDay->format($currentDay->format('Ymd')),
260 ];
261 })
262 ;
263 $this->container->bookmarkService
264 ->expects(static::once())
265 ->method('filterDay')
266 ->willReturnCallback(function (): array {
267 return [
268 (new Bookmark())->setId(1)->setUrl('http://url.tld')->setTitle('title'), 255 (new Bookmark())->setId(1)->setUrl('http://url.tld')->setTitle('title'),
269 (new Bookmark()) 256 (new Bookmark())
270 ->setId(2) 257 ->setId(2)
@@ -320,14 +307,7 @@ class DailyControllerTest extends TestCase
320 // Links dataset: 2 links with thumbnails 307 // Links dataset: 2 links with thumbnails
321 $this->container->bookmarkService 308 $this->container->bookmarkService
322 ->expects(static::once()) 309 ->expects(static::once())
323 ->method('days') 310 ->method('findByDate')
324 ->willReturnCallback(function (): array {
325 return [];
326 })
327 ;
328 $this->container->bookmarkService
329 ->expects(static::once())
330 ->method('filterDay')
331 ->willReturnCallback(function (): array { 311 ->willReturnCallback(function (): array {
332 return []; 312 return [];
333 }) 313 })
@@ -347,7 +327,7 @@ class DailyControllerTest extends TestCase
347 static::assertSame(200, $result->getStatusCode()); 327 static::assertSame(200, $result->getStatusCode());
348 static::assertSame('daily', (string) $result->getBody()); 328 static::assertSame('daily', (string) $result->getBody());
349 static::assertCount(0, $assignedVariables['linksToDisplay']); 329 static::assertCount(0, $assignedVariables['linksToDisplay']);
350 static::assertSame('Today', $assignedVariables['dayDesc']); 330 static::assertSame('Today - ' . (new \DateTime())->format('F d, Y'), $assignedVariables['dayDesc']);
351 static::assertEquals((new \DateTime())->setTime(0, 0)->getTimestamp(), $assignedVariables['day']); 331 static::assertEquals((new \DateTime())->setTime(0, 0)->getTimestamp(), $assignedVariables['day']);
352 static::assertEquals((new \DateTime())->setTime(0, 0), $assignedVariables['dayDate']); 332 static::assertEquals((new \DateTime())->setTime(0, 0), $assignedVariables['dayDate']);
353 } 333 }
@@ -361,6 +341,7 @@ class DailyControllerTest extends TestCase
361 new \DateTimeImmutable('2020-05-17'), 341 new \DateTimeImmutable('2020-05-17'),
362 new \DateTimeImmutable('2020-05-15'), 342 new \DateTimeImmutable('2020-05-15'),
363 new \DateTimeImmutable('2020-05-13'), 343 new \DateTimeImmutable('2020-05-13'),
344 new \DateTimeImmutable('+1 month'),
364 ]; 345 ];
365 346
366 $request = $this->createMock(Request::class); 347 $request = $this->createMock(Request::class);
@@ -371,6 +352,7 @@ class DailyControllerTest extends TestCase
371 (new Bookmark())->setId(2)->setCreated($dates[1])->setUrl('http://domain.tld/2'), 352 (new Bookmark())->setId(2)->setCreated($dates[1])->setUrl('http://domain.tld/2'),
372 (new Bookmark())->setId(3)->setCreated($dates[1])->setUrl('http://domain.tld/3'), 353 (new Bookmark())->setId(3)->setCreated($dates[1])->setUrl('http://domain.tld/3'),
373 (new Bookmark())->setId(4)->setCreated($dates[2])->setUrl('http://domain.tld/4'), 354 (new Bookmark())->setId(4)->setCreated($dates[2])->setUrl('http://domain.tld/4'),
355 (new Bookmark())->setId(5)->setCreated($dates[3])->setUrl('http://domain.tld/5'),
374 ]); 356 ]);
375 357
376 $this->container->pageCacheManager 358 $this->container->pageCacheManager
@@ -397,13 +379,14 @@ class DailyControllerTest extends TestCase
397 static::assertSame('http://shaarli/subfolder/', $assignedVariables['index_url']); 379 static::assertSame('http://shaarli/subfolder/', $assignedVariables['index_url']);
398 static::assertSame('http://shaarli/subfolder/daily-rss', $assignedVariables['page_url']); 380 static::assertSame('http://shaarli/subfolder/daily-rss', $assignedVariables['page_url']);
399 static::assertFalse($assignedVariables['hide_timestamps']); 381 static::assertFalse($assignedVariables['hide_timestamps']);
400 static::assertCount(2, $assignedVariables['days']); 382 static::assertCount(3, $assignedVariables['days']);
401 383
402 $day = $assignedVariables['days'][$dates[0]->format('Ymd')]; 384 $day = $assignedVariables['days'][$dates[0]->format('Ymd')];
385 $date = $dates[0]->setTime(23, 59, 59);
403 386
404 static::assertEquals($dates[0], $day['date']); 387 static::assertEquals($date, $day['date']);
405 static::assertSame($dates[0]->format(\DateTime::RSS), $day['date_rss']); 388 static::assertSame($date->format(\DateTime::RSS), $day['date_rss']);
406 static::assertSame(format_date($dates[0], false), $day['date_human']); 389 static::assertSame(format_date($date, false), $day['date_human']);
407 static::assertSame('http://shaarli/subfolder/daily?day='. $dates[0]->format('Ymd'), $day['absolute_url']); 390 static::assertSame('http://shaarli/subfolder/daily?day='. $dates[0]->format('Ymd'), $day['absolute_url']);
408 static::assertCount(1, $day['links']); 391 static::assertCount(1, $day['links']);
409 static::assertSame(1, $day['links'][0]['id']); 392 static::assertSame(1, $day['links'][0]['id']);
@@ -411,10 +394,11 @@ class DailyControllerTest extends TestCase
411 static::assertEquals($dates[0], $day['links'][0]['created']); 394 static::assertEquals($dates[0], $day['links'][0]['created']);
412 395
413 $day = $assignedVariables['days'][$dates[1]->format('Ymd')]; 396 $day = $assignedVariables['days'][$dates[1]->format('Ymd')];
397 $date = $dates[1]->setTime(23, 59, 59);
414 398
415 static::assertEquals($dates[1], $day['date']); 399 static::assertEquals($date, $day['date']);
416 static::assertSame($dates[1]->format(\DateTime::RSS), $day['date_rss']); 400 static::assertSame($date->format(\DateTime::RSS), $day['date_rss']);
417 static::assertSame(format_date($dates[1], false), $day['date_human']); 401 static::assertSame(format_date($date, false), $day['date_human']);
418 static::assertSame('http://shaarli/subfolder/daily?day='. $dates[1]->format('Ymd'), $day['absolute_url']); 402 static::assertSame('http://shaarli/subfolder/daily?day='. $dates[1]->format('Ymd'), $day['absolute_url']);
419 static::assertCount(2, $day['links']); 403 static::assertCount(2, $day['links']);
420 404
@@ -424,6 +408,18 @@ class DailyControllerTest extends TestCase
424 static::assertSame(3, $day['links'][1]['id']); 408 static::assertSame(3, $day['links'][1]['id']);
425 static::assertSame('http://domain.tld/3', $day['links'][1]['url']); 409 static::assertSame('http://domain.tld/3', $day['links'][1]['url']);
426 static::assertEquals($dates[1], $day['links'][1]['created']); 410 static::assertEquals($dates[1], $day['links'][1]['created']);
411
412 $day = $assignedVariables['days'][$dates[2]->format('Ymd')];
413 $date = $dates[2]->setTime(23, 59, 59);
414
415 static::assertEquals($date, $day['date']);
416 static::assertSame($date->format(\DateTime::RSS), $day['date_rss']);
417 static::assertSame(format_date($date, false), $day['date_human']);
418 static::assertSame('http://shaarli/subfolder/daily?day='. $dates[2]->format('Ymd'), $day['absolute_url']);
419 static::assertCount(1, $day['links']);
420 static::assertSame(4, $day['links'][0]['id']);
421 static::assertSame('http://domain.tld/4', $day['links'][0]['url']);
422 static::assertEquals($dates[2], $day['links'][0]['created']);
427 } 423 }
428 424
429 /** 425 /**
@@ -475,4 +471,246 @@ class DailyControllerTest extends TestCase
475 static::assertFalse($assignedVariables['hide_timestamps']); 471 static::assertFalse($assignedVariables['hide_timestamps']);
476 static::assertCount(0, $assignedVariables['days']); 472 static::assertCount(0, $assignedVariables['days']);
477 } 473 }
474
475 /**
476 * Test simple display index with week parameter
477 */
478 public function testSimpleIndexWeekly(): void
479 {
480 $currentDay = new \DateTimeImmutable('2020-05-13');
481 $expectedDay = new \DateTimeImmutable('2020-05-11');
482
483 $request = $this->createMock(Request::class);
484 $request->method('getQueryParam')->willReturnCallback(function (string $key) use ($currentDay): ?string {
485 return $key === 'week' ? $currentDay->format('YW') : null;
486 });
487 $response = new Response();
488
489 // Save RainTPL assigned variables
490 $assignedVariables = [];
491 $this->assignTemplateVars($assignedVariables);
492
493 $this->container->bookmarkService
494 ->expects(static::once())
495 ->method('findByDate')
496 ->willReturnCallback(
497 function (): array {
498 return [
499 (new Bookmark())
500 ->setId(1)
501 ->setUrl('http://url.tld')
502 ->setTitle(static::generateString(50))
503 ->setDescription(static::generateString(500))
504 ,
505 (new Bookmark())
506 ->setId(2)
507 ->setUrl('http://url2.tld')
508 ->setTitle(static::generateString(50))
509 ->setDescription(static::generateString(500))
510 ,
511 ];
512 }
513 )
514 ;
515
516 $result = $this->controller->index($request, $response);
517
518 static::assertSame(200, $result->getStatusCode());
519 static::assertSame('daily', (string) $result->getBody());
520 static::assertSame(
521 'Weekly - Week 20 (May 11, 2020) - Shaarli',
522 $assignedVariables['pagetitle']
523 );
524
525 static::assertCount(2, $assignedVariables['linksToDisplay']);
526 static::assertEquals($expectedDay->setTime(0, 0), $assignedVariables['dayDate']);
527 static::assertSame($expectedDay->setTime(0, 0)->getTimestamp(), $assignedVariables['day']);
528 static::assertSame('', $assignedVariables['previousday']);
529 static::assertSame('', $assignedVariables['nextday']);
530 static::assertSame('Week 20 (May 11, 2020)', $assignedVariables['dayDesc']);
531 static::assertSame('week', $assignedVariables['type']);
532 static::assertSame('Weekly', $assignedVariables['localizedType']);
533 }
534
535 /**
536 * Test simple display index with month parameter
537 */
538 public function testSimpleIndexMonthly(): void
539 {
540 $currentDay = new \DateTimeImmutable('2020-05-13');
541 $expectedDay = new \DateTimeImmutable('2020-05-01');
542
543 $request = $this->createMock(Request::class);
544 $request->method('getQueryParam')->willReturnCallback(function (string $key) use ($currentDay): ?string {
545 return $key === 'month' ? $currentDay->format('Ym') : null;
546 });
547 $response = new Response();
548
549 // Save RainTPL assigned variables
550 $assignedVariables = [];
551 $this->assignTemplateVars($assignedVariables);
552
553 $this->container->bookmarkService
554 ->expects(static::once())
555 ->method('findByDate')
556 ->willReturnCallback(
557 function (): array {
558 return [
559 (new Bookmark())
560 ->setId(1)
561 ->setUrl('http://url.tld')
562 ->setTitle(static::generateString(50))
563 ->setDescription(static::generateString(500))
564 ,
565 (new Bookmark())
566 ->setId(2)
567 ->setUrl('http://url2.tld')
568 ->setTitle(static::generateString(50))
569 ->setDescription(static::generateString(500))
570 ,
571 ];
572 }
573 )
574 ;
575
576 $result = $this->controller->index($request, $response);
577
578 static::assertSame(200, $result->getStatusCode());
579 static::assertSame('daily', (string) $result->getBody());
580 static::assertSame(
581 'Monthly - May, 2020 - Shaarli',
582 $assignedVariables['pagetitle']
583 );
584
585 static::assertCount(2, $assignedVariables['linksToDisplay']);
586 static::assertEquals($expectedDay->setTime(0, 0), $assignedVariables['dayDate']);
587 static::assertSame($expectedDay->setTime(0, 0)->getTimestamp(), $assignedVariables['day']);
588 static::assertSame('', $assignedVariables['previousday']);
589 static::assertSame('', $assignedVariables['nextday']);
590 static::assertSame('May, 2020', $assignedVariables['dayDesc']);
591 static::assertSame('month', $assignedVariables['type']);
592 static::assertSame('Monthly', $assignedVariables['localizedType']);
593 }
594
595 /**
596 * Test simple display RSS with week parameter
597 */
598 public function testSimpleRssWeekly(): void
599 {
600 $dates = [
601 new \DateTimeImmutable('2020-05-19'),
602 new \DateTimeImmutable('2020-05-13'),
603 ];
604 $expectedDates = [
605 new \DateTimeImmutable('2020-05-24 23:59:59'),
606 new \DateTimeImmutable('2020-05-17 23:59:59'),
607 ];
608
609 $this->container->environment['QUERY_STRING'] = 'week';
610 $request = $this->createMock(Request::class);
611 $request->method('getQueryParam')->willReturnCallback(function (string $key): ?string {
612 return $key === 'week' ? '' : null;
613 });
614 $response = new Response();
615
616 $this->container->bookmarkService->expects(static::once())->method('search')->willReturn([
617 (new Bookmark())->setId(1)->setCreated($dates[0])->setUrl('http://domain.tld/1'),
618 (new Bookmark())->setId(2)->setCreated($dates[1])->setUrl('http://domain.tld/2'),
619 (new Bookmark())->setId(3)->setCreated($dates[1])->setUrl('http://domain.tld/3'),
620 ]);
621
622 // Save RainTPL assigned variables
623 $assignedVariables = [];
624 $this->assignTemplateVars($assignedVariables);
625
626 $result = $this->controller->rss($request, $response);
627
628 static::assertSame(200, $result->getStatusCode());
629 static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]);
630 static::assertSame('dailyrss', (string) $result->getBody());
631 static::assertSame('Shaarli', $assignedVariables['title']);
632 static::assertSame('http://shaarli/subfolder/', $assignedVariables['index_url']);
633 static::assertSame('http://shaarli/subfolder/daily-rss?week', $assignedVariables['page_url']);
634 static::assertFalse($assignedVariables['hide_timestamps']);
635 static::assertCount(2, $assignedVariables['days']);
636
637 $day = $assignedVariables['days'][$dates[0]->format('YW')];
638 $date = $expectedDates[0];
639
640 static::assertEquals($date, $day['date']);
641 static::assertSame($date->format(\DateTime::RSS), $day['date_rss']);
642 static::assertSame('Week 21 (May 18, 2020)', $day['date_human']);
643 static::assertSame('http://shaarli/subfolder/daily?week='. $dates[0]->format('YW'), $day['absolute_url']);
644 static::assertCount(1, $day['links']);
645
646 $day = $assignedVariables['days'][$dates[1]->format('YW')];
647 $date = $expectedDates[1];
648
649 static::assertEquals($date, $day['date']);
650 static::assertSame($date->format(\DateTime::RSS), $day['date_rss']);
651 static::assertSame('Week 20 (May 11, 2020)', $day['date_human']);
652 static::assertSame('http://shaarli/subfolder/daily?week='. $dates[1]->format('YW'), $day['absolute_url']);
653 static::assertCount(2, $day['links']);
654 }
655
656 /**
657 * Test simple display RSS with month parameter
658 */
659 public function testSimpleRssMonthly(): void
660 {
661 $dates = [
662 new \DateTimeImmutable('2020-05-19'),
663 new \DateTimeImmutable('2020-04-13'),
664 ];
665 $expectedDates = [
666 new \DateTimeImmutable('2020-05-31 23:59:59'),
667 new \DateTimeImmutable('2020-04-30 23:59:59'),
668 ];
669
670 $this->container->environment['QUERY_STRING'] = 'month';
671 $request = $this->createMock(Request::class);
672 $request->method('getQueryParam')->willReturnCallback(function (string $key): ?string {
673 return $key === 'month' ? '' : null;
674 });
675 $response = new Response();
676
677 $this->container->bookmarkService->expects(static::once())->method('search')->willReturn([
678 (new Bookmark())->setId(1)->setCreated($dates[0])->setUrl('http://domain.tld/1'),
679 (new Bookmark())->setId(2)->setCreated($dates[1])->setUrl('http://domain.tld/2'),
680 (new Bookmark())->setId(3)->setCreated($dates[1])->setUrl('http://domain.tld/3'),
681 ]);
682
683 // Save RainTPL assigned variables
684 $assignedVariables = [];
685 $this->assignTemplateVars($assignedVariables);
686
687 $result = $this->controller->rss($request, $response);
688
689 static::assertSame(200, $result->getStatusCode());
690 static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]);
691 static::assertSame('dailyrss', (string) $result->getBody());
692 static::assertSame('Shaarli', $assignedVariables['title']);
693 static::assertSame('http://shaarli/subfolder/', $assignedVariables['index_url']);
694 static::assertSame('http://shaarli/subfolder/daily-rss?month', $assignedVariables['page_url']);
695 static::assertFalse($assignedVariables['hide_timestamps']);
696 static::assertCount(2, $assignedVariables['days']);
697
698 $day = $assignedVariables['days'][$dates[0]->format('Ym')];
699 $date = $expectedDates[0];
700
701 static::assertEquals($date, $day['date']);
702 static::assertSame($date->format(\DateTime::RSS), $day['date_rss']);
703 static::assertSame('May, 2020', $day['date_human']);
704 static::assertSame('http://shaarli/subfolder/daily?month='. $dates[0]->format('Ym'), $day['absolute_url']);
705 static::assertCount(1, $day['links']);
706
707 $day = $assignedVariables['days'][$dates[1]->format('Ym')];
708 $date = $expectedDates[1];
709
710 static::assertEquals($date, $day['date']);
711 static::assertSame($date->format(\DateTime::RSS), $day['date_rss']);
712 static::assertSame('April, 2020', $day['date_human']);
713 static::assertSame('http://shaarli/subfolder/daily?month='. $dates[1]->format('Ym'), $day['absolute_url']);
714 static::assertCount(2, $day['links']);
715 }
478} 716}
diff --git a/tests/helper/DailyPageHelperTest.php b/tests/helper/DailyPageHelperTest.php
new file mode 100644
index 00000000..e0378491
--- /dev/null
+++ b/tests/helper/DailyPageHelperTest.php
@@ -0,0 +1,262 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Helper;
6
7use Shaarli\Bookmark\Bookmark;
8use Shaarli\TestCase;
9use Slim\Http\Request;
10
11class DailyPageHelperTest extends TestCase
12{
13 /**
14 * @dataProvider getRequestedTypes
15 */
16 public function testExtractRequestedType(array $queryParams, string $expectedType): void
17 {
18 $request = $this->createMock(Request::class);
19 $request->method('getQueryParam')->willReturnCallback(function ($key) use ($queryParams): ?string {
20 return $queryParams[$key] ?? null;
21 });
22
23 $type = DailyPageHelper::extractRequestedType($request);
24
25 static::assertSame($type, $expectedType);
26 }
27
28 /**
29 * @dataProvider getRequestedDateTimes
30 */
31 public function testExtractRequestedDateTime(
32 string $type,
33 string $input,
34 ?Bookmark $bookmark,
35 \DateTimeInterface $expectedDateTime,
36 string $compareFormat = 'Ymd'
37 ): void {
38 $dateTime = DailyPageHelper::extractRequestedDateTime($type, $input, $bookmark);
39
40 static::assertSame($dateTime->format($compareFormat), $expectedDateTime->format($compareFormat));
41 }
42
43 public function testExtractRequestedDateTimeExceptionUnknownType(): void
44 {
45 $this->expectException(\Exception::class);
46 $this->expectExceptionMessage('Unsupported daily format type');
47
48 DailyPageHelper::extractRequestedDateTime('nope', null, null);
49 }
50
51 /**
52 * @dataProvider getFormatsByType
53 */
54 public function testGetFormatByType(string $type, string $expectedFormat): void
55 {
56 $format = DailyPageHelper::getFormatByType($type);
57
58 static::assertSame($expectedFormat, $format);
59 }
60
61 public function testGetFormatByTypeExceptionUnknownType(): void
62 {
63 $this->expectException(\Exception::class);
64 $this->expectExceptionMessage('Unsupported daily format type');
65
66 DailyPageHelper::getFormatByType('nope');
67 }
68
69 /**
70 * @dataProvider getStartDatesByType
71 */
72 public function testGetStartDatesByType(
73 string $type,
74 \DateTimeImmutable $dateTime,
75 \DateTimeInterface $expectedDateTime
76 ): void {
77 $startDateTime = DailyPageHelper::getStartDateTimeByType($type, $dateTime);
78
79 static::assertEquals($expectedDateTime, $startDateTime);
80 }
81
82 public function testGetStartDatesByTypeExceptionUnknownType(): void
83 {
84 $this->expectException(\Exception::class);
85 $this->expectExceptionMessage('Unsupported daily format type');
86
87 DailyPageHelper::getStartDateTimeByType('nope', new \DateTimeImmutable());
88 }
89
90 /**
91 * @dataProvider getEndDatesByType
92 */
93 public function testGetEndDatesByType(
94 string $type,
95 \DateTimeImmutable $dateTime,
96 \DateTimeInterface $expectedDateTime
97 ): void {
98 $endDateTime = DailyPageHelper::getEndDateTimeByType($type, $dateTime);
99
100 static::assertEquals($expectedDateTime, $endDateTime);
101 }
102
103 public function testGetEndDatesByTypeExceptionUnknownType(): void
104 {
105 $this->expectException(\Exception::class);
106 $this->expectExceptionMessage('Unsupported daily format type');
107
108 DailyPageHelper::getEndDateTimeByType('nope', new \DateTimeImmutable());
109 }
110
111 /**
112 * @dataProvider getDescriptionsByType
113 */
114 public function testGeDescriptionsByType(
115 string $type,
116 \DateTimeImmutable $dateTime,
117 string $expectedDescription
118 ): void {
119 $description = DailyPageHelper::getDescriptionByType($type, $dateTime);
120
121 static::assertEquals($expectedDescription, $description);
122 }
123
124 public function getDescriptionByTypeExceptionUnknownType(): void
125 {
126 $this->expectException(\Exception::class);
127 $this->expectExceptionMessage('Unsupported daily format type');
128
129 DailyPageHelper::getDescriptionByType('nope', new \DateTimeImmutable());
130 }
131
132 /**
133 * @dataProvider getRssLengthsByType
134 */
135 public function testGeRssLengthsByType(string $type): void {
136 $length = DailyPageHelper::getRssLengthByType($type);
137
138 static::assertIsInt($length);
139 }
140
141 public function testGeRssLengthsByTypeExceptionUnknownType(): void
142 {
143 $this->expectException(\Exception::class);
144 $this->expectExceptionMessage('Unsupported daily format type');
145
146 DailyPageHelper::getRssLengthByType('nope');
147 }
148
149 /**
150 * Data provider for testExtractRequestedType() test method.
151 */
152 public function getRequestedTypes(): array
153 {
154 return [
155 [['month' => null], DailyPageHelper::DAY],
156 [['month' => ''], DailyPageHelper::MONTH],
157 [['month' => 'content'], DailyPageHelper::MONTH],
158 [['week' => null], DailyPageHelper::DAY],
159 [['week' => ''], DailyPageHelper::WEEK],
160 [['week' => 'content'], DailyPageHelper::WEEK],
161 [['day' => null], DailyPageHelper::DAY],
162 [['day' => ''], DailyPageHelper::DAY],
163 [['day' => 'content'], DailyPageHelper::DAY],
164 ];
165 }
166
167 /**
168 * Data provider for testExtractRequestedDateTime() test method.
169 */
170 public function getRequestedDateTimes(): array
171 {
172 return [
173 [DailyPageHelper::DAY, '20201013', null, new \DateTime('2020-10-13')],
174 [
175 DailyPageHelper::DAY,
176 '',
177 (new Bookmark())->setCreated($date = new \DateTime('2020-10-13 12:05:31')),
178 $date,
179 ],
180 [DailyPageHelper::DAY, '', null, new \DateTime()],
181 [DailyPageHelper::WEEK, '202030', null, new \DateTime('2020-07-20')],
182 [
183 DailyPageHelper::WEEK,
184 '',
185 (new Bookmark())->setCreated($date = new \DateTime('2020-10-13 12:05:31')),
186 new \DateTime('2020-10-13'),
187 ],
188 [DailyPageHelper::WEEK, '', null, new \DateTime(), 'Ym'],
189 [DailyPageHelper::MONTH, '202008', null, new \DateTime('2020-08-01'), 'Ym'],
190 [
191 DailyPageHelper::MONTH,
192 '',
193 (new Bookmark())->setCreated($date = new \DateTime('2020-10-13 12:05:31')),
194 new \DateTime('2020-10-13'),
195 'Ym'
196 ],
197 [DailyPageHelper::MONTH, '', null, new \DateTime(), 'Ym'],
198 ];
199 }
200
201 /**
202 * Data provider for testGetFormatByType() test method.
203 */
204 public function getFormatsByType(): array
205 {
206 return [
207 [DailyPageHelper::DAY, 'Ymd'],
208 [DailyPageHelper::WEEK, 'YW'],
209 [DailyPageHelper::MONTH, 'Ym'],
210 ];
211 }
212
213 /**
214 * Data provider for testGetStartDatesByType() test method.
215 */
216 public function getStartDatesByType(): array
217 {
218 return [
219 [DailyPageHelper::DAY, new \DateTimeImmutable('2020-10-09 04:05:06'), new \DateTime('2020-10-09 00:00:00')],
220 [DailyPageHelper::WEEK, new \DateTimeImmutable('2020-10-09 04:05:06'), new \DateTime('2020-10-05 00:00:00')],
221 [DailyPageHelper::MONTH, new \DateTimeImmutable('2020-10-09 04:05:06'), new \DateTime('2020-10-01 00:00:00')],
222 ];
223 }
224
225 /**
226 * Data provider for testGetEndDatesByType() test method.
227 */
228 public function getEndDatesByType(): array
229 {
230 return [
231 [DailyPageHelper::DAY, new \DateTimeImmutable('2020-10-09 04:05:06'), new \DateTime('2020-10-09 23:59:59')],
232 [DailyPageHelper::WEEK, new \DateTimeImmutable('2020-10-09 04:05:06'), new \DateTime('2020-10-11 23:59:59')],
233 [DailyPageHelper::MONTH, new \DateTimeImmutable('2020-10-09 04:05:06'), new \DateTime('2020-10-31 23:59:59')],
234 ];
235 }
236
237 /**
238 * Data provider for testGetDescriptionsByType() test method.
239 */
240 public function getDescriptionsByType(): array
241 {
242 return [
243 [DailyPageHelper::DAY, $date = new \DateTimeImmutable(), 'Today - ' . $date->format('F d, Y')],
244 [DailyPageHelper::DAY, $date = new \DateTimeImmutable('-1 day'), 'Yesterday - ' . $date->format('F d, Y')],
245 [DailyPageHelper::DAY, new \DateTimeImmutable('2020-10-09 04:05:06'), 'October 9, 2020'],
246 [DailyPageHelper::WEEK, new \DateTimeImmutable('2020-10-09 04:05:06'), 'Week 41 (October 5, 2020)'],
247 [DailyPageHelper::MONTH, new \DateTimeImmutable('2020-10-09 04:05:06'), 'October, 2020'],
248 ];
249 }
250
251 /**
252 * Data provider for testGetDescriptionsByType() test method.
253 */
254 public function getRssLengthsByType(): array
255 {
256 return [
257 [DailyPageHelper::DAY],
258 [DailyPageHelper::WEEK],
259 [DailyPageHelper::MONTH],
260 ];
261 }
262}
diff --git a/tpl/default/daily.html b/tpl/default/daily.html
index 3749bffb..5e038c39 100644
--- a/tpl/default/daily.html
+++ b/tpl/default/daily.html
@@ -7,11 +7,24 @@
7{include="page.header"} 7{include="page.header"}
8 8
9<div class="pure-g"> 9<div class="pure-g">
10 <div class="pure-u-1 pure-alert pure-alert-success tag-sort">
11 <a href="{$base_path}/daily?day">{'Daily'|t}</a>
12 <a href="{$base_path}/daily?week">{'Weekly'|t}</a>
13 <a href="{$base_path}/daily?month">{'Monthly'|t}</a>
14 </div>
15</div>
16
17
18<div class="pure-g">
10 <div class="pure-u-lg-1-6 pure-u-1-24"></div> 19 <div class="pure-u-lg-1-6 pure-u-1-24"></div>
11 <div class="pure-u-lg-2-3 pure-u-22-24 page-form page-visitor" id="daily"> 20 <div class="pure-u-lg-2-3 pure-u-22-24 page-form page-visitor" id="daily">
12 <h2 class="window-title"> 21 <h2 class="window-title">
13 {'The Daily Shaarli'|t} 22 {$localizedType} Shaarli
14 <a href="{$base_path}/daily-rss" title="{'1 RSS entry per day'|t}"><i class="fa fa-rss"></i></a> 23 <a href="{$base_path}/daily-rss?{$type}"
24 title="{function="t('1 RSS entry per :type', '', 1, 'shaarli', [':type' => t($type)])"}"
25 >
26 <i class="fa fa-rss"></i>
27 </a>
15 </h2> 28 </h2>
16 29
17 <div id="plugin_zone_start_daily" class="plugin_zone"> 30 <div id="plugin_zone_start_daily" class="plugin_zone">
@@ -25,19 +38,19 @@
25 <div class="pure-g"> 38 <div class="pure-g">
26 <div class="pure-u-lg-1-3 pure-u-1 center"> 39 <div class="pure-u-lg-1-3 pure-u-1 center">
27 {if="$previousday"} 40 {if="$previousday"}
28 <a href="{$base_path}/daily?day={$previousday}"> 41 <a href="{$base_path}/daily?{$type}={$previousday}">
29 <i class="fa fa-arrow-left"></i> 42 <i class="fa fa-arrow-left"></i>
30 {'Previous day'|t} 43 {function="t('Previous :type', '', 1, 'shaarli', [':type' => t($type)], true)"}
31 </a> 44 </a>
32 {/if} 45 {/if}
33 </div> 46 </div>
34 <div class="daily-desc pure-u-lg-1-3 pure-u-1 center"> 47 <div class="daily-desc pure-u-lg-1-3 pure-u-1 center">
35 {'All links of one day in a single page.'|t} 48 {function="t('All links of one :type in a single page.', '', 1, 'shaarli', [':type' => t($type)])"}
36 </div> 49 </div>
37 <div class="pure-u-lg-1-3 pure-u-1 center"> 50 <div class="pure-u-lg-1-3 pure-u-1 center">
38 {if="$nextday"} 51 {if="$nextday"}
39 <a href="{$base_path}/daily?day={$nextday}"> 52 <a href="{$base_path}/daily?{$type}={$nextday}">
40 {'Next day'|t} 53 {function="t('Next :type', '', 1, 'shaarli', [':type' => t($type)], true)"}
41 <i class="fa fa-arrow-right"></i> 54 <i class="fa fa-arrow-right"></i>
42 </a> 55 </a>
43 {/if} 56 {/if}
@@ -45,10 +58,7 @@
45 </div> 58 </div>
46 <div> 59 <div>
47 <h3 class="window-subtitle"> 60 <h3 class="window-subtitle">
48 {if="!empty($dayDesc)"} 61 {$dayDesc}
49 {$dayDesc} -
50 {/if}
51 {function="format_date($dayDate, false)"}
52 </h3> 62 </h3>
53 63
54 <div id="plugin_zone_about_daily" class="plugin_zone"> 64 <div id="plugin_zone_about_daily" class="plugin_zone">
diff --git a/tpl/default/dailyrss.html b/tpl/default/dailyrss.html
index d40d9496..871a3ba7 100644
--- a/tpl/default/dailyrss.html
+++ b/tpl/default/dailyrss.html
@@ -1,9 +1,9 @@
1<?xml version="1.0" encoding="UTF-8"?> 1<?xml version="1.0" encoding="UTF-8"?>
2<rss version="2.0"> 2<rss version="2.0">
3 <channel> 3 <channel>
4 <title>Daily - {$title}</title> 4 <title>{$localizedType} - {$title}</title>
5 <link>{$index_url}</link> 5 <link>{$index_url}</link>
6 <description>Daily shaared bookmarks</description> 6 <description>{function="t('All links of one :type in a single page.', '', 1, 'shaarli', [':type' => t($type)])"}</description>
7 <language>{$language}</language> 7 <language>{$language}</language>
8 <copyright>{$index_url}</copyright> 8 <copyright>{$index_url}</copyright>
9 <generator>Shaarli</generator> 9 <generator>Shaarli</generator>
@@ -18,12 +18,15 @@
18 {loop="$value.links"} 18 {loop="$value.links"}
19 <h3><a href="{$value.url}">{$value.title}</a></h3> 19 <h3><a href="{$value.url}">{$value.title}</a></h3>
20 <small> 20 <small>
21 {if="!$hide_timestamps"}{$value.created|format_date} - {/if}{if="$value.tags"}{$value.tags}{/if}<br> 21 {if="!$hide_timestamps"}{$value.created|format_date} &#8212; {/if}
22 <a href="{$index_url}shaare/{$value.shorturl}">{'Permalink'|t}</a>
23 {if="$value.tags"} &#8212; {$value.tags}{/if}
24 <br>
22 {$value.url} 25 {$value.url}
23 </small><br> 26 </small><br>
24 {if="$value.thumbnail"}<img src="{$index_url}{$value.thumbnail}#" alt="thumbnail" />{/if}<br> 27 {if="$value.thumbnail"}<img src="{$index_url}{$value.thumbnail}#" alt="thumbnail" />{/if}<br>
25 {if="$value.description"}{$value.description}{/if} 28 {if="$value.description"}{$value.description}{/if}
26 <br><br><hr> 29 <br><hr>
27 {/loop} 30 {/loop}
28 ]]></description> 31 ]]></description>
29 </item> 32 </item>