Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
64.68% |
141 / 218 |
|
70.27% |
26 / 37 |
CRAP | |
0.00% |
0 / 1 |
Record | |
64.68% |
141 / 218 |
|
70.27% |
26 / 37 |
386.57 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setCoverRouter | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
renderTemplate | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
1 | |||
__invoke | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
getCoreMetadata | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getCollectionBriefRecord | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getCollectionMetadata | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getComments | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
getExport | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getFormatClass | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
getFormatList | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getLabelList | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getListEntry | |
100.00% |
16 / 16 |
|
100.00% |
1 / 1 |
2 | |||
getListNotes | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
12 | |||
getPreviews | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getPreviewData | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
getPreviewLink | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
getPreviewIds | |
100.00% |
16 / 16 |
|
100.00% |
1 / 1 |
9 | |||
getTags | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
1 | |||
getTagsFromFavorites | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
2 | |||
getTitleHtml | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
12 | |||
getLink | |
100.00% |
12 / 12 |
|
100.00% |
1 / 1 |
2 | |||
getTab | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
1 | |||
getToolbar | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getSearchResult | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getCheckbox | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
2 | |||
getCover | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getPreviewCoverLinkSetting | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
20 | |||
getCoverDetails | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
30 | |||
getCoverSize | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
getThumbnailAlignment | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
42 | |||
getQrCode | |
100.00% |
20 / 20 |
|
100.00% |
1 / 1 |
7 | |||
getThumbnail | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getUrlList | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
getLinkDetails | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
10 | |||
hasOpenUrlReplaceSetting | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
deduplicateLinks | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | /** |
4 | * Record driver view helper |
5 | * |
6 | * PHP version 8 |
7 | * |
8 | * Copyright (C) Villanova University 2010. |
9 | * |
10 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License version 2, |
12 | * as published by the Free Software Foundation. |
13 | * |
14 | * This program is distributed in the hope that it will be useful, |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | * GNU General Public License for more details. |
18 | * |
19 | * You should have received a copy of the GNU General Public License |
20 | * along with this program; if not, write to the Free Software |
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
22 | * |
23 | * @category VuFind |
24 | * @package View_Helpers |
25 | * @author Demian Katz <demian.katz@villanova.edu> |
26 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
27 | * @link https://vufind.org/wiki/development Wiki |
28 | */ |
29 | |
30 | namespace VuFind\View\Helper\Root; |
31 | |
32 | use Laminas\Config\Config; |
33 | use VuFind\Cover\Router as CoverRouter; |
34 | use VuFind\Db\Entity\UserEntityInterface; |
35 | use VuFind\Db\Entity\UserListEntityInterface; |
36 | use VuFind\Db\Service\CommentsServiceInterface; |
37 | use VuFind\Db\Service\DbServiceAwareInterface; |
38 | use VuFind\Db\Service\DbServiceAwareTrait; |
39 | use VuFind\Db\Service\UserListServiceInterface; |
40 | use VuFind\Db\Service\UserResourceServiceInterface; |
41 | use VuFind\Tags\TagsService; |
42 | |
43 | use function get_class; |
44 | use function in_array; |
45 | use function is_callable; |
46 | |
47 | /** |
48 | * Record driver view helper |
49 | * |
50 | * @category VuFind |
51 | * @package View_Helpers |
52 | * @author Demian Katz <demian.katz@villanova.edu> |
53 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
54 | * @link https://vufind.org/wiki/development Wiki |
55 | */ |
56 | class Record extends \Laminas\View\Helper\AbstractHelper implements DbServiceAwareInterface |
57 | { |
58 | use ClassBasedTemplateRendererTrait; |
59 | use DbServiceAwareTrait; |
60 | |
61 | /** |
62 | * Context view helper |
63 | * |
64 | * @var \VuFind\View\Helper\Root\Context |
65 | */ |
66 | protected $contextHelper; |
67 | |
68 | /** |
69 | * Cover router |
70 | * |
71 | * @var CoverRouter |
72 | */ |
73 | protected $coverRouter = null; |
74 | |
75 | /** |
76 | * Record driver |
77 | * |
78 | * @var \VuFind\RecordDriver\AbstractBase |
79 | */ |
80 | protected $driver; |
81 | |
82 | /** |
83 | * Constructor |
84 | * |
85 | * @param TagsService $tagsService Tags service |
86 | * @param Config $config Configuration from config.ini |
87 | */ |
88 | public function __construct(protected TagsService $tagsService, protected ?Config $config = null) |
89 | { |
90 | $this->config = $config; |
91 | } |
92 | |
93 | /** |
94 | * Inject the cover router |
95 | * |
96 | * @param CoverRouter $router Cover router |
97 | * |
98 | * @return void |
99 | */ |
100 | public function setCoverRouter($router) |
101 | { |
102 | $this->coverRouter = $router; |
103 | } |
104 | |
105 | /** |
106 | * Render a template within a record driver folder. |
107 | * |
108 | * @param string $name Template name to render |
109 | * @param array $context Variables needed for rendering template; these will |
110 | * be temporarily added to the global view context, then reverted after the |
111 | * template is rendered (default = record driver only). |
112 | * @param bool $throw If true (default), an exception is thrown if the |
113 | * template is not found. Otherwise an empty string is returned. |
114 | * |
115 | * @return string |
116 | */ |
117 | public function renderTemplate($name, $context = null, $throw = true) |
118 | { |
119 | $template = 'RecordDriver/%s/' . $name; |
120 | $className = get_class($this->driver); |
121 | return $this->renderClassTemplate( |
122 | $template, |
123 | $className, |
124 | $context ?? ['driver' => $this->driver], |
125 | $throw |
126 | ); |
127 | } |
128 | |
129 | /** |
130 | * Store a record driver object and return this object so that the appropriate |
131 | * template can be rendered. |
132 | * |
133 | * @param \VuFind\RecordDriver\AbstractBase $driver Record driver object. |
134 | * |
135 | * @return Record |
136 | */ |
137 | public function __invoke($driver) |
138 | { |
139 | // Set up context helper: |
140 | $contextHelper = $this->getView()->plugin('context'); |
141 | $this->contextHelper = $contextHelper($this->getView()); |
142 | |
143 | // Set up driver context: |
144 | $this->driver = $driver; |
145 | return $this; |
146 | } |
147 | |
148 | /** |
149 | * Render the core metadata area of the record view. |
150 | * |
151 | * @return string |
152 | */ |
153 | public function getCoreMetadata() |
154 | { |
155 | return $this->renderTemplate('core.phtml'); |
156 | } |
157 | |
158 | /** |
159 | * Render the a brief record for use in collection mode. |
160 | * |
161 | * @return string |
162 | */ |
163 | public function getCollectionBriefRecord() |
164 | { |
165 | return $this->renderTemplate('collection-record.phtml'); |
166 | } |
167 | |
168 | /** |
169 | * Render the core metadata area of the collection view. |
170 | * |
171 | * @return string |
172 | */ |
173 | public function getCollectionMetadata() |
174 | { |
175 | return $this->renderTemplate('collection-info.phtml'); |
176 | } |
177 | |
178 | /** |
179 | * Get comments associated with the current record. |
180 | * |
181 | * @return CommentsEntityInterface[] |
182 | */ |
183 | public function getComments(): array |
184 | { |
185 | return $this->getDbService(CommentsServiceInterface::class)->getRecordComments( |
186 | $this->driver->getUniqueId(), |
187 | $this->driver->getSourceIdentifier() |
188 | ); |
189 | } |
190 | |
191 | /** |
192 | * Export the record in the requested format. For legal values, see |
193 | * the export helper's getFormatsForRecord() method. |
194 | * |
195 | * @param string $format Export format to display |
196 | * |
197 | * @return string Exported data |
198 | */ |
199 | public function getExport($format) |
200 | { |
201 | $format = strtolower($format); |
202 | return $this->renderTemplate('export-' . $format . '.phtml'); |
203 | } |
204 | |
205 | /** |
206 | * Get the CSS class used to properly render a format. (Note that this may |
207 | * not be used by every theme). |
208 | * |
209 | * @param string $format Format text to convert into CSS class |
210 | * |
211 | * @return string |
212 | */ |
213 | public function getFormatClass($format) |
214 | { |
215 | return $this->renderTemplate( |
216 | 'format-class.phtml', |
217 | ['format' => $format] |
218 | ); |
219 | } |
220 | |
221 | /** |
222 | * Render a list of record formats. |
223 | * |
224 | * @return string |
225 | */ |
226 | public function getFormatList() |
227 | { |
228 | return $this->renderTemplate('format-list.phtml'); |
229 | } |
230 | |
231 | /** |
232 | * Render a list of record labels. |
233 | * |
234 | * @return string |
235 | */ |
236 | public function getLabelList() |
237 | { |
238 | return $this->renderTemplate('label-list.phtml'); |
239 | } |
240 | |
241 | /** |
242 | * Render an entry in a favorite list. |
243 | * |
244 | * @param ?UserListEntityInterface $list Currently selected list (null for |
245 | * combined favorites) |
246 | * @param ?UserEntityInterface $user Current logged in user (null if none) |
247 | * |
248 | * @return string |
249 | */ |
250 | public function getListEntry($list = null, $user = null) |
251 | { |
252 | // Get list of lists containing this entry |
253 | $lists = null; |
254 | if ($user) { |
255 | $lists = $this->getDbService(UserListServiceInterface::class)->getListsContainingRecord( |
256 | $this->driver->getUniqueID(), |
257 | $this->driver->getSourceIdentifier(), |
258 | $user |
259 | ); |
260 | } |
261 | return $this->renderTemplate( |
262 | 'list-entry.phtml', |
263 | [ |
264 | 'driver' => $this->driver, |
265 | 'list' => $list, |
266 | 'user' => $user, |
267 | 'lists' => $lists, |
268 | ] |
269 | ); |
270 | } |
271 | |
272 | /** |
273 | * Get notes associated with this record in user lists. |
274 | * |
275 | * @param int $list_id ID of list to load tags from (null for all lists) |
276 | * @param int $user_id ID of user to load tags from (null for all users) |
277 | * |
278 | * @return string[] |
279 | */ |
280 | public function getListNotes($list_id = null, $user_id = null) |
281 | { |
282 | $data = $this->getDbService(UserResourceServiceInterface::class)->getFavoritesForRecord( |
283 | $this->driver->getUniqueId(), |
284 | $this->driver->getSourceIdentifier(), |
285 | $list_id, |
286 | $user_id |
287 | ); |
288 | $notes = []; |
289 | foreach ($data as $current) { |
290 | if (!empty($note = $current->getNotes())) { |
291 | $notes[] = $note; |
292 | } |
293 | } |
294 | return $notes; |
295 | } |
296 | |
297 | /** |
298 | * Render previews (data and link) of the item if configured. |
299 | * |
300 | * @return string |
301 | */ |
302 | public function getPreviews() |
303 | { |
304 | return $this->getPreviewData() . $this->getPreviewLink(); |
305 | } |
306 | |
307 | /** |
308 | * Render data needed to get previews. |
309 | * |
310 | * @return string |
311 | */ |
312 | public function getPreviewData() |
313 | { |
314 | return $this->renderTemplate( |
315 | 'previewdata.phtml', |
316 | ['driver' => $this->driver, 'config' => $this->config] |
317 | ); |
318 | } |
319 | |
320 | /** |
321 | * Render links to previews of the item if configured. |
322 | * |
323 | * @return string |
324 | */ |
325 | public function getPreviewLink() |
326 | { |
327 | return $this->renderTemplate( |
328 | 'previewlink.phtml', |
329 | ['driver' => $this->driver, 'config' => $this->config] |
330 | ); |
331 | } |
332 | |
333 | /** |
334 | * Collects ISBN, LCCN, and OCLC numbers to use in calling preview APIs |
335 | * |
336 | * @return array |
337 | */ |
338 | public function getPreviewIds() |
339 | { |
340 | // Extract identifiers from record driver if it supports appropriate methods: |
341 | $isbn = is_callable([$this->driver, 'getCleanISBN']) |
342 | ? $this->driver->getCleanISBN() : ''; |
343 | $lccn = is_callable([$this->driver, 'getLCCN']) |
344 | ? $this->driver->getLCCN() : ''; |
345 | $oclc = is_callable([$this->driver, 'getOCLC']) |
346 | ? $this->driver->getOCLC() : []; |
347 | |
348 | // Turn identifiers into class names to communicate with jQuery logic: |
349 | $idClasses = []; |
350 | if (!empty($isbn)) { |
351 | $idClasses[] = 'ISBN' . $isbn; |
352 | } |
353 | if (!empty($lccn)) { |
354 | $idClasses[] = 'LCCN' . $lccn; |
355 | } |
356 | if (!empty($oclc)) { |
357 | foreach ($oclc as $oclcNum) { |
358 | if (!empty($oclcNum)) { |
359 | $idClasses[] = 'OCLC' . $oclcNum; |
360 | } |
361 | } |
362 | } |
363 | return $idClasses; |
364 | } |
365 | |
366 | /** |
367 | * Get tags associated with the currently-loaded record. |
368 | * |
369 | * @param UserListEntityInterface|int|null $listOrId ID of list to load tags from (null for no restriction) |
370 | * @param UserEntityInterface|int|null $userOrId ID of user to load tags from (null for all users) |
371 | * @param string $sort Sort type ('count' or 'tag') |
372 | * @param UserEntityInterface|int|null $ownerOrId ID of user to check for ownership |
373 | * |
374 | * @return array |
375 | */ |
376 | public function getTags( |
377 | UserListEntityInterface|int|null $listOrId = null, |
378 | UserEntityInterface|int|null $userOrId = null, |
379 | string $sort = 'count', |
380 | UserEntityInterface|int|null $ownerOrId = null |
381 | ): array { |
382 | return $this->tagsService->getRecordTags( |
383 | $this->driver->getUniqueId(), |
384 | $this->driver->getSourceIdentifier(), |
385 | 0, |
386 | $listOrId, |
387 | $userOrId, |
388 | $sort, |
389 | $ownerOrId |
390 | ); |
391 | } |
392 | |
393 | /** |
394 | * Get tags associated with the currently-loaded record AND with a favorites list. |
395 | * |
396 | * @param UserListEntityInterface|int|null $listOrId ID of list to load tags from (null for tags that |
397 | * are associated with ANY list, but excluding non-list tags) |
398 | * @param UserEntityInterface|int|null $userOrId ID of user to load tags from (null for all users) |
399 | * @param string $sort Sort type ('count' or 'tag') |
400 | * @param UserEntityInterface|int|null $ownerOrId ID of user to check for ownership |
401 | * |
402 | * @return array |
403 | */ |
404 | public function getTagsFromFavorites( |
405 | UserListEntityInterface|int|null $listOrId = null, |
406 | UserEntityInterface|int|null $userOrId = null, |
407 | string $sort = 'count', |
408 | UserEntityInterface|int|null $ownerOrId = null |
409 | ): array { |
410 | return $this->tagsService->getRecordTagsFromFavorites( |
411 | $this->driver->getUniqueId(), |
412 | $this->driver->getSourceIdentifier(), |
413 | 0, |
414 | $listOrId, |
415 | $userOrId, |
416 | $sort, |
417 | $ownerOrId |
418 | ); |
419 | } |
420 | |
421 | /** |
422 | * Get HTML to render a title. |
423 | * |
424 | * @param int $maxLength Maximum length of non-highlighted title. |
425 | * |
426 | * @return string |
427 | */ |
428 | public function getTitleHtml($maxLength = 180) |
429 | { |
430 | $highlightedTitle = $this->driver->tryMethod('getHighlightedTitle'); |
431 | $title = trim($this->driver->tryMethod('getTitle')); |
432 | if (!empty($highlightedTitle)) { |
433 | $highlight = $this->getView()->plugin('highlight'); |
434 | $addEllipsis = $this->getView()->plugin('addEllipsis'); |
435 | return $highlight($addEllipsis($highlightedTitle, $title)); |
436 | } |
437 | if (!empty($title)) { |
438 | $escapeHtml = $this->getView()->plugin('escapeHtml'); |
439 | $truncate = $this->getView()->plugin('truncate'); |
440 | return $escapeHtml($truncate($title, $maxLength)); |
441 | } |
442 | $transEsc = $this->getView()->plugin('transEsc'); |
443 | return $transEsc('Title not available'); |
444 | } |
445 | |
446 | /** |
447 | * Render the link of the specified type. |
448 | * |
449 | * @param string $type Link type |
450 | * @param string $lookfor String to search for at link |
451 | * |
452 | * @return string |
453 | */ |
454 | public function getLink($type, $lookfor) |
455 | { |
456 | $link = $this->renderTemplate( |
457 | 'link-' . $type . '.phtml', |
458 | ['driver' => $this->driver, 'lookfor' => $lookfor] |
459 | ); |
460 | |
461 | $prepend = (!str_contains($link, '?')) ? '?' : '&'; |
462 | |
463 | $link .= $this->getView()->plugin('searchTabs') |
464 | ->getCurrentHiddenFilterParams( |
465 | $this->driver->getSearchBackendIdentifier(), |
466 | false, |
467 | $prepend |
468 | ); |
469 | return $link; |
470 | } |
471 | |
472 | /** |
473 | * Render the contents of the specified record tab. |
474 | * |
475 | * @param \VuFind\RecordTab\TabInterface $tab Tab to display |
476 | * |
477 | * @return string |
478 | */ |
479 | public function getTab(\VuFind\RecordTab\TabInterface $tab) |
480 | { |
481 | $context = ['driver' => $this->driver, 'tab' => $tab]; |
482 | $classParts = explode('\\', $tab::class); |
483 | $template = 'RecordTab/' . strtolower(array_pop($classParts)) . '.phtml'; |
484 | $oldContext = $this->contextHelper->apply($context); |
485 | $html = $this->view->render($template); |
486 | $this->contextHelper->restore($oldContext); |
487 | return $html; |
488 | } |
489 | |
490 | /** |
491 | * Render a toolbar for use on the record view. |
492 | * |
493 | * @return string |
494 | */ |
495 | public function getToolbar() |
496 | { |
497 | return $this->renderTemplate('toolbar.phtml'); |
498 | } |
499 | |
500 | /** |
501 | * Render a search result for the specified view mode. |
502 | * |
503 | * @param string $view View mode to use. |
504 | * |
505 | * @return string |
506 | */ |
507 | public function getSearchResult($view) |
508 | { |
509 | return $this->renderTemplate('result-' . $view . '.phtml'); |
510 | } |
511 | |
512 | /** |
513 | * Render an HTML checkbox control for the current record. |
514 | * |
515 | * @param string $idPrefix Prefix for checkbox HTML ids |
516 | * @param string $formAttr ID of form for [form] attribute |
517 | * @param int $number Result number (for label of checkbox) |
518 | * |
519 | * @return string |
520 | */ |
521 | public function getCheckbox($idPrefix = '', $formAttr = false, $number = null) |
522 | { |
523 | $id = $this->driver->getSourceIdentifier() . '|' |
524 | . $this->driver->getUniqueId(); |
525 | $context |
526 | = ['id' => $id, 'number' => $number, 'prefix' => $idPrefix]; |
527 | if ($formAttr) { |
528 | $context['formAttr'] = $formAttr; |
529 | } |
530 | return $this->contextHelper->renderInContext( |
531 | 'record/checkbox.phtml', |
532 | $context |
533 | ); |
534 | } |
535 | |
536 | /** |
537 | * Render a cover for the current record. |
538 | * |
539 | * @param string $context Context of code being generated |
540 | * @param string $default The default size of the cover |
541 | * @param string $link The link for the anchor |
542 | * |
543 | * @return string |
544 | */ |
545 | public function getCover($context, $default, $link = false) |
546 | { |
547 | $details = $this->getCoverDetails($context, $default, $link); |
548 | return $details['html']; |
549 | } |
550 | |
551 | /** |
552 | * Should cover images be linked to previews (when applicable) in the provided |
553 | * template context? |
554 | * |
555 | * @param string $context Context of code being generated |
556 | * |
557 | * @return bool |
558 | */ |
559 | protected function getPreviewCoverLinkSetting($context) |
560 | { |
561 | static $previewContexts = false; |
562 | if (false === $previewContexts) { |
563 | $previewContexts = isset($this->config->Content->linkPreviewsToCovers) |
564 | ? array_map( |
565 | 'trim', |
566 | explode(',', $this->config->Content->linkPreviewsToCovers) |
567 | ) : ['*']; |
568 | } |
569 | return in_array('*', $previewContexts) |
570 | || in_array($context, $previewContexts); |
571 | } |
572 | |
573 | /** |
574 | * Get the rendered cover plus some useful parameters. |
575 | * |
576 | * @param string $context Context of code being generated |
577 | * @param string $default The default size of the cover |
578 | * @param string $link The link for the anchor |
579 | * |
580 | * @return array |
581 | */ |
582 | public function getCoverDetails($context, $default, $link = false) |
583 | { |
584 | $details = compact('link', 'context') + [ |
585 | 'driver' => $this->driver, 'cover' => false, 'size' => false, |
586 | 'linkPreview' => $this->getPreviewCoverLinkSetting($context), |
587 | ]; |
588 | $preferredSize = $this->getCoverSize($context, $default); |
589 | if (empty($preferredSize)) { // covers disabled entirely |
590 | $details['html'] = ''; |
591 | } else { |
592 | // Find best option if more than one size is defined (e.g. small:medium) |
593 | foreach (explode(':', $preferredSize) as $size) { |
594 | if ($details['cover'] = $this->getThumbnail($size)) { |
595 | $details['size'] = $size; |
596 | break; |
597 | } |
598 | } |
599 | if ($details['size'] === false) { |
600 | [$details['size']] = explode(':', $preferredSize); |
601 | } |
602 | $details['html'] = $this->renderTemplate('cover.phtml', $details); |
603 | } |
604 | return $details; |
605 | } |
606 | |
607 | /** |
608 | * Get the configured thumbnail size for record lists |
609 | * |
610 | * @param string $context Context of code being generated |
611 | * @param string $default The default size of the cover |
612 | * |
613 | * @return string |
614 | */ |
615 | protected function getCoverSize($context, $default = 'medium') |
616 | { |
617 | if ( |
618 | isset($this->config->Content->coversize) |
619 | && !$this->config->Content->coversize |
620 | ) { |
621 | // covers disabled entirely |
622 | return false; |
623 | } |
624 | // check for context-specific overrides |
625 | return $this->config->Content->coversize[$context] ?? $default; |
626 | } |
627 | |
628 | /** |
629 | * Get the configured thumbnail alignment |
630 | * |
631 | * @param string $context telling the context asking, prepends the config key |
632 | * |
633 | * @return string |
634 | */ |
635 | public function getThumbnailAlignment($context = 'result') |
636 | { |
637 | $view = $this->getView(); |
638 | $configField = $context . 'ThumbnailsOnLeft'; |
639 | $left = !isset($this->config->Site->$configField) |
640 | ? true : $this->config->Site->$configField; |
641 | $mirror = !isset($this->config->Site->mirrorThumbnailsRTL) |
642 | ? true : $this->config->Site->mirrorThumbnailsRTL; |
643 | if ($view->layout()->rtl && !$mirror) { |
644 | $left = !$left; |
645 | } |
646 | return $left ? 'left' : 'right'; |
647 | } |
648 | |
649 | /** |
650 | * Generate a qrcode URL (return false if unsupported). |
651 | * |
652 | * @param string $context Context of code being generated (core or results) |
653 | * @param array $extra Extra details to pass to the QR code template |
654 | * @param string $level QR code level |
655 | * @param int $size QR code size |
656 | * @param int $margin QR code margin |
657 | * |
658 | * @return string|bool |
659 | */ |
660 | public function getQrCode( |
661 | $context, |
662 | $extra = [], |
663 | $level = 'L', |
664 | $size = 3, |
665 | $margin = 4 |
666 | ) { |
667 | if (!isset($this->config->QRCode)) { |
668 | return false; |
669 | } |
670 | |
671 | switch ($context) { |
672 | case 'core': |
673 | case 'results': |
674 | $key = 'showIn' . ucwords(strtolower($context)); |
675 | break; |
676 | default: |
677 | return false; |
678 | } |
679 | |
680 | if ( |
681 | !isset($this->config->QRCode->$key) |
682 | || !$this->config->QRCode->$key |
683 | ) { |
684 | return false; |
685 | } |
686 | |
687 | $template = $context . '-qrcode.phtml'; |
688 | |
689 | // Try to build text: |
690 | $text = $this->renderTemplate( |
691 | $template, |
692 | $extra + ['driver' => $this->driver] |
693 | ); |
694 | $qrcode = [ |
695 | 'text' => $text, 'level' => $level, 'size' => $size, 'margin' => $margin, |
696 | ]; |
697 | |
698 | $urlHelper = $this->getView()->plugin('url'); |
699 | return $urlHelper('qrcode-show') . '?' . http_build_query($qrcode); |
700 | } |
701 | |
702 | /** |
703 | * Generate a thumbnail URL (return false if unsupported). |
704 | * |
705 | * @param string $size Size of thumbnail (small, medium or large -- small is |
706 | * default). |
707 | * |
708 | * @return string|bool |
709 | */ |
710 | public function getThumbnail($size = 'small') |
711 | { |
712 | // Find out whether or not AJAX covers are enabled; this will control |
713 | // whether dynamic URLs are resolved immediately or deferred until later |
714 | // (see third parameter of getUrl() below). |
715 | $ajaxcovers = $this->config->Content->ajaxcovers ?? false; |
716 | return $this->coverRouter |
717 | ? $this->coverRouter->getUrl($this->driver, $size, !$ajaxcovers) |
718 | : false; |
719 | } |
720 | |
721 | /** |
722 | * Get all URLs associated with the record. Returns an array of strings. |
723 | * |
724 | * @return array |
725 | */ |
726 | public function getUrlList() |
727 | { |
728 | // Use a filter to pick URLs from the output of getLinkDetails(): |
729 | $filter = function ($i) { |
730 | return $i['url']; |
731 | }; |
732 | return array_map($filter, $this->getLinkDetails()); |
733 | } |
734 | |
735 | /** |
736 | * Get all the links associated with this record. Returns an array of |
737 | * associative arrays each containing 'desc' and 'url' keys. |
738 | * |
739 | * @param bool $openUrlActive Is there an active OpenURL on the page? |
740 | * |
741 | * @return array |
742 | */ |
743 | public function getLinkDetails($openUrlActive = false) |
744 | { |
745 | // See if there are any links available: |
746 | $urls = $this->driver->tryMethod('getURLs'); |
747 | if (empty($urls) || ($openUrlActive && $this->hasOpenUrlReplaceSetting())) { |
748 | return []; |
749 | } |
750 | |
751 | // If we found links, we may need to convert from the "route" format |
752 | // to the "full URL" format. |
753 | $urlHelper = $this->getView()->plugin('url'); |
754 | $serverUrlHelper = $this->getView()->plugin('serverurl'); |
755 | $formatLink = function ($link) use ($urlHelper, $serverUrlHelper) { |
756 | // Error if route AND URL are missing at this point! |
757 | if (!isset($link['route']) && !isset($link['url'])) { |
758 | throw new \Exception('Invalid URL array.'); |
759 | } |
760 | |
761 | // Build URL from route/query details if missing: |
762 | if (!isset($link['url'])) { |
763 | $routeParams = $link['routeParams'] ?? []; |
764 | |
765 | $link['url'] = $serverUrlHelper( |
766 | $urlHelper($link['route'], $routeParams) |
767 | ); |
768 | if (isset($link['queryString'])) { |
769 | $link['url'] .= $link['queryString']; |
770 | } |
771 | } |
772 | |
773 | // Apply prefix if found |
774 | if (isset($link['prefix'])) { |
775 | $link['url'] = $link['prefix'] . $link['url']; |
776 | } |
777 | // Use URL as description if missing: |
778 | if (!isset($link['desc'])) { |
779 | $link['desc'] = $link['url']; |
780 | } |
781 | |
782 | return $link; |
783 | }; |
784 | |
785 | return $this->deduplicateLinks(array_map($formatLink, $urls)); |
786 | } |
787 | |
788 | /** |
789 | * Get all the links associated with this record depending on the OpenURL setting |
790 | * replace_other_urls. Returns an array of associative arrays each containing |
791 | * 'desc' and 'url' keys. |
792 | * |
793 | * @return bool |
794 | */ |
795 | protected function hasOpenUrlReplaceSetting() |
796 | { |
797 | return isset($this->config->OpenURL->replace_other_urls) |
798 | && $this->config->OpenURL->replace_other_urls; |
799 | } |
800 | |
801 | /** |
802 | * Remove duplicates from the array. All keys and values are being used |
803 | * recursively to compare, so if there are 2 links with the same url |
804 | * but different desc, they will both be preserved. |
805 | * |
806 | * @param array $links array of associative arrays, |
807 | * each containing 'desc' and 'url' keys |
808 | * |
809 | * @return array |
810 | */ |
811 | protected function deduplicateLinks($links) |
812 | { |
813 | return array_values(array_unique($links, SORT_REGULAR)); |
814 | } |
815 | } |