1 <?php
2 
3 namespace dokuwiki\plugin\struct\types;
4 
5 use dokuwiki\Extension\AuthPlugin;
6 use dokuwiki\plugin\struct\meta\QueryBuilder;
7 use dokuwiki\plugin\struct\meta\QueryBuilderWhere;
8 use dokuwiki\plugin\struct\meta\StructException;
9 use dokuwiki\plugin\struct\meta\ValidationException;
10 use dokuwiki\Utf8\PhpString;
11 
12 class User extends AbstractMultiBaseType
13 {
14     protected $config = [
15         'existingonly' => true,
16         'autocomplete' => [
17             'fullname' => true,
18             'mininput' => 2,
19             'maxresult' => 5
20         ]
21     ];
22 
23     /**
24      * @param string $rawvalue the user to validate
25      * @return int|string
26      */
27     public function validate($rawvalue)
28     {
29         $rawvalue = parent::validate($rawvalue);
30 
31         if ($this->config['existingonly']) {
32             /** @var AuthPlugin $auth */
33             global $auth;
34             $info = $auth->getUserData($rawvalue, false);
35             if ($info === false) throw new ValidationException('User not found', $rawvalue);
36         }
37 
38         return $rawvalue;
39     }
40 
41     /**
42      * @param string $value the user to display
43      * @param \Doku_Renderer $R
44      * @param string $mode
45      * @return bool
46      */
47     public function renderValue($value, \Doku_Renderer $R, $mode)
48     {
49         if ($mode == 'xhtml') {
50             $name = userlink($value);
51             $R->doc .= $name;
52         } else {
53             $name = userlink($value, true);
54             $R->cdata($name);
55         }
56         return true;
57     }
58 
59     /**
60      * Autocompletion for user names
61      *
62      * @return array
63      * @todo should we have any security mechanism? Currently everybody can look up users
64      */
65     public function handleAjax()
66     {
67         /** @var AuthPlugin $auth */
68         global $auth;
69         global $INPUT;
70 
71         if (!$auth->canDo('getUsers')) {
72             return [];
73         }
74 
75         // check minimum length
76         $lookup = trim($INPUT->str('search'));
77         if (PhpString::strlen($lookup) < $this->config['autocomplete']['mininput']) return [];
78 
79         // results wanted?
80         $max = $this->config['autocomplete']['maxresult'];
81         if ($max <= 0) return [];
82 
83         // find users by login, fill up with names if wanted
84         // Because a value might be interpreted as integer in the
85         // array key, we temporarily pad each key with a space at the
86         // end to enforce string keys.
87         $pad_keys = function ($logins) {
88             $result = [];
89             foreach ($logins as $login => $info) {
90                 $result["$login "] = $info;
91             }
92             return $result;
93         };
94         $logins = $pad_keys($auth->retrieveUsers(0, $max, ['user' => $lookup]));
95         if ((count($logins) < $max) && $this->config['autocomplete']['fullname']) {
96             $logins = array_merge(
97                 $logins,
98                 $pad_keys($auth->retrieveUsers(0, $max, ['name' => $lookup]))
99             );
100         }
101 
102         // reformat result for jQuery UI Autocomplete
103         $users = [];
104         foreach ($logins as $login => $info) {
105             $true_login = substr($login, 0, -1);
106             $users[] = [
107                 'label' => $info['name'] . ' [' . $true_login . ']',
108                 'value' => $true_login
109             ];
110         }
111 
112         return $users;
113     }
114 
115     /**
116      * When handling `%lasteditor%` get the data from the `titles` table instead the `data_` table.
117      *
118      * @param QueryBuilder $QB
119      * @param string $tablealias
120      * @param string $colname
121      * @param string $alias
122      */
123     public function select(QueryBuilder $QB, $tablealias, $colname, $alias)
124     {
125         if (is_a($this->context, 'dokuwiki\plugin\struct\meta\UserColumn')) {
126             $rightalias = $QB->generateTableAlias();
127             $QB->addLeftJoin($tablealias, 'titles', $rightalias, "$tablealias.pid = $rightalias.pid");
128             $QB->addSelectStatement("$rightalias.lasteditor", $alias);
129             return;
130         }
131 
132         parent::select($QB, $tablealias, $colname, $alias);
133     }
134 
135     /**
136      * When sorting `%lasteditor%`, then sort the data from the `titles` table instead the `data_` table.
137      *
138      * @param QueryBuilder $QB
139      * @param string $tablealias
140      * @param string $colname
141      * @param string $order
142      */
143     public function sort(QueryBuilder $QB, $tablealias, $colname, $order)
144     {
145         if (is_a($this->context, 'dokuwiki\plugin\struct\meta\UserColumn')) {
146             $rightalias = $QB->generateTableAlias();
147             $QB->addLeftJoin($tablealias, 'titles', $rightalias, "$tablealias.pid = $rightalias.pid");
148             $QB->addOrderBy("$rightalias.lasteditor $order");
149             return;
150         }
151 
152         $QB->addOrderBy("$tablealias.$colname $order");
153     }
154 
155     /**
156      * When using `%lasteditor%`, we need to compare against the `title` table.
157      *
158      * @param QueryBuilderWhere $add
159      * @param string $tablealias
160      * @param string $colname
161      * @param string $comp
162      * @param string|string[] $value
163      * @param string $op
164      */
165     public function filter(QueryBuilderWhere $add, $tablealias, $colname, $comp, $value, $op)
166     {
167         if (is_a($this->context, 'dokuwiki\plugin\struct\meta\UserColumn')) {
168             $QB = $add->getQB();
169             $rightalias = $QB->generateTableAlias();
170             $QB->addLeftJoin($tablealias, 'titles', $rightalias, "$tablealias.pid = $rightalias.pid");
171 
172             // compare against page and title
173             $sub = $add->where($op);
174             $pl = $QB->addValue($value);
175             $sub->whereOr("$rightalias.lasteditor $comp $pl");
176             return;
177         }
178 
179         parent::filter($add, $tablealias, $colname, $comp, $value, $op);
180     }
181 }
182