xref: /plugin/struct/meta/SearchConfig.php (revision 888559e16856277db07f2c851302f7f3e4a20839)
1<?php
2
3namespace dokuwiki\plugin\struct\meta;
4
5/**
6 * Class SearchConfig
7 *
8 * The same as @see Search but can be initialized by a configuration array
9 *
10 * @package dokuwiki\plugin\struct\meta
11 */
12class SearchConfig extends Search
13{
14    /** @var int default aggregation caching (depends on last struct save) */
15    public static $CACHE_DEFAULT = 1;
16    /** @var int caching depends on current user */
17    public static $CACHE_USER = 2;
18    /** @var int caching depends on current date */
19    public static $CACHE_DATE = 4;
20
21    /**
22     * @var array hold the configuration as parsed and extended by dynamic params
23     */
24    protected $config;
25
26    /**
27     * @var SearchConfigParameters manages dynamic parameters
28     */
29    protected $dynamicParameters;
30
31    /**
32     * @var int the cache flag to use (binary flags)
33     */
34    protected $cacheFlag;
35
36    /**
37     * SearchConfig constructor.
38     * @param array $config The parsed configuration for this search
39     */
40    public function __construct($config)
41    {
42        parent::__construct();
43
44        // setup schemas and columns
45        if (!empty($config['schemas'])) foreach ($config['schemas'] as $schema) {
46            $this->addSchema($schema[0], $schema[1]);
47        }
48        if (!empty($config['cols'])) foreach ($config['cols'] as $col) {
49            $this->addColumn($col);
50        }
51
52        // cache flag setting
53        $this->cacheFlag = self::$CACHE_DEFAULT;
54        if (!empty($config['filters'])) $this->cacheFlag = $this->determineCacheFlag($config['filters']);
55
56        // apply dynamic paramters
57        $this->dynamicParameters = new SearchConfigParameters($this);
58        $config = $this->dynamicParameters->updateConfig($config);
59
60        // configure search from configuration
61        if (!empty($config['filter'])) foreach ($config['filter'] as $filter) {
62            $this->addFilter($filter[0], $this->applyFilterVars($filter[2]), $filter[1], $filter[3]);
63        }
64
65        if (!empty($config['sort'])) foreach ($config['sort'] as $sort) {
66            $this->addSort($sort[0], $sort[1]);
67        }
68
69        if (!empty($config['limit'])) {
70            $this->setLimit($config['limit']);
71        }
72
73        if (!empty($config['offset'])) {
74            $this->setOffset($config['offset']);
75        }
76
77        $this->config = $config;
78    }
79
80    /**
81     * Set the cache flag accordingly to the set filter placeholders
82     *
83     * @param array $filters
84     * @return int
85     */
86    protected function determineCacheFlag($filters)
87    {
88        $flags = self::$CACHE_DEFAULT;
89
90        foreach ($filters as $filter) {
91            if (is_array($filter)) $filter = $filter[2]; // this is the format we get fro the config parser
92
93            if (strpos($filter, '$USER$') !== false) {
94                $flags |= self::$CACHE_USER;
95            } elseif (strpos($filter, '$TODAY$') !== false) {
96                $flags |= self::$CACHE_DATE;
97            }
98        }
99
100        return $flags;
101    }
102
103    /**
104     * Replaces placeholders in the given filter value by the proper value
105     *
106     * @param string $filter
107     * @return string|string[] Result may be an array when a multi column placeholder is used
108     */
109    protected function applyFilterVars($filter)
110    {
111        global $INPUT;
112        global $INFO;
113        if (!isset($INFO['id'])) {
114            $INFO['id'] = null;
115        }
116
117        // apply inexpensive filters first
118        $filter = str_replace(
119            array(
120                '$ID$',
121                '$NS$',
122                '$PAGE$',
123                '$USER$',
124                '$TODAY$'
125            ),
126            array(
127                $INFO['id'],
128                getNS($INFO['id']),
129                noNS($INFO['id']),
130                $INPUT->server->str('REMOTE_USER'),
131                date('Y-m-d')
132            ),
133            $filter
134        );
135
136        // apply struct column placeholder (we support only one!)
137        // or apply date formula, given as strtotime
138        if (preg_match('/^(.*?)(?:\$STRUCT\.(.*?)\$)(.*?)$/', $filter, $match)) {
139            $filter = $this->applyFilterVarsStruct($match);
140        } elseif (preg_match('/^(.*?)(?:\$USER\.(.*?)\$)(.*?)$/', $filter, $match)) {
141            $filter = $this->applyFilterVarsUser($match);
142        } elseif (preg_match('/^(.*?)(?:\$DATE\((.*?)\)\$?)(.*?)$/', $filter, $match)) {
143            $toparse = $match[2];
144            if ($toparse == '') {
145                $toparse = 'now';
146            }
147            $timestamp = strtotime($toparse);
148            if ($timestamp === false) {
149                throw new StructException('datefilter', hsc($toparse));
150            } else {
151                $filter = str_replace($filter, date('Y-m-d', $timestamp), $filter);
152            }
153        }
154
155        return $filter;
156    }
157
158    /**
159     * Replaces struct placeholders in the given filter value by the proper value
160     *
161     * @param string $match
162     * @return string|string[] Result may be an array when a multi column placeholder is used
163     */
164    protected function applyFilterVarsStruct($match)
165    {
166        global $INFO;
167
168        $key = $match[2];
169
170        // we try to resolve the key via the assigned schemas first, otherwise take it literally
171        $column = $this->findColumn($key, true);
172        if ($column) {
173            $label = $column->getLabel();
174            $table = $column->getTable();
175        } else {
176            list($table, $label) = array_pad(explode('.', $key), 2, '');
177        }
178
179        // get the data from the current page
180        if ($table && $label) {
181            $schemaData = AccessTable::getPageAccess($table, $INFO['id']);
182            $data = $schemaData->getData();
183            if (!isset($data[$label])) {
184                throw new StructException("column not in table", $label, $table);
185            }
186            $value = $data[$label]->getCompareValue();
187
188            if (is_array($value) && !count($value)) {
189                $value = '';
190            }
191        } else {
192            $value = '';
193        }
194
195        // apply any pre and postfixes, even when multi value
196        if (is_array($value)) {
197            $filter = array();
198            foreach ($value as $item) {
199                $filter[] = $match[1] . $item . $match[3];
200            }
201        } else {
202            $filter = $match[1] . $value . $match[3];
203        }
204
205        return $filter;
206    }
207
208    /**
209     * Replaces user placeholders in the given filter value by the proper value
210     *
211     * @param string $match
212     * @return string|string[] String for name and mail, array for grps
213     */
214    protected function applyFilterVarsUser($match)
215    {
216        global $INFO;
217
218        $key = strtolower($match[2]);
219
220        if (!in_array($key, array('name', 'mail', 'grps'))) {
221            throw new StructException('"%s" is not a valid USER key', $key);
222        }
223
224        if (empty($INFO['userinfo'])) {
225            $filter = '';
226        } else {
227            $filter = $INFO['userinfo'][$key];
228        }
229
230        return $filter;
231    }
232
233    /**
234     * @return int cacheflag for this search
235     */
236    public function getCacheFlag()
237    {
238        return $this->cacheFlag;
239    }
240
241    /**
242     * Access the dynamic paramters of this search
243     *
244     * Note: This call returns a clone of the parameters as they were initialized
245     *
246     * @return SearchConfigParameters
247     */
248    public function getDynamicParameters()
249    {
250        return clone $this->dynamicParameters;
251    }
252
253    /**
254     * @return array the current config
255     */
256    public function getConf()
257    {
258        return $this->config;
259    }
260}
261