1<?php
2
3 /**
4 * Функции шаблонов DokuWiki
5 *
6 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
7 * @author Andreas Gohr <andi@splitbrain.org>
8 */
9
10 use dokuwiki\ActionRouter;
11 use dokuwiki\Action\Exception\FatalException;
12 use dokuwiki\Extension\PluginInterface;
13 use dokuwiki\Ui\Admin;
14 use dokuwiki\StyleUtils;
15 use dokuwiki\Menu\Item\AbstractItem;
16 use dokuwiki\Form\Form;
17 use dokuwiki\Menu\MobileMenu;
18 use dokuwiki\Ui\Subscribe;
19 use dokuwiki\Extension\AdminPlugin;
20 use dokuwiki\Extension\Event;
21 use dokuwiki\File\PageResolver;
22
23/**
24* Доступ к файлу шаблона
25*
26* Возвращает путь к указанному файлу внутри текущего шаблона, использует
27* шаблон по умолчанию, если пользовательская версия не существует.
28*
29* @param string $ файл
30* @возвращаемая строка
31*
32* @автор Андреас Гор <andi@splitbrain.org>
33*/
34 function template($file)
35 {
36 global $conf;
37
38 if (@is_readable(DOKU_INC . 'lib/tpl/' . $conf['template'] . '/' . $file))
39 return DOKU_INC . 'lib/tpl/' . $conf['template'] . '/' . $file;
40
41 return DOKU_INC . 'lib/tpl/dokuwiki/' . $file;
42 }
43
44/**
45* Удобная функция для доступа к каталогу шаблонов из локальной ФС
46*
47* Заменяет устаревшую константу DOKU_TPLINC.
48*
49* @param string $ tpl Шаблон для использования, по умолчанию текущий
50* @возвращаемая строка
51*
52* @автор Андреас Гор <andi@splitbrain.org>
53*/
54 function tpl_incdir($tpl = '')
55 {
56 global $conf;
57 if (!$tpl) $tpl = $conf['template'];
58 return DOKU_INC . 'lib/tpl/' . $tpl . '/';
59 }
60
61/**
62* Удобная функция доступа к каталогу шаблонов из Интернета
63*
64* Заменяет устаревшую константу DOKU_TPL
65*
66* @param string $ tpl Шаблон для использования, по умолчанию текущий
67* @возвращаемая строка
68*
69* @автор Андреас Гор <andi@splitbrain.org>
70*/
71 function tpl_basedir($tpl = '')
72 {
73 global $conf;
74 if (!$tpl) $tpl = $conf['template'];
75 return DOKU_BASE . 'lib/tpl/' . $tpl . '/';
76 }
77
78/**
79* Распечатать содержимое
80*
81* Эта функция используется для печати всего обычного контента.
82* (определяется глобальной переменной $ACT) путем вызова соответствующего
83* выходные функции из html.php
84*
85* Все, что не использует основной файл шаблона, не
86* обрабатывается этой функцией. ACL-списки здесь тоже не обрабатываются.
87*
88* @param bool $ prependTOC следует ли здесь отображать оглавление?
89* @return bool true, если есть какой-либо вывод
90*
91* @triggers TPL_ACT_RENDER
92* @triggers TPL_CONTENT_DISPLAY
93* @автор Андреас Гор <andi@splitbrain.org>
94*/
95 function tpl_content($prependTOC = true)
96 {
97 global $ACT;
98 global $INFO;
99 $INFO['prependTOC'] = $prependTOC;
100
101 ob_start();
102 Event::createAndTrigger('TPL_ACT_RENDER', $ACT, 'tpl_content_core');
103 $html_output = ob_get_clean();
104 Event::createAndTrigger('TPL_CONTENT_DISPLAY', $html_output, function ($html_output) {
105 echo $html_output;
106 });
107
108 return !empty($html_output);
109 }
110
111/**
112* Действие по умолчанию TPL_ACT_RENDER
113*
114* @return bool
115*/
116 function tpl_content_core()
117 {
118 $router = ActionRouter::getInstance();
119 try {
120 $router->getAction()->tplContent();
121 } catch (FatalException $e) {
122 // there was no content for the action
123 msg(hsc($e->getMessage()), -1);
124 return false;
125 }
126 return true;
127 }
128
129/**
130* Размещает оглавление там, где вызывается функция
131*
132* Если вы используете это, вы, скорее всего, захотите вызвать tpl_content с помощью
133* ложный аргумент
134*
135* @param bool $ return Следует ли вернуть оглавление вместо его печати?
136* @возвращаемая строка
137*
138* @автор Андреас Гор <andi@splitbrain.org>
139*/
140 function tpl_toc($return = false)
141 {
142 global $TOC;
143 global $ACT;
144 global $ID;
145 global $REV;
146 global $INFO;
147 global $conf;
148 $toc = [];
149
150 if (is_array($TOC)) {
151 // if a TOC was prepared in global scope, always use it
152 $toc = $TOC;
153 } elseif (($ACT == 'show' || str_starts_with($ACT, 'export')) && !$REV && $INFO['exists']) {
154 // get TOC from metadata, render if neccessary
155 $meta = p_get_metadata($ID, '', METADATA_RENDER_USING_CACHE);
156 $tocok = $meta['internal']['toc'] ?? true;
157 $toc = $meta['description']['tableofcontents'] ?? null;
158 if (!$tocok || !is_array($toc) || !$conf['tocminheads'] || count($toc) < $conf['tocminheads']) {
159 $toc = [];
160 }
161 } elseif ($ACT == 'admin') {
162 // try to load admin plugin TOC
163 /** @var AdminPlugin $plugin */
164 if ($plugin = plugin_getRequestAdminPlugin()) {
165 $toc = $plugin->getTOC();
166 $TOC = $toc; // avoid later rebuild
167 }
168 }
169
170 Event::createAndTrigger('TPL_TOC_RENDER', $toc, null, false);
171 $html = html_TOC($toc);
172 if ($return) return $html;
173 echo $html;
174 return '';
175 }
176
177/**
178* Обработка содержимого страницы администратора
179*
180* @return bool
181*
182* @автор Андреас Гор <andi@splitbrain.org>
183*/
184 function tpl_admin()
185 {
186 global $INFO;
187 global $TOC;
188 global $INPUT;
189
190 $plugin = null;
191 $class = $INPUT->str('page');
192 if (!empty($class)) {
193 $pluginlist = plugin_list('admin');
194
195 if (in_array($class, $pluginlist)) {
196 // attempt to load the plugin
197 /** @var AdminPlugin $plugin */
198 $plugin = plugin_load('admin', $class);
199 }
200 }
201
202 if ($plugin instanceof PluginInterface) {
203 if (!is_array($TOC)) $TOC = $plugin->getTOC(); //if TOC wasn't requested yet
204 if ($INFO['prependTOC']) tpl_toc();
205 $plugin->html();
206 } else {
207 $admin = new Admin();
208 $admin->show();
209 }
210 return true;
211 }
212
213/**
214* Распечатайте правильные HTML-мета-заголовки
215*
216* Это необходимо разместить в заголовке вашего шаблона.
217*
218* @param bool $ alt Нужно ли добавлять каналы и ссылки альтернативного формата?
219* @return bool
220* @вызывает JsonException
221*
222* @автор Андреас Гор <andi@splitbrain.org>
223* @triggers TPL_METAHEADER_OUTPUT
224*/
225 function tpl_metaheaders($alt = true)
226 {
227 global $ID;
228 global $REV;
229 global $INFO;
230 global $JSINFO;
231 global $ACT;
232 global $QUERY;
233 global $lang;
234 global $conf;
235 global $updateVersion;
236 /** @var Input $INPUT */
237 global $INPUT;
238
239 // подготавливаем массив головок
240 $head = [];
241
242 // подготовить seed для js и css
243 $tseed = $updateVersion;
244 $depends = getConfigFiles('main');
245 $depends[] = DOKU_CONF . "tpl/" . $conf['template'] . "/style.ini";
246 foreach ($depends as $f) $tseed .= @filemtime($f);
247 $tseed = md5($tseed);
248
249 // обычные вещи
250 $head['meta'][] = ['name' => 'generator', 'content' => 'DokuWiki'];
251 if (actionOK('search')) {
252 $head['link'][] = [
253 'rel' => 'search',
254 'type' => 'application/opensearchdescription+xml',
255 'href' => DOKU_BASE . 'lib/exe/opensearch.php',
256 'title' => $conf['title']
257 ];
258 }
259
260 $head['link'][] = ['rel' => 'start', 'href' => DOKU_BASE];
261 if (actionOK('index')) {
262 $head['link'][] = [
263 'rel' => 'contents',
264 'href' => wl($ID, 'do=index', false, '&'),
265 'title' => $lang['btn_index']
266 ];
267 }
268
269 if (actionOK('manifest')) {
270 $head['link'][] = [
271 'rel' => 'manifest',
272 'href' => DOKU_BASE . 'lib/exe/manifest.php'
273 ];
274 }
275
276 $styleUtil = new StyleUtils();
277 $styleIni = $styleUtil->cssStyleini();
278 $replacements = $styleIni['replacements'];
279 if (!empty($replacements['__theme_color__'])) {
280 $head['meta'][] = [
281 'name' => 'theme-color',
282 'content' => $replacements['__theme_color__']
283 ];
284 }
285
286 if ($alt) {
287 if (actionOK('rss')) {
288 $head['link'][] = [
289 'rel' => 'alternate',
290 'type' => 'application/rss+xml',
291 'title' => $lang['btn_recent'],
292 'href' => DOKU_BASE . 'feed.php'
293 ];
294 $head['link'][] = [
295 'rel' => 'alternate',
296 'type' => 'application/rss+xml',
297 'title' => $lang['currentns'],
298 'href' => DOKU_BASE . 'feed.php?mode=list&ns=' . (isset($INFO) ? $INFO['namespace'] : '')
299 ];
300 }
301 if (($ACT == 'show' || $ACT == 'search') && $INFO['writable']) {
302 $head['link'][] = [
303 'rel' => 'edit',
304 'title' => $lang['btn_edit'],
305 'href' => wl($ID, 'do=edit', false, '&')
306 ];
307 }
308
309 if (actionOK('rss') && $ACT == 'search') {
310 $head['link'][] = [
311 'rel' => 'alternate',
312 'type' => 'application/rss+xml',
313 'title' => $lang['searchresult'],
314 'href' => DOKU_BASE . 'feed.php?mode=search&q=' . $QUERY
315 ];
316 }
317
318 if (actionOK('export_xhtml')) {
319 $head['link'][] = [
320 'rel' => 'alternate',
321 'type' => 'text/html',
322 'title' => $lang['plainhtml'],
323 'href' => exportlink($ID, 'xhtml', '', false, '&')
324 ];
325 }
326
327 if (actionOK('export_raw')) {
328 $head['link'][] = [
329 'rel' => 'alternate',
330 'type' => 'text/plain',
331 'title' => $lang['wikimarkup'],
332 'href' => exportlink($ID, 'raw', '', false, '&')
333 ];
334 }
335 }
336
337 // настройка тегов робота, подходящих для разных режимов
338 if (($ACT == 'show' || $ACT == 'export_xhtml') && !$REV) {
339 if ($INFO['exists']) {
340 //delay indexing:
341 if ((time() - $INFO['lastmod']) >= $conf['indexdelay'] && !isHiddenPage($ID)) {
342 $head['meta'][] = ['name' => 'robots', 'content' => 'index,follow'];
343 } else {
344 $head['meta'][] = ['name' => 'robots', 'content' => 'noindex,nofollow'];
345 }
346 $canonicalUrl = wl($ID, '', true, '&');
347 if ($ID == $conf['start']) {
348 $canonicalUrl = DOKU_URL;
349 }
350 $head['link'][] = ['rel' => 'canonical', 'href' => $canonicalUrl];
351 } else {
352 $head['meta'][] = ['name' => 'robots', 'content' => 'noindex,follow'];
353 }
354 } elseif (defined('DOKU_MEDIADETAIL')) {
355 $head['meta'][] = ['name' => 'robots', 'content' => 'index,follow'];
356 } else {
357 $head['meta'][] = ['name' => 'robots', 'content' => 'noindex,nofollow'];
358 }
359
360 // установить метаданные
361 if ($ACT == 'show' || $ACT == 'export_xhtml') {
362 // keywords (explicit or implicit)
363 if (!empty($INFO['meta']['subject'])) {
364 $head['meta'][] = ['name' => 'keywords', 'content' => implode(',', $INFO['meta']['subject'])];
365 } else {
366 $head['meta'][] = ['name' => 'keywords', 'content' => str_replace(':', ',', $ID)];
367 }
368 }
369
370 // загрузка таблиц стилей
371 $head['link'][] = [
372 'rel' => 'stylesheet',
373 'href' => DOKU_BASE . 'lib/exe/css.php?t=' . rawurlencode($conf['template']) . '&tseed=' . $tseed
374 ];
375
376 $script = "var NS='" . (isset($INFO) ? $INFO['namespace'] : '') . "';";
377 if ($conf['useacl'] && $INPUT->server->str('REMOTE_USER')) {
378 $script .= "var SIG=" . toolbar_signature() . ";";
379 }
380 jsinfo();
381 $script .= 'var JSINFO = ' . json_encode($JSINFO, JSON_THROW_ON_ERROR) . ';';
382 $script .= '(function(H){H.className=H.className.replace(/\bno-js\b/,\'js\')})(document.documentElement);';
383 $head['script'][] = ['_data' => $script];
384
385 // загрузить jquery
386 $jquery = getCdnUrls();
387 foreach ($jquery as $src) {
388 $head['script'][] = [
389 '_data' => '',
390 'src' => $src
391 ] + ($conf['defer_js'] ? ['defer' => 'defer'] : []);
392 }
393
394 // загружаем наш диспетчер javascript
395 $head['script'][] = [
396 '_data' => '',
397 'src' => DOKU_BASE . 'lib/exe/js.php' . '?t=' . rawurlencode($conf['template']) . '&tseed=' . $tseed
398 ] + ($conf['defer_js'] ? ['defer' => 'defer'] : []);
399
400 // вызвать событие здесь
401 Event::createAndTrigger('TPL_METAHEADER_OUTPUT', $head, '_tpl_metaheaders_action', true);
402 return true;
403 }
404
405/**
406* печатает массив, созданный tpl_metaheaders
407*
408* $data — это массив различных тегов заголовков. Каждый тег может иметь несколько
409* экземпляры. Атрибуты задаются как пары ключ-значение. Значения будут HTML
410* кодируются автоматически, поэтому их следует предоставлять как есть в массиве $data.
411*
412* Для тегов, имеющих атрибут body, укажите данные body в специальном поле
413* атрибут '_data'. Это поле НЕ БУДЕТ ЭКРАНИРОВАНО автоматически.
414*
415* Встроенные скрипты будут использовать любой одноразовый номер, указанный в переменной среды «NONCE».
416*
417* @param массив $ данные
418*
419* @автор Андреас Гор <andi@splitbrain.org>
420*/
421 function _tpl_metaheaders_action($data)
422 {
423 $nonce = getenv('NONCE');
424 foreach ($data as $tag => $inst) {
425 foreach ($inst as $attr) {
426 if (empty($attr)) {
427 continue;
428 }
429 if ($nonce && $tag == 'script' && !empty($attr['_data'])) {
430 $attr['nonce'] = $nonce; // add nonce to inline script tags
431 }
432 echo '<', $tag, ' ', buildAttributes($attr);
433 if (isset($attr['_data']) || $tag == 'script') {
434 echo '>', $attr['_data'] ?? '', '</', $tag, '>';
435 } else {
436 echo '/>';
437 }
438 echo "\n";
439 }
440 }
441 }
442
443/**
444* Вывести данный скрипт как встроенный тег скрипта
445*
446* Эта функция добавит атрибут nonce, если он доступен.
447*
448* Скрипт НЕ экранируется автоматически!
449*
450* @param string $ скрипт
451* @param bool $ return Возврат или прямая печать?
452* @return string | недействительный
453*/
454 function tpl_inlineScript($script, $return = false)
455 {
456 $nonce = getenv('NONCE');
457 if ($nonce) {
458 $script = '<script nonce="' . $nonce . '">' . $script . '</script>';
459 } else {
460 $script = '<script>' . $script . '</script>';
461 }
462
463 if ($return) return $script;
464 echo $script;
465 }
466
467/**
468* Распечатать ссылку
469*
470* Просто создает ссылку.
471*
472* @param string $ url
473* @param string $ имя
474* @param string $ еще
475* @param bool $ return если true вернуть ссылку html, в противном случае вывести
476* @return bool | строка html ссылки или true, если выводится
477*
478* @автор Андреас Гор <andi@splitbrain.org>
479*/
480 function tpl_link($url, $name, $more = '', $return = false)
481 {
482 $out = '<a href="' . $url . '" ';
483 if ($more) $out .= ' ' . $more;
484 $out .= ">$name</a>";
485 if ($return) return $out;
486 echo $out;
487 return true;
488 }
489
490/**
491* Печатает ссылку на WikiPage
492*
493* Обертка вокруг html_wikilink
494*
495* @param string $ id идентификатор страницы
496* @param string | null $ name имя ссылки
497* @param bool $ возврат
498* @return true | строка
499*
500* @автор Андреас Гор <andi@splitbrain.org>
501*/
502 function tpl_pagelink($id, $name = null, $return = false)
503 {
504 $out = '<bdi>' . html_wikilink($id, $name) . '</bdi>';
505 if ($return) return $out;
506 echo $out;
507 return true;
508 }
509
510/**
511* получить родительскую страницу
512*
513* Пытается выяснить, какая страница является родительской.
514* возвращает false, если ничего не доступно
515*
516* @param string $ id идентификатор страницы
517* @return false | строка
518*
519* @автор Андреас Гор <andi@splitbrain.org>
520*/
521 function tpl_getparent($id)
522 {
523 $resolver = new PageResolver('root');
524
525 $parent = getNS($id) . ':';
526 $parent = $resolver->resolveId($parent);
527 if ($parent == $id) {
528 $pos = strrpos(getNS($id), ':');
529 $parent = substr($parent, 0, $pos) . ':';
530 $parent = $resolver->resolveId($parent);
531 if ($parent == $id) return false;
532 }
533 return $parent;
534 }
535
536/**
537* Распечатать одну из кнопок
538*
539* @param string $ тип
540* @param bool $ возврат
541* @return bool | string html, или false, если данных нет, true, если выведено
542* @see tpl_get_action
543*
544* @автор Адриан Лэнг <mail@adrianlang.de>
545* @deprecated 2017-09-01 см. devel:menus
546*/
547 function tpl_button($type, $return = false)
548 {
549 dbg_deprecated('see devel:menus');
550 $data = tpl_get_action($type);
551 if ($data === false) {
552 return false;
553 } elseif (!is_array($data)) {
554 $out = sprintf($data, 'button');
555 } else {
556 /**
557 * @var string $accesskey
558 * @var string $id
559 * @var string $method
560 * @var array $params
561 */
562 extract($data);
563 if ($id === '#dokuwiki__top') {
564 $out = html_topbtn();
565 } else {
566 $out = html_btn($type, $id, $accesskey, $params, $method);
567 }
568 }
569 if ($return) return $out;
570 echo $out;
571 return true;
572 }
573
574/**
575* Как кнопки действий, но ссылки
576*
577* @param string $ тип действие команда
578* @param string $ pre префикс ссылки
579* @param string $ suf суффикс ссылки
580* @param string $ внутренний innerHML ссылки
581* @param bool $ return если true, то возвращает html, в противном случае печатает
582* @return bool | string html или false, если данных нет, true, если выведено
583*
584* @see tpl_get_action
585* @автор Адриан Лэнг <mail@adrianlang.de>
586* @deprecated 2017-09-01 см. devel:menus
587*/
588 function tpl_actionlink($type, $pre = '', $suf = '', $inner = '', $return = false)
589 {
590 dbg_deprecated('see devel:menus');
591 global $lang;
592 $data = tpl_get_action($type);
593 if ($data === false) {
594 return false;
595 } elseif (!is_array($data)) {
596 $out = sprintf($data, 'link');
597 } else {
598 /**
599 * @var string $accesskey
600 * @var string $id
601 * @var string $method
602 * @var bool $nofollow
603 * @var array $params
604 * @var string $replacement
605 */
606 extract($data);
607 if (strpos($id, '#') === 0) {
608 $linktarget = $id;
609 } else {
610 $linktarget = wl($id, $params);
611 }
612 $caption = $lang['btn_' . $type];
613 if (strpos($caption, '%s')) {
614 $caption = sprintf($caption, $replacement);
615 }
616 $akey = '';
617 $addTitle = '';
618 if ($accesskey) {
619 $akey = 'accesskey="' . $accesskey . '" ';
620 $addTitle = ' [' . strtoupper($accesskey) . ']';
621 }
622 $rel = $nofollow ? 'rel="nofollow" ' : '';
623 $out = tpl_link(
624 $linktarget,
625 $pre . ($inner ?: $caption) . $suf,
626 'class="action ' . $type . '" ' .
627 $akey . $rel .
628 'title="' . hsc($caption) . $addTitle . '"',
629 true
630 );
631 }
632 if ($return) return $out;
633 echo $out;
634 return true;
635 }
636
637/**
638* Проверьте действия и получите данные для кнопок и ссылок
639*
640* @param string $ тип
641* @return массив | bool | строка
642*
643* @автор Адриан Лэнг <mail@adrianlang.de>
644* @автор Андреас Гор <andi@splitbrain.org>
645* @автор Маттиас Гримм <matthiasgrimm@users.sourceforge.net>
646* @deprecated 2017-09-01 см. devel:menus
647*/
648 function tpl_get_action($type)
649 {
650 dbg_deprecated('see devel:menus');
651 if ($type == 'history') $type = 'revisions';
652 if ($type == 'subscription') $type = 'subscribe';
653 if ($type == 'img_backto') $type = 'imgBackto';
654
655 $class = '\\dokuwiki\\Menu\\Item\\' . ucfirst($type);
656 if (class_exists($class)) {
657 try {
658 /** @var AbstractItem $item */
659 $item = new $class();
660 $data = $item->getLegacyData();
661 $unknown = false;
662 } catch (RuntimeException $ignored) {
663 return false;
664 }
665 } else {
666 global $ID;
667 $data = [
668 'accesskey' => null,
669 'type' => $type,
670 'id' => $ID,
671 'method' => 'get',
672 'params' => ['do' => $type],
673 'nofollow' => true,
674 'replacement' => ''
675 ];
676 $unknown = true;
677 }
678
679 $evt = new Event('TPL_ACTION_GET', $data);
680 if ($evt->advise_before()) {
681 //handle unknown types
682 if ($unknown) {
683 $data = '[unknown %s type]';
684 }
685 }
686 $evt->advise_after();
687 unset($evt);
688
689 return $data;
690 }
691
692/**
693* Обертка вокруг tpl_button() и tpl_actionlink()
694*
695* @param string $ тип действие команда
696* @param bool $ ссылка ссылка или кнопка формы?
697* @param string | bool $ wrapper Обертка HTML-элемента
698* @param bool $ return return или print
699* @param string $ pre префикс для ссылок
700* @param string $ suf суффикс для ссылок
701* @param string $ внутренний внутренний HTML для ссылок
702* @return bool | строка
703*
704* @автор Аника Хенке <anika@selfthinker.org>
705* @deprecated 2017-09-01 см. devel:menus
706*/
707 function tpl_action($type, $link = false, $wrapper = false, $return = false, $pre = '', $suf = '', $inner = '')
708 {
709 dbg_deprecated('see devel:menus');
710 $out = '';
711 if ($link) {
712 $out .= tpl_actionlink($type, $pre, $suf, $inner, true);
713 } else {
714 $out .= tpl_button($type, true);
715 }
716 if ($out && $wrapper) $out = "<$wrapper>$out</$wrapper>";
717
718 if ($return) return $out;
719 echo $out;
720 return (bool)$out;
721 }
722
723/**
724* Распечатать форму поиска
725*
726* Если первый параметр задан как div с идентификатором 'qsearch_out', то будет
727* добавляется, который инструктирует страницу ajax quicksearch включиться и разместить
728* его вывод в этот div. Второй параметр управляет собственным
729* атрибут автозаполнения. Если установлено значение false, этот атрибут будет установлен с
730* значение "off" указывает браузеру отключить встроенные функции
731* функция автодополнения (MSIE и Firefox)
732*
733* @param bool $ ajax
734* @param bool $ автозаполнение
735* @return bool
736*
737* @автор Андреас Гор <andi@splitbrain.org>
738*/
739 function tpl_searchform($ajax = true, $autocomplete = true)
740 {
741 global $lang;
742 global $ACT;
743 global $QUERY;
744 global $ID;
745
746 // don't print the search form if search action has been disabled
747 if (!actionOK('search')) return false;
748
749 $searchForm = new Form([
750 'action' => wl(),
751 'method' => 'get',
752 'role' => 'search',
753 'class' => 'search',
754 'id' => 'dw__search',
755 ], true);
756 $searchForm->addTagOpen('div')->addClass('no');
757 $searchForm->setHiddenField('do', 'search');
758 $searchForm->setHiddenField('id', $ID);
759 $searchForm->addTextInput('q')
760 ->addClass('edit')
761 ->attrs([
762 'title' => '[F]',
763 'accesskey' => 'f',
764 'placeholder' => $lang['btn_search'],
765 'autocomplete' => $autocomplete ? 'on' : 'off',
766 ])
767 ->id('qsearch__in')
768 ->val($ACT === 'search' ? $QUERY : '')
769 ->useInput(false);
770 $searchForm->addButton('', $lang['btn_search'])->attrs([
771 'type' => 'submit',
772 'title' => $lang['btn_search'],
773 ]);
774 if ($ajax) {
775 $searchForm->addTagOpen('div')->id('qsearch__out')->addClass('ajax_qsearch JSpopup');
776 $searchForm->addTagClose('div');
777 }
778 $searchForm->addTagClose('div');
779
780 echo $searchForm->toHTML('QuickSearch');
781
782 return true;
783 }
784
785/**
786* Распечатать след навигационной цепочки
787*
788* @param string $ sep Разделитель между записями
789* @param bool $ return return или print
790* @return bool | строка
791*
792* @автор Андреас Гор <andi@splitbrain.org>
793*/
794 function tpl_breadcrumbs($sep = null, $return = false)
795 {
796 global $lang;
797 global $conf;
798
799 //check if enabled
800 if (!$conf['breadcrumbs']) return false;
801
802 //set default
803 if (is_null($sep)) $sep = '•';
804
805 $out = '';
806
807 $crumbs = breadcrumbs(); //setup crumb trace
808
809 $crumbs_sep = ' <span class="bcsep">' . $sep . '</span> ';
810
811 //render crumbs, highlight the last one
812 $out .= '<span class="bchead">' . $lang['breadcrumb'] . '</span>';
813 $last = count($crumbs);
814 $i = 0;
815 foreach ($crumbs as $id => $name) {
816 $i++;
817 $out .= $crumbs_sep;
818 if ($i == $last) $out .= '<span class="curid">';
819 $out .= '<bdi>' . tpl_link(wl($id), hsc($name), 'class="breadcrumbs" title="' . $id . '"', true) . '</bdi>';
820 if ($i == $last) $out .= '</span>';
821 }
822 if ($return) return $out;
823 echo $out;
824 return (bool)$out;
825 }
826
827/**
828* Иерархическая навигационная цепочка
829*
830* Этот код был предложен в качестве замены обычным хлебным крошкам.
831* Имеет смысл только при наличии глубокой структуры сайта.
832*
833* @param string $ sep Разделитель между записями
834* @param bool $ return return или print
835* @return bool | строка
836*
837* @todo может вести себя странно в языках с письмом справа налево
838* @автор <fredrik@averpil.com>
839* @автор Андреас Гор <andi@splitbrain.org>
840* @автор Найджел Макни <oracle.shinoda@gmail.com>
841* @автор Шон Коутс <sean@caedmon.net>
842*/
843 function tpl_youarehere($sep = null, $return = false)
844 {
845 global $conf;
846 global $ID;
847 global $lang;
848
849 // check if enabled
850 if (!$conf['youarehere']) return false;
851
852 //set default
853 if (is_null($sep)) $sep = ' » ';
854
855 $out = '';
856
857 $parts = explode(':', $ID);
858 $count = count($parts);
859
860 $out .= '<span class="bchead">' . $lang['youarehere'] . ' </span>';
861
862 // always print the startpage
863 $out .= '<span class="home">' . tpl_pagelink(':' . $conf['start'], null, true) . '</span>';
864
865 // print intermediate namespace links
866 $part = '';
867 for ($i = 0; $i < $count - 1; $i++) {
868 $part .= $parts[$i] . ':';
869 $page = $part;
870 if ($page == $conf['start']) continue; // Skip startpage
871
872 // output
873 $out .= $sep . tpl_pagelink($page, null, true);
874 }
875
876 // print current page, skipping start page, skipping for namespace index
877 if (isset($page)) {
878 $page = (new PageResolver('root'))->resolveId($page);
879 if ($page == $part . $parts[$i]) {
880 if ($return) return $out;
881 echo $out;
882 return true;
883 }
884 }
885 $page = $part . $parts[$i];
886 if ($page == $conf['start']) {
887 if ($return) return $out;
888 echo $out;
889 return true;
890 }
891 $out .= $sep;
892 $out .= tpl_pagelink($page, null, true);
893 if ($return) return $out;
894 echo $out;
895 return (bool)$out;
896 }
897
898/**
899* Распечатать информацию, если пользователь вошел в систему
900* и в этом случае показывать полное имя
901*
902* Можно ли в будущем добавить ссылку на профиль?
903*
904* @return bool
905*
906* @автор Андреас Гор <andi@splitbrain.org>
907*/
908 function tpl_userinfo()
909 {
910 global $lang;
911 /** @var Input $INPUT */
912 global $INPUT;
913
914 if ($INPUT->server->str('REMOTE_USER')) {
915 echo $lang['loggedinas'] . ' ' . userlink();
916 return true;
917 }
918 return false;
919 }
920
921/**
922* Распечатать некоторую информацию о текущей странице
923*
924* @param bool $ ret возвращает содержимое вместо его печати
925* @return bool | строка
926*
927* @автор Андреас Гор <andi@splitbrain.org>
928*/
929 function tpl_pageinfo($ret = false)
930 {
931 global $conf;
932 global $lang;
933 global $INFO;
934 global $ID;
935
936 // return if we are not allowed to view the page
937 if (!auth_quickaclcheck($ID)) {
938 return false;
939 }
940
941 // prepare date and path
942 $fn = $INFO['filepath'];
943 if (!$conf['fullpath']) {
944 if ($INFO['rev']) {
945 $fn = str_replace($conf['olddir'] . '/', '', $fn);
946 } else {
947 $fn = str_replace($conf['datadir'] . '/', '', $fn);
948 }
949 }
950 $fn = utf8_decodeFN($fn);
951 $date = dformat($INFO['lastmod']);
952
953 // print it
954 if ($INFO['exists']) {
955 $out = '<bdi>' . $fn . '</bdi>';
956 $out .= ' · ';
957 $out .= $lang['lastmod'];
958 $out .= ' ';
959 $out .= $date;
960 if ($INFO['editor']) {
961 $out .= ' ' . $lang['by'] . ' ';
962 $out .= '<bdi>' . editorinfo($INFO['editor']) . '</bdi>';
963 } else {
964 $out .= ' (' . $lang['external_edit'] . ')';
965 }
966 if ($INFO['locked']) {
967 $out .= ' · ';
968 $out .= $lang['lockedby'];
969 $out .= ' ';
970 $out .= '<bdi>' . editorinfo($INFO['locked']) . '</bdi>';
971 }
972 if ($ret) {
973 return $out;
974 } else {
975 echo $out;
976 return true;
977 }
978 }
979 return false;
980 }
981
982/**
983* Печатает или возвращает имя указанной страницы (текущей, если не указано).
984*
985* Если включено использование заголовка, будет использоваться первый заголовок, в противном случае
986* используется указанный идентификатор.
987*
988* @param string $ id идентификатор страницы
989* @param bool $ ret возвращает содержимое вместо печати
990* @return bool | строка
991*
992* @автор Андреас Гор <andi@splitbrain.org>
993*/
994 function tpl_pagetitle($id = null, $ret = false)
995 {
996 global $ACT, $conf, $lang;
997
998 if (is_null($id)) {
999 global $ID;
1000 $id = $ID;
1001 }
1002
1003 $name = $id;
1004 if (useHeading('navigation')) {
1005 $first_heading = p_get_first_heading($id);
1006 if ($first_heading) $name = $first_heading;
1007 }
1008
1009 // default page title is the page name, modify with the current action
1010 switch ($ACT) {
1011 // admin functions
1012 case 'admin':
1013 $page_title = $lang['btn_admin'];
1014 // try to get the plugin name
1015 /** @var AdminPlugin $plugin */
1016 if ($plugin = plugin_getRequestAdminPlugin()) {
1017 $plugin_title = $plugin->getMenuText($conf['lang']);
1018 $page_title = $plugin_title ?: $plugin->getPluginName();
1019 }
1020 break;
1021
1022 // show action as title
1023 case 'login':
1024 case 'profile':
1025 case 'register':
1026 case 'resendpwd':
1027 case 'index':
1028 case 'search':
1029 $page_title = $lang['btn_' . $ACT];
1030 break;
1031
1032 // add pen during editing
1033 case 'edit':
1034 case 'preview':
1035 $page_title = "✎ " . $name;
1036 break;
1037
1038 // add action to page name
1039 case 'revisions':
1040 $page_title = $name . ' - ' . $lang['btn_revs'];
1041 break;
1042
1043 // add action to page name
1044 case 'backlink':
1045 case 'recent':
1046 case 'subscribe':
1047 $page_title = $name . ' - ' . $lang['btn_' . $ACT];
1048 break;
1049
1050 default: // SHOW and anything else not included
1051 $page_title = $name;
1052 }
1053
1054 if ($ret) {
1055 return hsc($page_title);
1056 } else {
1057 echo hsc($page_title);
1058 return true;
1059 }
1060 }
1061
1062/**
1063* Возвращает запрошенный тег EXIF / IPTC из текущего изображения
1064*
1065* Если $tags — это массив, то все заданные теги проверяются до тех пор, пока не будет найден
1066* значение найдено. Если значение не найдено, возвращается $alt.
1067*
1068* Какие тексты известны, определяется в функциях _exifTagNames
1069* и _iptcTagNames() в inc / jpeg.php (Вам необходимо добавить IPTC
1070* к именам последнего)
1071*
1072* Разрешено только в: detail.php
1073*
1074* @param array | string $ tags тег или массив тегов для проверки
1075* @param string $ alt альтернативный вывод, если данные не найдены
1076* @param null | string $ src источник изображения, если не указан, используется глобальный $SRC
1077* @возвращаемая строка
1078*
1079* @автор Андреас Гор <andi@splitbrain.org>
1080*/
1081 function tpl_img_getTag($tags, $alt = '', $src = null)
1082 {
1083 // Init Exif Reader
1084 global $SRC, $imgMeta;
1085
1086 if (is_null($src)) $src = $SRC;
1087 if (is_null($src)) return $alt;
1088
1089 if (!isset($imgMeta)) {
1090 $imgMeta = new JpegMeta($src);
1091 }
1092 if ($imgMeta === false) return $alt;
1093 $info = cleanText($imgMeta->getField($tags));
1094 if (!$info) return $alt;
1095 return $info;
1096 }
1097
1098
1099/**
1100* Мусор собирает открытый объект JpegMeta.
1101*/
1102 function tpl_img_close()
1103 {
1104 global $imgMeta;
1105 $imgMeta = null;
1106 }
1107
1108/**
1109* Выводит HTML-список описаний метатегов текущего изображения
1110*/
1111 function tpl_img_meta()
1112 {
1113 global $lang;
1114
1115 $tags = tpl_get_img_meta();
1116
1117 echo '<dl>';
1118 foreach ($tags as $tag) {
1119 $label = $lang[$tag['langkey']];
1120 if (!$label) $label = $tag['langkey'] . ':';
1121
1122 echo '<dt>' . $label . '</dt><dd>';
1123 if ($tag['type'] == 'date') {
1124 echo dformat($tag['value']);
1125 } else {
1126 echo hsc($tag['value']);
1127 }
1128 echo '</dd>';
1129 }
1130 echo '</dl>';
1131 }
1132
1133/**
1134* Возвращает метаданные, настроенные в файле конфигурации mediameta, готовые для создания html
1135*
1136* @return массив с массивами, содержащими записи:
1137* - строка langkey key для поиска в переменной $lang, если не найдена, выводится как есть
1138* - строковый тип значения
1139* - строковое значение тега (неэкранированное)
1140*/
1141 function tpl_get_img_meta()
1142 {
1143
1144 $config_files = getConfigFiles('mediameta');
1145 foreach ($config_files as $config_file) {
1146 if (file_exists($config_file)) {
1147 include($config_file);
1148 }
1149 }
1150 $tags = [];
1151 foreach ($fields as $tag) {
1152 $t = [];
1153 if (!empty($tag[0])) {
1154 $t = [$tag[0]];
1155 }
1156 if (isset($tag[3]) && is_array($tag[3])) {
1157 $t = array_merge($t, $tag[3]);
1158 }
1159 $value = tpl_img_getTag($t);
1160 if ($value) {
1161 $tags[] = ['langkey' => $tag[1], 'type' => $tag[2], 'value' => $value];
1162 }
1163 }
1164 return $tags;
1165 }
1166
1167/**
1168* Печатает изображение со ссылкой на полноразмерную версию
1169*
1170* Разрешено только в: detail.php
1171*
1172* @triggers TPL_IMG_DISPLAY
1173* @param int $ maxwidth - максимальная ширина изображения
1174* @param int $ maxheight - максимальная высота изображения
1175* @param bool $ link - ссылка на исходный размер?
1176* @param array $ params - дополнительные атрибуты изображения
1177* @return bool Результат TPL_IMG_DISPLAY
1178*/
1179 function tpl_img($maxwidth = 0, $maxheight = 0, $link = true, $params = null)
1180 {
1181 global $IMG;
1182 /** @var Input $INPUT */
1183 global $INPUT;
1184 global $REV;
1185 $w = (int)tpl_img_getTag('File.Width');
1186 $h = (int)tpl_img_getTag('File.Height');
1187
1188 //изменить размер до указанных максимальных значений
1189 $ratio = 1;
1190 if ($w >= $h) {
1191 if ($maxwidth && $w >= $maxwidth) {
1192 $ratio = $maxwidth / $w;
1193 } elseif ($maxheight && $h > $maxheight) {
1194 $ratio = $maxheight / $h;
1195 }
1196 } elseif ($maxheight && $h >= $maxheight) {
1197 $ratio = $maxheight / $h;
1198 } elseif ($maxwidth && $w > $maxwidth) {
1199 $ratio = $maxwidth / $w;
1200 }
1201 if ($ratio) {
1202 $w = floor($ratio * $w);
1203 $h = floor($ratio * $h);
1204 }
1205
1206 //подготовить URL-адреса
1207 $url = ml($IMG, ['cache' => $INPUT->str('cache'), 'rev' => $REV], true, '&');
1208 $src = ml($IMG, ['cache' => $INPUT->str('cache'), 'rev' => $REV, 'w' => $w, 'h' => $h], true, '&');
1209
1210 //подготовить атрибуты
1211 $alt = tpl_img_getTag('Simple.Title');
1212 if (is_null($params)) {
1213 $p = [];
1214 } else {
1215 $p = $params;
1216 }
1217 if ($w) $p['width'] = $w;
1218 if ($h) $p['height'] = $h;
1219 $p['class'] = 'img_detail';
1220 if ($alt) {
1221 $p['alt'] = $alt;
1222 $p['title'] = $alt;
1223 } else {
1224 $p['alt'] = '';
1225 }
1226 $p['src'] = $src;
1227
1228 $data = ['url' => ($link ? $url : null), 'params' => $p];
1229 return Event::createAndTrigger('TPL_IMG_DISPLAY', $data, '_tpl_img_action', true);
1230 }
1231
1232/**
1233* Действие по умолчанию для TPL_IMG_DISPLAY
1234*
1235* @param массив $ данные
1236* @return bool
1237*/
1238 function _tpl_img_action($data)
1239 {
1240 global $lang;
1241 $p = buildAttributes($data['params']);
1242
1243 if ($data['url']) echo '<a href="' . hsc($data['url']) . '" title="' . $lang['mediaview'] . '">';
1244 echo '<img ' . $p . '/>';
1245 if ($data['url']) echo '</a>';
1246 return true;
1247 }
1248
1249/**
1250* Эта функция вставляет небольшой gif-файл, который на самом деле является функцией индексатора.
1251*
1252* Должен вызываться где-то в самом конце шаблона main.php
1253*
1254* @return bool
1255*/
1256 function tpl_indexerWebBug()
1257 {
1258 global $ID;
1259
1260 $p = [];
1261 $p['src'] = DOKU_BASE . 'lib/exe/taskrunner.php?id=' . rawurlencode($ID) .
1262 '&' . time();
1263 $p['width'] = 2; //no more 1x1 px image because we live in times of ad blockers...
1264 $p['height'] = 1;
1265 $p['alt'] = '';
1266 $att = buildAttributes($p);
1267 echo "<img $att />";
1268 return true;
1269 }
1270
1271/**
1272* tpl_getConf($id)
1273*
1274* используйте эту функцию для доступа к переменным конфигурации шаблона
1275*
1276* @param string $ id имя значения для доступа
1277* @param mixed $ notset что возвращать, если настройка недоступна
1278* @return смешанный
1279*/
1280 function tpl_getConf($id, $notset = false)
1281 {
1282 global $conf;
1283 static $tpl_configloaded = false;
1284
1285 $tpl = $conf['template'];
1286
1287 if (!$tpl_configloaded) {
1288 $tconf = tpl_loadConfig();
1289 if ($tconf !== false) {
1290 foreach ($tconf as $key => $value) {
1291 if (isset($conf['tpl'][$tpl][$key])) continue;
1292 $conf['tpl'][$tpl][$key] = $value;
1293 }
1294 $tpl_configloaded = true;
1295 }
1296 }
1297
1298 return $conf['tpl'][$tpl][$id] ?? $notset;
1299 }
1300
1301/**
1302* tpl_loadConfig()
1303*
1304* считывает все переменные конфигурации шаблона
1305* эта функция автоматически вызывается tpl_getConf()
1306*
1307* @return false | массив
1308*/
1309 function tpl_loadConfig()
1310 {
1311
1312 $file = tpl_incdir() . '/conf/default.php';
1313 $conf = [];
1314
1315 if (!file_exists($file)) return false;
1316
1317 // загрузить файл конфигурации по умолчанию
1318 include($file);
1319
1320 return $conf;
1321 }
1322
1323// методы языка
1324
1323// методы языка
1324
1325/**
1326* tpl_getLang($id)
1327*
1328* используйте эту функцию для доступа к переменным языка шаблона
1329*
1330* @param string $ id ключ языковой строки
1331* @возвращаемая строка
1332*/
1333 function tpl_getLang($id)
1334 {
1335 static $lang = [];
1336
1337 if (count($lang) === 0) {
1338 global $conf, $config_cascade; // definitely don't invoke "global $lang"
1339
1340 $path = tpl_incdir() . 'lang/';
1341
1342 $lang = [];
1343
1344 // не включайте один раз
1345 @include($path . 'en/lang.php');
1346 foreach ($config_cascade['lang']['template'] as $config_file) {
1347 if (file_exists($config_file . $conf['template'] . '/en/lang.php')) {
1348 include($config_file . $conf['template'] . '/en/lang.php');
1349 }
1350 }
1351
1352 if ($conf['lang'] != 'en') {
1353 @include($path . $conf['lang'] . '/lang.php');
1354 foreach ($config_cascade['lang']['template'] as $config_file) {
1355 if (file_exists($config_file . $conf['template'] . '/' . $conf['lang'] . '/lang.php')) {
1356 include($config_file . $conf['template'] . '/' . $conf['lang'] . '/lang.php');
1357 }
1358 }
1359 }
1360 }
1361 return $lang[$id] ?? '';
1362 }
1363
1364/**
1365* Извлечь файл, зависящий от языка, и передать его в xhtml-рендерер для отображения
1366* эквивалент шаблона p_locale_xhtml()
1367*
1368* @param string $ id идентификатор страницы вики, зависящей от языка
1369* @return string анализирует содержимое страницы вики в формате xhtml
1370*/
1371 function tpl_locale_xhtml($id)
1372 {
1373 return p_cached_output(tpl_localeFN($id));
1374 }
1375
1376/**
1377* Добавляет соответствующий путь к имени файла, зависящему от языка
1378*
1379* @param string $ id идентификатор локализованного текста
1380* @return string вики-текст
1381*/
1382 function tpl_localeFN($id)
1383 {
1384 $path = tpl_incdir() . 'lang/';
1385 global $conf;
1386 $file = DOKU_CONF . 'template_lang/' . $conf['template'] . '/' . $conf['lang'] . '/' . $id . '.txt';
1387 if (!file_exists($file)) {
1388 $file = $path . $conf['lang'] . '/' . $id . '.txt';
1389 if (!file_exists($file)) {
1390 //fall back to english
1391 $file = $path . 'en/' . $id . '.txt';
1392 }
1393 }
1394 return $file;
1395 }
1396
1397/**
1398* выводит «основной контент» во всплывающем окне медиаменеджера
1399*
1400* В зависимости от действий пользователя это может быть список
1401* файлы в пространстве имен, диалоговое окно редактирования метаданных или
1402* сообщение о ссылках на страницы
1403*
1404* Разрешено только в mediamanager.php
1405*
1406* @triggers МЕДИАМЕНЕДЖЕР_КОНТЕНТ_ВЫВОД
1407* @param bool $ fromajax - установить true при вызове этой функции через ajax
1408* @param string $ сортировка
1409*
1410* @автор Андреас Гор <andi@splitbrain.org>
1411*/
1412 function tpl_mediaContent($fromajax = false, $sort = 'natural')
1413 {
1414 global $IMG;
1415 global $AUTH;
1416 global $INUSE;
1417 global $NS;
1418 global $JUMPTO;
1419 /** @var Input $INPUT */
1420 global $INPUT;
1421
1422 $do = $INPUT->extract('do')->str('do');
1423 if (in_array($do, ['save', 'cancel'])) $do = '';
1424
1425 if (!$do) {
1426 if ($INPUT->bool('edit')) {
1427 $do = 'metaform';
1428 } elseif (is_array($INUSE)) {
1429 $do = 'filesinuse';
1430 } else {
1431 $do = 'filelist';
1432 }
1433 }
1434
1435 // выводим панель содержимого, обернутую в событие.
1436 if (!$fromajax) echo '<div id="media__content">';
1437 $data = ['do' => $do];
1438 $evt = new Event('MEDIAMANAGER_CONTENT_OUTPUT', $data);
1439 if ($evt->advise_before()) {
1440 $do = $data['do'];
1441 if ($do == 'filesinuse') {
1442 media_filesinuse($INUSE, $IMG);
1443 } elseif ($do == 'filelist') {
1444 media_filelist($NS, $AUTH, $JUMPTO, false, $sort);
1445 } elseif ($do == 'searchlist') {
1446 media_searchlist($INPUT->str('q'), $NS, $AUTH);
1447 } else {
1448 msg('Unknown action ' . hsc($do), -1);
1449 }
1450 }
1451 $evt->advise_after();
1452 unset($evt);
1453 if (!$fromajax) echo '</div>';
1454 }
1455
1456/**
1457* Печатает центральный столбец в полноэкранном медиа-менеджере
1458* В зависимости от открытой вкладки это может быть список
1459* файлы в пространстве имен, форма загрузки или форма поиска
1460*
1461* @author Катя Арзамасцева <pshns@ukr.net>
1462*/
1463 function tpl_mediaFileList()
1464 {
1465 global $AUTH;
1466 global $NS;
1467 global $JUMPTO;
1468 global $lang;
1469 /** @var Input $INPUT */
1470 global $INPUT;
1471
1472 $opened_tab = $INPUT->str('tab_files');
1473 if (!$opened_tab || !in_array($opened_tab, ['files', 'upload', 'search'])) $opened_tab = 'files';
1474 if ($INPUT->str('mediado') == 'update') $opened_tab = 'upload';
1475
1476 echo '<h2 class="a11y">' . $lang['mediaselect'] . '</h2>' . NL;
1477
1478 media_tabs_files($opened_tab);
1479
1480 echo '<div class="panelHeader">' . NL;
1481 echo '<h3>';
1482 $tabTitle = $NS ?: '[' . $lang['mediaroot'] . ']';
1483 printf($lang['media_' . $opened_tab], '<strong>' . hsc($tabTitle) . '</strong>');
1484 echo '</h3>' . NL;
1485 if ($opened_tab === 'search' || $opened_tab === 'files') {
1486 media_tab_files_options();
1487 }
1488 echo '</div>' . NL;
1489
1490 echo '<div class="panelContent">' . NL;
1491 if ($opened_tab == 'files') {
1492 media_tab_files($NS, $AUTH, $JUMPTO);
1493 } elseif ($opened_tab == 'upload') {
1494 media_tab_upload($NS, $AUTH, $JUMPTO);
1495 } elseif ($opened_tab == 'search') {
1496 media_tab_search($NS, $AUTH);
1497 }
1498 echo '</div>' . NL;
1499 }
1500
1501/**
1502* Печатает третий столбец в полноэкранном медиа-менеджере
1503* В зависимости от открытой вкладки это могут быть сведения о
1504* выбранный файл, диалоговое окно редактирования метаданных или
1505* список ревизий файлов
1506*
1507* @param string $ изображение
1508* @param boolean $ rev
1509*
1510* @author Катя Арзамасцева <pshns@ukr.net>
1511*/
1512 function tpl_mediaFileDetails($image, $rev)
1513 {
1514 global $conf, $DEL, $lang;
1515 /** @var Input $INPUT */
1516 global $INPUT;
1517
1518 $removed = (
1519 !file_exists(mediaFN($image)) &&
1520 file_exists(mediaMetaFN($image, '.changes')) &&
1521 $conf['mediarevisions']
1522 );
1523 if (!$image || (!file_exists(mediaFN($image)) && !$removed) || $DEL) return;
1524 if ($rev && !file_exists(mediaFN($image, $rev))) $rev = false;
1525 $ns = getNS($image);
1526 $do = $INPUT->str('mediado');
1527
1528 $opened_tab = $INPUT->str('tab_details');
1529
1530 $tab_array = ['view'];
1531 [, $mime] = mimetype($image);
1532 if ($mime == 'image/jpeg') {
1533 $tab_array[] = 'edit';
1534 }
1535 if ($conf['mediarevisions']) {
1536 $tab_array[] = 'history';
1537 }
1538
1539 if (!$opened_tab || !in_array($opened_tab, $tab_array)) $opened_tab = 'view';
1540 if ($INPUT->bool('edit')) $opened_tab = 'edit';
1541 if ($do == 'restore') $opened_tab = 'view';
1542
1543 media_tabs_details($image, $opened_tab);
1544
1545 echo '<div class="panelHeader"><h3>';
1546 [$ext] = mimetype($image, false);
1547 $class = preg_replace('/[^_\-a-z0-9]+/i', '_', $ext);
1548 $class = 'select mediafile mf_' . $class;
1549
1550 $attributes = $rev ? ['rev' => $rev] : [];
1551 $tabTitle = sprintf(
1552 '<strong><a href="%s" class="%s" title="%s">%s</a></strong>',
1553 ml($image, $attributes),
1554 $class,
1555 $lang['mediaview'],
1556 $image
1557 );
1558 if ($opened_tab === 'view' && $rev) {
1559 printf($lang['media_viewold'], $tabTitle, dformat($rev));
1560 } else {
1561 printf($lang['media_' . $opened_tab], $tabTitle);
1562 }
1563
1564 echo '</h3></div>' . NL;
1565
1566 echo '<div class="panelContent">' . NL;
1567
1568 if ($opened_tab == 'view') {
1569 media_tab_view($image, $ns, null, $rev);
1570 } elseif ($opened_tab == 'edit' && !$removed) {
1571 media_tab_edit($image, $ns);
1572 } elseif ($opened_tab == 'history' && $conf['mediarevisions']) {
1573 media_tab_history($image, $ns);
1574 }
1575
1576 echo '</div>' . NL;
1577 }
1578
1579/**
1580* выводит дерево пространства имен во всплывающем окне медиаменеджера
1581*
1582* Разрешено только в mediamanager.php
1583*
1584* @автор Андреас Гор <andi@splitbrain.org>
1585*/
1586 function tpl_mediaTree()
1587 {
1588 global $NS;
1589 echo '<div id="media__tree">';
1590 media_nstree($NS);
1591 echo '</div>';
1592 }
1593
1594/**
1595* Распечатать выпадающее меню со всеми действиями DokuWiki
1596*
1597* Примечание: здесь не будут использоваться красивые URL-адреса.
1598*
1599* @param string $ пусто пустая метка параметра
1600* @param string $ кнопка подписи кнопки отправки
1601*
1602* @автор Андреас Гор <andi@splitbrain.org>
1603* @deprecated 2017-09-01 см. devel:menus
1604*/
1605 function tpl_actiondropdown($empty = '', $button = '>')
1606 {
1607 dbg_deprecated('see devel:menus');
1608 $menu = new MobileMenu();
1609 echo $menu->getDropdown($empty, $button);
1610 }
1611
1612/**
1613* Распечатать информационную строку об использованной лицензии
1614*
1615* @param string $ img распечатать изображение? (|кнопка|значок)
1616* @param bool $ imgonly пропустить текстовое описание?
1617* @param bool $ return, если true, не печатать, а возвращать HTML
1618* @param bool $ обернуть в div с class="license"?
1619* @возвращаемая строка
1620*
1621* @автор Андреас Гор <andi@splitbrain.org>
1622*/
1623 function tpl_license($img = 'badge', $imgonly = false, $return = false, $wrap = true)
1624 {
1625 global $license;
1626 global $conf;
1627 global $lang;
1628 if (!$conf['license']) return '';
1629 if (!is_array($license[$conf['license']])) return '';
1630 $lic = $license[$conf['license']];
1631 $target = ($conf['target']['extern']) ? ' target="' . $conf['target']['extern'] . '"' : '';
1632
1633 $out = '';
1634 if ($wrap) $out .= '<div class="license">';
1635 if ($img) {
1636 $src = license_img($img);
1637 if ($src) {
1638 $out .= '<a href="' . $lic['url'] . '" rel="license"' . $target;
1639 $out .= '><img src="' . DOKU_BASE . $src . '" alt="' . $lic['name'] . '" /></a>';
1640 if (!$imgonly) $out .= ' ';
1641 }
1642 }
1643 if (!$imgonly) {
1644 $out .= $lang['license'] . ' ';
1645 $out .= '<bdi><a href="' . $lic['url'] . '" rel="license" class="urlextern"' . $target;
1646 $out .= '>' . $lic['name'] . '</a></bdi>';
1647 }
1648 if ($wrap) $out .= '</div>';
1649
1650 if ($return) return $out;
1651 echo $out;
1652 return '';
1653 }
1654
1655/**
1656* Включает визуализированный HTML-код указанной страницы
1657*
1658* Эта функция полезна для заполнения боковых панелей или подобных функций в
1659* шаблон
1660*
1661* @param string $ pageid Имя страницы, которую вы хотите включить
1662* @param bool $ print Следует ли печатать содержимое или только возвращать его
1663* @param bool $ propagate Искать также и в более высоких пространствах имен?
1664* @param bool $ useacl Включать страницу только в том случае, если списки контроля доступа проверены?
1665* @return bool | null | строка
1666*/
1667 function tpl_include_page($pageid, $print = true, $propagate = false, $useacl = true)
1668 {
1669 if ($propagate) {
1670 $pageid = page_findnearest($pageid, $useacl);
1671 } elseif ($useacl && auth_quickaclcheck($pageid) == AUTH_NONE) {
1672 return false;
1673 }
1674 if (!$pageid) return false;
1675
1676 global $TOC;
1677 $oldtoc = $TOC;
1678 $html = p_wiki_xhtml($pageid, '', false);
1679 $TOC = $oldtoc;
1680
1681 if ($print) echo $html;
1682 return $html;
1683 }
1684
1685/**
1686* Отобразить форму подписки
1687*
1688* @автор Адриан Лэнг <lang@cosmocode.de>
1689* @устаревший 2020-07-23
1690*/
1691 function tpl_subscribe()
1692 {
1693 dbg_deprecated(Subscribe::class . '::show()');
1694 (new Subscribe())->show();
1695 }
1696
1697/**
1698* Пытается отправить уже созданный контент прямо в браузер
1699*
1700* Оборачивает ob_flush() и flush()
1701*
1702* @автор Андреас Гор <andi@splitbrain.org>
1703*/
1704 function tpl_flush()
1705 {
1706 if (ob_get_level() > 0) ob_flush();
1707 flush();
1708 }
1709
1710/**
1711* Пытается найти файл ресурсов в указанных местах.
1712*
1713* Если указанное местоположение начинается с двоеточия, предполагается, что это медиа
1714* файл, в противном случае предполагается, что он относится к текущему шаблону
1715*
1716* @параметр строка []$ поиск мест для просмотра
1717* @param bool $ abs , если использовать абсолютный URL
1718* @param массив &$imginfo заполнен с помощью getimagesize()
1719* @param bool $ fallback использовать резервное изображение, если цель не найдена, или вернуть «false», если она потенциальная
1720* требуется ложный результат
1721* @возвращаемая строка
1722*
1723* @автор Андреас Гор <andi@splitbrain.org>
1724*/
1725 function tpl_getMediaFile($search, $abs = false, &$imginfo = null, $fallback = true)
1726 {
1727 $img = '';
1728 $file = '';
1729 $ismedia = false;
1730 // перебираем кандидатов, пока не будет найдено совпадение:
1731 foreach ($search as $img) {
1732 if (str_starts_with($img, ':')) {
1733 $file = mediaFN($img);
1734 $ismedia = true;
1735 } else {
1736 $file = tpl_incdir() . $img;
1737 $ismedia = false;
1738 }
1739
1740 if (file_exists($file)) break;
1741 }
1742
1743 // управлять несуществующей целью
1744 if (!file_exists($file)) {
1745 // дать результат для резервного изображения
1746 if ($fallback) {
1747 $file = DOKU_INC . 'lib/images/blank.gif';
1748 // остановить процесс, если требуется ложный результат (если $fallback равен false)
1749 } else {
1750 return false;
1751 }
1752 }
1753
1754 // извлечь данные изображения, если требуется
1755 if (!is_null($imginfo)) {
1756 $imginfo = getimagesize($file);
1757 }
1758
1759 // создать URL
1760 if ($ismedia) {
1761 $url = ml($img, '', true, '', $abs);
1762 } else {
1763 $url = tpl_basedir() . $img;
1764 if ($abs) $url = DOKU_URL . substr($url, strlen(DOKU_REL));
1765 }
1766
1767 return $url;
1768 }
1769
1770/**
1771* PHP включает файл
1772*
1773* либо из каталога conf, если он существует, в противном случае используйте
1774* файл в корневом каталоге шаблона.
1775*
1776* Функция учитывает настройки каскада конфигураций и ищет заданные
1777* файл рядом с «основными» файлами конфигурации, в порядке защищенный, локальный,
1778* по умолчанию.
1779*
1780* Примечание: здесь не выполняется экранирование или проверка на работоспособность. Никогда не передавайте пользовательский ввод
1781* к этой функции!
1782*
1783* @param string $ файл
1784*
1785* @автор Андреас Гор <andi@splitbrain.org>
1786* @автор Аника Хенке <anika@selfthinker.org>
1787*/
1788 function tpl_includeFile($file)
1789 {
1790 global $config_cascade;
1791 foreach (['protected', 'local', 'default'] as $config_group) {
1792 if (empty($config_cascade['main'][$config_group])) continue;
1793 foreach ($config_cascade['main'][$config_group] as $conf_file) {
1794 $dir = dirname($conf_file);
1795 if (file_exists("$dir/$file")) {
1796 include("$dir/$file");
1797 return;
1798 }
1799 }
1800 }
1801
1802 // все еще здесь? попробуйте шаблон dir
1803 $file = tpl_incdir() . $file;
1804 if (file_exists($file)) {
1805 include($file);
1806 }
1807 }
1808
1809/**
1810* Возвращает тег <link> для различных типов иконок (favicon|mobile|generic)
1811*
1812* @param array $ types - список типов иконок для отображения (favicon|mobile|generic)
1813* @возвращаемая строка
1814*
1815* @автор Аника Хенке <anika@selfthinker.org>
1816*/
1817 function tpl_favicon($types = ['favicon'])
1818 {
1819
1820 $return = '';
1821
1822 foreach ($types as $type) {
1823 switch ($type) {
1824 case 'favicon':
1825 $look = [':wiki:favicon.ico', ':favicon.ico', 'images/favicon.ico'];
1826 $return .= '<link rel="shortcut icon" href="' . tpl_getMediaFile($look) . '" />' . NL;
1827 break;
1828 case 'mobile':
1829 $look = [':wiki:apple-touch-icon.png', ':apple-touch-icon.png', 'images/apple-touch-icon.png'];
1830 $return .= '<link rel="apple-touch-icon" href="' . tpl_getMediaFile($look) . '" />' . NL;
1831 break;
1832 case 'generic':
1833 // ideal world solution, which doesn't work in any browser yet
1834 $look = [':wiki:favicon.svg', ':favicon.svg', 'images/favicon.svg'];
1835 $return .= '<link rel="icon" href="' . tpl_getMediaFile($look) . '" type="image/svg+xml" />' . NL;
1836 break;
1837 }
1838 }
1839
1840 return $return;
1841 }
1842
1843/**
1844* Печать полноэкранного медиа-менеджера
1845*
1846* @author Катя Арзамасцева <pshns@ukr.net>
1847*/
1848 function tpl_media()
1849 {
1850 global $NS, $IMG, $JUMPTO, $REV, $lang, $fullscreen, $INPUT;
1851 $fullscreen = true;
1852 require_once DOKU_INC . 'lib/exe/mediamanager.php';
1853
1854 $rev = '';
1855 $image = cleanID($INPUT->str('image'));
1856 if (isset($IMG)) $image = $IMG;
1857 if (isset($JUMPTO)) $image = $JUMPTO;
1858 if (isset($REV) && !$JUMPTO) $rev = $REV;
1859
1860 echo '<div id="mediamanager__page">' . NL;
1861 echo '<h1>' . $lang['btn_media'] . '</h1>' . NL;
1862 html_msgarea();
1863
1864 echo '<div class="panel namespaces">' . NL;
1865 echo '<h2>' . $lang['namespaces'] . '</h2>' . NL;
1866 echo '<div class="panelHeader">';
1867 echo $lang['media_namespaces'];
1868 echo '</div>' . NL;
1869
1870 echo '<div class="panelContent" id="media__tree">' . NL;
1871 media_nstree($NS);
1872 echo '</div>' . NL;
1873 echo '</div>' . NL;
1874
1875 echo '<div class="panel filelist">' . NL;
1876 tpl_mediaFileList();
1877 echo '</div>' . NL;
1878
1879 echo '<div class="panel file">' . NL;
1880 echo '<h2 class="a11y">' . $lang['media_file'] . '</h2>' . NL;
1881 tpl_mediaFileDetails($image, $rev);
1882 echo '</div>' . NL;
1883
1884 echo '</div>' . NL;
1885 }
1886
1887/**
1888* Возвращаем полезные классы макета
1889*
1890* @возвращаемая строка
1891*
1892* @автор Аника Хенке <anika@selfthinker.org>
1893*/
1894 function tpl_classes()
1895 {
1896 global $ACT, $conf, $ID, $INFO;
1897 /** @var Input $INPUT */
1898 global $INPUT;
1899
1900 $classes = [
1901 'dokuwiki',
1902 'mode_' . $ACT,
1903 'tpl_' . $conf['template'],
1904 $INPUT->server->bool('REMOTE_USER') ? 'loggedIn' : '',
1905 (isset($INFO['exists']) && $INFO['exists']) ? '' : 'notFound',
1906 ($ID == $conf['start']) ? 'home' : ''
1907 ];
1908 return implode(' ', $classes);
1909 }
1910
1911/**
1912* Создать событие для меню инструментов
1913*
1914* @param string $ toolsname имя меню
1915* @param массив $ элементы
1916* @param string $ view например 'main', 'detail', ...
1917*
1918* @автор Аника Хенке <anika@selfthinker.org>
1919* @deprecated 2017-09-01 см. devel:menus
1920*/
1921 function tpl_toolsevent($toolsname, $items, $view = 'main')
1922 {
1923 dbg_deprecated('see devel:menus');
1924 $data = ['view' => $view, 'items' => $items];
1925
1926 $hook = 'TEMPLATE_' . strtoupper($toolsname) . '_DISPLAY';
1927 $evt = new Event($hook, $data);
1928 if ($evt->advise_before()) {
1929 foreach ($evt->data['items'] as $html) echo $html;
1930 }
1931 $evt->advise_after();
1932 }
1933
Полный перевод для понимая
1<?php
2
3/**
4* Функции шаблонов DokuWiki
5*
6* @license GPL 2 ( http://www.gnu.org/licenses/gpl.html)
7* @автор Андреас Гор <andi@splitbrain.org>
8*/
9
10использовать dokuwiki \ ActionRouter ;
11используйте dokuwiki \ Действие \ Исключение \ FatalException ;
12использовать dokuwiki \ Extension \ PluginInterface ;
13используйте dokuwiki \ Ui \ Admin ;
14использовать dokuwiki \ StyleUtils ;
15использовать dokuwiki \ Меню \ Элемент \ AbstractItem ;
16использовать dokuwiki \ Форма \ Форма ;
17используйте dokuwiki \ Menu \ MobileMenu ;
18используйте dokuwiki \ Ui \ Subscribe ;
19используйте dokuwiki \ Extension \ AdminPlugin ;
20использовать dokuwiki \ Extension \ Event ;
21используйте dokuwiki \ File \ PageResolver ;
22
23/**
24* Доступ к файлу шаблона
25*
26* Возвращает путь к указанному файлу внутри текущего шаблона, использует
27* шаблон по умолчанию, если пользовательская версия не существует.
28*
29* @param string $ файл
30* @возвращаемая строка
31*
32* @автор Андреас Гор <andi@splitbrain.org>
33*/
34 шаблон функции ($ file )
35{
36 глобальная $ conf ;
37
38 если (@ is_readable ( DOKU_INC . ' lib / tpl /' . $ conf [ 'template' ] . '/' . $ file ))
39 вернуть DOKU_INC.'lib / tpl /'.$ conf [ ' template' ] . ' /' . $ file ;
40
41 вернуть DOKU_INC.'lib / tpl / dokuwiki /'.$ file ;
42}
43
44/**
45* Удобная функция для доступа к каталогу шаблонов из локальной ФС
46*
47* Заменяет устаревшую константу DOKU_TPLINC.
48*
49* @param string $ tpl Шаблон для использования, по умолчанию текущий
50* @возвращаемая строка
51*
52* @автор Андреас Гор <andi@splitbrain.org>
53*/
54функция tpl_incdir ($ tpl = '' )
55{
56 глобальная $ conf ;
57 если (!$ tpl ) $ tpl = $ conf [ 'шаблон' ];
58 вернуть DOKU_INC . ' lib / tpl /' . $ tpl . '/' ;
59}
60
61/**
62* Удобная функция доступа к каталогу шаблонов из Интернета
63*
64* Заменяет устаревшую константу DOKU_TPL
65*
66* @param string $ tpl Шаблон для использования, по умолчанию текущий
67* @возвращаемая строка
68*
69* @автор Андреас Гор <andi@splitbrain.org>
70*/
71функция tpl_basedir ($ tpl = '' )
72{
73 глобальная $ conf ;
74 если (!$ tpl ) $ tpl = $ conf [ 'шаблон' ];
75 вернуть DOKU_BASE.'lib / tpl /'.$ tpl . ' / ' ;
76}
77
78/**
79* Распечатать содержимое
80*
81* Эта функция используется для печати всего обычного контента.
82* (определяется глобальной переменной $ACT) путем вызова соответствующего
83* выходные функции из html.php
84*
85* Все, что не использует основной файл шаблона, не
86* обрабатывается этой функцией. ACL-списки здесь тоже не обрабатываются.
87*
88* @param bool $ prependTOC следует ли здесь отображать оглавление?
89* @return bool true, если есть какой-либо вывод
90*
91* @triggers TPL_ACT_RENDER
92* @triggers TPL_CONTENT_DISPLAY
93* @автор Андреас Гор <andi@splitbrain.org>
94*/
95функция tpl_content ($ prependTOC = true )
96{
97 глобальный $ ACT ;
98 глобальная $ ИНФОРМАЦИЯ ;
99 $ INFO [ 'prependTOC' ] = $ prependTOC ;
100
101 ob_start ();
102 Событие :: createAndTrigger ( 'TPL_ACT_RENDER' , $ ACT , 'tpl_content_core' );
103 $ html_output = ob_get_clean ();
104 Событие :: createAndTrigger ( 'TPL_CONTENT_DISPLAY' , $ html_output , function ($ html_output ) {
105 echo $ html_output ;
106 });
107
108 возврат ! пусто ($ html_output );
109}
110
111/**
112* Действие по умолчанию TPL_ACT_RENDER
113*
114* @return bool
115*/
116функция tpl_content_core ()
117{
118 $ router = ActionRouter :: getInstance ();
119 пытаться {
120 $ маршрутизатор -> getAction ()-> tplContent ();
121 } поймать ( FatalException $ e ) {
122 // не было контента для действия
123 сообщение ( hsc ($ e -> getMessage ()), -1 );
124 вернуть ложь ;
125 }
126 вернуть истину ;
127}
128
129/**
130* Размещает оглавление там, где вызывается функция
131*
132* Если вы используете это, вы, скорее всего, захотите вызвать tpl_content с помощью
133* ложный аргумент
134*
135* @param bool $ return Следует ли вернуть оглавление вместо его печати?
136* @возвращаемая строка
137*
138* @автор Андреас Гор <andi@splitbrain.org>
139*/
140функция tpl_toc ($ return = false )
141{
142 глобальный $ TOC ;
143 глобальный $ ACT ;
144 глобальный $ ID ;
145 глобальный $ REV ;
146 глобальная $ ИНФОРМАЦИЯ ;
147 глобальная $ conf ;
148 $ toc = [];
149
150 если ( is_array ($ TOC )) {
151 // если TOC был подготовлен в глобальном масштабе, всегда используйте его
152 $ toc = $ TOC ;
153 } elseif (($ ACT == 'show' || str_starts_with ($ ACT , 'export' )) && !$ REV && $ INFO [ 'exists' ]) {
154 // получить TOC из метаданных, отобразить при необходимости
155 $ meta = p_get_metadata ($ ID , '' , METADATA_RENDER_USING_CACHE );
156 $ tocok = $ meta [ 'internal' ][ 'toc' ] ?? true ;
157 $ toc = $ meta [ 'description' ][ 'tableofcontents' ] ?? null ;
158 если (!$ tocok || ! is_array ($ toc ) || !$ conf [ 'tocminheads' ] || count ($ toc ) < $ conf [ 'tocminheads' ]) {
159 $ toc = [];
160 }
161 } elseif ($ ACT == 'админ' ) {
162 // попробуем загрузить оглавление плагина администратора
163 /** @var AdminPlugin $plugin */
164 если ($ plugin = plugin_getRequestAdminPlugin ()) {
165 $ toc = $ plugin -> getTOC ();
166 $ TOC = $ toc ; // избежать последующей перестройки
167 }
168 }
169
170 Событие :: createAndTrigger ( 'TPL_TOC_RENDER' , $ toc , null , false );
171 $ html = html_TOC ($ toc );
172 если ($ return ) вернуть $ html ;
173 эхо $ html ;
174 возвращаться '' ;
175}
176
177/**
178* Обработка содержимого страницы администратора
179*
180* @return bool
181*
182* @автор Андреас Гор <andi@splitbrain.org>
183*/
184функция tpl_admin ()
185{
186 глобальная $ ИНФОРМАЦИЯ ;
187 глобальный $ TOC ;
188 глобальный $ ВХОД ;
189
190 $ плагин = null ;
191 $ класс = $ ВХОД -> стр ( 'страница' );
192 если (! пусто ($ класс )) {
193 $ pluginlist = plugin_list ( 'admin' );
194
195 если ( in_array ($ class , $ pluginlist )) {
196 // попытка загрузить плагин
197 /** @var AdminPlugin $plugin */
198 $ plugin = plugin_load ( 'admin' , $ class );
199 }
200 }
201
202 если ($ plugin instanceof PluginInterface ) {
203 if (! is_array ($ TOC )) $ TOC = $ plugin -> getTOC (); //если TOC еще не был запрошен
204 если ($ INFO [ 'prependTOC' ]) tpl_toc ();
205 $ плагин -> html ();
206 } еще {
207 $ админ = новый Администратор ();
208 $ админ -> показать ();
209 }
210 вернуть истину ;
211}
212
213/**
214* Распечатайте правильные HTML-мета-заголовки
215*
216* Это необходимо разместить в заголовке вашего шаблона.
217*
218* @param bool $ alt Нужно ли добавлять каналы и ссылки альтернативного формата?
219* @return bool
220* @вызывает JsonException
221*
222* @автор Андреас Гор <andi@splitbrain.org>
223* @triggers TPL_METAHEADER_OUTPUT
224*/
225функция tpl_metaheaders ($ alt = true )
226{
227 глобальный $ ID ;
228 глобальный $ REV ;
229 глобальная $ ИНФОРМАЦИЯ ;
230 глобальный $ JSINFO ;
231 глобальный $ ACT ;
232 глобальный $ ЗАПРОС ;
233 глобальный $ lang ;
234 глобальная $ conf ;
235 глобальная $ updateVersion ;
236 /** @var Вход $INPUT */
237 глобальный $ ВХОД ;
238
239 // подготавливаем массив головок
240 $ голова = [];
241
242 // подготовить seed для js и css
243 $ tseed = $ updateVersion ;
244 $ зависит = getConfigFiles ( 'main' );
245 $ зависит [] = DOKU_CONF . "tpl/" . $ conf [ 'template' ] . "/style.ini" ;
246 foreach ($ зависит от $ f ) $ tseed .= @ filemtime ($ f );
247 $ tseed = md5 ($ tseed );
248
249 // обычные вещи
250 $ head [ 'meta' ][] = [ 'name' => 'generator' , 'content' => 'DokuWiki' ];
251 если ( действиеOK ( 'поиск' )) {
252 $ head [ 'ссылка' ][] = [
253 'rel' => 'поиск' ,
254 'тип' => ' приложение / opensearchdescription +xml' ,
255 'href' => DOKU_BASE . ' lib / exe / opensearch.php ' ,
256 'заголовок' => $ conf [ 'заголовок' ]
257 ];
258 }
259
260 $ head [ 'link' ][] = [ 'rel' => 'start' , 'href' => DOKU_BASE ];
261 если ( действиеOK ( 'индекс' )) {
262 $ head [ 'ссылка' ][] = [
263 'rel' => 'содержимое' ,
264 'href' => wl ($ ID , 'do=index' , false , '&' ),
265 'title' => $ lang [ 'btn_index' ]
266 ];
267 }
268
269 если ( действиеOK ( 'манифест' )) {
270 $ head [ 'ссылка' ][] = [
271 'rel' => 'манифест' ,
272 'href' => DOKU_BASE . ' lib / exe / manifest.php '
273 ];
274 }
275
276 $ styleUtil = new StyleUtils ();
277 $ styleIni = $ styleUtil -> cssStyleini ();
278 $ replacements = $ styleIni [ 'replacements' ];
279 если (! пусто ($ replacements [ '__theme_color__' ])) {
280 $ голова [ 'мета' ][] = [
281 'имя' => 'цвет темы' ,
282 'content' => $ replacements [ '__theme_color__' ]
283 ];
284 }
285
286 если ($ альт ) {
287 если ( действиеOK ( 'rss' )) {
288 $ head [ 'ссылка' ][] = [
289 'rel' => 'альтернативный' ,
290 'тип' => ' приложение / rss +xml' ,
291 'title' => $ lang [ 'btn_recent' ],
292 'href' => DOKU_BASE . ' feed.php '
293 ];
294 $ head [ 'ссылка' ][] = [
295 'rel' => 'альтернативный' ,
296 'тип' => ' приложение / rss +xml' ,
297 'title' => $ lang [ 'currentns' ],
298 'href' => DOKU_BASE . ' feed.php ?mode=list&ns=' . ( isset ($ INFO ) ? $ INFO [ 'namespace' ] : '' )
299 ];
300 }
301 если (($ ACT == 'показать' || $ ACT == 'поиск' ) && $ INFO [ 'записываемый' ]) {
302 $ head [ 'ссылка' ][] = [
303 'rel' => 'редактировать' ,
304 'title' => $ lang [ 'btn_edit' ],
305 'href' => wl ($ ID , 'do=edit' , false , '&' )
306 ];
307 }
308
309 если ( actionOK ( 'rss' ) && $ ACT == 'search' ) {
310 $ head [ 'ссылка' ][] = [
311 'rel' => 'альтернативный' ,
312 'тип' => ' приложение / rss +xml' ,
313 'title' => $ lang [ 'searchresult' ],
314 'href' => DOKU_BASE . ' feed.php ?mode=search&q=' . $ ЗАПРОС
315 ];
316 }
317
318 если ( actionOK ( 'export_xhtml' )) {
319 $ head [ 'ссылка' ][] = [
320 'rel' => 'альтернативный' ,
321 'тип' => ' текст / html ' ,
322 'title' => $ lang [ 'plainhtml' ],
323 'href' => экспортссылка ($ ID , 'xhtml' , '' , false , '&' )
324 ];
325 }
326
327 если ( actionOK ( 'export_raw' )) {
328 $ head [ 'ссылка' ][] = [
329 'rel' => 'альтернативный' ,
330 'тип' => ' текст / обычный ' ,
331 'title' => $ lang [ 'wikimarkup' ],
332 'href' => экспортссылка ($ ID , 'raw' , '' , false , '&' )
333 ];
334 }
335 }
336
337 // настройка тегов робота, подходящих для разных режимов
338 если (($ ACT == 'show' || $ ACT == 'export_xhtml' ) && !$ REV ) {
339 если ($ INFO [ 'существует' ]) {
340 //задержка индексации:
341 если (( время () - $ INFO [ 'lastmod' ]) >= $ conf [ 'indexdelay' ] && ! isHiddenPage ($ ID )) {
342 $ head [ 'meta' ][] = [ 'name' => 'robots' , 'content' => 'index,follow' ];
343 } еще {
344 $ head [ 'meta' ][] = [ 'name' => 'robots' , 'content' => 'noindex,nofollow' ];
345 }
346 $ canonicalUrl = wl ($ ID , '' , true , '&' );
347 если ($ ID == $ conf [ 'start' ]) {
348 $ canonicalUrl = DOKU_URL ;
349 }
350 $ head [ 'link' ][] = [ 'rel' => 'canonical' , 'href' => $ canonicalUrl ];
351 } еще {
352 $ head [ 'meta' ][] = [ 'name' => 'robots' , 'content' => 'noindex,follow' ];
353 }
354 } elseif ( определено ('DOKU_MEDIADETAIL')) {
355 $ head [ 'meta' ][] = [ 'name' => 'robots' , 'content' => 'index,follow' ];
356 } еще {
357 $ head [ 'meta' ][] = [ 'name' => 'robots' , 'content' => 'noindex,nofollow' ];
358 }
359
360 // установить метаданные
361 если ($ ACT == 'показать' || $ ACT == 'export_xhtml' ) {
362 // ключевые слова (явные или неявные)
363 если (! пусто ($ INFO [ 'meta' ][ 'subject' ])) {
364 $ head [ 'meta' ][] = [ 'name' => 'keywords' , 'content' => implode ( ',' , $ INFO [ 'meta' ][ 'subject' ])];
365 } еще {
366 $ head [ 'meta' ][] = [ 'name' => 'keywords' , 'content' => str_replace ( ':' , ',' , $ ID )];
367 }
368 }
369
370 // загрузка таблиц стилей
371 $ head [ 'ссылка' ][] = [
372 'rel' => 'таблица стилей' ,
373 'href' => DOKU_BASE . ' lib / exe / css.php ?t=' . rawurlencode ($ conf [ 'template' ]) . '&tseed=' . $ tseed
374 ];
375
376 $ script = "var NS=" . ( isset ($ INFO ) ? $ INFO [ 'namespace' ] : '' ) . "';" ;
377 если ($ conf [ 'useacl' ] && $ INPUT -> сервер -> str ( 'REMOTE_USER' )) {
378 $ скрипт .= "var SIG=" . тулбар_сигнатура () . ";" ;
379 }
380 jsinfo ();
381 $ script .= 'var JSINFO = ' . json_encode ($ JSINFO , JSON_THROW_ON_ERROR ) . ';' ;
382 $ script .= '(function(H){H.className=H.className.replace(/\bno-js\b/, \' js \' )})(document.documentElement);' ;
383 $ head [ 'script' ][] = [ '_data' => $ script ];
384
385 // загрузить jquery
386 $ jquery = getCdnUrls ();
387 foreach ($ jquery как $ src ) {
388 $ head [ 'скрипт' ][] = [
389 '_data' => '' ,
390 'источник' => $ источник
391 ] + ($ conf [ 'defer_js' ] ? [ 'defer' => 'defer' ] : []);
392 }
393
394 // загружаем наш диспетчер javascript
395 $ head [ 'скрипт' ][] = [
396 '_data' => '' ,
397 'src' => DOKU_BASE . ' lib / exe / js.php ' . '?t=' . rawurlencode ($ conf [ 'шаблон' ]) . '&tseed=' . $ семя
398 ] + ($ conf [ 'defer_js' ] ? [ 'defer' => 'defer' ] : []);
399
400 // вызвать событие здесь
401 Событие :: createAndTrigger ( 'TPL_METAHEADER_OUTPUT' , $ head , '_tpl_metaheaders_action' , true );
402 вернуть истину ;
403}
404
405/**
406* печатает массив, созданный tpl_metaheaders
407*
408* $data — это массив различных тегов заголовков. Каждый тег может иметь несколько
409* экземпляры. Атрибуты задаются как пары ключ-значение. Значения будут HTML
410* кодируются автоматически, поэтому их следует предоставлять как есть в массиве $data.
411*
412* Для тегов, имеющих атрибут body, укажите данные body в специальном поле
413* атрибут '_data'. Это поле НЕ БУДЕТ ЭКРАНИРОВАНО автоматически.
414*
415* Встроенные скрипты будут использовать любой одноразовый номер, указанный в переменной среды «NONCE».
416*
417* @param массив $ данные
418*
419* @автор Андреас Гор <andi@splitbrain.org>
420*/
421функция _tpl_metaheaders_action ($ data )
422{
423 $ nonce = getenv ( 'NONCE' );
424 foreach ($ data как $ tag => $ inst ) {
425 foreach ($ inst as $ attr ) {
426 если ( пусто ($ attr )) {
427 продолжать ;
428 }
429 если ($ nonce && $ tag == 'script' && ! empty ($ attr [ '_data' ])) {
430 $ attr [ 'nonce' ] = $ nonce ; // добавить nonce к встроенным тегам скрипта
431 }
432 echo '<' , $ tag , ' ' , buildAttributes ($ attr );
433 если ( isset ($ attr [ '_data' ]) || $ tag == 'script' ) {
434 echo '>' , $ attr [ '_data' ] ?? '' , '</' , $ tag , '>' ;
435 } еще {
436 эхо '/>' ;
437 }
438 эхо " \n " ;
439 }
440 }
441}
442
443/**
444* Вывести данный скрипт как встроенный тег скрипта
445*
446* Эта функция добавит атрибут nonce, если он доступен.
447*
448* Скрипт НЕ экранируется автоматически!
449*
450* @param string $ скрипт
451* @param bool $ return Возврат или прямая печать?
452* @return string | недействительный
453*/
454функция tpl_inlineScript ($ script , $ return = false )
455{
456 $ nonce = getenv ( 'NONCE' );
457 если ($ nonce ) {
458 $ скрипт = '<script nonce="' . $ nonce . '">' . $ скрипт . '</script>' ;
459 } еще {
460 $ скрипт = '<скрипт>' . $ скрипт . '</скрипт>' ;
461 }
462
463 если ($ return ) return $ script ;
464 эхо $ скрипт ;
465}
466
467/**
468* Распечатать ссылку
469*
470* Просто создает ссылку.
471*
472* @param string $ url
473* @param string $ имя
474* @param string $ еще
475* @param bool $ return если true вернуть ссылку html, в противном случае вывести
476* @return bool | строка html ссылки или true, если выводится
477*
478* @автор Андреас Гор <andi@splitbrain.org>
479*/
480функция tpl_link ($ url , $ name , $ more = '' , $ return = false )
481{
482 $ out = '<a href="' . $ url . '" ' ;
483 если ($ more ) $ out .= ' ' . $ more ;
484 $ out .= "> $ имя </a>" ;
485 если ($ return ) вернуть $ out ;
486 вывести $ ;
487 вернуть истину ;
488}
489
490/**
491* Печатает ссылку на WikiPage
492*
493* Обертка вокруг html_wikilink
494*
495* @param string $ id идентификатор страницы
496* @param string | null $ name имя ссылки
497* @param bool $ возврат
498* @return true | строка
499*
500* @автор Андреас Гор <andi@splitbrain.org>
501*/
502функция tpl_pagelink ($ id , $ name = null , $ return = false )
503{
504 $ out = '<bdi>' . html_wikilink ( $ id , $ name ) . '</bdi>' ;
505 если ($ return ) вернуть $ out ;
506 вывести $ ;
507 вернуть истину ;
508}
509
510/**
511* получить родительскую страницу
512*
513* Пытается выяснить, какая страница является родительской.
514* возвращает false, если ничего не доступно
515*
516* @param string $ id идентификатор страницы
517* @return false | строка
518*
519* @автор Андреас Гор <andi@splitbrain.org>
520*/
521функция tpl_getparent ($ id )
522{
523 $ resolver = new PageResolver ( 'root' );
524
525 $ parent = getNS ($ id ). ':' ;
526 $ parent = $ resolver -> resolveId ($ parent );
527 если ($ родитель == $ идентификатор ) {
528 $ pos = strrpos ( getNS ($ id ), ':' );
529 $ parent = substr ($ parent , 0 , $ pos ). ':' ;
530 $ parent = $ resolver -> resolveId ($ parent );
531 если ($ parent == $ id ) вернуть false ;
532 }
533 вернуть $ родитель ;
534}
535
536/**
537* Распечатать одну из кнопок
538*
539* @param string $ тип
540* @param bool $ возврат
541* @return bool | string html, или false, если данных нет, true, если выведено
542* @see tpl_get_action
543*
544* @автор Адриан Лэнг <mail@adrianlang.de>
545* @deprecated 2017-09-01 см. devel:menus
546*/
547функция tpl_button ($ тип , $ возврат = ложь )
548{
549 dbg_deprecated ( 'см. devel:menus' );
550 $ data = tpl_get_action ($ type );
551 если ($ данные === ложь ) {
552 вернуть ложь ;
553 } elseif (! is_array ($ data )) {
554 $ out = sprintf ($ data , 'button' );
555 } еще {
556 /**
557 * @var string $accesskey
558 * @var string $id
559 * @var string $метод
560 * @var массив $params
561 */
562 извлечь ($ data );
563 если ($ id === '#dokuwiki__top' ) {
564 $ out = html_topbtn ();
565 } еще {
566 $ out = html_btn ($ type , $ id , $ accesskey , $ params , $ method );
567 }
568 }
569 если ($ return ) вернуть $ out ;
570 вывести $ ;
571 вернуть истину ;
572}
573
574/**
575* Как кнопки действий, но ссылки
576*
577* @param string $ тип действие команда
578* @param string $ pre префикс ссылки
579* @param string $ suf суффикс ссылки
580* @param string $ внутренний innerHML ссылки
581* @param bool $ return если true, то возвращает html, в противном случае печатает
582* @return bool | string html или false, если данных нет, true, если выведено
583*
584* @see tpl_get_action
585* @автор Адриан Лэнг <mail@adrianlang.de>
586* @deprecated 2017-09-01 см. devel:menus
587*/
588функция tpl_actionlink ($ type , $ pre = '' , $ suf = '' , $ inner = '' , $ return = false )
589{
590 dbg_deprecated ( 'см. devel:menus' );
591 глобальный $ lang ;
592 $ data = tpl_get_action ($ type );
593 если ($ данные === ложь ) {
594 вернуть ложь ;
595 } elseif (! is_array ($ data )) {
596 $ out = sprintf ($ data , 'link' );
597 } еще {
598 /**
599 * @var string $accesskey
600 * @var string $id
601 * @var string $метод
602 * @var bool $nofollow
603 * @var массив $params
604 * @var string $replacement
605 */
606 извлечь ($ data );
607 если ( strpos ($ id , '#' ) === 0 ) {
608 $ linktarget = $ id ;
609 } еще {
610 $ linktarget = wl ($ id , $ params );
611 }
612 $ caption = $ lang [ 'btn_' . $ type ];
613 если ( strpos ($ caption , '%s' )) {
614 $ caption = sprintf ($ caption , $ replacement );
615 }
616 $ akey = '' ;
617 $ addTitle = '' ;
618 если ($ accesskey ) {
619 $ akey = 'accesskey="' . $ accesskey . '" ' ;
620 $ addTitle = '[' . strtoupper ($ accesskey ) . ']' ;
621 }
622 $ rel = $ nofollow ? 'rel="nofollow" ' : '' ;
623 $ out = tpl_link (
624 $ ссылкацель ,
625 $ pre . ($ inner ?: $ caption ) . $ suf ,
626 'класс="действие' . $ тип . '" ' .
627 $ akey . $ rel .
628 'title="' . hsc ($ caption ) . $ addTitle . '"' ,
629 истинный
630 );
631 }
632 если ($ return ) вернуть $ out ;
633 вывести $ ;
634 вернуть истину ;
635}
636
637/**
638* Проверьте действия и получите данные для кнопок и ссылок
639*
640* @param string $ тип
641* @return массив | bool | строка
642*
643* @автор Адриан Лэнг <mail@adrianlang.de>
644* @автор Андреас Гор <andi@splitbrain.org>
645* @автор Маттиас Гримм <matthiasgrimm@users.sourceforge.net>
646* @deprecated 2017-09-01 см. devel:menus
647*/
648функция tpl_get_action ($ тип )
649{
650 dbg_deprecated ( 'см. devel:menus' );
651 если ($ type == 'history' ) $ type = 'revisions' ;
652 если ($ type == 'подписка' ) $ type = 'подписка' ;
653 если ($ type == 'img_backto' ) $ type = 'imgBackto' ;
654
655 $ class = ' \\ dokuwiki \\ Меню \\ Элемент \\ ' . ucfirst ($ type );
656 если ( class_exists ($ class )) {
657 пытаться {
658 /** @var AbstractItem $item */
659 $ item = новый $ class ();
660 $ data = $ item -> getLegacyData ();
661 $ неизвестно = ложь ;
662 } catch ( RuntimeException $ игнорируется ) {
663 вернуть ложь ;
664 }
665 } еще {
666 глобальный $ ID ;
667 $ данные = [
668 'accesskey' => null ,
669 'тип' => $ тип ,
670 'id' => $ ID ,
671 'метод' => 'получить' ,
672 'params' => [ 'do' => $ type ],
673 'nofollow' => правда ,
674 'замена' => ''
675 ];
676 $ неизвестно = правда ;
677 }
678
679 $ evt = новое событие ( 'TPL_ACTION_GET' , $ data );
680 если ($ evt -> advice_before ()) {
681 //обработка неизвестных типов
682 если ($ неизвестно ) {
683 $ data = '[неизвестный тип %s]' ;
684 }
685 }
686 $ evt -> advice_after ();
687 снято ($ evt );
688
689 вернуть $ данные ;
690}
691
692/**
693* Обертка вокруг tpl_button() и tpl_actionlink()
694*
695* @param string $ тип действие команда
696* @param bool $ ссылка ссылка или кнопка формы?
697* @param string | bool $ wrapper Обертка HTML-элемента
698* @param bool $ return return или print
699* @param string $ pre префикс для ссылок
700* @param string $ suf суффикс для ссылок
701* @param string $ внутренний внутренний HTML для ссылок
702* @return bool | строка
703*
704* @автор Аника Хенке <anika@selfthinker.org>
705* @deprecated 2017-09-01 см. devel:menus
706*/
707функция tpl_action ($ type , $ link = false , $ wrapper = false , $ return = false , $ pre = '' , $ suf = '' , $ inner = '' )
708{
709 dbg_deprecated ( 'см. devel:menus' );
710 $ out = '' ;
711 если ($ ссылка ) {
712 $ out .= tpl_actionlink ($ type , $ pre , $ suf , $ inner , true );
713 } еще {
714 $ out .= tpl_button ( $ type , true );
715 }
716 если ($ out && $ wrapper ) $ out = "< $ wrapper > $ out </ $ wrapper >" ;
717
718 если ($ return ) вернуть $ out ;
719 вывести $ ;
720 return ( bool )$ out ;
721}
722
723/**
724* Распечатать форму поиска
725*
726* Если первый параметр задан как div с идентификатором 'qsearch_out', то будет
727* добавляется, который инструктирует страницу ajax quicksearch включиться и разместить
728* его вывод в этот div. Второй параметр управляет собственным
729* атрибут автозаполнения. Если установлено значение false, этот атрибут будет установлен с
730* значение "off" указывает браузеру отключить встроенные функции
731* функция автодополнения (MSIE и Firefox)
732*
733* @param bool $ ajax
734* @param bool $ автозаполнение
735* @return bool
736*
737* @автор Андреас Гор <andi@splitbrain.org>
738*/
739функция tpl_searchform ($ ajax = true , $ autocomplete = true )
740{
741 глобальный $ lang ;
742 глобальный $ ACT ;
743 глобальный $ ЗАПРОС ;
744 глобальный $ ID ;
745
746 // не печатать форму поиска, если действие поиска отключено
747 если (! actionOK ( 'search' )) вернуть false ;
748
749 $ searchForm = новая форма ([
750 'действие' => wl (),
751 'метод' => 'получить' ,
752 'роль' => 'поиск' ,
753 'класс' => 'поиск' ,
754 'id' => 'dw__search' ,
755 ], истинный );
756 $ searchForm -> addTagOpen ( 'div' )-> addClass ( 'no' );
757 $ searchForm -> setHiddenField ( 'сделать' , 'поиск' );
758 $ searchForm -> setHiddenField ( 'id' , $ ID );
759 $ searchForm -> addTextInput ( 'q' )
760 -> добавитьКласс ( 'редактировать' )
761 -> атрибуты ([
762 'заголовок' => '[F]' ,
763 'accesskey' => 'f' ,
764 'placeholder' => $ lang [ 'btn_search' ],
765 'autocomplete' => $ autocomplete ? 'on' : 'off' ,
766 ])
767 -> идентификатор ( 'qsearch__in' )
768 -> val ($ ACT === 'поиск' ? $ QUERY : '' )
769 -> useInput ( false );
770 $ searchForm -> addButton ( '' , $ lang [ 'btn_search' ])-> attrs ([
771 'тип' => 'отправить' ,
772 'title' => $ lang [ 'btn_search' ],
773 ]);
774 если ($ ajax ) {
775 $ searchForm -> addTagOpen ( 'div' )-> id ( 'qsearch__out' )-> addClass ( 'ajax_qsearch JSpopup' );
776 $ searchForm -> addTagClose ( 'div' );
777 }
778 $ searchForm -> addTagClose ( 'div' );
779
780 echo $ searchForm -> toHTML ( 'Быстрый поиск' );
781
782 вернуть истину ;
783}
784
785/**
786* Распечатать след навигационной цепочки
787*
788* @param string $ sep Разделитель между записями
789* @param bool $ return return или print
790* @return bool | строка
791*
792* @автор Андреас Гор <andi@splitbrain.org>
793*/
794функция tpl_breadcrumbs ($ sep = null , $ return = false )
795{
796 глобальный $ lang ;
797 глобальная $ conf ;
798
799 //проверить, включено ли
800 если (!$ conf [ 'хлебные крошки' ]) вернуть false ;
801
802 //установить значение по умолчанию
803 если ( is_null ($ sep )) $ sep = '•' ;
804
805 $ out = '' ;
806
807 $ crumbs = breadcrumbs (); //настройка трассировки крошек
808
809 $ crumbs_sep = ' <span class="bcsep">' . $ sep . '</span> ' ;
810
811 //рендерим крошки, выделяем последнюю
812 $ out .= '<span class="bchead">' . $ lang [ 'хлебные крошки' ] . '</span>' ;
813 $ last = count ($ crubs );
814 $ я = 0 ;
815 foreach ($ крошки как $ id => $ name ) {
816 $ я ++;
817 $ out .= $ crumbs_sep ;
818 если ($ i == $ last ) $ out .= '<span class="curid">' ;
819 $ out .= '<bdi>' . tpl_link ( wl ( $ id ), hsc ( $ name ), ' class="breadcrumbs" title="' . $ id . '"' , true ) . '</bdi>' ;
820 если ($ i == $ last ) $ out .= '</span>' ;
821 }
822 если ($ return ) вернуть $ out ;
823 вывести $ ;
824 return ( bool )$ out ;
825}
826
827/**
828* Иерархическая навигационная цепочка
829*
830* Этот код был предложен в качестве замены обычным хлебным крошкам.
831* Имеет смысл только при наличии глубокой структуры сайта.
832*
833* @param string $ sep Разделитель между записями
834* @param bool $ return return или print
835* @return bool | строка
836*
837* @todo может вести себя странно в языках с письмом справа налево
838* @автор <fredrik@averpil.com>
839* @автор Андреас Гор <andi@splitbrain.org>
840* @автор Найджел Макни <oracle.shinoda@gmail.com>
841* @автор Шон Коутс <sean@caedmon.net>
842*/
843функция tpl_youarehere ($ sep = null , $ return = false )
844{
845 глобальная $ conf ;
846 глобальный $ ID ;
847 глобальный $ lang ;
848
849 // проверить, включено ли
850 если (!$ conf [ 'youarehere' ]) вернуть false ;
851
852 //установить значение по умолчанию
853 если ( is_null ($ sep )) $ sep = ' » ' ;
854
855 $ out = '' ;
856
857 $ parts = Explode ( ':' , $ ID );
858 $ count = count ($ parts );
859
860 $ out .= '<span class="bchead">' . $ lang [ 'выздесь' ] . ' </span>' ;
861
862 // всегда печатать стартовую страницу
863 $ out .= '<span class="home">' . tpl_pagelink ( ':' . $ conf [ 'start' ], null , true ) . '</span>' ;
864
865 // распечатать промежуточные ссылки пространства имен
866 $ часть = '' ;
867 для ($ i = 0 ; $ i < $ count - 1 ; $ i ++) {
868 $ часть .= $ часть [$ i ]. ':' ;
869 $ страница = $ часть ;
870 if ($ page == $ conf [ 'start' ]) continue ; // Пропустить начальную страницу
871
872 // выход
873 $ out .= $ sep . tpl_pagelink ( $ page , null , true );
874 }
875
876 // распечатать текущую страницу, пропустив начальную страницу, пропустив индекс пространства имен
877 если ( isset ($ страница )) {
878 $ page = ( new PageResolver ( 'root' ))-> resolveId ( $ page );
879 если ($ страница == $ часть . $ части [$ я ]) {
880 если ($ return ) вернуть $ out ;
881 вывести $ ;
882 вернуть истину ;
883 }
884 }
885 $ страница = $ часть . $ части [$ i ];
886 если ($ страница == $ конф [ 'старт' ]) {
887 если ($ return ) вернуть $ out ;
888 вывести $ ;
889 вернуть истину ;
890 }
891 $ out .= $ sep ;
892 $ out .= tpl_pagelink ($ page , null , true );
893 если ($ return ) вернуть $ out ;
894 вывести $ ;
895 return ( bool )$ out ;
896}
897
898/**
899* Распечатать информацию, если пользователь вошел в систему
900* и в этом случае показывать полное имя
901*
902* Можно ли в будущем добавить ссылку на профиль?
903*
904* @return bool
905*
906* @автор Андреас Гор <andi@splitbrain.org>
907*/
908функция tpl_userinfo ()
909{
910 глобальный $ lang ;
911 /** @var Вход $INPUT */
912 глобальный $ ВХОД ;
913
914 если ($ INPUT -> сервер -> str ( 'REMOTE_USER' )) {
915 echo $ lang [ 'loggedinas' ] . ' ' . userlink ();
916 вернуть истину ;
917 }
918 вернуть ложь ;
919}
920
921/**
922* Распечатать некоторую информацию о текущей странице
923*
924* @param bool $ ret возвращает содержимое вместо его печати
925* @return bool | строка
926*
927* @автор Андреас Гор <andi@splitbrain.org>
928*/
929функция tpl_pageinfo ($ ret = false )
930{
931 глобальная $ conf ;
932 глобальный $ lang ;
933 глобальная $ ИНФОРМАЦИЯ ;
934 глобальный $ ID ;
935
936 // возвращаем, если нам не разрешено просматривать страницу
937 если (! auth_quickaclcheck ($ ID )) {
938 вернуть ложь ;
939 }
940
941 // подготовить дату и путь
942 $ fn = $ INFO [ 'путь_к_файлу' ];
943 если (!$ conf [ 'полный_путь' ]) {
944 если ($ ИНФОРМАЦИЯ [ 'рев' ]) {
945 $ fn = str_replace ($ conf [ 'olddir' ] . '/' , '' , $ fn );
946 } еще {
947 $ fn = str_replace ($ conf [ 'datadir' ] . '/' , '' , $ fn );
948 }
949 }
950 $ fn = utf8_decodeFN ($ fn );
951 $ date = dformat ($ INFO [ 'lastmod' ]);
952
953 // распечатать это
954 если ($ INFO [ 'существует' ]) {
955 $ out = '<bdi>' . $ fn . '</bdi>' ;
956 $ out .= ' · ' ;
957 $ out .= $ lang [ 'lastmod' ];
958 $ out .= ' ' ;
959 $ out .= $ date ;
960 если ($ INFO [ 'редактор' ]) {
961 $ out .= ' ' . $ lang [ 'by' ] . ' ' ;
962 $ out .= '<bdi>' . editorinfo ($ INFO [ 'editor' ]) . '</bdi>' ;
963 } еще {
964 $ out .= ' (' . $ lang [ 'external_edit' ] . ')' ;
965 }
966 если ($ INFO [ 'заблокировано' ]) {
967 $ out .= ' · ' ;
968 $ out .= $ lang [ 'lockedby' ];
969 $ out .= ' ' ;
970 $ out .= '<bdi>' . editorinfo ($ INFO [ 'locked' ]) . '</bdi>' ;
971 }
972 если ($ рет ) {
973 возврат $ из ;
974 } еще {
975 вывести $ ;
976 вернуть истину ;
977 }
978 }
979 вернуть ложь ;
980}
981
982/**
983* Печатает или возвращает имя указанной страницы (текущей, если не указано).
984*
985* Если включено использование заголовка, будет использоваться первый заголовок, в противном случае
986* используется указанный идентификатор.
987*
988* @param string $ id идентификатор страницы
989* @param bool $ ret возвращает содержимое вместо печати
990* @return bool | строка
991*
992* @автор Андреас Гор <andi@splitbrain.org>
993*/
994функция tpl_pagetitle ($ id = null , $ ret = false )
995{
996 глобальные $ ACT , $ conf , $ lang ;
997
998 если ( is_null ($ id )) {
999 глобальный $ ID ;
1000 $ id = $ ID ;
1001 }
1002
1003 $ имя = $ идентификатор ;
1004 если ( useHeading ( 'навигация' )) {
1005 $ first_heading = p_get_first_heading ($ id );
1006 если ($ first_heading ) $ name = $ first_heading ;
1007 }
1008
1009 // заголовок страницы по умолчанию — это имя страницы, измените его с помощью текущего действия
1010 переключатель ($ ACT ) {
1011 // административные функции
1012 случай «администратор» :
1013 $ page_title = $ lang [ 'btn_admin' ];
1014 // попробуем получить имя плагина
1015 /** @var AdminPlugin $plugin */
1016 если ($ plugin = plugin_getRequestAdminPlugin ()) {
1017 $ plugin_title = $ plugin -> getMenuText ($ conf [ 'lang' ]);
1018 $ page_title = $ plugin_title ?: $ plugin -> getPluginName ();
1019 }
1020 перерыв ;
1021
1022 // показать действие как заголовок
1023 случай «логин» :
1024 кейс «профиль» :
1025 случай «регистр» :
1026 случай 'resendpwd' :
1027 случай «индекс» :
1028 случай «поиск» :
1029 $ page_title = $ lang [ 'btn_' . $ ACT ];
1030 перерыв ;
1031
1032 // добавить ручку во время редактирования
1033 случай «редактирование» :
1034 случай «предварительный просмотр» :
1035 $ page_title = "✎ " . $ name ;
1036 перерыв ;
1037
1038 // добавить действие к названию страницы
1039 дело «пересмотры» :
1040 $ page_title = $ name . ' - ' . $ lang [ 'btn_revs' ];
1041 перерыв ;
1042
1043 // добавить действие к названию страницы
1044 случай «обратная ссылка» :
1045 случай «недавний» :
1046 случай «подписаться» :
1047 $ page_title = $ name .'- ' .$ lang [ 'btn_' .$ ACT ];
1048 перерыв ;
1049
1050 по умолчанию : // SHOW и все остальное, что не включено
1051 $ page_title = $ name ;
1052 }
1053
1054 если ($ рет ) {
1055 вернуть hsc ($ page_title );
1056 } еще {
1057 echo hsc ($ page_title );
1058 вернуть истину ;
1059 }
1060}
1061
1062/**
1063* Возвращает запрошенный тег EXIF / IPTC из текущего изображения
1064*
1065* Если $tags — это массив, то все заданные теги проверяются до тех пор, пока не будет найден
1066* значение найдено. Если значение не найдено, возвращается $alt.
1067*
1068* Какие тексты известны, определяется в функциях _exifTagNames
1069* и _iptcTagNames() в inc / jpeg.php (Вам необходимо добавить IPTC
1070* к именам последнего)
1071*
1072* Разрешено только в: detail.php
1073*
1074* @param array | string $ tags тег или массив тегов для проверки
1075* @param string $ alt альтернативный вывод, если данные не найдены
1076* @param null | string $ src источник изображения, если не указан, используется глобальный $SRC
1077* @возвращаемая строка
1078*
1079* @автор Андреас Гор <andi@splitbrain.org>
1080*/
1081функция tpl_img_getTag ($ tags , $ alt = '' , $ src = null )
1082{
1083 // Инициализация Exif-ридера
1084 глобальный $ SRC , $ imgMeta ;
1085
1086 если ( is_null ($ src )) $ src = $ SRC ;
1087 если ( is_null ($ src )) вернуть $ alt ;
1088
1089 если (! isset ($ imgMeta )) {
1090 $ imgMeta = new JpegMeta ($ src );
1091 }
1092 если ($ imgMeta === false ) вернуть $ alt ;
1093 $ info = cleanText ($ imgMeta -> getField ($ tags ));
1094 если (!$ info ) вернуть $ alt ;
1095 вернуть $ информацию ;
1096}
1097
1098
1099/**
1100* Мусор собирает открытый объект JpegMeta.
1101*/
1102функция tpl_img_close ()
1103{
1104 глобальный $ imgMeta ;
1105 $ imgMeta = null ;
1106}
1107
1108/**
1109* Выводит HTML-список описаний метатегов текущего изображения
1110*/
1111функция tpl_img_meta ()
1112{
1113 глобальный $ lang ;
1114
1115 $ теги = tpl_get_img_meta ();
1116
1117 эхо '<dl>' ;
1118 foreach ($ теги как $ теги ) {
1119 $ label = $ lang [$ tag [ 'langkey' ]];
1120 если (!$ label ) $ label = $ tag [ 'langkey' ]. ':' ;
1121
1122 echo '<dt>' . $ label . '</dt><dd>' ;
1123 если ($ тег [ 'тип' ] == 'дата' ) {
1124 echo dformat ($ тег [ 'значение' ]);
1125 } еще {
1126 echo hsc ($ тег [ 'значение' ]);
1127 }
1128 эхо '</dd>' ;
1129 }
1130 эхо '</dl>' ;
1131}
1132
1133/**
1134* Возвращает метаданные, настроенные в файле конфигурации mediameta, готовые для создания html
1135*
1136* @return массив с массивами, содержащими записи:
1137* - строка langkey key для поиска в переменной $lang, если не найдена, выводится как есть
1138* - строковый тип значения
1139* - строковое значение тега (неэкранированное)
1140*/
1141функция tpl_get_img_meta ()
1142{
1143
1144 $ config_files = getConfigFiles ( 'mediameta' );
1145 foreach ($ config_files как $ config_file ) {
1146 если ( file_exists ($ config_file )) {
1147 включить ($ config_file );
1148 }
1149 }
1150 $ теги = [];
1151 foreach ($ поля как $ тег ) {
1152 $ т = [];
1153 если (! пусто ($ тег [ 0 ])) {
1154 $ т = [$ тег [ 0 ]];
1155 }
1156 если ( isset ($ тег [ 3 ]) && is_array ($ тег [ 3 ])) {
1157 $ t = array_merge ($ t , $ tag [ 3 ]);
1158 }
1159 $ value = tpl_img_getTag ($ t );
1160 если ($ значение ) {
1161 $ теги [] = [ 'langkey' => $ тег [ 1 ], 'type' => $ тег [ 2 ], 'value' => $ значение ];
1162 }
1163 }
1164 вернуть $ теги ;
1165}
1166
1167/**
1168* Печатает изображение со ссылкой на полноразмерную версию
1169*
1170* Разрешено только в: detail.php
1171*
1172* @triggers TPL_IMG_DISPLAY
1173* @param int $ maxwidth - максимальная ширина изображения
1174* @param int $ maxheight - максимальная высота изображения
1175* @param bool $ link - ссылка на исходный размер?
1176* @param array $ params - дополнительные атрибуты изображения
1177* @return bool Результат TPL_IMG_DISPLAY
1178*/
1179функция tpl_img ($ maxwidth = 0 , $ maxheight = 0 , $ link = true , $ params = null )
1180{
1181 глобальный $ IMG ;
1182 /** @var Вход $INPUT */
1183 глобальный $ ВХОД ;
1184 глобальный $ REV ;
1185 $ w = ( int ) tpl_img_getTag ( 'Файл.Ширина' );
1186 $ h = ( int ) tpl_img_getTag ( 'Файл.Высота' );
1187
1188 //изменить размер до указанных максимальных значений
1189Коэффициент $ = 1 ;
1190 если ($ w >= $ h ) {
1191 если ($ максширина && $ w >= $ максширина ) {
1192 $ отношение = $ максимальнаяширина / $ w ;
1193 } elseif ($ maxheight && $ h > $ maxheight ) {
1194 $ отношение = $ максимальная высота / $ h ;
1195 }
1196 } elseif ($ maxheight && $ h >= $ maxheight ) {
1197 $ отношение = $ максимальная высота / $ h ;
1198 } elseif ($ maxwidth && $ w > $ maxwidth ) {
1199 $ отношение = $ максимальнаяширина / $ w ;
1200 }
1201 если ($ отношение ) {
1202 $ w = пол ($ отношение * $ w );
1203 $ h = пол ( соотношение $ * $ h );
1204 }
1205
1206 //подготовить URL-адреса
1207 $ url = ml ($ IMG , [ 'cache' => $ INPUT -> str ( 'cache' ), 'rev' => $ REV ], true , '&' );
1208 $ src = ml ($ IMG , [ 'cache' => $ INPUT -> str ( 'cache' ), 'rev' => $ REV , 'w' => $ w , 'h' => $ h ], true , '&' );
1209
1210 //подготовить атрибуты
1211 $ alt = tpl_img_getTag ( 'Простой.Заголовок' );
1212 если ( is_null ($ params )) {
1213 $ р = [];
1214 } еще {
1215 $ p = $ параметры ;
1216 }
1217 если ($ w ) $ p [ 'ширина' ] = $ w ;
1218 если ($ h ) $ p [ 'высота' ] = $ h ;
1219 $ p [ 'class' ] = 'img_detail' ;
1220 если ($ альт ) {
1221 $ p [ 'alt' ] = $ alt ;
1222 $ p [ 'title' ] = $ alt ;
1223 } еще {
1224 $ p [ 'alt' ] = '' ;
1225 }
1226 $ p [ 'источник' ] = $ источник ;
1227
1228 $ data = [ 'url' => ($ link ? $ url : null ), 'params' => $ p ];
1229 return Event :: createAndTrigger ( 'TPL_IMG_DISPLAY' , $ data , '_tpl_img_action' , true );
1230}
1231
1232/**
1233* Действие по умолчанию для TPL_IMG_DISPLAY
1234*
1235* @param массив $ данные
1236* @return bool
1237*/
1238функция _tpl_img_action ($ data )
1239{
1240 глобальный $ lang ;
1241 $ p = buildAttributes ($ data [ 'params' ]);
1242
1243 если ($ data [ 'url' ]) echo ' <a href="' . hsc ($ data [ 'url' ]) . '" title="' . $ lang [ 'mediaview' ] . '"> ' ;
1244 echo '<img ' . $ p . '/>' ;
1245 если ($ data [ 'url' ]) echo '</a>' ;
1246 вернуть истину ;
1247}
1248
1249/**
1250* Эта функция вставляет небольшой gif-файл, который на самом деле является функцией индексатора.
1251*
1252* Должен вызываться где-то в самом конце шаблона main.php
1253*
1254* @return bool
1255*/
1256функция tpl_indexerWebBug ()
1257{
1258 глобальный $ ID ;
1259
1260 $ р = [];
1261 $ p [ 'src' ] = DOKU_BASE . ' lib / exe / taskrunner.php ?id=' . rawurlencode ($ ID ) .
1262 '&' . время ();
1263 $ p [ 'width' ] = 2 ; //больше никаких изображений размером 1x1 пиксель, потому что мы живем во времена блокировщиков рекламы...
1264 $ p [ 'высота' ] = 1 ;
1265 $ p [ 'alt' ] = '' ;
1266 $ att = buildAttributes ($ p );
1267 echo "<img $ att />" ;
1268 вернуть истину ;
1269}
1270
1271/**
1272* tpl_getConf($id)
1273*
1274* используйте эту функцию для доступа к переменным конфигурации шаблона
1275*
1276* @param string $ id имя значения для доступа
1277* @param mixed $ notset что возвращать, если настройка недоступна
1278* @return смешанный
1279*/
1280функция tpl_getConf ($ id , $ notset = false )
1281{
1282 глобальная $ conf ;
1283 статический $ tpl_configloaded = false ;
1284
1285 $ tpl = $ conf [ 'шаблон' ];
1286
1287 если (!$ tpl_configloaded ) {
1288 $ tconf = tpl_loadConfig ();
1289 если ($ tconf !== false ) {
1290 foreach ($ tconf as $ key => $ value ) {
1291 если ( isset ($ conf [ 'tpl' ][$ tpl ][$ key ])) продолжить ;
1292 $ conf [ 'tpl' ][$ tpl ][$ key ] = $ value ;
1293 }
1294 $ tpl_configloaded = true ;
1295 }
1296 }
1297
1298 вернуть $ conf [ 'tpl' ][$ tpl ][$ id ] ?? $ notset ;
1299}
1300
1301/**
1302* tpl_loadConfig()
1303*
1304* считывает все переменные конфигурации шаблона
1305* эта функция автоматически вызывается tpl_getConf()
1306*
1307* @return false | массив
1308*/
1309функция tpl_loadConfig ()
1310{
1311
1312 $ file = tpl_incdir (
) . ' /conf/default.php ' ;1313 $ conf = [];
1314
1315 если (! file_exists ($ file )) вернуть false ;
1316
1317 // загрузить файл конфигурации по умолчанию
1318 включить ($ файл );
1319
1320 вернуть $ conf ;
1321}
1322
1323// методы языка
1324
1325/**
1326* tpl_getLang($id)
1327*
1328* используйте эту функцию для доступа к переменным языка шаблона
1329*
1330* @param string $ id ключ языковой строки
1331* @возвращаемая строка
1332*/
1333функция tpl_getLang ($ id )
1334{
1335 статический $ lang = [];
1336
1337 если ( количество ($ язык ) === 0 ) {
1338 global $ conf , $ config_cascade ; // определенно не вызывайте "global $lang"
1339
1340 $ path = tpl_incdir () . 'lang/' ;
1341
1342 $ lang = [];
1343
1344 // не включайте один раз
1345 @include ( $ path.'en / lang.php ' ) ;
1346 foreach ($ config_cascade [ 'lang' ][ 'template' ] как $ config_file ) {
1347 если ( file_exists ($ config_file . $ conf [ 'template' ] . '/ en / lang.php ' )) {
1348 include ($ config_file .$ conf [ 'template' ]. '/ en / lang.php ' );
1349 }
1350 }
1351
1352 if ($ conf [ 'lang' ] != 'en' ) {
1353 @include ($ path .$ conf [ 'lang' ] . '/ lang.php ' );
1354 foreach ($ config_cascade [ 'lang' ][ 'template' ] как $ config_file ) {
1355 если ( file_exists ($ config_file . $ conf [ 'template' ] . '/' . $ conf [ 'lang' ] . '/ lang.php ' )) {
1356 include ($ config_file .$ conf [ 'template' ]. '/' .$ conf [ 'lang' ]. '/ lang.php ' );
1357 }
1358 }
1359 }
1360 }
1361 вернуть $ lang [$ id ] ?? '' ;
1362}
1363
1364/**
1365* Извлечь файл, зависящий от языка, и передать его в xhtml-рендерер для отображения
1366* эквивалент шаблона p_locale_xhtml()
1367*
1368* @param string $ id идентификатор страницы вики, зависящей от языка
1369* @return string анализирует содержимое страницы вики в формате xhtml
1370*/
1371функция tpl_locale_xhtml ($ id )
1372{
1373 вернуть p_cached_output ( tpl_localeFN ($ id ));
1374}
1375
1376/**
1377* Добавляет соответствующий путь к имени файла, зависящему от языка
1378*
1379* @param string $ id идентификатор локализованного текста
1380* @return string вики-текст
1381*/
1382функция tpl_localeFN ($ id )
1383{
1384 $ path = tpl_incdir () . 'lang/' ;
1385 глобальная $ conf ;
1386 $ file = DOKU_CONF.'template_lang /'.$ conf [ 'template' ] . '/' .$ conf [ ' lang' ]. '/' .$ id . '.txt' ;
1387 если (! file_exists ($ file )) {
1388 $ file = $ path .$ conf [ 'lang' ]. '/' .$ id . '.txt' ;
1389 если (! file_exists ($ file )) {
1390 //возвращаемся к английскому
1391 $ file = $ path . 'en/' . $ id . '.txt' ;
1392 }
1393 }
1394 вернуть $ файл ;
1395}
1396
1397/**
1398* выводит «основной контент» во всплывающем окне медиаменеджера
1399*
1400* В зависимости от действий пользователя это может быть список
1401* файлы в пространстве имен, диалоговое окно редактирования метаданных или
1402* сообщение о ссылках на страницы
1403*
1404* Разрешено только в mediamanager.php
1405*
1406* @triggers МЕДИАМЕНЕДЖЕР_КОНТЕНТ_ВЫВОД
1407* @param bool $ fromajax - установить true при вызове этой функции через ajax
1408* @param string $ сортировка
1409*
1410* @автор Андреас Гор <andi@splitbrain.org>
1411*/
1412функция tpl_mediaContent ($ fromajax = false , $ sort = 'natural' )
1413{
1414 глобальный $ IMG ;
1415 глобальная $ AUTH ;
1416 глобальный $ INUSE ;
1417 глобальный $ NS ;
1418 глобальный $ JUMPTO ;
1419 /** @var Вход $INPUT */
1420 глобальный $ ВХОД ;
1421
1422 $ do = $ INPUT -> extract ( 'do' )-> str ( 'do' );
1423 если ( in_array ( $ do , [ 'сохранить' , 'отмена' ])) $ do = '' ;
1424
1425 если (!$ сделать ) {
1426 если ($ INPUT -> bool ( 'редактировать' )) {
1427 $ do = 'метаформа' ;
1428 } elseif ( is_array ($ INUSE )) {
1429 $ do = 'filesinuse' ;
1430 } еще {
1431 $ do = 'список_файлов' ;
1432 }
1433 }
1434
1435 // выводим панель содержимого, обернутую в событие.
1436 если (!$ fromajax ) echo '<div id="media__content">' ;
1437 $ данные = [ 'делать' => $ делать ];
1438 $ evt = новое событие ( 'MEDIAMANAGER_CONTENT_OUTPUT' , $ data );
1439 если ($ evt -> advice_before ()) {
1440 $ do = $ data [ 'do' ];
1441 если ($ do == 'filesinuse' ) {
1442 media_filesinuse ($ INUSE , $ IMG );
1443 } elseif ($ do == 'список файлов' ) {
1444 media_filelist ($ NS , $ AUTH , $ JUMPTO , false , $ sort );
1445 } elseif ($ do == 'searchlist' ) {
1446 media_searchlist ($ INPUT -> str ( 'q' ), $ NS , $ AUTH );
1447 } еще {
1448 msg ( 'Неизвестное действие ' . hsc ($ do ), -1 );
1449 }
1450 }
1451 $ evt -> advice_after ();
1452 снято ($ evt );
1453 если (!$ fromajax ) echo '</div>' ;
1454}
1455
1456/**
1457* Печатает центральный столбец в полноэкранном медиа-менеджере
1458* В зависимости от открытой вкладки это может быть список
1459* файлы в пространстве имен, форма загрузки или форма поиска
1460*
1461* @author Катя Арзамасцева <pshns@ukr.net>
1462*/
1463функция tpl_mediaFileList ()
1464{
1465 глобальная $ AUTH ;
1466 глобальный $ NS ;
1467 глобальный $ JUMPTO ;
1468 глобальный $ lang ;
1469 /** @var Вход $INPUT */
1470 глобальный $ ВХОД ;
1471
1472 $ open_tab = $ INPUT -> str ( 'tab_files' );
1473 если (!$ open_tab || ! in_array ($ open_tab , [ 'файлы' , 'загрузка' , 'поиск' ])) $ open_tab = 'файлы' ;
1474 если ($ INPUT -> str ( 'mediado' ) == 'update' ) $ open_tab = 'upload' ;
1475
1476 echo '<h2 class="a11y">' . $ lang [ 'mediaselect' ] . '</h2>' . NL ;
1477
1478 media_tabs_files ($ opened_tab );
1479
1480 echo '<div class="panelHeader">' . NL ;
1481 эхо '<h3>' ;
1482 $ tabTitle = $ NS ?: '[' . $ lang [ 'mediaroot' ] . ']' ;
1483 printf ($ lang [ 'media_' . $ open_tab ], '<strong>' . hsc ($ tabTitle ) . '</strong>' );
1484 эхо '</h3>' . NL ;
1485 если ($ open_tab === 'поиск' || $ open_tab === 'файлы' ) {
1486 параметры_файлов_вкладки_медиа ();
1487 }
1488 эхо '</div>' . NL ;
1489
1490 echo '<div class="panelContent">' . NL ;
1491 если ($ open_tab == 'файлы' ) {
1492 media_tab_files ($ NS , $ AUTH , $ JUMPTO );
1493 } elseif ($ opened_tab == 'загрузить' ) {
1494 media_tab_upload ($ NS , $ AUTH , $ JUMPTO );
1495 } elseif ($ open_tab == 'поиск' ) {
1496 media_tab_search ($ NS , $ AUTH );
1497 }
1498 эхо '</div>' . NL ;
1499}
1500
1501/**
1502* Печатает третий столбец в полноэкранном медиа-менеджере
1503* В зависимости от открытой вкладки это могут быть сведения о
1504* выбранный файл, диалоговое окно редактирования метаданных или
1505* список ревизий файлов
1506*
1507* @param string $ изображение
1508* @param boolean $ rev
1509*
1510* @author Катя Арзамасцева <pshns@ukr.net>
1511*/
1512функция tpl_mediaFileDetails ($ image , $ rev )
1513{
1514 глобальный $ conf , $ DEL , $ lang ;
1515 /** @var Вход $INPUT */
1516 глобальный $ ВХОД ;
1517
1518 $ удалено = (
1519 ! file_exists ( mediaFN ($ image )) &&
1520 file_exists ( mediaMetaFN ($ image , '.changes' )) &&
1521 $ conf [ 'mediarevisions' ]
1522 );
1523 если (!$ image || (! file_exists ( mediaFN ($ image )) && !$ removed ) || $ DEL ) return ;
1524 если ($ rev && ! file_exists ( mediaFN ( $ image , $ rev ))) $ rev = false ;
1525 $ ns = getNS ($ image );
1526 $ do = $ INPUT -> str ( 'mediado' );
1527
1528 $ open_tab = $ INPUT -> str ( 'tab_details' );
1529
1530 $ tab_array = [ 'view' ];
1531 [, $ mime ] = mimetype ($ image );
1532 если ($ mime == ' изображение / jpeg ' ) {
1533 $ tab_array [] = 'редактировать' ;
1534 }
1535 если ($ conf [ 'mediarevisions' ]) {
1536 $ tab_array [] = 'история' ;
1537 }
1538
1539 если (!$ open_tab || ! in_array ($ open_tab , $ tab_array )) $ open_tab = 'view' ;
1540 если ($ INPUT -> bool ( 'редактировать' )) $ open_tab = 'редактировать' ;
1541 если ($ do == 'restore' ) $ open_tab = 'view' ;
1542
1543 media_tabs_details ($ image , $ opened_tab );
1544
1545 echo '<div class="panelHeader"><h3>' ;
1546 [$ ext ] = mimetype ($ image , false );
1547 $ class = preg_replace ( '/[^_\-a-z0-9]+/i' , '_' , $ ext );
1548 $ class = 'выберите медиафайл mf_' . $ class ;
1549
1550 $ атрибуты = $ rev ? [ 'rev' => $ rev ] : [];
1551 $ tabTitle = sprintf (
1552 '<strong><a href="%s" class="%s" title="%s">%s</a></strong>' ,
1553 мл ($ изображение , $ атрибуты ),
1554 $ класс ,
1555 $ lang [ 'mediaview' ],
1556 $ изображение
1557 );
1558 если ($ open_tab === 'view' && $ rev ) {
1559 printf ($ lang [ 'media_viewold' ], $ tabTitle , dformat ($ rev ));
1560 } еще {
1561 printf ($ lang [ 'media_' .$ opened_tab ],$ tabTitle );
1562 }
1563
1564 echo '</h3></div>' . NL ;
1565
1566 echo '<div class="panelContent">' . NL ;
1567
1568 если ($ open_tab == 'view' ) {
1569 media_tab_view ($ image , $ ns , null , $ rev );
1570 } elseif ($ opening_tab == 'edit' && !$ removed ) {
1571 media_tab_edit ($ image , $ ns );
1572 } elseif ($ opened_tab == 'history' && $ conf [ 'mediarevisions' ]) {
1573 media_tab_history ($ image , $ ns );
1574 }
1575
1576 эхо '</div>' . NL ;
1577}
1578
1579/**
1580* выводит дерево пространства имен во всплывающем окне медиаменеджера
1581*
1582* Разрешено только в mediamanager.php
1583*
1584* @автор Андреас Гор <andi@splitbrain.org>
1585*/
1586функция tpl_mediaTree ()
1587{
1588 глобальный $ NS ;
1589 эхо '<div id="media__tree">' ;
1590 media_nstree ($ NS );
1591 эхо '</div>' ;
1592}
1593
1594/**
1595* Распечатать выпадающее меню со всеми действиями DokuWiki
1596*
1597* Примечание: здесь не будут использоваться красивые URL-адреса.
1598*
1599* @param string $ пусто пустая метка параметра
1600* @param string $ кнопка подписи кнопки отправки
1601*
1602* @автор Андреас Гор <andi@splitbrain.org>
1603* @deprecated 2017-09-01 см. devel:menus
1604*/
1605функция tpl_actiondropdown ($ empty = '' , $ button = '>' )
1606{
1607 dbg_deprecated ( 'см. devel:menus' );
1608 $ menu = new MobileMenu ();
1609 echo $ menu -> getDropdown ($ empty , $ button );
1610}
1611
1612/**
1613* Распечатать информационную строку об использованной лицензии
1614*
1615* @param string $ img распечатать изображение? (|кнопка|значок)
1616* @param bool $ imgonly пропустить текстовое описание?
1617* @param bool $ return, если true, не печатать, а возвращать HTML
1618* @param bool $ обернуть в div с class="license"?
1619* @возвращаемая строка
1620*
1621* @автор Андреас Гор <andi@splitbrain.org>
1622*/
1623функция tpl_license ($ img = 'badge' , $ imgonly = false , $ return = false , $ wrap = true )
1624{
1625 глобальная лицензия $ ;
1626 глобальная $ conf ;
1627 глобальный $ lang ;
1628 если (!$ conf [ 'license' ]) вернуть '' ;
1629 если (! is_array ($ license [$ conf [ 'license' ]])) вернуть '' ;
1630 $ lic = $ license [$ conf [ 'license' ]];
1631 $ target = ($ conf [ 'target' ][ 'extern' ]) ? ' target="' . $ conf [ 'target' ][ 'extern' ] . '"' : '' ;
1632
1633 $ out = '' ;
1634 если ($ wrap ) $ out .= '<div class="license">' ;
1635 если ($ img ) {
1636 $ src = license_img ($ img );
1637 если ($ ист ) {
1638 $ out .= '<a href="' . $ lic [ 'url' ] . '" rel="лицензия"' . $ target ;
1639 $ out .= '><img src="' . DOKU_BASE . $ src . '" alt="' . $ lic [ 'name' ] . '" /></a>' ;
1640 если (!$ imgonly ) $ out .= ' ' ;
1641 }
1642 }
1643 если (!$ imgonly ) {
1644 $ out .= $ lang [ 'license' ] . ' ' ;
1645 $ out .= '<bdi><a href="' . $ lic [ 'url' ] . '" rel="license" class="urlextern"' . $ target ;
1646 $ out .= '>' . $ lic [ 'имя' ] . '</a></bdi>' ;
1647 }
1648 если ($ wrap ) $ out .= '</div>' ;
1649
1650 если ($ return ) вернуть $ out ;
1651 вывести $ ;
1652 возвращаться '' ;
1653}
1654
1655/**
1656* Включает визуализированный HTML-код указанной страницы
1657*
1658* Эта функция полезна для заполнения боковых панелей или подобных функций в
1659* шаблон
1660*
1661* @param string $ pageid Имя страницы, которую вы хотите включить
1662* @param bool $ print Следует ли печатать содержимое или только возвращать его
1663* @param bool $ propagate Искать также и в более высоких пространствах имен?
1664* @param bool $ useacl Включать страницу только в том случае, если списки контроля доступа проверены?
1665* @return bool | null | строка
1666*/
1667функция tpl_include_page ($ pageid , $ print = true , $ propagate = false , $ useacl = true )
1668{
1669 если ($ распространять ) {
1670 $ pageid = page_findnearest ($ pageid , $ useacl );
1671 } elseif ($ useacl && auth_quickaclcheck ($ pageid ) == AUTH_NONE ) {
1672 вернуть ложь ;
1673 }
1674 если (!$ pageid ) вернуть false ;
1675
1676 глобальный $ TOC ;
1677 $ oldtoc = $ TOC ;
1678 $ html = p_wiki_xhtml ($ pageid , '' , false );
1679 $ TOC = $ oldtoc ;
1680
1681 если ($ print ) echo $ html ;
1682 вернуть $ html ;
1683}
1684
1685/**
1686* Отобразить форму подписки
1687*
1688* @автор Адриан Лэнг <lang@cosmocode.de>
1689* @устаревший 2020-07-23
1690*/
1691функция tpl_subscribe ()
1692{
1693 dbg_deprecated ( Подписаться :: класс . '::show()' );
1694 ( новая Подписка ())-> показать ();
1695}
1696
1697/**
1698* Пытается отправить уже созданный контент прямо в браузер
1699*
1700* Оборачивает ob_flush() и flush()
1701*
1702* @автор Андреас Гор <andi@splitbrain.org>
1703*/
1704функция tpl_flush ()
1705{
1706 если ( ob_get_level () > 0 ) ob_flush ();
1707 румянец ();
1708}
1709
1710/**
1711* Пытается найти файл ресурсов в указанных местах.
1712*
1713* Если указанное местоположение начинается с двоеточия, предполагается, что это медиа
1714* файл, в противном случае предполагается, что он относится к текущему шаблону
1715*
1716* @параметр строка []$ поиск мест для просмотра
1717* @param bool $ abs , если использовать абсолютный URL
1718* @param массив &$imginfo заполнен с помощью getimagesize()
1719* @param bool $ fallback использовать резервное изображение, если цель не найдена, или вернуть «false», если она потенциальная
1720* требуется ложный результат
1721* @возвращаемая строка
1722*
1723* @автор Андреас Гор <andi@splitbrain.org>
1724*/
1725функция tpl_getMediaFile ($ search , $ abs = false , &$ imginfo = null , $ fallback = true )
1726{
1727 $ img = '' ;
1728 $ файл = '' ;
1729 $ ismedia = false ;
1730 // перебираем кандидатов, пока не будет найдено совпадение:
1731 foreach ($ поиск как $ img ) {
1732 если ( str_starts_with ($ img , ':' )) {
1733 $ file = mediaFN ($ img );
1734 $ ismedia = true ;
1735 } еще {
1736 $ file = tpl_incdir () . $ img ;
1737 $ ismedia = false ;
1738 }
1739
1740 если ( file_exists ($ file )) прерывание ;
1741 }
1742
1743 // управлять несуществующей целью
1744 если (! file_exists ($ file )) {
1745 // дать результат для резервного изображения
1746 если ($ откат ) {
1747 $ file = DOKU_INC . ' lib / images / blank.gif ' ;
1748 // остановить процесс, если требуется ложный результат (если $fallback равен false)
1749 } еще {
1750 вернуть ложь ;
1751 }
1752 }
1753
1754 // извлечь данные изображения, если требуется
1755 если (! is_null ($ imginfo )) {
1756 $ imginfo = getimagesize ($ file );
1757 }
1758
1759 // создать URL
1760 если ($ ismedia ) {
1761 $ url = ml ($ img , '' , true , '' , $ abs );
1762 } еще {
1763 $ url = tpl_basedir (). $ изображение ;
1764 если ($ abs ) $ url = DOKU_URL.substr ( $ url , strlen ( DOKU_REL ))
;1765 }
1766
1767 вернуть $ url ;
1768}
1769
1770/**
1771* PHP включает файл
1772*
1773* либо из каталога conf, если он существует, в противном случае используйте
1774* файл в корневом каталоге шаблона.
1775*
1776* Функция учитывает настройки каскада конфигураций и ищет заданные
1777* файл рядом с «основными» файлами конфигурации, в порядке защищенный, локальный,
1778* по умолчанию.
1779*
1780* Примечание: здесь не выполняется экранирование или проверка на работоспособность. Никогда не передавайте пользовательский ввод
1781* к этой функции!
1782*
1783* @param string $ файл
1784*
1785* @автор Андреас Гор <andi@splitbrain.org>
1786* @автор Аника Хенке <anika@selfthinker.org>
1787*/
1788функция tpl_includeFile ($ файл )
1789{
1790 глобальный $ config_cascade ;
1791 foreach ([ 'protected' , 'local' , 'default' ] как $ config_group ) {
1792 если ( пусто ($ config_cascade [ 'main' ][$ config_group ])) продолжить ;
1793 foreach ($ config_cascade [ 'main' ][$ config_group ] как $ conf_file ) {
1794 $ dir = имя_каталога ($ conf_file );
1795 если ( file_exists ( " $ dir / $ file " )) {
1796 include ( " $ dir / $ file " );
1797 возвращаться ;
1798 }
1799 }
1800 }
1801
1802 // все еще здесь? попробуйте шаблон dir
1803 $ file = tpl_incdir () . $ file ;
1804 если ( file_exists ($ file )) {
1805 включить ($ файл );
1806 }
1807}
1808
1809/**
1810* Возвращает тег <link> для различных типов иконок (favicon|mobile|generic)
1811*
1812* @param array $ types - список типов иконок для отображения (favicon|mobile|generic)
1813* @возвращаемая строка
1814*
1815* @автор Аника Хенке <anika@selfthinker.org>
1816*/
1817функция tpl_favicon ($ types = [ 'favicon' ])
1818{
1819
1820 $ return = '' ;
1821
1822 foreach ($ типы как $ тип ) {
1823 переключатель ($ тип ) {
1824 случай 'favicon' :
1825 $ look = [ ':wiki:favicon.ico' , ':favicon.ico' , ' изображения / favicon.ico ' ];
1826 $ return .= '<link rel="значок ярлыка" href="' . tpl_getMediaFile ($ look ) . '" />' . NL ;
1827 перерыв ;
1828 случай «мобильный» :
1829 $ look = [ ':wiki:apple-touch-icon.png' , ':apple-touch-icon.png' , ' изображения / apple-touch-icon.png ' ];
1830 $ return .= '<link rel="apple-touch-icon" href="' . tpl_getMediaFile ($ look ) . '" />' . NL ;
1831 перерыв ;
1832 случай «общий» :
1833 // идеальное решение, которое пока не работает ни в одном браузере
1834 $ look = [ ':wiki:favicon.svg' , ':favicon.svg' , ' изображения / favicon.svg ' ];
1835 $ return .= '<link rel="icon" href="' . tpl_getMediaFile ($ look ) . '" type=" image / svg +xml" />' . NL ;
1836 перерыв ;
1837 }
1838 }
1839
1840 возврат $ возврат ;
1841}
1842
1843/**
1844* Печать полноэкранного медиа-менеджера
1845*
1846* @author Катя Арзамасцева <pshns@ukr.net>
1847*/
1848функция tpl_media ()
1849{
1850 глобальные $ NS , $ IMG , $ JUMPTO , $ REV , $ lang , $ fullscreen , $ INPUT ;
1851 $ полноэкранный = правда ;
1852 require_once DOKU_INC . ' lib / exe / mediamanager.php ' ;
1853
1854 $ rev = '' ;
1855 $ image = cleanID ($ INPUT -> str ( 'image' ));
1856 если ( isset ($ IMG )) $ image = $ IMG ;
1857 если ( isset ($ JUMPTO )) $ image = $ JUMPTO ;
1858 если ( isset ($ REV ) && !$ JUMPTO ) $ rev = $ REV ;
1859
1860 echo '<div id="mediamanager__page">' . NL ;
1861 эхо '<h1>' . $ язык [ 'btn_media' ] . '</h1>' . НЛ ;
1862 html_msgarea ();
1863
1864 echo '<div class="panel namespaces">' . NL ;
1865 echo '<h2>' . $ lang [ 'пространства имен' ] . '</h2>' . NL ;
1866 echo '<div class="panelHeader">' ;
1867 echo $ lang [ 'media_namespaces' ];
1868 эхо '</div>' . NL ;
1869
1870 echo '<div class="panelContent" id="media__tree">' . NL ;
1871 media_nstree ($ NS );
1872 эхо '</div>' . NL ;
1873 эхо '</div>' . NL ;
1874
1875 echo '<div class="panel filelist">' . НЛ ;
1876 tpl_mediaFileList ();
1877 эхо '</div>' . NL ;
1878
1879 echo '<div class="panel file">' . NL ;
1880 echo '<h2 class="a11y">' . $ lang [ 'media_file' ] . '</h2>' . NL ;
1881 tpl_mediaFileDetails ($ image , $ rev );
1882 эхо '</div>' . NL ;
1883
1884 эхо '</div>' . NL ;
1885}
1886
1887/**
1888* Возвращаем полезные классы макета
1889*
1890* @возвращаемая строка
1891*
1892* @автор Аника Хенке <anika@selfthinker.org>
1893*/
1894функция tpl_classes ()
1895{
1896 глобальные $ ACT , $ conf , $ ID , $ INFO ;
1897 /** @var Вход $INPUT */
1898 глобальный $ ВХОД ;
1899
1900 $ классы = [
1901 'dokuwiki' ,
1902 'mode_' . $ ACT ,
1903 'tpl_' . $ conf [ 'шаблон' ],
1904 $ INPUT -> сервер -> bool ( 'REMOTE_USER' ) ? 'loggedIn' : '' ,
1905 ( isset ($ INFO [ 'существует' ]) && $ INFO [ 'существует' ]) ? '' : 'notFound' ,
1906 ($ ID == $ conf [ 'start' ]) ? 'home' : ''
1907 ];
1908 return implode ( ' ' , $ classes );
1909}
1910
1911/**
1912* Создать событие для меню инструментов
1913*
1914* @param string $ toolsname имя меню
1915* @param массив $ элементы
1916* @param string $ view например 'main', 'detail', ...
1917*
1918* @автор Аника Хенке <anika@selfthinker.org>
1919* @deprecated 2017-09-01 см. devel:menus
1920*/
1921функция tpl_toolsevent ($ toolsname , $ items , $ view = 'main' )
1922{
1923 dbg_deprecated ( 'см. devel:menus' );
1924 $ data = [ 'view' => $ view , 'items' => $ items ];
1925
1926 $ hook = 'TEMPLATE_' . strtoupper ($ toolsname ) . '_DISPLAY' ;
1927 $ evt = новое событие ($ hook , $ data );
1928 если ($ evt -> advice_before ()) {
1929 foreach ($ evt -> data [ 'items' ] as $ html ) echo $ html ;
1930 }
1931 $ evt -> advice_after ();
1932}
1933