1<?php
2/**
3 * Helper Component of Medialist plugin
4 *
5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author  Satoshi Sahara <sahara.satoshi@gmail.com>
7 */
8
9// must be run within Dokuwiki
10if (!defined('DOKU_INC')) die();
11
12class helper_plugin_medialist extends DokuWiki_Plugin {
13
14    /**
15     * syntax parser
16     *
17     * @param $data string matched the regex {{medialist>[^\r\n]+?}}
18     * @return array parameter for render process
19     *
20     * -----------------------------------------------------------------------
21     * [ns:]   namespace, must end ":" or ":*"
22     * [page]  page id
23     *
24     *  {{pagelist>ns:}}
25     *       - show media files in the given namespace
26     *       - show button to open the given ns by fullscreen media manager
27     *  {{pagelist>ns: page}}
28     *       - distinguish linked files in the page if they found in the list
29     *  {{pagelist>ns: +page}}
30     *       - add linked media files to the list (@BOTH@)
31     *
32     *  {{pagelist>page}}
33     *       - show media files linked in the given page
34     *  {{pagelist>page ns:}}
35     *       - show button to open the given ns by fullscreen media manager
36     * -----------------------------------------------------------------------
37     */
38    public function parse($data) {
39        global $INFO;
40
41        $params = array(); // parameter array for render process
42
43        $match = substr($data, 12, -2);
44        $match = str_replace('  ',' ', trim($match)); // remove excessive white spaces
45
46        // v1 syntax (backword compatibility for 2009-05-21 release)
47        // @PAGE@, @NAMESPACE@, @ALL@ are complete keyword arguments,
48        // not replacement patterns.
49        switch ($match) {
50            case '@PAGE@':
51                $match = '@ID@';  break;
52            case '@NAMESPACE@':
53                $match = '@NS@:*'; break;
54            case '@ALL@':
55            case '@BOTH@':
56                $match = '@NS@:* +@ID@'; break;
57        }
58
59        // v2 syntax (available since 2016-06-XX release)
60        // - enable replacement patterns @ID@, @NS@, @PAGE@
61        //   for media file search scope
62        // - Namespace search if scope parameter ends colon ":", and
63        //   require "*" after the colon for recursive search
64
65        // replacement patterns identical with Namespace Template
66        // @see https://www.dokuwiki.org/namespace_templates#syntax
67        $args = $match;
68        $args = str_replace('@ID@', $INFO['id'], $args);
69        $args = str_replace('@NS@', getNS($INFO['id']), $args);
70        $args = str_replace('@PAGE@', noNS($INFO['id']), $args);
71
72        $args = explode(' ', $args, 2);
73
74        // check first parameter
75        if (substr($args[0], -1) == ':') {
76                $params['ns'] = substr($args[0], 0, -1);
77                $params['depth'] = 1;  // set depth option for search()
78        } elseif (substr($args[0], -2) == ':*') {
79                $params['ns'] = substr($args[0], 0, -2);
80        } else {
81                $params['page'] = $args[0];
82        }
83
84        // check second parameter
85        if (!empty($args[1])) {
86            if (isset($params['ns'])) {
87                $params['page'] = ltrim($args[1], '+');
88                $params['append'] = (bool) ($args[1][0] == '+');
89            } else {
90                $params['uploadns'] = rtrim($args[1],':');
91            }
92        }
93        return $params;
94   }
95
96
97    /**
98     * Renders xhtml
99     */
100    public function render_xhtml($params) {
101
102        $linked_media = array();
103        $stored_media = array();
104
105        // search internal files in the given namespace
106        if (isset($params['ns'])) {
107            // search option for lookup_stored_media()
108            $opt = array();
109            if (array_key_exists('depth', $params)) {
110                $opt = $opt + array('depth' => $params['depth']);
111            }
112            $stored_media = $this->_lookup_stored_media($params['ns'], $opt);
113        }
114
115        // search linked/used media in the given page
116        if (isset($params['page'])) {
117            $linked_media = $this->_lookup_linked_media($params['page']);
118        }
119
120        if (isset($params['append']) && $params['append']) {
121            $media = array_unique(array_merge($stored_media, $linked_media), SORT_REGULAR);
122        } else {
123            $media = isset($params['ns']) ? $stored_media : $linked_media;
124            if (!$params['ns'] && $params['page']) {
125                $linked_media = array();
126            }
127        }
128
129        // prepare list items
130        $items = array();
131        foreach ($media as $item) {
132            $base = !isset($params['ns']) ? getNS($item['id']) : $params['ns'];
133
134            if (in_array($item, $linked_media)) {
135                $item = $item + array('level' => 1, 'base' => $base, 'linked'=> 1);
136            } else {
137                $item = $item + array('level' => 1, 'base' => $base);
138            }
139            $items[] = $item;
140        }
141
142        // create output
143        $out  = '';
144        $out .= '<div class="medialist">'. DOKU_LF;
145
146        // mediamanager button
147        $uploadns = isset($params['uploadns']) ? $params['uploadns'] : $params['ns'];
148        $tab = empty($items) ? 'upload' : 'files';
149        if (isset($uploadns) && (auth_quickaclcheck("$uploadns:*") >= AUTH_UPLOAD)) {
150            $out .= '<div class="mediamanager">';
151            $out .= $this->_mediamanager_button($uploadns, $tab);
152            $out .= '</div>'. DOKU_LF;
153        }
154
155        // list of media files
156        if (!empty($items)) {
157            $out .= html_buildlist($items, 'medialist', array($this, '_media_item'));
158            $out .= DOKU_LF;
159        } elseif ($this->getConf('emptyInfo')) {
160            $out .= '<div class="info">';
161            $out .= '<strong>'.$this->getPluginName().'</strong>'.': nothing to show here.';
162            $out .= '</div>'. DOKU_LF;;
163        }
164        $out .= '</div>'. DOKU_LF;
165        return $out;
166    }
167
168    /**
169     * Callback function for html_buildlist()
170     *
171     * @author Michael Klier <chi@chimeric.de>
172     */
173    public function _media_item($item) {
174        global $conf, $lang;
175
176        $out = '';
177
178        $link = array();
179        $link['url']    = ml($item['id']);
180        $link['class']  = isset($item['linked']) ? 'media linked' : 'media';
181        $link['target'] = $conf['target']['media'];
182        $link['title']  = noNS($item['id']);
183
184        // link text and mediainfo
185        if ($item['type'] == 'internalmedia') {
186            // Internal file
187            if (!empty($item['base'])) {
188                // remove base namespace to get shorten link text
189                $link['name'] = preg_replace('/^'.$item['base'].':/','', $item['id']);
190            } else {
191                $link['name'] = $item['id'];
192            }
193            $mediainfo  = strftime($conf['dformat'], $item['mtime']).'&nbsp;';
194            $mediainfo .= filesize_h($item['size']);
195        } else {
196            // External link
197            $link['name'] = $item['id'];
198            $mediainfo = $lang['qb_extlink']; // External Link
199        }
200
201        // add file icons
202        list($ext,$mime) = mimetype($item['id']);
203        $class = preg_replace('/[^_\-a-z0-9]+/i','_',$ext);
204        $link['class'] .= ' mediafile mf_'.$class;
205
206        // build the list item
207        if ($this->getConf('checkboxes')) {
208            $out .= '<input type="checkbox" id="delete['.$item['id'].']" />';
209            $out .= '<label for="delete['.$item['id'].']">'.'</label>';
210        }
211        $out .= '<a href="' . $link['url'] . '" ';
212        $out .= 'class="' . $link['class'] . '" ';
213        $out .= 'target="' . $link['target'] . '" ';
214        $out .= 'title="' . $link['title'] . '">';
215        $out .= $link['name'];
216        $out .= '</a>';
217        $out .= '&nbsp;<span class="mediainfo">('.$mediainfo.')</span>' . DOKU_LF;
218
219        return $out;
220    }
221
222    /**
223     * button to open a given namespace with the Fullscreen Media Manager
224     * @param $ns  string namespace
225     * @param $tab string tab name of MediaManager (files|upload|search)
226     * @return string html
227     */
228    protected function _mediamanager_button($ns, $tab=null) {
229        global $INFO, $lang;
230
231        $method  = 'get';
232        $params  = array('do' => 'media', 'ns' => $ns);
233        if (in_array($tab, array('files','upload','search'))) {
234            $params += array('tab_files' => $tab);
235        }
236        $label   = hsc("$ns:*");
237        $tooltip = ($tab == 'upload') ? $lang['btn_upload'] :$lang['btn_media'];
238        $accesskey = '';
239        return html_btn('media', $INFO['id'], $accesskey, $params, $method, $tooltip, $label);
240    }
241
242
243    /**
244     * searches media files linked in the given page
245     * returns an array of items
246     */
247    protected function _lookup_linked_media($id) {
248        $linked_media = array();
249
250        if (!page_exists($id)) {
251            //msg('MediaList: page "'. hsc($id) . '" not exists!', -1);
252        }
253
254        if (auth_quickaclcheck($id) >= AUTH_READ) {
255            // get the instructions
256            $ins = p_cached_instructions(wikiFN($id), true, $id);
257
258            // get linked media files
259            foreach ($ins as $node) {
260                if ($node[0] == 'internalmedia') {
261                    $id = cleanID($node[1][0]);
262                    $fn = mediaFN($id);
263                    if (!file_exists($fn)) continue;
264                    $linked_media[] = array(
265                        'id'    => $id,
266                        'size'  => filesize($fn),
267                        'mtime' => filemtime($fn),
268                        'type'  => $node[0],
269                    );
270                } elseif ($node[0] == 'externalmedia') {
271                    $linked_media[] = array(
272                        'id'    => $node[1][0],
273                        'size'  => null,
274                        'mtime' => null,
275                        'type'  => $node[0],
276                    );
277                }
278            }
279
280        }
281        return array_unique($linked_media, SORT_REGULAR);
282    }
283
284    /**
285     * searches media files stored in the given namespace and sub-tiers
286     * returns an array of items
287     */
288    protected function _lookup_stored_media($ns, $opt=array('depth'=>1)) {
289        global $conf;
290
291        $stored_media = array();
292
293        $dir = utf8_encodeFN(str_replace(':','/', $ns));
294
295        if (!is_dir($conf['mediadir'] . '/' . $dir)) {
296            //msg('MediaList: namespace "'. hsc($ns). '" not exists!', -1);
297        }
298
299        if (auth_quickaclcheck("$ns:*") >= AUTH_READ) {
300            // search media files in the namespace
301            $res = array(); // search result
302            search($res, $conf['mediadir'], 'search_media', $opt, $dir);
303
304            // prepare return array
305            foreach ($res as $item) {
306                $stored_media[] = array(
307                        'id'    => $item['id'],
308                        'size'  => $item['size'],
309                        'mtime' => $item['mtime'],
310                        'type'  => 'internalmedia',
311                );
312            }
313        }
314        return $stored_media;
315    }
316
317}
318
319