14d6d17d0SAndreas Gohr<?php 2c6d8c1d9SAndreas Gohr 3c6d8c1d9SAndreas Gohruse dokuwiki\Extension\AuthPlugin; 4c6d8c1d9SAndreas Gohr 54d6d17d0SAndreas Gohr/** 64d6d17d0SAndreas Gohr * DokuWiki Plugin acknowledge (Helper Component) 74d6d17d0SAndreas Gohr * 84d6d17d0SAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 94d6d17d0SAndreas Gohr * @author Andreas Gohr, Anna Dabrowska <dokuwiki@cosmocode.de> 104d6d17d0SAndreas Gohr */ 114d6d17d0SAndreas Gohrclass helper_plugin_acknowledge extends DokuWiki_Plugin 124d6d17d0SAndreas Gohr{ 134d6d17d0SAndreas Gohr 14cabb51d3SAndreas Gohr /** 15cabb51d3SAndreas Gohr * @return helper_plugin_sqlite|null 16cabb51d3SAndreas Gohr */ 17cabb51d3SAndreas Gohr public function getDB() 18cabb51d3SAndreas Gohr { 19cabb51d3SAndreas Gohr /** @var \helper_plugin_sqlite $sqlite */ 20cabb51d3SAndreas Gohr $sqlite = plugin_load('helper', 'sqlite'); 21cabb51d3SAndreas Gohr if ($sqlite === null) { 22cabb51d3SAndreas Gohr msg($this->getLang('error sqlite plugin missing'), -1); 23cabb51d3SAndreas Gohr return null; 24cabb51d3SAndreas Gohr } 25*f09444ffSAndreas Gohr $sqlite->getAdapter()->setUseNativeAlter(true); 26cabb51d3SAndreas Gohr if (!$sqlite->init('acknowledgement', __DIR__ . '/db')) { 27cabb51d3SAndreas Gohr return null; 28cabb51d3SAndreas Gohr } 29cabb51d3SAndreas Gohr 309c3eae1eSAnna Dabrowska $this->registerUDF($sqlite); 319c3eae1eSAnna Dabrowska 32cabb51d3SAndreas Gohr return $sqlite; 33cabb51d3SAndreas Gohr } 34cabb51d3SAndreas Gohr 35cabb51d3SAndreas Gohr /** 369c3eae1eSAnna Dabrowska * Register user defined functions 379c3eae1eSAnna Dabrowska * 389c3eae1eSAnna Dabrowska * @param helper_plugin_sqlite $sqlite 399c3eae1eSAnna Dabrowska */ 409c3eae1eSAnna Dabrowska protected function registerUDF($sqlite) 419c3eae1eSAnna Dabrowska { 429c3eae1eSAnna Dabrowska $sqlite->create_function('AUTH_ISMEMBER', [$this, 'auth_isMember'], -1); 43*f09444ffSAndreas Gohr $sqlite->create_function('MATCHES_PAGE_PATTERN', [$this, 'matchPagePattern'], 2); 449c3eae1eSAnna Dabrowska } 459c3eae1eSAnna Dabrowska 469c3eae1eSAnna Dabrowska /** 479c3eae1eSAnna Dabrowska * Wrapper function for auth_isMember which accepts groups as string 489c3eae1eSAnna Dabrowska * 499c3eae1eSAnna Dabrowska * @param string $memberList 509c3eae1eSAnna Dabrowska * @param string $user 519c3eae1eSAnna Dabrowska * @param string $groups 529c3eae1eSAnna Dabrowska * @return bool 539c3eae1eSAnna Dabrowska */ 549c3eae1eSAnna Dabrowska public function auth_isMember($memberList, $user, $groups) 559c3eae1eSAnna Dabrowska { 5695113ed8SAnna Dabrowska return auth_isMember($memberList, $user, explode('///', $groups)); 579c3eae1eSAnna Dabrowska } 589c3eae1eSAnna Dabrowska 599c3eae1eSAnna Dabrowska /** 60ef3ab392SAndreas Gohr * Delete a page 61ef3ab392SAndreas Gohr * 62ef3ab392SAndreas Gohr * Cascades to delete all assigned data, etc. 63ef3ab392SAndreas Gohr * 64ef3ab392SAndreas Gohr * @param string $page Page ID 65ef3ab392SAndreas Gohr */ 66ef3ab392SAndreas Gohr public function removePage($page) 67ef3ab392SAndreas Gohr { 68ef3ab392SAndreas Gohr $sqlite = $this->getDB(); 69ef3ab392SAndreas Gohr if (!$sqlite) return; 70ef3ab392SAndreas Gohr 71ef3ab392SAndreas Gohr $sql = "DELETE FROM pages WHERE page = ?"; 72ef3ab392SAndreas Gohr $sqlite->query($sql, $page); 73ef3ab392SAndreas Gohr } 74ef3ab392SAndreas Gohr 75ef3ab392SAndreas Gohr /** 765dee13f7SAnna Dabrowska * Update last modified date of page if content has changed 77ef3ab392SAndreas Gohr * 78ef3ab392SAndreas Gohr * @param string $page Page ID 79ef3ab392SAndreas Gohr * @param int $lastmod timestamp of last non-minor change 80ef3ab392SAndreas Gohr */ 815dee13f7SAnna Dabrowska public function storePageDate($page, $lastmod, $newContent) 82ef3ab392SAndreas Gohr { 83ed4e8871SAnna Dabrowska $changelog = new \dokuwiki\ChangeLog\PageChangeLog($page); 84789aa26fSAnna Dabrowska $revs = $changelog->getRevisions(0, 1); 85ed4e8871SAnna Dabrowska 86ed4e8871SAnna Dabrowska // compare content 87ed4e8871SAnna Dabrowska $oldContent = str_replace(NL, '', io_readFile(wikiFN($page, $revs[0]))); 88ed4e8871SAnna Dabrowska $newContent = str_replace(NL, '', $newContent); 89ed4e8871SAnna Dabrowska if ($oldContent === $newContent) return; 90ed4e8871SAnna Dabrowska 91ef3ab392SAndreas Gohr $sqlite = $this->getDB(); 92ef3ab392SAndreas Gohr if (!$sqlite) return; 93ef3ab392SAndreas Gohr 94ef3ab392SAndreas Gohr $sql = "REPLACE INTO pages (page, lastmod) VALUES (?,?)"; 95ef3ab392SAndreas Gohr $sqlite->query($sql, $page, $lastmod); 96ef3ab392SAndreas Gohr } 97ef3ab392SAndreas Gohr 98ef3ab392SAndreas Gohr /** 99*f09444ffSAndreas Gohr * Clears direct assignments for a page 100*f09444ffSAndreas Gohr * 101cabb51d3SAndreas Gohr * @param string $page Page ID 102cabb51d3SAndreas Gohr */ 103*f09444ffSAndreas Gohr public function clearPageAssignments($page) 104cabb51d3SAndreas Gohr { 105cabb51d3SAndreas Gohr $sqlite = $this->getDB(); 106cabb51d3SAndreas Gohr if (!$sqlite) return; 107cabb51d3SAndreas Gohr 108*f09444ffSAndreas Gohr $sql = "UPDATE assignments SET pageassignees = '' WHERE page = ?"; 109*f09444ffSAndreas Gohr $sqlite->query($sql, $page); 110*f09444ffSAndreas Gohr } 111*f09444ffSAndreas Gohr 112*f09444ffSAndreas Gohr /** 113*f09444ffSAndreas Gohr * Get all the assignment patterns 114*f09444ffSAndreas Gohr * @return array (pattern => assignees) 115*f09444ffSAndreas Gohr */ 116*f09444ffSAndreas Gohr public function getAssignmentPatterns() 117*f09444ffSAndreas Gohr { 118*f09444ffSAndreas Gohr $sqlite = $this->getDB(); 119*f09444ffSAndreas Gohr if (!$sqlite) return []; 120*f09444ffSAndreas Gohr 121*f09444ffSAndreas Gohr $sql = "SELECT pattern, assignees FROM assignments_patterns"; 122*f09444ffSAndreas Gohr $result = $sqlite->query($sql); 123*f09444ffSAndreas Gohr $patterns = $sqlite->res2arr($result); 124*f09444ffSAndreas Gohr $sqlite->res_close($result); 125*f09444ffSAndreas Gohr 126*f09444ffSAndreas Gohr return array_combine( 127*f09444ffSAndreas Gohr array_column($patterns, 'pattern'), 128*f09444ffSAndreas Gohr array_column($patterns, 'assignees') 129*f09444ffSAndreas Gohr ); 130*f09444ffSAndreas Gohr } 131*f09444ffSAndreas Gohr 132*f09444ffSAndreas Gohr /** 133*f09444ffSAndreas Gohr * Save new assignment patterns 134*f09444ffSAndreas Gohr * 135*f09444ffSAndreas Gohr * This resaves all patterns and reapplies them 136*f09444ffSAndreas Gohr * 137*f09444ffSAndreas Gohr * @param array $patterns (pattern => assignees) 138*f09444ffSAndreas Gohr */ 139*f09444ffSAndreas Gohr public function saveAssignmentPatterns($patterns) { 140*f09444ffSAndreas Gohr $sqlite = $this->getDB(); 141*f09444ffSAndreas Gohr if (!$sqlite) return; 142*f09444ffSAndreas Gohr 143*f09444ffSAndreas Gohr $sqlite->query('BEGIN TRANSACTION'); 144*f09444ffSAndreas Gohr 145*f09444ffSAndreas Gohr /** @noinsp0ection SqlWithoutWhere Remove all assignments */ 146*f09444ffSAndreas Gohr $sql = "UPDATE assignments SET autoassignees = ''"; 147*f09444ffSAndreas Gohr $sqlite->query($sql); 148*f09444ffSAndreas Gohr 149*f09444ffSAndreas Gohr /** @noinspection SqlWithoutWhere Remove all patterns */ 150*f09444ffSAndreas Gohr $sql = "DELETE FROM assignments_patterns"; 151*f09444ffSAndreas Gohr $sqlite->query($sql); 152*f09444ffSAndreas Gohr 153*f09444ffSAndreas Gohr // insert new patterns and gather affected pages 154*f09444ffSAndreas Gohr $pages = []; 155*f09444ffSAndreas Gohr 156*f09444ffSAndreas Gohr $sql = "REPLACE INTO assignments_patterns (pattern, assignees) VALUES (?,?)"; 157*f09444ffSAndreas Gohr foreach ($patterns as $pattern => $assignees) { 158*f09444ffSAndreas Gohr $pattern = trim($pattern); 159*f09444ffSAndreas Gohr $assignees = trim($assignees); 160*f09444ffSAndreas Gohr if (!$pattern || !$assignees) continue; 161*f09444ffSAndreas Gohr $sqlite->query($sql, $pattern, $assignees); 162*f09444ffSAndreas Gohr 163*f09444ffSAndreas Gohr // patterns may overlap, so we need to gather all affected pages first 164*f09444ffSAndreas Gohr $affectedPages = $this->getPagesMatchingPattern($pattern); 165*f09444ffSAndreas Gohr foreach ($affectedPages as $page) { 166*f09444ffSAndreas Gohr if(isset($pages[$page])) { 167*f09444ffSAndreas Gohr $pages[$page] .= ',' . $assignees; 168*f09444ffSAndreas Gohr } else { 169*f09444ffSAndreas Gohr $pages[$page] = $assignees; 170*f09444ffSAndreas Gohr } 171*f09444ffSAndreas Gohr } 172*f09444ffSAndreas Gohr } 173*f09444ffSAndreas Gohr 174*f09444ffSAndreas Gohr $sql = "INSERT INTO assignments (page, autoassignees) VALUES (?, ?) 175*f09444ffSAndreas Gohr ON CONFLICT(page) 176*f09444ffSAndreas Gohr DO UPDATE SET autoassignees = ?"; 177*f09444ffSAndreas Gohr foreach ($pages as $page => $assignees) { 178*f09444ffSAndreas Gohr // remove duplicates and empty entries 179*f09444ffSAndreas Gohr $assignees = join(',', array_unique(array_filter(array_map('trim', explode(',', $assignees))))); 180*f09444ffSAndreas Gohr $sqlite->query($sql, $page, $assignees, $assignees); 181*f09444ffSAndreas Gohr } 182*f09444ffSAndreas Gohr 183*f09444ffSAndreas Gohr $sqlite->query('COMMIT TRANSACTION'); 184*f09444ffSAndreas Gohr } 185*f09444ffSAndreas Gohr 186*f09444ffSAndreas Gohr /** 187*f09444ffSAndreas Gohr * Get all known pages that match the given pattern 188*f09444ffSAndreas Gohr * 189*f09444ffSAndreas Gohr * @param $pattern 190*f09444ffSAndreas Gohr * @return string[] 191*f09444ffSAndreas Gohr */ 192*f09444ffSAndreas Gohr public function getPagesMatchingPattern($pattern) { 193*f09444ffSAndreas Gohr $sqlite = $this->getDB(); 194*f09444ffSAndreas Gohr if (!$sqlite) return []; 195*f09444ffSAndreas Gohr 196*f09444ffSAndreas Gohr $sql = "SELECT page FROM pages WHERE MATCHES_PAGE_PATTERN(?, page)"; 197*f09444ffSAndreas Gohr $result = $sqlite->query($sql, $pattern); 198*f09444ffSAndreas Gohr $pages = $sqlite->res2arr($result); 199*f09444ffSAndreas Gohr $sqlite->res_close($result); 200*f09444ffSAndreas Gohr 201*f09444ffSAndreas Gohr return array_column($pages, 'page'); 202*f09444ffSAndreas Gohr } 203*f09444ffSAndreas Gohr 204*f09444ffSAndreas Gohr /** 205*f09444ffSAndreas Gohr * Fills the page index with all unknown pages from the fulltext index 206*f09444ffSAndreas Gohr * @return void 207*f09444ffSAndreas Gohr */ 208*f09444ffSAndreas Gohr public function updatePageIndex() { 209*f09444ffSAndreas Gohr $sqlite = $this->getDB(); 210*f09444ffSAndreas Gohr if (!$sqlite) return; 211*f09444ffSAndreas Gohr 212*f09444ffSAndreas Gohr $pages = idx_getIndex('page',''); 213*f09444ffSAndreas Gohr $sql = "INSERT OR IGNORE INTO pages (page, lastmod) VALUES (?,?)"; 214*f09444ffSAndreas Gohr 215*f09444ffSAndreas Gohr $sqlite->query('BEGIN TRANSACTION'); 216*f09444ffSAndreas Gohr foreach ($pages as $page) { 217*f09444ffSAndreas Gohr $page = trim($page); 218*f09444ffSAndreas Gohr $lastmod = @filemtime(wikiFN($page)); 219*f09444ffSAndreas Gohr if($lastmod) { 220*f09444ffSAndreas Gohr $sqlite->query($sql, $page, $lastmod); 221*f09444ffSAndreas Gohr } 222*f09444ffSAndreas Gohr } 223*f09444ffSAndreas Gohr $sqlite->query('COMMIT TRANSACTION'); 224*f09444ffSAndreas Gohr } 225*f09444ffSAndreas Gohr 226*f09444ffSAndreas Gohr /** 227*f09444ffSAndreas Gohr * Set assignees for a given page as manually specified 228*f09444ffSAndreas Gohr * 229*f09444ffSAndreas Gohr * @param string $page Page ID 230*f09444ffSAndreas Gohr * @param string $assignees 231*f09444ffSAndreas Gohr * @return void 232*f09444ffSAndreas Gohr */ 233*f09444ffSAndreas Gohr public function setPageAssignees($page, $assignees) { 234*f09444ffSAndreas Gohr $sqlite = $this->getDB(); 235*f09444ffSAndreas Gohr if (!$sqlite) return; 236*f09444ffSAndreas Gohr 237*f09444ffSAndreas Gohr $assignees = join(',', array_unique(array_filter(array_map('trim', explode(',', $assignees))))); 238*f09444ffSAndreas Gohr 239*f09444ffSAndreas Gohr $sql = "REPLACE INTO assignments ('page', 'pageassignees') VALUES (?,?)"; 240cabb51d3SAndreas Gohr $sqlite->query($sql, $page, $assignees); 241cabb51d3SAndreas Gohr } 242cabb51d3SAndreas Gohr 243ef3ab392SAndreas Gohr /** 244*f09444ffSAndreas Gohr * Set assignees for a given page from the patterns 245*f09444ffSAndreas Gohr 246ef3ab392SAndreas Gohr * @param string $page Page ID 247ef3ab392SAndreas Gohr */ 248*f09444ffSAndreas Gohr public function setAutoAssignees($page) 249ef3ab392SAndreas Gohr { 250ef3ab392SAndreas Gohr $sqlite = $this->getDB(); 251ef3ab392SAndreas Gohr if (!$sqlite) return; 252cabb51d3SAndreas Gohr 253*f09444ffSAndreas Gohr $patterns = $this->getAssignmentPatterns(); 254*f09444ffSAndreas Gohr 255*f09444ffSAndreas Gohr // given assignees 256*f09444ffSAndreas Gohr $assignees = ''; 257*f09444ffSAndreas Gohr 258*f09444ffSAndreas Gohr // find all patterns that match the page and add the configured assignees 259*f09444ffSAndreas Gohr foreach ($patterns as $pattern => $assignees) { 260*f09444ffSAndreas Gohr if ($this->matchPagePattern($pattern, $page)) { 261*f09444ffSAndreas Gohr $assignees .= ',' . $assignees; 262*f09444ffSAndreas Gohr } 263*f09444ffSAndreas Gohr } 264*f09444ffSAndreas Gohr 265*f09444ffSAndreas Gohr // remove duplicates and empty entries 266*f09444ffSAndreas Gohr $assignees = join(',', array_unique(array_filter(array_map('trim', explode(',', $assignees))))); 267*f09444ffSAndreas Gohr 268*f09444ffSAndreas Gohr // store the assignees 269*f09444ffSAndreas Gohr $sql = "REPLACE INTO assignments ('page', 'autoassignees') VALUES (?,?)"; 270*f09444ffSAndreas Gohr $sqlite->query($sql, $page, $assignees); 271ef3ab392SAndreas Gohr } 272ef3ab392SAndreas Gohr 273ef3ab392SAndreas Gohr /** 274ef3ab392SAndreas Gohr * Is the given user one of the assignees for this page 275ef3ab392SAndreas Gohr * 276ef3ab392SAndreas Gohr * @param string $page Page ID 277ef3ab392SAndreas Gohr * @param string $user user name to check 278ef3ab392SAndreas Gohr * @param string[] $groups groups this user is in 279ef3ab392SAndreas Gohr * @return bool 280ef3ab392SAndreas Gohr */ 281ef3ab392SAndreas Gohr public function isUserAssigned($page, $user, $groups) 282ef3ab392SAndreas Gohr { 283ef3ab392SAndreas Gohr $sqlite = $this->getDB(); 284ef3ab392SAndreas Gohr if (!$sqlite) return false; 285ef3ab392SAndreas Gohr 286*f09444ffSAndreas Gohr $sql = "SELECT pageassignees,autoassignees FROM assignments WHERE page = ?"; 287ef3ab392SAndreas Gohr $result = $sqlite->query($sql, $page); 288*f09444ffSAndreas Gohr $row = (string)$sqlite->res2row($result); 289ef3ab392SAndreas Gohr $sqlite->res_close($result); 290*f09444ffSAndreas Gohr $assignees = $row['pageassignees'] . ',' . $row['autoassignees']; 291ef3ab392SAndreas Gohr return auth_isMember($assignees, $user, $groups); 292ef3ab392SAndreas Gohr } 293ef3ab392SAndreas Gohr 294ef3ab392SAndreas Gohr /** 295ef3ab392SAndreas Gohr * Has the given user acknowledged the given page? 296ef3ab392SAndreas Gohr * 297ef3ab392SAndreas Gohr * @param string $page 298ef3ab392SAndreas Gohr * @param string $user 2995773dd37SAnna Dabrowska * @return bool|int timestamp of acknowledgement or false 300ef3ab392SAndreas Gohr */ 301ef3ab392SAndreas Gohr public function hasUserAcknowledged($page, $user) 302ef3ab392SAndreas Gohr { 303ef3ab392SAndreas Gohr $sqlite = $this->getDB(); 304ef3ab392SAndreas Gohr if (!$sqlite) return false; 305ef3ab392SAndreas Gohr 306ef3ab392SAndreas Gohr $sql = "SELECT ack 307ef3ab392SAndreas Gohr FROM acks A, pages B 308ef3ab392SAndreas Gohr WHERE A.page = B.page 3095773dd37SAnna Dabrowska AND A.page = ? 3105773dd37SAnna Dabrowska AND A.user = ? 311ef3ab392SAndreas Gohr AND A.ack >= B.lastmod"; 312ef3ab392SAndreas Gohr 313ef3ab392SAndreas Gohr $result = $sqlite->query($sql, $page, $user); 314ef3ab392SAndreas Gohr $acktime = $sqlite->res2single($result); 315ef3ab392SAndreas Gohr $sqlite->res_close($result); 316ef3ab392SAndreas Gohr 317ef3ab392SAndreas Gohr return $acktime ? (int)$acktime : false; 318ef3ab392SAndreas Gohr } 3195773dd37SAnna Dabrowska 3205773dd37SAnna Dabrowska /** 321d9a8334dSAnna Dabrowska * Timestamp of the latest acknowledgment of the given page 322d9a8334dSAnna Dabrowska * by the given user 323d9a8334dSAnna Dabrowska * 324d9a8334dSAnna Dabrowska * @param string $page 325d9a8334dSAnna Dabrowska * @param string $user 326d9a8334dSAnna Dabrowska * @return bool|string 327d9a8334dSAnna Dabrowska */ 328d9a8334dSAnna Dabrowska public function getLatestUserAcknowledgement($page, $user) 329d9a8334dSAnna Dabrowska { 330d9a8334dSAnna Dabrowska $sqlite = $this->getDB(); 331d9a8334dSAnna Dabrowska if (!$sqlite) return false; 332d9a8334dSAnna Dabrowska 333d9a8334dSAnna Dabrowska $sql = "SELECT MAX(ack) 334d9a8334dSAnna Dabrowska FROM acks 335d9a8334dSAnna Dabrowska WHERE page = ? 336d9a8334dSAnna Dabrowska AND user = ?"; 337d9a8334dSAnna Dabrowska 338d9a8334dSAnna Dabrowska $result = $sqlite->query($sql, $page, $user); 339d9a8334dSAnna Dabrowska $latestAck = $sqlite->res2single($result); 340d9a8334dSAnna Dabrowska $sqlite->res_close($result); 341d9a8334dSAnna Dabrowska 342d9a8334dSAnna Dabrowska return $latestAck; 343d9a8334dSAnna Dabrowska } 344d9a8334dSAnna Dabrowska 345d9a8334dSAnna Dabrowska /** 3465773dd37SAnna Dabrowska * Save user's acknowledgement for a given page 3475773dd37SAnna Dabrowska * 3485773dd37SAnna Dabrowska * @param string $page 3495773dd37SAnna Dabrowska * @param string $user 3505773dd37SAnna Dabrowska * @return bool 3515773dd37SAnna Dabrowska */ 3525773dd37SAnna Dabrowska public function saveAcknowledgement($page, $user) 3535773dd37SAnna Dabrowska { 3545773dd37SAnna Dabrowska $sqlite = $this->getDB(); 3555773dd37SAnna Dabrowska if (!$sqlite) return false; 3565773dd37SAnna Dabrowska 3578e55e483SAnna Dabrowska $sql = "INSERT INTO acks (page, user, ack) VALUES (?,?, strftime('%s','now'))"; 3585773dd37SAnna Dabrowska 3595773dd37SAnna Dabrowska $result = $sqlite->query($sql, $page, $user); 3605773dd37SAnna Dabrowska $sqlite->res_close($result); 3615773dd37SAnna Dabrowska return true; 3625773dd37SAnna Dabrowska 3635773dd37SAnna Dabrowska } 36474126d4bSAnna Dabrowska 36574126d4bSAnna Dabrowska /** 36660ed3784SAnna Dabrowska * Fetch all assignments for a given user, with additional page information, 36760ed3784SAnna Dabrowska * filtering already granted acknowledgements. 36874126d4bSAnna Dabrowska * 36974126d4bSAnna Dabrowska * @param string $user 3708c50976eSAnna Dabrowska * @param array $groups 37174126d4bSAnna Dabrowska * @return array|bool 37274126d4bSAnna Dabrowska */ 3738c50976eSAnna Dabrowska public function getUserAssignments($user, $groups) 37474126d4bSAnna Dabrowska { 37574126d4bSAnna Dabrowska $sqlite = $this->getDB(); 37674126d4bSAnna Dabrowska if (!$sqlite) return false; 37774126d4bSAnna Dabrowska 378*f09444ffSAndreas Gohr $sql = "SELECT A.page, A.pageassignees, A.autoassignees, B.lastmod, C.user, C.ack FROM assignments A 37974126d4bSAnna Dabrowska JOIN pages B 38074126d4bSAnna Dabrowska ON A.page = B.page 38160ed3784SAnna Dabrowska LEFT JOIN acks C 3822541208bSAnna Dabrowska ON A.page = C.page AND ( (C.user = ? AND C.ack > B.lastmod) ) 383*f09444ffSAndreas Gohr WHERE AUTH_ISMEMBER(A.pageassignees || ',' || A.autoassignees , ? , ?) 3848e55e483SAnna Dabrowska AND ack IS NULL"; 38574126d4bSAnna Dabrowska 3862541208bSAnna Dabrowska $result = $sqlite->query($sql, $user, $user, implode('///', $groups)); 38774126d4bSAnna Dabrowska $assignments = $sqlite->res2arr($result); 38874126d4bSAnna Dabrowska $sqlite->res_close($result); 38974126d4bSAnna Dabrowska 39074126d4bSAnna Dabrowska return $assignments; 39174126d4bSAnna Dabrowska } 39274126d4bSAnna Dabrowska 39374126d4bSAnna Dabrowska /** 394863b6e48SAndreas Gohr * Get all pages a user needs to acknowledge and the last acknowledge date 395d6011abdSAnna Dabrowska * 396863b6e48SAndreas Gohr * @param string $user 397863b6e48SAndreas Gohr * @param array $groups 398d6011abdSAnna Dabrowska * @return array|bool 399d6011abdSAnna Dabrowska */ 400863b6e48SAndreas Gohr public function getUserAcknowledgements($user, $groups) 401d6011abdSAnna Dabrowska { 402d6011abdSAnna Dabrowska $sqlite = $this->getDB(); 403d6011abdSAnna Dabrowska if (!$sqlite) return false; 404d6011abdSAnna Dabrowska 405*f09444ffSAndreas Gohr $sql = "SELECT A.page, A.pageassignees, A.autoassignees, B.lastmod, C.user, MAX(C.ack) AS ack 406863b6e48SAndreas Gohr FROM assignments A 407863b6e48SAndreas Gohr JOIN pages B 408863b6e48SAndreas Gohr ON A.page = B.page 409863b6e48SAndreas Gohr LEFT JOIN acks C 410863b6e48SAndreas Gohr ON A.page = C.page AND C.user = ? 411*f09444ffSAndreas Gohr WHERE AUTH_ISMEMBER(A.pageassignees || ',' || A.autoassignees, ? , ?) 412863b6e48SAndreas Gohr GROUP BY A.page 413863b6e48SAndreas Gohr ORDER BY A.page 414863b6e48SAndreas Gohr "; 415863b6e48SAndreas Gohr 416863b6e48SAndreas Gohr $result = $sqlite->query($sql, $user, $user, implode('///', $groups)); 417863b6e48SAndreas Gohr $assignments = $sqlite->res2arr($result); 418863b6e48SAndreas Gohr $sqlite->res_close($result); 419863b6e48SAndreas Gohr 420863b6e48SAndreas Gohr return $assignments; 421863b6e48SAndreas Gohr } 422863b6e48SAndreas Gohr 423863b6e48SAndreas Gohr /** 424c6d8c1d9SAndreas Gohr * Resolve names of users assigned to a given page 425c6d8c1d9SAndreas Gohr * 426c6d8c1d9SAndreas Gohr * This can be slow on huge user bases! 427c6d8c1d9SAndreas Gohr * 428c6d8c1d9SAndreas Gohr * @param string $page 429c6d8c1d9SAndreas Gohr * @return array|false 430c6d8c1d9SAndreas Gohr */ 431c6d8c1d9SAndreas Gohr public function getPageAssignees($page) 432c6d8c1d9SAndreas Gohr { 433c6d8c1d9SAndreas Gohr $sqlite = $this->getDB(); 434c6d8c1d9SAndreas Gohr if (!$sqlite) return false; 435c6d8c1d9SAndreas Gohr /** @var AuthPlugin $auth */ 436c6d8c1d9SAndreas Gohr global $auth; 437c6d8c1d9SAndreas Gohr 438*f09444ffSAndreas Gohr $sql = "SELECT pageassignees || ',' || autoassignees AS 'assignments' 439c6d8c1d9SAndreas Gohr FROM assignments 440c6d8c1d9SAndreas Gohr WHERE page = ?"; 441c6d8c1d9SAndreas Gohr $result = $sqlite->query($sql, $page); 442c6d8c1d9SAndreas Gohr $assignments = $sqlite->res2single($result); 443c6d8c1d9SAndreas Gohr $sqlite->res_close($result); 444c6d8c1d9SAndreas Gohr 445c6d8c1d9SAndreas Gohr $users = []; 446c6d8c1d9SAndreas Gohr foreach (explode(',', $assignments) as $item) { 447c6d8c1d9SAndreas Gohr $item = trim($item); 448c6d8c1d9SAndreas Gohr if ($item === '') continue; 449c6d8c1d9SAndreas Gohr if ($item[0] == '@') { 450c6d8c1d9SAndreas Gohr $users = array_merge( 451c6d8c1d9SAndreas Gohr $users, 452c6d8c1d9SAndreas Gohr array_keys($auth->retrieveUsers(0, 0, ['grps' => substr($item, 1)])) 453c6d8c1d9SAndreas Gohr ); 454c6d8c1d9SAndreas Gohr } else { 455c6d8c1d9SAndreas Gohr $users[] = $item; 456c6d8c1d9SAndreas Gohr } 457c6d8c1d9SAndreas Gohr } 458c6d8c1d9SAndreas Gohr 459c6d8c1d9SAndreas Gohr return array_unique($users); 460c6d8c1d9SAndreas Gohr } 461c6d8c1d9SAndreas Gohr 462c6d8c1d9SAndreas Gohr /** 463c6d8c1d9SAndreas Gohr * Get ack status for all assigned users of a given page 464c6d8c1d9SAndreas Gohr * 465c6d8c1d9SAndreas Gohr * This can be slow! 466c6d8c1d9SAndreas Gohr * 467c6d8c1d9SAndreas Gohr * @param string $page 468c6d8c1d9SAndreas Gohr * @return array|false 469c6d8c1d9SAndreas Gohr */ 470c6d8c1d9SAndreas Gohr public function getPageAcknowledgements($page) 471c6d8c1d9SAndreas Gohr { 472c6d8c1d9SAndreas Gohr $users = $this->getPageAssignees($page); 473c6d8c1d9SAndreas Gohr if ($users === false) return false; 474c6d8c1d9SAndreas Gohr $sqlite = $this->getDB(); 475c6d8c1d9SAndreas Gohr if (!$sqlite) return false; 476c6d8c1d9SAndreas Gohr 477c6d8c1d9SAndreas Gohr $ulist = $sqlite->quote_and_join($users); 478c6d8c1d9SAndreas Gohr $sql = "SELECT A.page, A.lastmod, B.user, MAX(B.ack) AS ack 479c6d8c1d9SAndreas Gohr FROM pages A 480c6d8c1d9SAndreas Gohr LEFT JOIN acks B 481c6d8c1d9SAndreas Gohr ON A.page = B.page 482c6d8c1d9SAndreas Gohr AND B.user IN ($ulist) 483c6d8c1d9SAndreas Gohr WHERE A.page = ? 484c6d8c1d9SAndreas Gohr GROUP BY A.page, B.user 485c6d8c1d9SAndreas Gohr "; 486c6d8c1d9SAndreas Gohr $result = $sqlite->query($sql, $page); 487c6d8c1d9SAndreas Gohr $acknowledgements = $sqlite->res2arr($result); 488c6d8c1d9SAndreas Gohr $sqlite->res_close($result); 489c6d8c1d9SAndreas Gohr 490c6d8c1d9SAndreas Gohr // there should be at least one result, unless the page is unknown 491c6d8c1d9SAndreas Gohr if (!count($acknowledgements)) return false; 492c6d8c1d9SAndreas Gohr 493c6d8c1d9SAndreas Gohr $baseinfo = [ 494c6d8c1d9SAndreas Gohr 'page' => $acknowledgements[0]['page'], 495c6d8c1d9SAndreas Gohr 'lastmod' => $acknowledgements[0]['lastmod'], 496c6d8c1d9SAndreas Gohr 'user' => null, 497c6d8c1d9SAndreas Gohr 'ack' => null, 498c6d8c1d9SAndreas Gohr ]; 499c6d8c1d9SAndreas Gohr 500c6d8c1d9SAndreas Gohr // fill up the result with all users that never acknowledged the page 501c6d8c1d9SAndreas Gohr $combined = []; 502c6d8c1d9SAndreas Gohr foreach ($acknowledgements as $ack) { 503c6d8c1d9SAndreas Gohr if ($ack['user'] !== null) { 504c6d8c1d9SAndreas Gohr $combined[$ack['user']] = $ack; 505c6d8c1d9SAndreas Gohr } 506c6d8c1d9SAndreas Gohr } 507c6d8c1d9SAndreas Gohr foreach ($users as $user) { 508c6d8c1d9SAndreas Gohr if (!isset($combined[$user])) { 509c6d8c1d9SAndreas Gohr $combined[$user] = array_merge($baseinfo, ['user' => $user]); 510c6d8c1d9SAndreas Gohr } 511c6d8c1d9SAndreas Gohr } 512c6d8c1d9SAndreas Gohr 513c6d8c1d9SAndreas Gohr ksort($combined); 514c6d8c1d9SAndreas Gohr return array_values($combined); 515c6d8c1d9SAndreas Gohr } 516c6d8c1d9SAndreas Gohr 517c6d8c1d9SAndreas Gohr /** 518863b6e48SAndreas Gohr * Returns all acknowledgements 519863b6e48SAndreas Gohr * 520863b6e48SAndreas Gohr * @param int $limit maximum number of results 521863b6e48SAndreas Gohr * @return array|bool 522863b6e48SAndreas Gohr */ 523863b6e48SAndreas Gohr public function getAcknowledgements($limit = 100) 524863b6e48SAndreas Gohr { 525863b6e48SAndreas Gohr $sqlite = $this->getDB(); 526863b6e48SAndreas Gohr if (!$sqlite) return false; 527863b6e48SAndreas Gohr 528863b6e48SAndreas Gohr $sql = ' 52984db77b6SAndreas Gohr SELECT A.page, A.user, B.lastmod, max(A.ack) AS ack 53084db77b6SAndreas Gohr FROM acks A, pages B 53184db77b6SAndreas Gohr WHERE A.page = B.page 53284db77b6SAndreas Gohr GROUP BY A.user, A.page 533863b6e48SAndreas Gohr ORDER BY ack DESC 534863b6e48SAndreas Gohr LIMIT ? 535863b6e48SAndreas Gohr '; 536863b6e48SAndreas Gohr $result = $sqlite->query($sql, $limit); 537d6011abdSAnna Dabrowska $acknowledgements = $sqlite->res2arr($result); 538d6011abdSAnna Dabrowska $sqlite->res_close($result); 539d6011abdSAnna Dabrowska 540d6011abdSAnna Dabrowska return $acknowledgements; 541d6011abdSAnna Dabrowska } 542*f09444ffSAndreas Gohr 543*f09444ffSAndreas Gohr /** 544*f09444ffSAndreas Gohr * Check if the given pattern matches the given page 545*f09444ffSAndreas Gohr * 546*f09444ffSAndreas Gohr * @param string $pattern the pattern to check against 547*f09444ffSAndreas Gohr * @param string $page the cleaned pageid to check 548*f09444ffSAndreas Gohr * @return bool 549*f09444ffSAndreas Gohr */ 550*f09444ffSAndreas Gohr public function matchPagePattern($pattern, $page) 551*f09444ffSAndreas Gohr { 552*f09444ffSAndreas Gohr if (trim($pattern, ':') == '**') return true; // match all 553*f09444ffSAndreas Gohr 554*f09444ffSAndreas Gohr // regex patterns 555*f09444ffSAndreas Gohr if ($pattern[0] == '/') { 556*f09444ffSAndreas Gohr return (bool)preg_match($pattern, ":$page"); 557*f09444ffSAndreas Gohr } 558*f09444ffSAndreas Gohr 559*f09444ffSAndreas Gohr $pns = ':' . getNS($page) . ':'; 560*f09444ffSAndreas Gohr 561*f09444ffSAndreas Gohr $ans = ':' . cleanID($pattern) . ':'; 562*f09444ffSAndreas Gohr if (substr($pattern, -2) == '**') { 563*f09444ffSAndreas Gohr // upper namespaces match 564*f09444ffSAndreas Gohr if (strpos($pns, $ans) === 0) { 565*f09444ffSAndreas Gohr return true; 566*f09444ffSAndreas Gohr } 567*f09444ffSAndreas Gohr } elseif (substr($pattern, -1) == '*') { 568*f09444ffSAndreas Gohr // namespaces match exact 569*f09444ffSAndreas Gohr if ($ans == $pns) { 570*f09444ffSAndreas Gohr return true; 571*f09444ffSAndreas Gohr } 572*f09444ffSAndreas Gohr } else { 573*f09444ffSAndreas Gohr // exact match 574*f09444ffSAndreas Gohr if (cleanID($pattern) == $page) { 575*f09444ffSAndreas Gohr return true; 576*f09444ffSAndreas Gohr } 577*f09444ffSAndreas Gohr } 578*f09444ffSAndreas Gohr 579*f09444ffSAndreas Gohr return false; 580*f09444ffSAndreas Gohr } 5814d6d17d0SAndreas Gohr} 5824d6d17d0SAndreas Gohr 583