xref: /dokuwiki/inc/Ui/Media/Display.php (revision dd9e8e5ea54469964faab99223a61bd48146ac42)
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        [$dw, $dh] = $this->mediaFile->getDisplayDimensions($w, $h, false);
71        if ($dw <= 0) return '';
72
73        $id = $this->mediaFile->getId();
74        $rev = $this->mediaFile->getRev();
75        $cacheBust = $rev
76            ? ['rev' => $rev]
77            : ['t' => filemtime($this->mediaFile->getPath())];
78
79        // pass raw '&' separator so buildAttributes' hsc() encodes exactly once
80        $imgAttr = [
81            'src' => ml($id, ['w' => $w, 'h' => $h, 'fit' => 1, ...$cacheBust], true, '&'),
82            'alt' => $this->mediaFile->getDisplayName(),
83            'width' => $dw,
84            'height' => $dh,
85            'style' => 'max-width: ' . $dw . 'px;',
86        ];
87        $linkAttr = [
88            'href' => ml($id, $cacheBust, true, '&'),
89            'target' => '_blank',
90            'title' => $lang['mediaview'],
91        ];
92
93        return '<div class="image">'
94            . '<a ' . buildAttributes($linkAttr) . '>'
95            . '<img ' . buildAttributes($imgAttr) . ' />'
96            . '</a>'
97            . '</div>';
98    }
99
100    /**
101     * Return the URL to the icon for this file
102     *
103     * @return string
104     */
105    public function getIconUrl()
106    {
107        $link = 'lib/images/fileicons/svg/' . $this->mediaFile->getIcoClass() . '.svg';
108        if (!file_exists(DOKU_INC . $link)) $link = 'lib/images/fileicons/svg/file.svg';
109        return DOKU_BASE . $link;
110    }
111
112    /**
113     * Show IDs relative to this namespace
114     *
115     * @param string|null $ns Use null to disable
116     */
117    public function relativeDisplay($ns)
118    {
119        $this->relativeDisplay = $ns;
120    }
121
122    /**
123     * Scroll to this file on display?
124     *
125     * @param bool $set
126     */
127    public function scrollIntoView($set = true)
128    {
129        $this->scrollIntoView = $set;
130    }
131
132    /** @return string */
133    protected function formatDate()
134    {
135        return dformat($this->mediaFile->getLastModified());
136    }
137
138    /**
139     * Output the image dimension if any
140     *
141     * @param string $empty what to show when no dimensions are available
142     * @return string
143     */
144    protected function formatDimensions($empty = '&#160;')
145    {
146        $w = $this->mediaFile->getWidth();
147        $h = $this->mediaFile->getHeight();
148        if ($w && $h) {
149            return $w . '&#215;' . $h;
150        } else {
151            return $empty;
152        }
153    }
154
155    /** @return string */
156    protected function formatFileSize()
157    {
158        return filesize_h($this->mediaFile->getFileSize());
159    }
160
161    /** @return string */
162    protected function formatDisplayName()
163    {
164        if ($this->relativeDisplay !== null) {
165            $id = $this->mediaFile->getId();
166            if (str_starts_with($id, $this->relativeDisplay)) {
167                $id = substr($id, strlen($this->relativeDisplay));
168            }
169            return ltrim($id, ':');
170        } else {
171            return $this->mediaFile->getDisplayName();
172        }
173    }
174}
175