xref: /plugin/structpublish/meta/Assignments.php (revision 126b0b8e78a33b559ee525a8bc42a60b44e89ceb)
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