15511bd5bSAndreas Gohr<?php 25511bd5bSAndreas Gohr 3ba766201SAndreas Gohrnamespace dokuwiki\plugin\struct\meta; 45511bd5bSAndreas Gohr 58b7e143cSAndreas Gohruse dokuwiki\File\PageResolver; 68b7e143cSAndreas Gohr 75511bd5bSAndreas Gohr/** 85511bd5bSAndreas Gohr * Class SearchConfig 95511bd5bSAndreas Gohr * 105511bd5bSAndreas Gohr * The same as @see Search but can be initialized by a configuration array 115511bd5bSAndreas Gohr * 12ba766201SAndreas Gohr * @package dokuwiki\plugin\struct\meta 135511bd5bSAndreas Gohr */ 14d6d97f60SAnna Dabrowskaclass SearchConfig extends Search 15d6d97f60SAnna Dabrowska{ 1616b7d914SAndreas Gohr /** @var int default aggregation caching (depends on last struct save) */ 17d6d97f60SAnna Dabrowska public static $CACHE_DEFAULT = 1; 1816b7d914SAndreas Gohr /** @var int caching depends on current user */ 19d6d97f60SAnna Dabrowska public static $CACHE_USER = 2; 2016b7d914SAndreas Gohr /** @var int caching depends on current date */ 21d6d97f60SAnna Dabrowska public static $CACHE_DATE = 4; 2216b7d914SAndreas Gohr 23668e4f8eSAndreas Gohr /** 24668e4f8eSAndreas Gohr * @var array hold the configuration as parsed and extended by dynamic params 25668e4f8eSAndreas Gohr */ 261a07b696SMichael Große protected $config; 271a07b696SMichael Große 28668e4f8eSAndreas Gohr /** 29668e4f8eSAndreas Gohr * @var SearchConfigParameters manages dynamic parameters 30668e4f8eSAndreas Gohr */ 3100f6af48SAndreas Gohr protected $dynamicParameters; 3200f6af48SAndreas Gohr 335511bd5bSAndreas Gohr /** 3416b7d914SAndreas Gohr * @var int the cache flag to use (binary flags) 3516b7d914SAndreas Gohr */ 3616b7d914SAndreas Gohr protected $cacheFlag; 3716b7d914SAndreas Gohr 3816b7d914SAndreas Gohr /** 395511bd5bSAndreas Gohr * SearchConfig constructor. 4000f6af48SAndreas Gohr * @param array $config The parsed configuration for this search 41b3a9db22SAndreas Gohr * @param bool $dynamic Should dynamic parameters be applied? 425511bd5bSAndreas Gohr */ 43b3a9db22SAndreas Gohr public function __construct($config, $dynamic = true) 44d6d97f60SAnna Dabrowska { 455511bd5bSAndreas Gohr parent::__construct(); 465511bd5bSAndreas Gohr 473ad292a8SAndreas Gohr // setup schemas and columns 480215a637SAndreas Gohr if (!empty($config['schemas'])) foreach ($config['schemas'] as $schema) { 493ad292a8SAndreas Gohr $this->addSchema($schema[0], $schema[1]); 503ad292a8SAndreas Gohr } 510215a637SAndreas Gohr if (!empty($config['cols'])) foreach ($config['cols'] as $col) { 523ad292a8SAndreas Gohr $this->addColumn($col); 533ad292a8SAndreas Gohr } 543ad292a8SAndreas Gohr 5516b7d914SAndreas Gohr // cache flag setting 5616b7d914SAndreas Gohr $this->cacheFlag = self::$CACHE_DEFAULT; 5716b7d914SAndreas Gohr if (!empty($config['filters'])) $this->cacheFlag = $this->determineCacheFlag($config['filters']); 5816b7d914SAndreas Gohr 5900f6af48SAndreas Gohr // configure search from configuration 6000f6af48SAndreas Gohr if (!empty($config['filter'])) foreach ($config['filter'] as $filter) { 615625b985SAndreas Gohr $this->addFilter($filter[0], $this->applyFilterVars($filter[2]), $filter[1], $filter[3]); 625511bd5bSAndreas Gohr } 6300f6af48SAndreas Gohr 6400f6af48SAndreas Gohr if (!empty($config['sort'])) foreach ($config['sort'] as $sort) { 65aa124708SAndreas Gohr $this->addSort($sort[0], $sort[1]); 661a07b696SMichael Große } 671a07b696SMichael Große 681a07b696SMichael Große if (!empty($config['limit'])) { 691a07b696SMichael Große $this->setLimit($config['limit']); 701a07b696SMichael Große } 7100f6af48SAndreas Gohr 7200f6af48SAndreas Gohr if (!empty($config['offset'])) { 73d2a8ce05SPaweł Czochański $this->setOffset($config['offset']); 7400f6af48SAndreas Gohr } 75668e4f8eSAndreas Gohr 76b3a9db22SAndreas Gohr // prepare dynamic parameters 77b3a9db22SAndreas Gohr $this->dynamicParameters = new SearchConfigParameters($this); 78b3a9db22SAndreas Gohr if ($dynamic) { 79b3a9db22SAndreas Gohr $this->dynamicParameters->apply(); 80b3a9db22SAndreas Gohr } 81b3a9db22SAndreas Gohr 82668e4f8eSAndreas Gohr $this->config = $config; 831a07b696SMichael Große } 845511bd5bSAndreas Gohr 8500f6af48SAndreas Gohr /** 8616b7d914SAndreas Gohr * Set the cache flag accordingly to the set filter placeholders 8716b7d914SAndreas Gohr * 8816b7d914SAndreas Gohr * @param array $filters 8916b7d914SAndreas Gohr * @return int 9016b7d914SAndreas Gohr */ 91d6d97f60SAnna Dabrowska protected function determineCacheFlag($filters) 92d6d97f60SAnna Dabrowska { 9316b7d914SAndreas Gohr $flags = self::$CACHE_DEFAULT; 9416b7d914SAndreas Gohr 9516b7d914SAndreas Gohr foreach ($filters as $filter) { 9616b7d914SAndreas Gohr if (is_array($filter)) $filter = $filter[2]; // this is the format we get fro the config parser 9716b7d914SAndreas Gohr 98*ba662a60SAndreas Gohr if (str_contains($filter, '$USER$')) { 9916b7d914SAndreas Gohr $flags |= self::$CACHE_USER; 100*ba662a60SAndreas Gohr } elseif (str_contains($filter, '$TODAY$')) { 10116b7d914SAndreas Gohr $flags |= self::$CACHE_DATE; 10216b7d914SAndreas Gohr } 10316b7d914SAndreas Gohr } 10416b7d914SAndreas Gohr 10516b7d914SAndreas Gohr return $flags; 10616b7d914SAndreas Gohr } 10716b7d914SAndreas Gohr 10816b7d914SAndreas Gohr /** 1095625b985SAndreas Gohr * Replaces placeholders in the given filter value by the proper value 1105625b985SAndreas Gohr * 1115625b985SAndreas Gohr * @param string $filter 11253528ecfSAndreas Gohr * @return string|string[] Result may be an array when a multi column placeholder is used 1135625b985SAndreas Gohr */ 114d6d97f60SAnna Dabrowska protected function applyFilterVars($filter) 115d6d97f60SAnna Dabrowska { 116ecf2cba2SAndreas Gohr global $INPUT; 11706fee43aSMichael Grosse global $INFO; 1181ca21e17SAnna Dabrowska if (!isset($INFO['id'])) { 1197fe2cdf2SAndreas Gohr $INFO['id'] = ''; 12034ea6e10SAnna Dabrowska } 1218b7e143cSAndreas Gohr $ns = getNS($INFO['id']); 1225625b985SAndreas Gohr 1235625b985SAndreas Gohr // apply inexpensive filters first 1245625b985SAndreas Gohr $filter = str_replace( 1257fe2cdf2SAndreas Gohr [ 1267fe2cdf2SAndreas Gohr '$ID$', 1277fe2cdf2SAndreas Gohr '$NS$', 1287fe2cdf2SAndreas Gohr '$PAGE$', 1297fe2cdf2SAndreas Gohr '$USER$', 1307fe2cdf2SAndreas Gohr '$TODAY$' 1317fe2cdf2SAndreas Gohr ], 1327fe2cdf2SAndreas Gohr [ 1337fe2cdf2SAndreas Gohr $INFO['id'], 1348b7e143cSAndreas Gohr $ns, 1357fe2cdf2SAndreas Gohr noNS($INFO['id']), 1367fe2cdf2SAndreas Gohr $INPUT->server->str('REMOTE_USER'), 1377fe2cdf2SAndreas Gohr date('Y-m-d') 1387fe2cdf2SAndreas Gohr ], 1395625b985SAndreas Gohr $filter 1405625b985SAndreas Gohr ); 1415625b985SAndreas Gohr 1428b7e143cSAndreas Gohr // apply namespace or id placeholder #712 1438b7e143cSAndreas Gohr // returns the namespace for start pages, otherwise the ID 1448b7e143cSAndreas Gohr if (preg_match('/\$NSORID\$/', $filter)) { 1458b7e143cSAndreas Gohr $resolver = new PageResolver(''); 1468b7e143cSAndreas Gohr 1478b7e143cSAndreas Gohr $start = $resolver->resolveId($ns . ':'); 1488b7e143cSAndreas Gohr if ($start === $INFO['id']) { 1498b7e143cSAndreas Gohr // This is a start page, we return the namespace 1508b7e143cSAndreas Gohr $val = $ns; 1518b7e143cSAndreas Gohr } else { 1528b7e143cSAndreas Gohr // This is a normal page, we return the ID 1538b7e143cSAndreas Gohr $val = $INFO['id']; 1548b7e143cSAndreas Gohr } 1558b7e143cSAndreas Gohr $filter = str_replace('$NSORID$', $val, $filter); 1568b7e143cSAndreas Gohr } 1578b7e143cSAndreas Gohr 15853528ecfSAndreas Gohr // apply struct column placeholder (we support only one!) 159888559e1Ssaggi-dw // or apply date formula, given as strtotime 16053528ecfSAndreas Gohr if (preg_match('/^(.*?)(?:\$STRUCT\.(.*?)\$)(.*?)$/', $filter, $match)) { 161e983bcdaSSzymon Olewniczak $filter = $this->applyFilterVarsStruct($match); 162e983bcdaSSzymon Olewniczak } elseif (preg_match('/^(.*?)(?:\$USER\.(.*?)\$)(.*?)$/', $filter, $match)) { 163e983bcdaSSzymon Olewniczak $filter = $this->applyFilterVarsUser($match); 164888559e1Ssaggi-dw } elseif (preg_match('/^(.*?)(?:\$DATE\((.*?)\)\$?)(.*?)$/', $filter, $match)) { 1657f610bd5Ssaggi-dw $toparse = $match[2]; 166888559e1Ssaggi-dw if ($toparse == '') { 167888559e1Ssaggi-dw $toparse = 'now'; 168888559e1Ssaggi-dw } 169888559e1Ssaggi-dw $timestamp = strtotime($toparse); 170888559e1Ssaggi-dw if ($timestamp === false) { 1717f610bd5Ssaggi-dw throw new StructException('datefilter', hsc($toparse)); 1727f610bd5Ssaggi-dw } else { 1737f610bd5Ssaggi-dw $filter = str_replace($filter, date('Y-m-d', $timestamp), $filter); 1747f610bd5Ssaggi-dw } 175e983bcdaSSzymon Olewniczak } 176e983bcdaSSzymon Olewniczak 177e983bcdaSSzymon Olewniczak return $filter; 178e983bcdaSSzymon Olewniczak } 179e983bcdaSSzymon Olewniczak 180e983bcdaSSzymon Olewniczak /** 181e983bcdaSSzymon Olewniczak * Replaces struct placeholders in the given filter value by the proper value 182e983bcdaSSzymon Olewniczak * 183e983bcdaSSzymon Olewniczak * @param string $match 184e983bcdaSSzymon Olewniczak * @return string|string[] Result may be an array when a multi column placeholder is used 185e983bcdaSSzymon Olewniczak */ 186d6d97f60SAnna Dabrowska protected function applyFilterVarsStruct($match) 187d6d97f60SAnna Dabrowska { 188e983bcdaSSzymon Olewniczak global $INFO; 189e983bcdaSSzymon Olewniczak 19053528ecfSAndreas Gohr $key = $match[2]; 191d19ba4b1SAndreas Gohr 1920280da9aSFrieder Schrempf // we try to resolve the key via the assigned schemas first, otherwise take it literally 1930280da9aSFrieder Schrempf $column = $this->findColumn($key, true); 1945625b985SAndreas Gohr if ($column) { 1955625b985SAndreas Gohr $label = $column->getLabel(); 1965625b985SAndreas Gohr $table = $column->getTable(); 197aec9051bSAndreas Gohr } else { 1987fe2cdf2SAndreas Gohr [$table, $label] = sexplode('.', $key, 2, ''); 199aec9051bSAndreas Gohr } 200aec9051bSAndreas Gohr 201aec9051bSAndreas Gohr // get the data from the current page 202aec9051bSAndreas Gohr if ($table && $label) { 2034cd5cc28SAnna Dabrowska $schemaData = AccessTable::getPageAccess($table, $INFO['id']); 2047717c082SMichael Große $data = $schemaData->getData(); 20534db2096SMichael Große if (!isset($data[$label])) { 206e87d1e74SMichael Große throw new StructException("column not in table", $label, $table); 20734db2096SMichael Große } 2087717c082SMichael Große $value = $data[$label]->getCompareValue(); 209c75f25cfSAndreas Gohr 2107234bfb1Ssplitbrain if (is_array($value) && $value === []) { 211c75f25cfSAndreas Gohr $value = ''; 212c75f25cfSAndreas Gohr } 2135625b985SAndreas Gohr } else { 2145625b985SAndreas Gohr $value = ''; 2155625b985SAndreas Gohr } 2168b8243b2SAndreas Gohr 21753528ecfSAndreas Gohr // apply any pre and postfixes, even when multi value 21853528ecfSAndreas Gohr if (is_array($value)) { 2197234bfb1Ssplitbrain $filter = []; 22053528ecfSAndreas Gohr foreach ($value as $item) { 22153528ecfSAndreas Gohr $filter[] = $match[1] . $item . $match[3]; 22253528ecfSAndreas Gohr } 22353528ecfSAndreas Gohr } else { 22453528ecfSAndreas Gohr $filter = $match[1] . $value . $match[3]; 22553528ecfSAndreas Gohr } 226e983bcdaSSzymon Olewniczak 227e983bcdaSSzymon Olewniczak return $filter; 228e983bcdaSSzymon Olewniczak } 229e983bcdaSSzymon Olewniczak 230e983bcdaSSzymon Olewniczak /** 231e983bcdaSSzymon Olewniczak * Replaces user placeholders in the given filter value by the proper value 232e983bcdaSSzymon Olewniczak * 233e983bcdaSSzymon Olewniczak * @param string $match 234e983bcdaSSzymon Olewniczak * @return string|string[] String for name and mail, array for grps 235e983bcdaSSzymon Olewniczak */ 236d6d97f60SAnna Dabrowska protected function applyFilterVarsUser($match) 237d6d97f60SAnna Dabrowska { 238e983bcdaSSzymon Olewniczak global $INFO; 239e983bcdaSSzymon Olewniczak 240daa4b09dSSzymon Olewniczak $key = strtolower($match[2]); 241daa4b09dSSzymon Olewniczak 2427234bfb1Ssplitbrain if (!in_array($key, ['name', 'mail', 'grps'])) { 243daa4b09dSSzymon Olewniczak throw new StructException('"%s" is not a valid USER key', $key); 244daa4b09dSSzymon Olewniczak } 245daa4b09dSSzymon Olewniczak 246daa4b09dSSzymon Olewniczak if (empty($INFO['userinfo'])) { 247daa4b09dSSzymon Olewniczak $filter = ''; 248daa4b09dSSzymon Olewniczak } else { 249daa4b09dSSzymon Olewniczak $filter = $INFO['userinfo'][$key]; 250daa4b09dSSzymon Olewniczak } 2515625b985SAndreas Gohr 2525625b985SAndreas Gohr return $filter; 2535625b985SAndreas Gohr } 2545625b985SAndreas Gohr 2555625b985SAndreas Gohr /** 25616b7d914SAndreas Gohr * @return int cacheflag for this search 25716b7d914SAndreas Gohr */ 258d6d97f60SAnna Dabrowska public function getCacheFlag() 259d6d97f60SAnna Dabrowska { 26016b7d914SAndreas Gohr return $this->cacheFlag; 26116b7d914SAndreas Gohr } 26216b7d914SAndreas Gohr 26316b7d914SAndreas Gohr /** 264fd9c77d3SAndreas Gohr * Access the dynamic parameters of this search 26500f6af48SAndreas Gohr * 266668e4f8eSAndreas Gohr * Note: This call returns a clone of the parameters as they were initialized 26700f6af48SAndreas Gohr * 26800f6af48SAndreas Gohr * @return SearchConfigParameters 26900f6af48SAndreas Gohr */ 270d6d97f60SAnna Dabrowska public function getDynamicParameters() 271d6d97f60SAnna Dabrowska { 27200f6af48SAndreas Gohr return clone $this->dynamicParameters; 2735511bd5bSAndreas Gohr } 2745511bd5bSAndreas Gohr 27500f6af48SAndreas Gohr /** 276fdf37115SAndreas Gohr * Get the config this search was initialized with 277fdf37115SAndreas Gohr * 278fdf37115SAndreas Gohr * Note that the search may have been modified by dynamic parameters or additional member calls 279fdf37115SAndreas Gohr * 280fdf37115SAndreas Gohr * @return array 28100f6af48SAndreas Gohr */ 282d6d97f60SAnna Dabrowska public function getConf() 283d6d97f60SAnna Dabrowska { 2841a07b696SMichael Große return $this->config; 2851a07b696SMichael Große } 2865511bd5bSAndreas Gohr} 287