1<?php
2/**
3 * Template Functions
4 *
5 * This file provides template specific custom functions that are
6 * not provided by the DokuWiki core.
7 * It is common practice to start each function with an underscore
8 * to make sure it won't interfere with future core functions.
9 */
10
11// Must be run within DokuWiki
12if (!defined('DOKU_INC')) die();
13
14/**
15 * Font-family stack and @font-face stylesheet for the template font setting (conf/metadata.php).
16 *
17 * @return array{sheet:string,stack:string}
18 */
19function _tpl_font_config() {
20	$key = tpl_getConf('font');
21	$configs = array(
22		'0xproto' => array(
23			'sheet' => '0xproto.css',
24			'stack' => '"0xProto Nerd Font Mono", "0xProto Nerd Font", monospace',
25		),
26		'3270' => array(
27			'sheet' => '3270.css',
28			'stack' => '"3270 Nerd Font Mono", "3270 Nerd Font", monospace',
29		),
30		'agave' => array(
31			'sheet' => 'agave.css',
32			'stack' => '"Agave Nerd Font Mono", "Agave Nerd Font", monospace',
33		),
34		'anonymouspro' => array(
35			'sheet' => 'anonymouspro.css',
36			'stack' => '"AnonymousPro Nerd Font Mono", "AnonymousPro Nerd Font", monospace',
37		),
38		'arimo' => array(
39			'sheet' => 'arimo.css',
40			'stack' => '"Arimo Nerd Font Mono", "Arimo Nerd Font", monospace',
41		),
42		'aurulentsansmono' => array(
43			'sheet' => 'aurulentsansmono.css',
44			'stack' => '"AurulentSansMono Nerd Font Mono", "AurulentSansMono Nerd Font", monospace',
45		),
46		'bigblueterminal' => array(
47			'sheet' => 'bigblueterminal.css',
48			'stack' => '"BigBlueTermPlus Nerd Font Mono", "BigBlueTermPlus Nerd Font", monospace',
49		),
50		'bigblueterminal437' => array(
51			'sheet' => 'bigblueterm437-nerd-font.css',
52			'stack' => '"BigBlueTerm437 Nerd Font Mono", "BigBlueTerm437 Nerd Font", monospace',
53		),
54		'bitstreamverasansmono' => array(
55			'sheet' => 'bitstreamverasansmono.css',
56			'stack' => '"BitstromWera Nerd Font Mono", "BitstromWera Nerd Font", monospace',
57		),
58		'blexmono' => array(
59			'sheet' => 'blexmono-nerd-font-mono.css',
60			'stack' => '"BlexMono Nerd Font", "BlexMono Nerd Font", monospace',
61		),
62		'cascadiacode' => array(
63			'sheet' => 'cascadiacode.css',
64			'stack' => '"CaskaydiaCove NF", monospace',
65		),
66		'cascadiamono' => array(
67			'sheet' => 'cascadiamono.css',
68			'stack' => '"CaskaydiaMono NF", monospace',
69		),
70		'codenewroman' => array(
71			'sheet' => 'codenewroman.css',
72			'stack' => '"CodeNewRoman Nerd Font Mono", "CodeNewRoman Nerd Font", monospace',
73		),
74		'comicshannsmono' => array(
75			'sheet' => 'comicshannsmono.css',
76			'stack' => '"ComicShannsMono Nerd Font Mono", "ComicShannsMono Nerd Font", monospace',
77		),
78		'commitmono' => array(
79			'sheet' => 'commitmono.css',
80			'stack' => '"CommitMono Nerd Font Mono", "CommitMono Nerd Font", monospace',
81		),
82		'cousine' => array(
83			'sheet' => 'cousine.css',
84			'stack' => '"Cousine Nerd Font Mono", "Cousine Nerd Font", monospace',
85		),
86		'd2coding' => array(
87			'sheet' => 'd2coding.css',
88			'stack' => '"D2CodingLigature Nerd Font Mono", "D2CodingLigature Nerd Font", monospace',
89		),
90		'daddytimemono' => array(
91			'sheet' => 'daddytimemono.css',
92			'stack' => '"DaddyTimeMono Nerd Font Mono", "DaddyTimeMono Nerd Font", monospace',
93		),
94		'dejavusansmono' => array(
95			'sheet' => 'dejavusansmono.css',
96			'stack' => '"DejaVuSansM Nerd Font Mono", "DejaVuSansM Nerd Font", monospace',
97		),
98		'departuremono' => array(
99			'sheet' => 'departuremono.css',
100			'stack' => '"DepartureMono Nerd Font Mono", "DepartureMono Nerd Font", monospace',
101		),
102		'droidsansmono' => array(
103			'sheet' => 'droidsansmono.css',
104			'stack' => '"DroidSansM Nerd Font Mono", "DroidSansM Nerd Font", monospace',
105		),
106		'envycoder' => array(
107			'sheet' => 'envycoder.css',
108			'stack' => '"EnvyCodeR Nerd Font Mono", "EnvyCodeR Nerd Font", monospace',
109		),
110		'fantasquesansmono' => array(
111			'sheet' => 'fantasquesansmono.css',
112			'stack' => '"FantasqueSansM Nerd Font Mono", "FantasqueSansM Nerd Font", monospace',
113		),
114		'firacode' => array(
115			'sheet' => 'firacode.css',
116			'stack' => '"FiraCode Nerd Font Mono", "FiraCode Nerd Font", monospace',
117		),
118		'firamono' => array(
119			'sheet' => 'firamono.css',
120			'stack' => '"FiraMono Nerd Font Mono", "FiraMono Nerd Font", monospace',
121		),
122		'geistmono' => array(
123			'sheet' => 'geistmono.css',
124			'stack' => '"GeistMono Nerd Font Mono", "GeistMono Nerd Font", monospace',
125		),
126		'gohu' => array(
127			'sheet' => 'gohu.css',
128			'stack' => '"GohuFont 11 Nerd Font Mono", "GohuFont 11 Nerd Font", monospace',
129		),
130		'go-mono' => array(
131			'sheet' => 'go-mono.css',
132			'stack' => '"Go-Mono Nerd Font Mono", "Go-Mono Nerd Font", monospace',
133		),
134		'hack' => array(
135			'sheet' => 'hack.css',
136			'stack' => '"Hack Nerd Font Mono", "Hack Nerd Font", monospace',
137		),
138		'hasklig' => array(
139			'sheet' => 'hasklig.css',
140			'stack' => '"Hasklug Nerd Font Mono", "Hasklug Nerd Font", monospace',
141		),
142		'heavydata' => array(
143			'sheet' => 'heavydata.css',
144			'stack' => '"HeavyData Nerd Font", fantasy',
145		),
146		'hermit' => array(
147			'sheet' => 'hermit.css',
148			'stack' => '"Hurmit Nerd Font Mono", "Hurmit Nerd Font", monospace',
149		),
150		'ia-writer' => array(
151			'sheet' => 'ia-writer.css',
152			'stack' => '"iMWritingDuo Nerd Font Mono", "iMWritingDuo Nerd Font", monospace',
153		),
154		'ibmplexmono' => array(
155			'sheet' => 'ibmplexmono.css',
156			'stack' => '"BlexMono Nerd Font Mono", "BlexMono Nerd Font", monospace',
157		),
158		'inconsolata' => array(
159			'sheet' => 'inconsolata.css',
160			'stack' => '"Inconsolata Nerd Font Mono", "Inconsolata Nerd Font", monospace',
161		),
162		'inconsolatago' => array(
163			'sheet' => 'inconsolatago.css',
164			'stack' => '"InconsolataGo Nerd Font Mono", "InconsolataGo Nerd Font", monospace',
165		),
166		'inconsolatalgc' => array(
167			'sheet' => 'inconsolatalgc.css',
168			'stack' => '"Inconsolata LGC Nerd Font Mono", "Inconsolata LGC Nerd Font", monospace',
169		),
170		'intelonemono' => array(
171			'sheet' => 'intelonemono.css',
172			'stack' => '"IntoneMono NF", monospace',
173		),
174		'iosevka' => array(
175			'sheet' => 'iosevka.css',
176			'stack' => '"Iosevka NFM", "Iosevka NF", monospace',
177		),
178		'iosevkaterm' => array(
179			'sheet' => 'iosevkaterm.css',
180			'stack' => '"IosevkaTerm NFM", "IosevkaTerm NF", monospace',
181		),
182		'iosevkatermslab' => array(
183			'sheet' => 'iosevkatermslab.css',
184			'stack' => '"IosevkaTermSlab NFM", "IosevkaTermSlab NF", monospace',
185		),
186		'jetbrainsmono' => array(
187			'sheet' => 'jetbrainsmono.css',
188			'stack' => '"JetBrainsMonoNL NFM", "JetBrainsMonoNL NF", monospace',
189		),
190		'lekton' => array(
191			'sheet' => 'lekton.css',
192			'stack' => '"Lekton Nerd Font Mono", "Lekton Nerd Font", monospace',
193		),
194		'liberationmono' => array(
195			'sheet' => 'liberationmono.css',
196			'stack' => '"LiberationMono Nerd Font Mono", "LiberationMono Nerd Font", monospace',
197		),
198		'lilex' => array(
199			'sheet' => 'lilex.css',
200			'stack' => '"Lilex Nerd Font Mono", "Lilex Nerd Font", monospace',
201		),
202		'mplus' => array(
203			'sheet' => 'mplus.css',
204			'stack' => '"M+1Code Nerd Font Mono", "M+1Code Nerd Font", monospace',
205		),
206		'martianmono' => array(
207			'sheet' => 'martianmono.css',
208			'stack' => '"MartianMono NFM", "MartianMono NF", monospace',
209		),
210		'meslo' => array(
211			'sheet' => 'meslo.css',
212			'stack' => '"MesloLGLDZ Nerd Font Mono", "MesloLGLDZ Nerd Font Font", monospace',
213		),
214		'monaspace' => array(
215			'sheet' => 'monaspace.css',
216			'stack' => '"MonaspiceAr Nerd Font Mono", "MonaspiceAr Nerd Font", monospace',
217		),
218		'monofur' => array(
219			'sheet' => 'monofur.css',
220			'stack' => '"Monofur Nerd Font Mono", "Monofur Nerd Font", monospace',
221		),
222		'monoid' => array(
223			'sheet' => 'monoid.css',
224			'stack' => '"Monoid Nerd Font Mono", "Monoid Nerd Font", monospace',
225		),
226		'mononoki' => array(
227			'sheet' => 'mononoki.css',
228			'stack' => '"Mononoki Nerd Font Mono", "Mononoki Nerd Font", monospace',
229		),
230		'nerdfontssymbolsonly' => array(
231			'sheet' => 'nerdfontssymbolsonly.css',
232			'stack' => '"Symbols Nerd Font Mono", "Symbols Nerd Font", monospace',
233		),
234		'noto' => array(
235			'sheet' => 'noto.css',
236			'stack' => '"NotoMono NFM", "NotoMono NF", monospace',
237		),
238		'opendyslexic' => array(
239			'sheet' => 'opendyslexic.css',
240			'stack' => '"OpenDyslexicM Nerd Font Mono", "OpenDyslexicM Nerd Font", monospace',
241		),
242		'overpass' => array(
243			'sheet' => 'overpass.css',
244			'stack' => '"OverpassM Nerd Font Mono", "OverpassM Nerd Font", monospace',
245		),
246		'profont' => array(
247			'sheet' => 'profont.css',
248			'stack' => '"ProFont IIx Nerd Font Mono", "ProFont IIx Nerd Font", monospace',
249		),
250		'proggyclean' => array(
251			'sheet' => 'proggyclean.css',
252			'stack' => '"ProggyClean Nerd Font Mono", "ProggyClean Nerd Font", monospace',
253		),
254		'recursive' => array(
255			'sheet' => 'recursive.css',
256			'stack' => '"RecMonoCasual Nerd Font Mono", "RecMonoCasual Nerd Font", monospace',
257		),
258		'robotomono' => array(
259			'sheet' => 'robotomono.css',
260			'stack' => '"RobotoMono Nerd Font Mono", "RobotoMono Nerd Font", monospace',
261		),
262		'sharetechmono' => array(
263			'sheet' => 'sharetechmono.css',
264			'stack' => '"ShureTechMono Nerd Font Mono", "ShureTechMono Nerd Font", monospace',
265		),
266		'sourcecodepro' => array(
267			'sheet' => 'sourcecodepro.css',
268			'stack' => '"SauceCodePro NFM", "SauceCodePro NF", monospace',
269		),
270		'spacemono' => array(
271			'sheet' => 'spacemono.css',
272			'stack' => '"SpaceMono Nerd Font Mono", "SpaceMono Nerd Font", monospace',
273		),
274		'terminus' => array(
275			'sheet' => 'terminus.css',
276			'stack' => '"Terminess Nerd Font Mono", "Terminess Nerd Font", monospace',
277		),
278		'tinos' => array(
279			'sheet' => 'tinos.css',
280			'stack' => '"Tinos Nerd Font", serif',
281		),
282		'ubuntu' => array(
283			'sheet' => 'ubuntu.css',
284			'stack' => '"Ubuntu Nerd Font", sans-serif',
285		),
286		'ubuntumono' => array(
287			'sheet' => 'ubuntumono.css',
288			'stack' => '"UbuntuMono Nerd Font Mono", "UbuntuMono Nerd Font", monospace',
289		),
290		'ubuntusans' => array(
291			'sheet' => 'ubuntusans.css',
292			'stack' => '"UbuntuSansMono NFM", "UbuntuSansMono NF", monospace',
293		),
294		'victormono' => array(
295			'sheet' => 'victormono.css',
296			'stack' => '"VictorMono NFM", "VictorMono NF", monospace',
297		),
298		'zedmono' => array(
299			'sheet' => 'zedmono.css',
300			'stack' => '"ZedMono NFM", "ZedMono NF", monospace',
301		),
302	);
303	return isset($configs[$key]) ? $configs[$key] : $configs['firacode'];
304}
305
306/**
307 * Filesystem path to this template directory (trailing slash).
308 *
309 * @return string
310 */
311function _tpl_fsdir() {
312	return dirname(__FILE__) . '/';
313}
314
315/**
316 * Return a DokuWiki HTTP client when available (compatible with old and new cores).
317 *
318 * @return \dokuwiki\HTTP\DokuHTTPClient|DokuHTTPClient|null
319 */
320function _tpl_http_client() {
321	if (class_exists('\\dokuwiki\\HTTP\\DokuHTTPClient')) {
322		return new \dokuwiki\HTTP\DokuHTTPClient();
323	}
324	if (class_exists('DokuHTTPClient')) {
325		return new DokuHTTPClient();
326	}
327	$legacy = DOKU_INC . 'inc/HTTPClient.php';
328	if (is_readable($legacy)) {
329		require_once $legacy;
330		return new DokuHTTPClient();
331	}
332	return null;
333}
334
335/**
336 * Download missing .woff2 files for the selected font (local CDN mode only).
337 *
338 * Only fetches the files referenced by the active @font-face sheet (~few MB),
339 * not the full Nerd Fonts bundle.
340 */
341function _tpl_ensure_local_fonts($sheet) {
342	$css_path = _tpl_fsdir() . 'assets/nerd-fonts/' . $sheet;
343	if (!is_readable($css_path)) return;
344
345	$css = file_get_contents($css_path);
346	if ($css === false || !preg_match_all('/url\("fonts\/([^"]+\.woff2)"\)/', $css, $matches)) return;
347
348	$fonts_dir = _tpl_fsdir() . 'assets/nerd-fonts/fonts/';
349	if (!function_exists('io_mkdir_p') || !function_exists('io_saveFile')) return;
350	io_mkdir_p($fonts_dir);
351
352	$http = _tpl_http_client();
353	if (!$http) return;
354	$http->timeout = 60;
355	$http->max_bodysize = 0;
356
357	$base_url = 'https://cdn.jsdelivr.net/gh/mshaugh/nerdfont-webfonts/build/fonts/';
358	foreach (array_unique($matches[1]) as $file) {
359		$dest = $fonts_dir . $file;
360		if (is_file($dest) && filesize($dest) > 0) continue;
361
362		$data = $http->get($base_url . rawurlencode($file));
363		if ($data !== false && strlen($data) > 128) {
364			io_saveFile($dest, $data);
365		}
366	}
367}
368
369/**
370 * Loads the glyph @font-face sheet and outputs font-family rules (after tpl_metaheaders()).
371 */
372function _tpl_font_headers() {
373	$cfg = _tpl_font_config();
374	$cdn = tpl_getConf('cdn');
375
376	switch ($cdn) {
377		case 'github':
378			$stylesheet_url = 'https://mshaugh.github.io/nerdfont-webfonts/build/' . $cfg['sheet'];
379			break;
380		case 'jsdelivr':
381			$stylesheet_url = 'https://cdn.jsdelivr.net/gh/mshaugh/nerdfont-webfonts/build/' . $cfg['sheet'];
382			break;
383		case 'local':
384			_tpl_ensure_local_fonts($cfg['sheet']);
385			$stylesheet_url = tpl_basedir() . 'assets/nerd-fonts/' . $cfg['sheet'];
386			break;
387		default:
388			$stylesheet_url = tpl_basedir() . 'assets/nerd-fonts/' . $cfg['sheet'];
389			break;
390	}
391
392	echo '<link rel="stylesheet" href="' . hsc($stylesheet_url) . '" />' . PHP_EOL;
393	echo '<style type="text/css">' . PHP_EOL
394		. 'body,' . PHP_EOL
395		. 'input,' . PHP_EOL
396		. 'textarea,' . PHP_EOL
397		. 'select,' . PHP_EOL
398		. 'button,' . PHP_EOL
399		. '.hacker-button,' . PHP_EOL
400		. 'pre,' . PHP_EOL
401		. 'code,' . PHP_EOL
402		. 'kbd, samp,' . PHP_EOL
403		. '#mediamanager__page h2,' . PHP_EOL
404		. '#mediamanager__page h3' . PHP_EOL
405		. '{ font-family: ' . $cfg['stack'] . '; }' . PHP_EOL
406		. "</style>";
407}
408
409function _tpl_usertools() {
410	/* The optional second parameter of tpl_action() switches between a link and a button,
411	 e.g. a button inside a <li> would be: tpl_action('edit', 0, 'li') */
412	tpl_toolsevent('usertools', array(
413		'admin'     => tpl_action('admin', 1, 'li', 1),
414		'profile'   => tpl_action('profile', 1, 'li', 1),
415		'register'  => tpl_action('register', 1, 'li', 1),
416		'login'     => tpl_action('login', 1, 'li', 1),
417	));
418}
419
420function _tpl_sitetools() {
421	tpl_toolsevent('sitetools', array(
422		'recent'    => tpl_action('recent', 1, 'li', 1),
423		'media'     => tpl_action('media', 1, 'li', 1),
424		'index'     => tpl_action('index', 1, 'li', 1),
425	));
426}
427
428function _tpl_pagetools() {
429	tpl_toolsevent('pagetools', array(
430		'edit'      => tpl_action('edit', 1, 'li', 1),
431		'revisions' => tpl_action('revisions', 1, 'li', 1),
432		'backlink'  => tpl_action('backlink', 1, 'li', 1),
433		'subscribe' => tpl_action('subscribe', 1, 'li', 1),
434		'revert'    => tpl_action('revert', 1, 'li', 1),
435		'top'       => tpl_action('top', 1, 'li', 1),
436	));
437}
438
439function _tpl_detailtools() {
440	echo tpl_action('mediaManager', 1, 'li', 1);
441	echo tpl_action('img_backto', 1, 'li', 1);
442}
443