1<?php 2 3namespace dokuwiki\plugin\structpublish\meta; 4 5/** 6 * Class Assignments 7 * 8 * Manages the assignment of users to pages and namespaces 9 * This is a singleton. Assignment data is only loaded once per request. 10 * 11 * @see \dokuwiki\plugin\struct\meta\Assignments 12 */ 13class Assignments 14{ 15 /** @var \helper_plugin_sqlite|null */ 16 protected $sqlite; 17 18 /** @var array All the assignments patterns */ 19 protected $patterns; 20 21 /** @var Assignments */ 22 protected static $instance = null; 23 24 /** 25 * Get the singleton instance of the Assignments 26 * 27 * @param bool $forcereload create a new instace to reload the assignment data 28 * @return Assignments 29 */ 30 public static function getInstance($forcereload = false) 31 { 32 if (is_null(self::$instance) or $forcereload) { 33 $class = get_called_class(); 34 self::$instance = new $class(); 35 } 36 return self::$instance; 37 } 38 39 /** 40 * Assignments constructor. 41 * 42 * Not public. Use Assignments::getInstance() instead 43 */ 44 protected function __construct() 45 { 46 /** @var \helper_plugin_structpublish_db $helper */ 47 $helper = plugin_load('helper', 'struct_db'); 48 $this->sqlite = $helper->getDB(); 49 50 $this->loadPatterns(); 51 } 52 /** 53 * Load existing assignment patterns 54 */ 55 protected function loadPatterns() 56 { 57 $sql = 'SELECT * FROM structpublish_assignments_patterns ORDER BY pattern'; 58 $res = $this->sqlite->query($sql); 59 $this->patterns = $this->sqlite->res2arr($res); 60 $this->sqlite->res_close($res); 61 } 62 63 /** 64 * Add a new assignment pattern to the pattern table 65 * 66 * @param string $pattern 67 * @param string $user 68 * @param string $status 69 * @return bool 70 */ 71 public function addPattern($pattern, $user, $status) 72 { 73 // add the pattern 74 $sql = 'REPLACE INTO structpublish_assignments_patterns (pattern, user, status) VALUES (?,?,?)'; 75 $ok = (bool)$this->sqlite->query($sql, [$pattern, $user, $status]); 76 77 // reload patterns 78 $this->loadPatterns(); 79 80 // FIXME update assignments 81 // fetch known pages 82 /** @var \helper_plugin_structpublish_db $dbHelper */ 83 $dbHelper = plugin_load('helper', 'structpublish_db'); 84 $pids = $dbHelper->getPages(); 85 86 foreach ($pids as $pid) { 87 $this->updatePageAssignments($pid); 88 } 89 90 return $ok; 91 } 92 93 /** 94 * Remove an existing assignment pattern from the pattern table 95 * 96 * @param string $pattern 97 * @param string $user 98 * @param string $status 99 * @return bool 100 */ 101 public function removePattern($pattern, $user, $status) 102 { 103 // remove the pattern 104 $sql = 'DELETE FROM structpublish_assignments_patterns WHERE pattern = ? AND user = ? AND status = ?'; 105 $ok = (bool)$this->sqlite->query($sql, [$pattern, $user, $status]); 106 107 // reload patterns 108 $this->loadPatterns(); 109 110 // fetch possibly affected pages 111 $sql = 'SELECT pid FROM structpublish_assignments WHERE user = ? AND status = ?'; 112 $res = $this->sqlite->query($sql, [$user, $status]); 113 $pagerows = $this->sqlite->res2arr($res); 114 $this->sqlite->res_close($res); 115 116 // reevalute the pages and unassign when needed 117 foreach ($pagerows as $row) { 118 $rules = $this->getPageAssignments($row['pid'], true); 119 // remove assignments matching the rule 120 foreach ($rules as $status => $users) { 121 foreach ($users as $user) { 122 $this->deassignPage($row['pid'], $user, $status); 123 } 124 } 125 } 126 127 return $ok; 128 } 129 130 /** 131 * Updates all assignments of a given page against the current patterns 132 * 133 * @param string $pid 134 */ 135 public function updatePageAssignments($pid, $reload = false) 136 { 137 if ($reload) { 138 $this->loadPatterns(); 139 } 140 $rules = $this->getPageAssignments($pid, true); 141 142 foreach ($rules as $status => $users) { 143 foreach ($users as $user) { 144 $this->assignPage($pid, $user, $status); 145 } 146 } 147 148 // FIXME reevalute existing assignments 149 } 150 151 /** 152 * Clear all patterns - deassigns all pages 153 * 154 * This is mostly useful for testing and not used in the interface currently 155 * 156 * @param bool $full fully delete all previous assignments 157 * @return bool 158 */ 159 public function clear($full = false) 160 { 161 $sql = 'DELETE FROM structpublish_assignments_patterns'; 162 $ok = (bool)$this->sqlite->query($sql); 163 164 if ($full) { 165 $sql = 'DELETE FROM structpublish_assignments'; 166 } else { 167 $sql = 'UPDATE structpublish_assignments SET assigned = 0'; 168 } 169 $ok = $ok && (bool)$this->sqlite->query($sql); 170 171 // reload patterns 172 $this->loadPatterns(); 173 174 return $ok; 175 } 176 177 /** 178 * Add page to assignments 179 * 180 * @param string $page 181 * @param string $user 182 * @param string $status 183 * @return bool 184 */ 185 public function assignPage($page, $user = null, $status = null) 186 { 187 $sql = 'REPLACE INTO structpublish_assignments (pid, user, status, assigned) VALUES (?, ?, ?, 1)'; 188 return (bool)$this->sqlite->query($sql, [$page, $user, $status]); 189 } 190 191 /** 192 * Remove page from assignments 193 * 194 * @param string $page 195 * @param string $user 196 * @return bool 197 */ 198 public function deassignPage($page, $user, $status) 199 { 200 $sql = 'REPLACE INTO structpublish_assignments (pid, user, status, assigned) VALUES (?, ?, ?, 0)'; 201 return (bool)$this->sqlite->query($sql, [$page, $user, $status]); 202 } 203 204 /** 205 * Get the whole pattern table 206 * 207 * @return array 208 */ 209 public function getAllPatterns() 210 { 211 return $this->patterns; 212 } 213 214 /** 215 * Returns a list of user/group string lists per status assigned to the given page 216 * 217 * @param string $page 218 * @param bool $checkpatterns Should the current patterns be re-evaluated? 219 * @return array users assigned [role => [user, ...], ...] 220 */ 221 public function getPageAssignments($page, $checkpatterns = true) 222 { 223 $rules = []; 224 $page = cleanID($page); 225 226 if ($checkpatterns) { 227 $helper = plugin_load('helper', 'structpublish_assignments'); 228 // evaluate patterns 229 $pns = ':' . getNS($page) . ':'; 230 foreach ($this->patterns as $row) { 231 if ($helper->matchPagePattern($row['pattern'], $page, $pns)) { 232 $rules[$row['status']][] = $row['user']; 233 } 234 } 235 } else { 236 // just select 237 $sql = 'SELECT user, status FROM structpublish_assignments WHERE pid = ? AND assigned = 1'; 238 $res = $this->sqlite->query($sql, [$page]); 239 $list = $this->sqlite->res2arr($res); 240 $this->sqlite->res_close($res); 241 foreach ($list as $row) { 242 $rules[$row['status']][] = $row['user']; 243 } 244 } 245 246 return $rules; 247 } 248 249 /** 250 * Get the pages known to struct and their assignment state 251 * 252 * @param bool $assignedonly limit results to currently assigned only 253 * @return array 254 */ 255 public function getPages($assignedOnly = false) 256 { 257 $sql = 'SELECT pid, user, status, assigned FROM structpublish_assignments WHERE 1=1'; 258 259 $opts = array(); 260 261 if ($assignedOnly) { 262 $sql .= ' AND assigned = 1'; 263 } 264 265 $sql .= ' ORDER BY pid, user, status'; 266 267 $res = $this->sqlite->query($sql, $opts); 268 $list = $this->sqlite->res2arr($res); 269 $this->sqlite->res_close($res); 270 271 $result = array(); 272 foreach ($list as $row) { 273 $pid = $row['pid']; 274 $user = $row['user']; 275 $status = $row['status']; 276 if (!isset($result[$pid])) $result[$pid] = array(); 277 $result[$pid][$user][$status] = (bool)$row['assigned']; 278 } 279 280 return $result; 281 } 282} 283