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