Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
97.22% covered (success)
97.22%
35 / 36
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
BookmarkFormatter
97.22% covered (success)
97.22%
35 / 36
50.00% covered (danger)
50.00%
1 / 2
6
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 format
97.14% covered (success)
97.14%
34 / 35
0.00% covered (danger)
0.00%
0 / 1
5
1<?php
2
3/**
4 * Bookmark Formatter.
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public License,
7 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
8 * obtain one at https://mozilla.org/MPL/2.0/.
9 *
10 * @package   phpMyFAQ
11 * @author    Thorsten Rinne <thorsten@phpmyfaq.de>
12 * @author    Jan Harms <model_railroader@gmx-topmail.de>
13 * @copyright 2025 phpMyFAQ Team
14 * @license   https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
15 * @link      https://www.phpmyfaq.de
16 * @since     2025-10-13
17 */
18
19declare(strict_types=1);
20
21namespace phpMyFAQ\Bookmark;
22
23use phpMyFAQ\Category;
24use phpMyFAQ\Configuration;
25use phpMyFAQ\Faq;
26use phpMyFAQ\Helper\FaqHelper;
27use phpMyFAQ\Link;
28use phpMyFAQ\Strings;
29use phpMyFAQ\User\CurrentUser;
30
31readonly class BookmarkFormatter
32{
33    public function __construct(
34        private Configuration $configuration,
35        private CurrentUser $currentUser,
36    ) {
37    }
38
39    /**
40     * Format a single bookmark DB object into the array used by the UI.
41     *
42     * Returns null if the bookmark cannot be resolved (missing FAQ, invalid id, ...).
43     *
44     * @return array{url:string,title:string,id:int,answer:string}|null
45     */
46    public function format(object $bookmark): ?array
47    {
48        if (($bookmark->faqid ?? null) === null) {
49            return null;
50        }
51
52        $faqId = (int) $bookmark->faqid;
53        if ($faqId <= 0) {
54            return null;
55        }
56
57        [$currentUser, $currentGroups] = CurrentUser::getCurrentUserGroupId($this->currentUser);
58        $faq = new Faq($this->configuration);
59        $faq->setUser($currentUser)->setGroups($currentGroups);
60        $faq->getFaq($faqId);
61        $faqData = $faq->faqRecord;
62
63        if (($faqData['id'] ?? null) === null) {
64            return null;
65        }
66
67        $category = new Category($this->configuration);
68        $categoryId = $category->getCategoryIdFromFaq((int) $faqData['id']);
69
70        $url = sprintf(
71            '%scontent/%d/%d/%s/%s.html',
72            $this->configuration->getDefaultUrl(),
73            $categoryId,
74            (int) $faqData['id'],
75            (string) ($faqData['lang'] ?? ''),
76            Link\Util\TitleSlugifier::slug((string) ($faqData['title'] ?? '')),
77        );
78
79        $link = new Link($url, $this->configuration);
80        $title = (string) ($faqData['title'] ?? '');
81        $link->text = Strings::htmlentities($title);
82        $link->setTitle($link->text);
83        $link->tooltip = $link->text;
84        $faqHelper = new FaqHelper($this->configuration);
85        $answer = (string) ($faqData['content'] ?? '');
86
87        return [
88            'url' => $link->toString(),
89            'title' => htmlspecialchars_decode($title),
90            'id' => (int) $faqData['id'],
91            'answer' => $answer === '' ? '' : $faqHelper->cleanUpContent($answer),
92        ];
93    }
94}