1910e7e15SAnna Dabrowska<?php 2910e7e15SAnna Dabrowska 3910e7e15SAnna Dabrowskanamespace dokuwiki\plugin\structpublish\meta; 4910e7e15SAnna Dabrowska 5f734c62fSAnna Dabrowskause dokuwiki\plugin\sqlite\SQLiteDB; 6f734c62fSAnna Dabrowska 7910e7e15SAnna Dabrowska/** 8910e7e15SAnna Dabrowska * Class Assignments 9910e7e15SAnna Dabrowska * 10910e7e15SAnna Dabrowska * Manages the assignment of users to pages and namespaces 11910e7e15SAnna Dabrowska * This is a singleton. Assignment data is only loaded once per request. 12910e7e15SAnna Dabrowska * 13910e7e15SAnna Dabrowska * @see \dokuwiki\plugin\struct\meta\Assignments 14910e7e15SAnna Dabrowska */ 15910e7e15SAnna Dabrowskaclass Assignments 16910e7e15SAnna Dabrowska{ 17f734c62fSAnna Dabrowska /** @var SQLiteDB */ 18910e7e15SAnna Dabrowska protected $sqlite; 19910e7e15SAnna Dabrowska 20910e7e15SAnna Dabrowska /** @var array All the assignments patterns */ 21910e7e15SAnna Dabrowska protected $patterns; 22910e7e15SAnna Dabrowska 23910e7e15SAnna Dabrowska /** @var Assignments */ 24910e7e15SAnna Dabrowska protected static $instance = null; 25910e7e15SAnna Dabrowska 26910e7e15SAnna Dabrowska /** 27910e7e15SAnna Dabrowska * Get the singleton instance of the Assignments 28910e7e15SAnna Dabrowska * 29*31e730e1SAnna Dabrowska * @param bool $forcereload create a new instance to reload the assignment data 30910e7e15SAnna Dabrowska * @return Assignments 31910e7e15SAnna Dabrowska */ 32910e7e15SAnna Dabrowska public static function getInstance($forcereload = false) 33910e7e15SAnna Dabrowska { 34910e7e15SAnna Dabrowska if (is_null(self::$instance) or $forcereload) { 35910e7e15SAnna Dabrowska $class = get_called_class(); 36910e7e15SAnna Dabrowska self::$instance = new $class(); 37910e7e15SAnna Dabrowska } 38910e7e15SAnna Dabrowska return self::$instance; 39910e7e15SAnna Dabrowska } 40910e7e15SAnna Dabrowska 41910e7e15SAnna Dabrowska /** 42910e7e15SAnna Dabrowska * Assignments constructor. 43910e7e15SAnna Dabrowska * 44910e7e15SAnna Dabrowska * Not public. Use Assignments::getInstance() instead 45910e7e15SAnna Dabrowska */ 46910e7e15SAnna Dabrowska protected function __construct() 47910e7e15SAnna Dabrowska { 48910e7e15SAnna Dabrowska /** @var \helper_plugin_structpublish_db $helper */ 49910e7e15SAnna Dabrowska $helper = plugin_load('helper', 'struct_db'); 50910e7e15SAnna Dabrowska $this->sqlite = $helper->getDB(); 51910e7e15SAnna Dabrowska 52910e7e15SAnna Dabrowska $this->loadPatterns(); 53910e7e15SAnna Dabrowska } 548b0ba635SAndreas Gohr 55910e7e15SAnna Dabrowska /** 56910e7e15SAnna Dabrowska * Load existing assignment patterns 57910e7e15SAnna Dabrowska */ 58910e7e15SAnna Dabrowska protected function loadPatterns() 59910e7e15SAnna Dabrowska { 60910e7e15SAnna Dabrowska $sql = 'SELECT * FROM structpublish_assignments_patterns ORDER BY pattern'; 61f734c62fSAnna Dabrowska $this->patterns = $this->sqlite->queryAll($sql); 62910e7e15SAnna Dabrowska } 63910e7e15SAnna Dabrowska 64910e7e15SAnna Dabrowska /** 65910e7e15SAnna Dabrowska * Add a new assignment pattern to the pattern table 66910e7e15SAnna Dabrowska * 67910e7e15SAnna Dabrowska * @param string $pattern 68910e7e15SAnna Dabrowska * @param string $user 69910e7e15SAnna Dabrowska * @param string $status 70910e7e15SAnna Dabrowska * @return bool 71910e7e15SAnna Dabrowska */ 72910e7e15SAnna Dabrowska public function addPattern($pattern, $user, $status) 73910e7e15SAnna Dabrowska { 74910e7e15SAnna Dabrowska // add the pattern 75910e7e15SAnna Dabrowska $sql = 'REPLACE INTO structpublish_assignments_patterns (pattern, user, status) VALUES (?,?,?)'; 76910e7e15SAnna Dabrowska $ok = (bool) $this->sqlite->query($sql, [$pattern, $user, $status]); 77910e7e15SAnna Dabrowska 78910e7e15SAnna Dabrowska // reload patterns 79910e7e15SAnna Dabrowska $this->loadPatterns(); 80910e7e15SAnna Dabrowska 81e6d97799SAnna Dabrowska // update assignments 82126b0b8eSAnna Dabrowska // fetch known pages 83126b0b8eSAnna Dabrowska /** @var \helper_plugin_structpublish_db $dbHelper */ 84126b0b8eSAnna Dabrowska $dbHelper = plugin_load('helper', 'structpublish_db'); 85126b0b8eSAnna Dabrowska $pids = $dbHelper->getPages(); 86126b0b8eSAnna Dabrowska 8750372189SAnna Dabrowska // wrap in transaction 8850372189SAnna Dabrowska $this->sqlite->query('BEGIN TRANSACTION'); 8950372189SAnna Dabrowska 90126b0b8eSAnna Dabrowska foreach ($pids as $pid) { 91126b0b8eSAnna Dabrowska $this->updatePageAssignments($pid); 92126b0b8eSAnna Dabrowska } 93910e7e15SAnna Dabrowska 9450372189SAnna Dabrowska $ok = $ok && $this->sqlite->query('COMMIT TRANSACTION'); 9550372189SAnna Dabrowska if (!$ok) { 9650372189SAnna Dabrowska $this->sqlite->query('ROLLBACK TRANSACTION'); 9750372189SAnna Dabrowska } 9850372189SAnna Dabrowska 99910e7e15SAnna Dabrowska return $ok; 100910e7e15SAnna Dabrowska } 101910e7e15SAnna Dabrowska 102910e7e15SAnna Dabrowska /** 103910e7e15SAnna Dabrowska * Remove an existing assignment pattern from the pattern table 104910e7e15SAnna Dabrowska * 105910e7e15SAnna Dabrowska * @param string $pattern 106910e7e15SAnna Dabrowska * @param string $user 107910e7e15SAnna Dabrowska * @param string $status 108910e7e15SAnna Dabrowska * @return bool 109910e7e15SAnna Dabrowska */ 110910e7e15SAnna Dabrowska public function removePattern($pattern, $user, $status) 111910e7e15SAnna Dabrowska { 112910e7e15SAnna Dabrowska // remove the pattern 113910e7e15SAnna Dabrowska $sql = 'DELETE FROM structpublish_assignments_patterns WHERE pattern = ? AND user = ? AND status = ?'; 114910e7e15SAnna Dabrowska $ok = (bool) $this->sqlite->query($sql, [$pattern, $user, $status]); 115910e7e15SAnna Dabrowska 116910e7e15SAnna Dabrowska // reload patterns 117910e7e15SAnna Dabrowska $this->loadPatterns(); 118910e7e15SAnna Dabrowska 119910e7e15SAnna Dabrowska // fetch possibly affected pages 120910e7e15SAnna Dabrowska $sql = 'SELECT pid FROM structpublish_assignments WHERE user = ? AND status = ?'; 121f734c62fSAnna Dabrowska $pagerows = $this->sqlite->queryAll($sql, [$user, $status]); 122910e7e15SAnna Dabrowska 12347759c1cSAnna Dabrowska // remove page assignments matching the pattern being removed 12447759c1cSAnna Dabrowska $ok = true; 125910e7e15SAnna Dabrowska foreach ($pagerows as $row) { 12647759c1cSAnna Dabrowska $ok = $ok && $this->deassignPage($row['pid'], $user, $status); 127910e7e15SAnna Dabrowska } 128910e7e15SAnna Dabrowska 129910e7e15SAnna Dabrowska return $ok; 130910e7e15SAnna Dabrowska } 131910e7e15SAnna Dabrowska 132910e7e15SAnna Dabrowska /** 133910e7e15SAnna Dabrowska * Updates all assignments of a given page against the current patterns 134910e7e15SAnna Dabrowska * 135910e7e15SAnna Dabrowska * @param string $pid 136910e7e15SAnna Dabrowska */ 137126b0b8eSAnna Dabrowska public function updatePageAssignments($pid, $reload = false) 138910e7e15SAnna Dabrowska { 139126b0b8eSAnna Dabrowska if ($reload) { 140910e7e15SAnna Dabrowska $this->loadPatterns(); 141126b0b8eSAnna Dabrowska } 142910e7e15SAnna Dabrowska $rules = $this->getPageAssignments($pid, true); 143910e7e15SAnna Dabrowska 144910e7e15SAnna Dabrowska foreach ($rules as $status => $users) { 145910e7e15SAnna Dabrowska foreach ($users as $user) { 146910e7e15SAnna Dabrowska $this->assignPage($pid, $user, $status); 147910e7e15SAnna Dabrowska } 148910e7e15SAnna Dabrowska } 149910e7e15SAnna Dabrowska 150f1c406c2SAnna Dabrowska // FIXME reevalute existing assignments for exclusion 151910e7e15SAnna Dabrowska } 152910e7e15SAnna Dabrowska 153910e7e15SAnna Dabrowska /** 154910e7e15SAnna Dabrowska * Clear all patterns - deassigns all pages 155910e7e15SAnna Dabrowska * 156910e7e15SAnna Dabrowska * This is mostly useful for testing and not used in the interface currently 157910e7e15SAnna Dabrowska * 158910e7e15SAnna Dabrowska * @param bool $full fully delete all previous assignments 159910e7e15SAnna Dabrowska * @return bool 160910e7e15SAnna Dabrowska */ 161910e7e15SAnna Dabrowska public function clear($full = false) 162910e7e15SAnna Dabrowska { 163910e7e15SAnna Dabrowska $sql = 'DELETE FROM structpublish_assignments_patterns'; 164910e7e15SAnna Dabrowska $ok = (bool) $this->sqlite->query($sql); 165910e7e15SAnna Dabrowska 166910e7e15SAnna Dabrowska if ($full) { 167910e7e15SAnna Dabrowska $sql = 'DELETE FROM structpublish_assignments'; 168910e7e15SAnna Dabrowska } else { 169910e7e15SAnna Dabrowska $sql = 'UPDATE structpublish_assignments SET assigned = 0'; 170910e7e15SAnna Dabrowska } 171910e7e15SAnna Dabrowska $ok = $ok && (bool) $this->sqlite->query($sql); 172910e7e15SAnna Dabrowska 173910e7e15SAnna Dabrowska // reload patterns 174910e7e15SAnna Dabrowska $this->loadPatterns(); 175910e7e15SAnna Dabrowska 176910e7e15SAnna Dabrowska return $ok; 177910e7e15SAnna Dabrowska } 178910e7e15SAnna Dabrowska 179910e7e15SAnna Dabrowska /** 180910e7e15SAnna Dabrowska * Add page to assignments 181910e7e15SAnna Dabrowska * 182910e7e15SAnna Dabrowska * @param string $page 183910e7e15SAnna Dabrowska * @param string $user 184910e7e15SAnna Dabrowska * @param string $status 185910e7e15SAnna Dabrowska * @return bool 186910e7e15SAnna Dabrowska */ 187910e7e15SAnna Dabrowska public function assignPage($page, $user = null, $status = null) 188910e7e15SAnna Dabrowska { 189910e7e15SAnna Dabrowska $sql = 'REPLACE INTO structpublish_assignments (pid, user, status, assigned) VALUES (?, ?, ?, 1)'; 190910e7e15SAnna Dabrowska return (bool) $this->sqlite->query($sql, [$page, $user, $status]); 191910e7e15SAnna Dabrowska } 192910e7e15SAnna Dabrowska 193910e7e15SAnna Dabrowska /** 194910e7e15SAnna Dabrowska * Remove page from assignments 195910e7e15SAnna Dabrowska * 196910e7e15SAnna Dabrowska * @param string $page 197910e7e15SAnna Dabrowska * @param string $user 198910e7e15SAnna Dabrowska * @return bool 199910e7e15SAnna Dabrowska */ 200910e7e15SAnna Dabrowska public function deassignPage($page, $user, $status) 201910e7e15SAnna Dabrowska { 20247759c1cSAnna Dabrowska $sql = 'UPDATE structpublish_assignments SET assigned = 0 WHERE pid = ? AND user = ? AND status = ?'; 203910e7e15SAnna Dabrowska return (bool) $this->sqlite->query($sql, [$page, $user, $status]); 204910e7e15SAnna Dabrowska } 205910e7e15SAnna Dabrowska 206910e7e15SAnna Dabrowska /** 207910e7e15SAnna Dabrowska * Get the whole pattern table 208910e7e15SAnna Dabrowska * 209910e7e15SAnna Dabrowska * @return array 210910e7e15SAnna Dabrowska */ 211910e7e15SAnna Dabrowska public function getAllPatterns() 212910e7e15SAnna Dabrowska { 213910e7e15SAnna Dabrowska return $this->patterns; 214910e7e15SAnna Dabrowska } 215910e7e15SAnna Dabrowska 216910e7e15SAnna Dabrowska /** 217e31c94d7SAndreas Gohr * Returns a list of user/group string lists per status assigned to the given page 218910e7e15SAnna Dabrowska * 219910e7e15SAnna Dabrowska * @param string $page 220910e7e15SAnna Dabrowska * @param bool $checkpatterns Should the current patterns be re-evaluated? 221e31c94d7SAndreas Gohr * @return array users assigned [role => [user, ...], ...] 222910e7e15SAnna Dabrowska */ 223910e7e15SAnna Dabrowska public function getPageAssignments($page, $checkpatterns = true) 224910e7e15SAnna Dabrowska { 225910e7e15SAnna Dabrowska $rules = []; 226910e7e15SAnna Dabrowska $page = cleanID($page); 227910e7e15SAnna Dabrowska 228910e7e15SAnna Dabrowska if ($checkpatterns) { 229910e7e15SAnna Dabrowska $helper = plugin_load('helper', 'structpublish_assignments'); 230910e7e15SAnna Dabrowska // evaluate patterns 231910e7e15SAnna Dabrowska $pns = ':' . getNS($page) . ':'; 232910e7e15SAnna Dabrowska foreach ($this->patterns as $row) { 233910e7e15SAnna Dabrowska if ($helper->matchPagePattern($row['pattern'], $page, $pns)) { 234910e7e15SAnna Dabrowska $rules[$row['status']][] = $row['user']; 235910e7e15SAnna Dabrowska } 236910e7e15SAnna Dabrowska } 237910e7e15SAnna Dabrowska } else { 238910e7e15SAnna Dabrowska // just select 239910e7e15SAnna Dabrowska $sql = 'SELECT user, status FROM structpublish_assignments WHERE pid = ? AND assigned = 1'; 240f734c62fSAnna Dabrowska $list = $this->sqlite->queryAll($sql, [$page]); 241910e7e15SAnna Dabrowska foreach ($list as $row) { 242910e7e15SAnna Dabrowska $rules[$row['status']][] = $row['user']; 243910e7e15SAnna Dabrowska } 244910e7e15SAnna Dabrowska } 245910e7e15SAnna Dabrowska 246910e7e15SAnna Dabrowska return $rules; 247910e7e15SAnna Dabrowska } 248910e7e15SAnna Dabrowska 249910e7e15SAnna Dabrowska /** 250910e7e15SAnna Dabrowska * Get the pages known to struct and their assignment state 251910e7e15SAnna Dabrowska * 252910e7e15SAnna Dabrowska * @param bool $assignedonly limit results to currently assigned only 253910e7e15SAnna Dabrowska * @return array 254910e7e15SAnna Dabrowska */ 255910e7e15SAnna Dabrowska public function getPages($assignedOnly = false) 256910e7e15SAnna Dabrowska { 257910e7e15SAnna Dabrowska $sql = 'SELECT pid, user, status, assigned FROM structpublish_assignments WHERE 1=1'; 258910e7e15SAnna Dabrowska 259910e7e15SAnna Dabrowska $opts = array(); 260910e7e15SAnna Dabrowska 261910e7e15SAnna Dabrowska if ($assignedOnly) { 262910e7e15SAnna Dabrowska $sql .= ' AND assigned = 1'; 263910e7e15SAnna Dabrowska } 264910e7e15SAnna Dabrowska 265910e7e15SAnna Dabrowska $sql .= ' ORDER BY pid, user, status'; 266910e7e15SAnna Dabrowska 267f734c62fSAnna Dabrowska $list = $this->sqlite->queryAll($sql, $opts); 268910e7e15SAnna Dabrowska 269910e7e15SAnna Dabrowska $result = array(); 270910e7e15SAnna Dabrowska foreach ($list as $row) { 271910e7e15SAnna Dabrowska $pid = $row['pid']; 272910e7e15SAnna Dabrowska $user = $row['user']; 273910e7e15SAnna Dabrowska $status = $row['status']; 2748b0ba635SAndreas Gohr if (!isset($result[$pid])) { 2758b0ba635SAndreas Gohr $result[$pid] = array(); 2768b0ba635SAndreas Gohr } 277910e7e15SAnna Dabrowska $result[$pid][$user][$status] = (bool) $row['assigned']; 278910e7e15SAnna Dabrowska } 279910e7e15SAnna Dabrowska 280910e7e15SAnna Dabrowska return $result; 281910e7e15SAnna Dabrowska } 282364fb00aSAnna Dabrowska 283364fb00aSAnna Dabrowska /** 284f734c62fSAnna Dabrowska * @return SQLiteDB 285364fb00aSAnna Dabrowska */ 286364fb00aSAnna Dabrowska public function getSqlite() 287364fb00aSAnna Dabrowska { 288364fb00aSAnna Dabrowska return $this->sqlite; 289364fb00aSAnna Dabrowska } 290910e7e15SAnna Dabrowska} 291