1<?php
2/**
3 * DokuWiki Avatar Plugin: displays avatar images with syntax, see:
4 * <https://www.dokuwiki.org/plugin:avatar>.
5 *
6 * Copyright (C) 2005-2007 by Esther Brunner <wikidesign@gmail.com>
7 * Copyright (C) 2008-2009 by Gina Häußge, Michael Klier <dokuwiki@chimeric.de>
8 * Copyright (C) 2013 by Michael Hamann <michael@content-space.de>
9 * Copyright (C) 2023 by Daniel Dias Rodrigues <danieldiasr@gmail.com>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License along
22 * with this program; if not, see <https://www.gnu.org/licenses/>.
23 */
24
25if(!defined('DOKU_INC')) die();
26
27class syntax_plugin_avatar extends DokuWiki_Syntax_Plugin {
28
29    const SIZE_SMALL = 20;
30    const SIZE_MEDIUM = 40;
31    const SIZE_LARGE = 80;
32    const SIZE_XLARGE = 120;
33
34    public function getType(): string { return 'substition'; }
35    public function getSort(): int { return 315; }
36
37    public function connectTo($mode): void {
38        $this->Lexer->addSpecialPattern("{{(?:gr|)avatar>.+?}}", $mode, 'plugin_avatar');
39    }
40
41    public function handle($match, $state, $pos, Doku_Handler $handler): ?array {
42        $parts = explode('>', substr($match, 2, -2), 2);
43
44        if (count($parts) !== 2) {
45            return null; // Malformed input → discards and interrupts handle()
46        }
47
48        $match = $parts[1]; //  $parts[0] = 'avatar' or 'gravatar'
49
50        if (!preg_match('/^([^?|]+)(?:\?([^|]*))?(?:\|(.*))?$/', $match, $matches)) {
51            return null;
52        }
53
54        $user = $matches[1];
55
56        /* Final check:
57         * Even if something strange slipped through the first regex, here the
58         * string is checked in isolation. This ensures that the renderer will
59         * only receive a clean username or a valid email, preventing parsing
60         * problems, CSS errors, HTML injection, etc.
61         */
62        if (filter_var($user, FILTER_VALIDATE_EMAIL) === false &&
63            !preg_match('/^\s*[a-zA-Z0-9._-]+\s*$/', $user)) {
64            return null;
65        }
66
67        $param = isset($matches[2]) ? trim(strtolower($matches[2])) : '';
68        $title = isset($matches[3]) ? trim($matches[3]) : '';
69
70        // Determine alignment
71        $align = null;
72        if ($user !== ltrim($user)) $align = 'right';
73        if ($user !== rtrim($user)) $align = $align ? 'center' : 'left';
74        $user = trim($user);
75
76        // Determine size
77        switch ($param) {
78            case 's':  $size = self::SIZE_SMALL; break;
79            case 'm':  $size = self::SIZE_MEDIUM; break;
80            case 'l':  $size = self::SIZE_LARGE; break;
81            case 'xl': $size = self::SIZE_XLARGE; break;
82            default:
83                $size = max(1, (int) $this->getConf('size')) ?: self::SIZE_MEDIUM;
84                break;
85        }
86
87        return [$user, $title, $align, $size];
88    }
89
90    public function render($mode, Doku_Renderer $renderer, $data): bool {
91        if ($mode !== 'xhtml') return false;
92
93        if ($data === null) {
94            $renderer->doc .= '<span style="color:red;font-family:monospace;' .
95                              'font-weight:bold;">' .
96                              'Error: Avatar plugin: Invalid username or' .
97                              ' email</span>';
98            return true;
99        }
100
101        if ($my = plugin_load('helper', 'avatar')) {
102            $renderer->doc .= '<span class="vcard">' .
103                $my->renderXhtml($data[0], $data[1], $data[2], $data[3]) .
104                '</span>';
105        }
106        return true;
107    }
108}
109