xref: /dokuwiki/inc/Ui/Media/Display.php (revision 6a8e48eda246f872402bf5b85763f276cd4c319d)
1<?php
2
3namespace dokuwiki\Ui\Media;
4
5use dokuwiki\File\MediaFile;
6
7class Display
8{
9    /** @var MediaFile */
10    protected $mediaFile;
11
12    /** @var string should IDs be shown relative to this namespace? Used in search results */
13    protected $relativeDisplay;
14
15    /** @var bool scroll to this file on display? */
16    protected $scrollIntoView = false;
17
18    /**
19     * Display constructor.
20     * @param MediaFile $mediaFile
21     */
22    public function __construct(MediaFile $mediaFile)
23    {
24        $this->mediaFile = $mediaFile;
25    }
26
27    /**
28     * Get the HTML to display a preview image if possible, otherwise show an icon
29     *
30     * @param int $w bounding box width to resize pixel based images to
31     * @param int $h bounding box height to resize pixel based images to
32     * @return string
33     */
34    public function getPreviewHtml($w, $h)
35    {
36        if ($this->mediaFile->isImage()) {
37            $src = ml($this->mediaFile->getId(), ['w' => $w, 'h' => $h]);
38        } else {
39            $src = $this->getIconUrl();
40        }
41
42        $attr = [
43            'alt' => $this->mediaFile->getDisplayName(),
44            'loading' => 'lazy',
45            'width' => $w,
46            'height' => $h,
47        ];
48
49        return '<img src="' . $src . '" ' . buildAttributes($attr) . ' />';
50    }
51
52    /**
53     * Get the HTML for the large detail-view preview
54     *
55     * Produces the <div class="image"><a><img/></a></div> block shown on the
56     * mediamanager "View" tab and in the media diff view. Unlike the thumbnail
57     * in {@see getPreviewHtml()}, this uses bounding-box fit so the full image
58     * is visible with correct aspect ratio, including EXIF-rotated JPEGs.
59     *
60     * @param int $w bounding box width
61     * @param int $h bounding box height
62     * @return string empty string for non-images or unreadable files
63     */
64    public function getDetailHtml($w = 500, $h = 500)
65    {
66        global $lang;
67
68        if (!$this->mediaFile->isImage()) return '';
69
70        // the src below uses fit=1, so predict the matching no-upscale dimensions
71        [$dw, $dh] = $this->mediaFile->getDisplayDimensions($w, $h, true);
72        if ($dw <= 0) return '';
73
74        $id = $this->mediaFile->getId();
75        $rev = $this->mediaFile->getRev();
76        $cacheBust = $rev
77            ? ['rev' => $rev]
78            : ['t' => filemtime($this->mediaFile->getPath())];
79
80        // pass raw '&' separator so buildAttributes' hsc() encodes exactly once
81        $imgAttr = [
82            'src' => ml($id, ['w' => $w, 'h' => $h, 'fit' => 1, ...$cacheBust], true, '&'),
83            'alt' => $this->mediaFile->getDisplayName(),
84            'width' => $dw,
85            'height' => $dh,
86            'style' => 'max-width: ' . $dw . 'px;',
87        ];
88        $linkAttr = [
89            'href' => ml($id, $cacheBust, true, '&'),
90            'target' => '_blank',
91            'title' => $lang['mediaview'],
92        ];
93
94        return '<div class="image">'
95            . '<a ' . buildAttributes($linkAttr) . '>'
96            . '<img ' . buildAttributes($imgAttr) . ' />'
97            . '</a>'
98            . '</div>';
99    }
100
101    /**
102     * Return the URL to the icon for this file
103     *
104     * @return string
105     */
106    public function getIconUrl()
107    {
108        $link = 'lib/images/fileicons/svg/' . $this->mediaFile->getIcoClass() . '.svg';
109        if (!file_exists(DOKU_INC . $link)) $link = 'lib/images/fileicons/svg/file.svg';
110        return DOKU_BASE . $link;
111    }
112
113    /**
114     * Show IDs relative to this namespace
115     *
116     * @param string|null $ns Use null to disable
117     */
118    public function relativeDisplay($ns)
119    {
120        $this->relativeDisplay = $ns;
121    }
122
123    /**
124     * Scroll to this file on display?
125     *
126     * @param bool $set
127     */
128    public function scrollIntoView($set = true)
129    {
130        $this->scrollIntoView = $set;
131    }
132
133    /** @return string */
134    protected function formatDate()
135    {
136        return dformat($this->mediaFile->getLastModified());
137    }
138
139    /**
140     * Output the image dimension if any
141     *
142     * @param string $empty what to show when no dimensions are available
143     * @return string
144     */
145    protected function formatDimensions($empty = '&#160;')
146    {
147        $w = $this->mediaFile->getWidth();
148        $h = $this->mediaFile->getHeight();
149        if ($w && $h) {
150            return $w . '&#215;' . $h;
151        } else {
152            return $empty;
153        }
154    }
155
156    /** @return string */
157    protected function formatFileSize()
158    {
159        return filesize_h($this->mediaFile->getFileSize());
160    }
161
162    /** @return string */
163    protected function formatDisplayName()
164    {
165        if ($this->relativeDisplay !== null) {
166            $id = $this->mediaFile->getId();
167            if (str_starts_with($id, $this->relativeDisplay)) {
168                $id = substr($id, strlen($this->relativeDisplay));
169            }
170            return ltrim($id, ':');
171        } else {
172            return $this->mediaFile->getDisplayName();
173        }
174    }
175}
176