Ajout : ./garradin
[garradin.git] / include / lib.template.php
1 <?php
2
3 namespace Garradin;
4
5 require_once ROOT . '/include/libs/template_lite/class.template.php';
6
7 class Template extends \Template_Lite
8 {
9 static protected $_instance = null;
10
11 static public function getInstance()
12 {
13 return self::$_instance ?: self::$_instance = new Template;
14 }
15
16 private function __clone()
17 {
18 }
19
20 public function __construct()
21 {
22 parent::__construct();
23
24 $this->cache = false;
25
26 $this->compile_dir = DATA_ROOT . '/cache/compiled';
27 $this->template_dir = ROOT . '/templates';
28
29 $this->compile_check = true;
30
31 $this->reserved_template_varname = 'tpl';
32
33 $this->assign('www_url', WWW_URL);
34 $this->assign('self_url', utils::getSelfUrl());
35
36 $this->assign('is_logged', false);
37 }
38 }
39
40 $tpl = Template::getInstance();
41
42 function tpl_csrf_field($params)
43 {
44 $name = utils::CSRF_field_name($params['key']);
45 $value = utils::CSRF_create($params['key']);
46
47 return '<input type="hidden" name="'.$name.'" value="'.$value.'" />';
48 }
49
50 function tpl_form_field($params)
51 {
52 if (!isset($params['name']))
53 throw new \BadFunctionCallException('name argument is mandatory');
54
55 $name = $params['name'];
56
57 if (isset($_POST[$name]))
58 $value = $_POST[$name];
59 elseif (isset($params['data']) && isset($params['data'][$name]))
60 $value = $params['data'][$name];
61 elseif (isset($params['default']))
62 $value = $params['default'];
63 else
64 $value = '';
65
66 if (is_array($value))
67 {
68 return $value;
69 }
70
71 if (isset($params['checked']))
72 {
73 if ($value == $params['checked'])
74 return ' checked="checked" ';
75
76 return '';
77 }
78 elseif (isset($params['selected']))
79 {
80 if ($value == $params['selected'])
81 return ' selected="selected" ';
82
83 return '';
84 }
85
86 return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
87 }
88
89 function tpl_format_tel($n)
90 {
91 $n = preg_replace('![^\d\+]!', '', $n);
92
93 if (substr($n, 0, 1) == '+')
94 {
95 $n = preg_replace('!^\+(?:1|2[07]|2\d{2}|3[0-469]|3\d{2}|4[013-9]|'
96 . '4\d{2}|5[1-8]|5\d{2}|6[0-6]|6\d{2}|7\d|8[1-469]|8\d{2}|'
97 . '9[0-58]|9\d{2})!', '\\0 ', $n);
98 }
99 elseif (preg_match('/^\d{10}$/', $n))
100 {
101 $n = preg_replace('!(\d{2})!', '\\1 ', $n);
102 }
103
104 return $n;
105 }
106
107 function tpl_strftime_fr($ts, $format)
108 {
109 return utils::strftime_fr($format, $ts);
110 }
111
112 function tpl_date_fr($ts, $format)
113 {
114 return utils::date_fr($format, $ts);
115 }
116
117 function tpl_format_droits($params)
118 {
119 $droits = $params['droits'];
120
121 $out = ['connexion' => '', 'inscription' => '', 'membres' => '', 'compta' => '',
122 'wiki' => '', 'config' => ''];
123 $classes = [
124 Membres::DROIT_AUCUN => 'aucun',
125 Membres::DROIT_ACCES => 'acces',
126 Membres::DROIT_ECRITURE=> 'ecriture',
127 Membres::DROIT_ADMIN => 'admin',
128 ];
129
130 foreach ($droits as $cle=>$droit)
131 {
132 $cle = str_replace('droit_', '', $cle);
133
134 if (array_key_exists($cle, $out))
135 {
136
137 $class = $classes[$droit];
138 $desc = false;
139 $s = false;
140
141 if ($cle == 'connexion')
142 {
143 if ($droit == Membres::DROIT_AUCUN)
144 $desc = 'N\'a pas le droit de se connecter';
145 else
146 $desc = 'A le droit de se connecter';
147 }
148 elseif ($cle == 'inscription')
149 {
150 if ($droit == Membres::DROIT_AUCUN)
151 $desc = 'N\'a pas le droit de s\'inscrire seul';
152 else
153 $desc = 'A le droit de s\'inscrire seul';
154 }
155 elseif ($cle == 'config')
156 {
157 $s = '&#x2611;';
158
159 if ($droit == Membres::DROIT_AUCUN)
160 $desc = 'Ne peut modifier la configuration';
161 else
162 $desc = 'Peut modifier la configuration';
163 }
164 elseif ($cle == 'compta')
165 {
166 $s = '&euro;';
167 }
168
169 if (!$s)
170 $s = strtoupper($cle[0]);
171
172 if (!$desc)
173 {
174 $desc = ucfirst($cle). ' : ';
175
176 if ($droit == Membres::DROIT_AUCUN)
177 $desc .= 'Pas accès';
178 elseif ($droit == Membres::DROIT_ACCES)
179 $desc .= 'Lecture uniquement';
180 elseif ($droit == Membres::DROIT_ECRITURE)
181 $desc .= 'Lecture & écriture';
182 else
183 $desc .= 'Administration';
184 }
185
186 $out[$cle] = '<b class="'.$class.' '.$cle.'" title="'
187 .htmlspecialchars($desc, ENT_QUOTES, 'UTF-8').'">'.$s.'</b>';
188 }
189 }
190
191 return implode(' ', $out);
192 }
193
194 function tpl_format_wiki($str)
195 {
196 $str = utils::htmlLinksOnUrls($str);
197 $str = utils::htmlSpip($str);
198 $str = utils::htmlGarbage2xhtml($str);
199 return $str;
200 }
201
202 function tpl_liens_wiki($str, $prefix)
203 {
204 return preg_replace_callback('!<a href="([^/.:@]+)">!i', function ($matches) use ($prefix) {
205 return '<a href="' . $prefix . Wiki::transformTitleToURI($matches[1]) . '">';
206 }, $str);
207 }
208
209 function tpl_pagination($params)
210 {
211 if (!isset($params['url']) || !isset($params['page']) || !isset($params['bypage']) || !isset($params['total']))
212 throw new \BadFunctionCallException("Paramètre manquant pour pagination");
213
214 if ($params['total'] == -1)
215 return '';
216
217 $pagination = utils::getGenericPagination($params['page'], $params['total'], $params['bypage']);
218
219 if (empty($pagination))
220 return '';
221
222 $out = '<ul class="pagination">';
223
224 foreach ($pagination as &$page)
225 {
226 $attributes = '';
227
228 if (!empty($page['class']))
229 $attributes .= ' class="' . htmlspecialchars($page['class']) . '" ';
230
231 $out .= '<li'.$attributes.'>';
232
233 $attributes = '';
234
235 if (!empty($page['accesskey']))
236 $attributes .= ' accesskey="' . htmlspecialchars($page['accesskey']) . '" ';
237
238 $out .= '<a' . $attributes . ' href="' . str_replace('[ID]', htmlspecialchars($page['id']), $params['url']) . '">';
239 $out .= htmlspecialchars($page['label']);
240 $out .= '</a>';
241 $out .= '</li>' . "\n";
242 }
243
244 $out .= '</ul>';
245
246 return $out;
247 }
248
249 function tpl_diff($params)
250 {
251 if (!isset($params['old']) || !isset($params['new']))
252 {
253 throw new Template_Exception('Paramètres old et new requis.');
254 }
255
256 $old = $params['old'];
257 $new = $params['new'];
258
259 require_once ROOT . '/include/libs/diff/class.simplediff.php';
260 $diff = \simpleDiff::diff_to_array(false, $old, $new, 3);
261
262 $out = '<table class="diff">';
263 $prev = key($diff);
264
265 foreach ($diff as $i=>$line)
266 {
267 if ($i > $prev + 1)
268 {
269 $out .= '<tr><td colspan="5" class="separator"><hr /></td></tr>';
270 }
271
272 list($type, $old, $new) = $line;
273
274 $class1 = $class2 = '';
275 $t1 = $t2 = '';
276
277 if ($type == \simpleDiff::INS)
278 {
279 $class2 = 'ins';
280 $t2 = '<b class="icn">➕</b>';
281 $old = htmlspecialchars($old, ENT_QUOTES, 'UTF-8');
282 $new = htmlspecialchars($new, ENT_QUOTES, 'UTF-8');
283 }
284 elseif ($type == \simpleDiff::DEL)
285 {
286 $class1 = 'del';
287 $t1 = '<b class="icn">➖</b>';
288 $old = htmlspecialchars($old, ENT_QUOTES, 'UTF-8');
289 $new = htmlspecialchars($new, ENT_QUOTES, 'UTF-8');
290 }
291 elseif ($type == \simpleDiff::CHANGED)
292 {
293 $class1 = 'del';
294 $class2 = 'ins';
295 $t1 = '<b class="icn">➖</b>';
296 $t2 = '<b class="icn">➕</b>';
297
298 $lineDiff = \simpleDiff::wdiff($old, $new);
299 $lineDiff = htmlspecialchars($lineDiff, ENT_QUOTES, 'UTF-8');
300
301 // Don't show new things in deleted line
302 $old = preg_replace('!\{\+(?:.*)\+\}!U', '', $lineDiff);
303 $old = str_replace(' ', ' ', $old);
304 $old = str_replace('-] [-', ' ', $old);
305 $old = preg_replace('!\[-(.*)-\]!U', '<del>\\1</del>', $old);
306
307 // Don't show old things in added line
308 $new = preg_replace('!\[-(?:.*)-\]!U', '', $lineDiff);
309 $new = str_replace(' ', ' ', $new);
310 $new = str_replace('+} {+', ' ', $new);
311 $new = preg_replace('!\{\+(.*)\+\}!U', '<ins>\\1</ins>', $new);
312 }
313 else
314 {
315 $old = htmlspecialchars($old, ENT_QUOTES, 'UTF-8');
316 $new = htmlspecialchars($new, ENT_QUOTES, 'UTF-8');
317 }
318
319 $out .= '<tr>';
320 $out .= '<td class="line">'.($i+1).'</td>';
321 $out .= '<td class="leftChange">'.$t1.'</td>';
322 $out .= '<td class="leftText '.$class1.'">'.$old.'</td>';
323 $out .= '<td class="rightChange">'.$t2.'</td>';
324 $out .= '<td class="rightText '.$class2.'">'.$new.'</td>';
325 $out .= '</tr>';
326
327 $prev = $i;
328 }
329
330 $out .= '</table>';
331 return $out;
332 }
333
334 function tpl_select_compte($params)
335 {
336 $name = $params['name'];
337 $comptes = $params['comptes'];
338 $selected = isset($params['data'][$params['name']]) ? $params['data'][$params['name']] : utils::post($name);
339 $id = isset($params['id']) ? $params['id'] : "f_$name";
340 $form_name = isset($params['form_name']) ? $params['form_name'] : $name;
341
342 $out = '<select name="'.$form_name.'" id="'.$id.'" class="large">';
343
344 foreach ($comptes as $compte)
345 {
346 // Ne pas montrer les comptes désactivés
347 if (!empty($compte['desactive']))
348 continue;
349
350 if (!isset($compte['id'][1]))
351 {
352 $out.= '<optgroup label="'.htmlspecialchars($compte['libelle'], ENT_QUOTES, 'UTF-8', false).'" class="niveau_1"></optgroup>';
353 }
354 elseif (!isset($compte['id'][2]) && empty($params['create']))
355 {
356 if ($compte['id'] > 10)
357 $out.= '</optgroup>';
358
359 $out.= '<optgroup label="'.htmlspecialchars($compte['id'] . ' - ' . $compte['libelle'], ENT_QUOTES, 'UTF-8', false).'" class="niveau_2">';
360 }
361 else
362 {
363 $out .= '<option value="'.htmlspecialchars($compte['id'], ENT_QUOTES, 'UTF-8', false).'" class="niveau_'.strlen($compte['id']).'"';
364
365 if ($selected == $compte['id'])
366 {
367 $out .= ' selected="selected"';
368 }
369
370 $out .= '>' . htmlspecialchars($compte['id'] . ' - ' . $compte['libelle'], ENT_QUOTES, 'UTF-8', false);
371 $out .= '</option>';
372 }
373 }
374
375 $out .= '</optgroup>';
376 $out .= '</select>';
377
378 return $out;
379 }
380
381 function escape_money($number)
382 {
383 return number_format((float)$number, 2, ',', ' ');
384 }
385
386 function tpl_html_money($number)
387 {
388 return '<b class="money">' . escape_money($number) . '</b>';
389 }
390
391 function tpl_html_champ_membre($params)
392 {
393 if (empty($params['config']) || empty($params['name']))
394 throw new \BadFunctionCallException('Paramètres type et name obligatoires.');
395
396 $config = $params['config'];
397 $type = $config['type'];
398
399 if ($params['name'] == 'passe' || (!empty($params['user_mode']) && !empty($config['private'])))
400 {
401 return '';
402 }
403
404 if ($type == 'select')
405 {
406 if (empty($config['options']))
407 throw new \BadFunctionCallException('Paramètre options obligatoire pour champ de type select.');
408 }
409 elseif ($type == 'country')
410 {
411 $type = 'select';
412 $config['options'] = utils::getCountryList();
413 $params['default'] = Config::getInstance()->get('pays');
414 }
415 elseif ($type == 'date')
416 {
417 $params['pattern'] = '\d{4}-\d{2}-\d{2}';
418 }
419 elseif ($type == 'multiple')
420 {
421 if (empty($config['options']))
422 throw new \BadFunctionCallException('Paramètre options obligatoire pour champ de type multiple.');
423 }
424
425 $field = '';
426 $value = tpl_form_field($params);
427 $attributes = 'name="' . htmlspecialchars($params['name'], ENT_QUOTES, 'UTF-8') . '" ';
428 $attributes .= 'id="f_' . htmlspecialchars($params['name'], ENT_QUOTES, 'UTF-8') . '" ';
429
430 if (!empty($params['disabled']))
431 {
432 $attributes .= 'disabled="disabled" ';
433 }
434
435 if (!empty($config['mandatory']))
436 {
437 $attributes .= 'required="required" ';
438 }
439
440 if (!empty($params['user_mode']) && empty($config['editable']))
441 {
442 $out = '<dt>' . htmlspecialchars($config['title'], ENT_QUOTES, 'UTF-8') . '</dt>';
443 $out .= '<dd>' . htmlspecialchars((trim($value) === '' ? 'Non renseigné' : $value), ENT_QUOTES, 'UTF-8') . '</dd>';
444 return $out;
445 }
446
447 if ($type == 'select')
448 {
449 $field .= '<select '.$attributes.'>';
450 foreach ($config['options'] as $k=>$v)
451 {
452 if (is_int($k))
453 $k = $v;
454
455 $field .= '<option value="' . htmlspecialchars($k, ENT_QUOTES, 'UTF-8') . '"';
456
457 if ($value == $k || empty($value) && !empty($params['default']))
458 $field .= ' selected="selected"';
459
460 $field .= '>' . htmlspecialchars($v, ENT_QUOTES, 'UTF-8') . '</option>';
461 }
462 $field .= '</select>';
463 }
464 elseif ($type == 'multiple')
465 {
466 if (is_array($value))
467 {
468 $binary = 0;
469
470 foreach ($value as $k => $v)
471 {
472 if (array_key_exists($k, $config['options']) && !empty($v))
473 {
474 $binary |= 0x01 << $k;
475 }
476 }
477
478 $value = $binary;
479 }
480
481 foreach ($config['options'] as $k=>$v)
482 {
483 $b = 0x01 << (int)$k;
484 $field .= '<label><input type="checkbox" name="'
485 . htmlspecialchars($params['name'], ENT_QUOTES, 'UTF-8') . '[' . (int)$k . ']" value="1" '
486 . (($value & $b) ? 'checked="checked"' : '') . ' ' . $attributes . '/> '
487 . htmlspecialchars($v, ENT_QUOTES, 'UTF-8') . '</label><br />';
488 }
489 }
490 elseif ($type == 'textarea')
491 {
492 $field .= '<textarea ' . $attributes . 'cols="30" rows="5">' . $value . '</textarea>';
493 }
494 else
495 {
496 if ($type == 'checkbox')
497 {
498 if (!empty($value))
499 {
500 $attributes .= 'checked="checked" ';
501 }
502
503 $value = '1';
504 }
505
506 $field .= '<input type="' . $type . '" ' . $attributes . ' value="' . $value . '" />';
507 }
508
509 $out = '
510 <dt>';
511
512 if ($type == 'checkbox')
513 {
514 $out .= $field . ' ';
515 }
516
517 $out .= '<label for="f_' . htmlspecialchars($params['name'], ENT_QUOTES, 'UTF-8') . '">'
518 . htmlspecialchars($config['title'], ENT_QUOTES, 'UTF-8') . '</label>';
519
520 if (!empty($config['mandatory']))
521 {
522 $out .= ' <b title="(Champ obligatoire)">obligatoire</b>';
523 }
524
525 $out .= '</dt>';
526
527 if (!empty($config['help']))
528 {
529 $out .= '
530 <dd class="help">' . htmlspecialchars($config['help'], ENT_QUOTES, 'UTF-8') . '</dd>';
531 }
532
533 if ($type != 'checkbox')
534 {
535 $out .= '
536 <dd>' . $field . '</dd>';
537 }
538
539 return $out;
540 }
541
542 $tpl->register_compiler('continue', function() { return 'continue;'; });
543
544 $tpl->register_function('csrf_field', 'Garradin\tpl_csrf_field');
545 $tpl->register_function('form_field', 'Garradin\tpl_form_field');
546 $tpl->register_function('select_compte', 'Garradin\tpl_select_compte');
547
548 $tpl->register_function('format_droits', 'Garradin\tpl_format_droits');
549
550 $tpl->register_function('pagination', 'Garradin\tpl_pagination');
551
552 $tpl->register_function('diff', 'Garradin\tpl_diff');
553 $tpl->register_function('html_champ_membre', 'Garradin\tpl_html_champ_membre');
554
555 $tpl->register_function('plugin_url', ['Garradin\utils', 'plugin_url']);
556
557 $tpl->register_modifier('get_country_name', ['Garradin\utils', 'getCountryName']);
558 $tpl->register_modifier('format_tel', 'Garradin\tpl_format_tel');
559 $tpl->register_modifier('format_wiki', 'Garradin\tpl_format_wiki');
560 $tpl->register_modifier('liens_wiki', 'Garradin\tpl_liens_wiki');
561 $tpl->register_modifier('escape_money', 'Garradin\escape_money');
562 $tpl->register_modifier('html_money', 'Garradin\tpl_html_money');
563 $tpl->register_modifier('abs', 'abs');
564
565 $tpl->register_modifier('display_champ_membre', function ($v, $config) {
566 if ($config['type'] == 'checkbox') {
567 return $v ? 'Oui' : 'Non';
568 } elseif ($config['type'] == 'email') {
569 return '<a href="mailto:' . $v . '">' . $v . '</a>';
570 } elseif ($config['type'] == 'tel') {
571 return '<a href="tel:' . $v . '">' . $v . '</a>';
572 } elseif ($config['type'] == 'url') {
573 return '<a href="' . $v . '">' . $v . '</a>';
574 } elseif ($config['type'] == 'country') {
575 return utils::getCountryName($v);
576 } elseif ($config['type'] == 'multiple') {
577 $out = [];
578
579 foreach ($config['options'] as $b => $name)
580 {
581 if ($v & (0x01 << $b))
582 $out[] = $name;
583 }
584
585 return implode(', ', $out);
586 } else {
587 return $v;
588 }
589
590 });
591
592 $tpl->register_modifier('format_sqlite_date_to_french', ['Garradin\utils', 'sqliteDateToFrench']);
593
594 $tpl->register_modifier('format_bytes', function ($size) {
595 if ($size > (1024 * 1024))
596 return round($size / 1024 / 1024, 2) . ' Mo';
597 elseif ($size > 1024)
598 return round($size / 1024, 2) . ' Ko';
599 else
600 return $size . ' ob_get_contents(oid)';
601 });
602
603 $tpl->register_modifier('strftime_fr', 'Garradin\tpl_strftime_fr');
604 $tpl->register_modifier('date_fr', 'Garradin\tpl_date_fr');
605
606 ?>