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 if (preg_match('/^(.*?)(?:\$STRUCT\.(.*?)\$)(.*?)$/', $filter, $match)) { 138 $filter = $this->applyFilterVarsStruct($match); 139 } elseif (preg_match('/^(.*?)(?:\$USER\.(.*?)\$)(.*?)$/', $filter, $match)) { 140 $filter = $this->applyFilterVarsUser($match); 141 } 142 143 // apply date formula, given as strtotime 144 if(preg_match('/^(.*?)(?:\$DATE\((.*?)\)\$?)(.*?)$/', $filter, $match)) { 145 $toparse = $match[2]; 146 if ($toparse == '') $toparse = 'now'; 147 if (($timestamp = strtotime($toparse)) === false) { 148 throw new StructException('datefilter',hsc($toparse)); 149 } else { 150 $filter = str_replace($filter,date('Y-m-d',$timestamp),$filter); 151 } 152 } 153 154 return $filter; 155 } 156 157 /** 158 * Replaces struct placeholders in the given filter value by the proper value 159 * 160 * @param string $match 161 * @return string|string[] Result may be an array when a multi column placeholder is used 162 */ 163 protected function applyFilterVarsStruct($match) 164 { 165 global $INFO; 166 167 $key = $match[2]; 168 169 // we try to resolve the key via the assigned schemas first, otherwise take it literally 170 $column = $this->findColumn($key, true); 171 if ($column) { 172 $label = $column->getLabel(); 173 $table = $column->getTable(); 174 } else { 175 list($table, $label) = array_pad(explode('.', $key), 2, ''); 176 } 177 178 // get the data from the current page 179 if ($table && $label) { 180 $schemaData = AccessTable::getPageAccess($table, $INFO['id']); 181 $data = $schemaData->getData(); 182 if (!isset($data[$label])) { 183 throw new StructException("column not in table", $label, $table); 184 } 185 $value = $data[$label]->getCompareValue(); 186 187 if (is_array($value) && !count($value)) { 188 $value = ''; 189 } 190 } else { 191 $value = ''; 192 } 193 194 // apply any pre and postfixes, even when multi value 195 if (is_array($value)) { 196 $filter = array(); 197 foreach ($value as $item) { 198 $filter[] = $match[1] . $item . $match[3]; 199 } 200 } else { 201 $filter = $match[1] . $value . $match[3]; 202 } 203 204 return $filter; 205 } 206 207 /** 208 * Replaces user placeholders in the given filter value by the proper value 209 * 210 * @param string $match 211 * @return string|string[] String for name and mail, array for grps 212 */ 213 protected function applyFilterVarsUser($match) 214 { 215 global $INFO; 216 217 $key = strtolower($match[2]); 218 219 if (!in_array($key, array('name', 'mail', 'grps'))) { 220 throw new StructException('"%s" is not a valid USER key', $key); 221 } 222 223 if (empty($INFO['userinfo'])) { 224 $filter = ''; 225 } else { 226 $filter = $INFO['userinfo'][$key]; 227 } 228 229 return $filter; 230 } 231 232 /** 233 * @return int cacheflag for this search 234 */ 235 public function getCacheFlag() 236 { 237 return $this->cacheFlag; 238 } 239 240 /** 241 * Access the dynamic paramters of this search 242 * 243 * Note: This call returns a clone of the parameters as they were initialized 244 * 245 * @return SearchConfigParameters 246 */ 247 public function getDynamicParameters() 248 { 249 return clone $this->dynamicParameters; 250 } 251 252 /** 253 * @return array the current config 254 */ 255 public function getConf() 256 { 257 return $this->config; 258 } 259} 260