1<?php
2
3namespace dokuwiki\plugin\structpublish\meta;
4
5use dokuwiki\plugin\sqlite\SQLiteDB;
6use dokuwiki\plugin\struct\meta\ConfigParser;
7use dokuwiki\plugin\struct\meta\Schema;
8use dokuwiki\plugin\struct\meta\SearchConfig;
9use dokuwiki\plugin\struct\meta\Value;
10
11/**
12 * Object representing a page revision and its properties
13 */
14class Revision
15{
16    /** @var SQLiteDB */
17    protected $sqlite;
18
19    protected $schema;
20
21    protected $id;
22    protected $rev;
23    protected $published;
24    protected $status;
25    protected $version;
26    protected $user;
27    protected $datetime;
28    /** @var bool|\dokuwiki\plugin\struct\meta\Column */
29    protected $statusCol;
30    protected $versionCol;
31    protected $userCol;
32    protected $datetimeCol;
33    protected $revisionCol;
34
35    /**
36     * Constructor
37     *
38     * @param string $id page id
39     * @param int $rev revision
40     */
41    public function __construct($id, $rev)
42    {
43        $this->id = $id;
44        $this->rev = $rev;
45        $this->published = 0;
46        $this->status = Constants::STATUS_DRAFT;
47
48        $this->schema = new Schema('structpublish');
49        $this->statusCol = $this->schema->findColumn('status');
50        $this->versionCol = $this->schema->findColumn('version');
51        $this->userCol = $this->schema->findColumn('user');
52        $this->datetimeCol = $this->schema->findColumn('datetime');
53        $this->revisionCol = $this->schema->findColumn('revision');
54
55        /** @var Value[] $values */
56        $values = $this->getCoreData(['revision=' . $this->rev]);
57
58        if (!empty($values)) {
59            $this->status = $values[$this->statusCol->getColref() - 1]->getRawValue();
60            $this->version = $values[$this->versionCol->getColref() - 1]->getRawValue();
61            $this->user = $values[$this->userCol->getColref() - 1]->getRawValue();
62            $this->datetime = $values[$this->datetimeCol->getColref() - 1]->getRawValue();
63        }
64    }
65
66    /**
67     * Store the currently set structpublish meta data in the database
68     *
69     * @return void
70     */
71    public function save()
72    {
73        if ($this->status === Constants::STATUS_PUBLISHED) {
74            $this->published = 1;
75        }
76
77        $this->updateCoreData($this->id);
78    }
79
80    /**
81     * Return the version of a published revision
82     *
83     * @return string|null
84     */
85    public function getVersion()
86    {
87        return $this->version;
88    }
89
90    /**
91     * Set the version of a published revision
92     *
93     * @param string $version
94     */
95    public function setVersion($version)
96    {
97        $this->version = $version;
98    }
99
100    /**
101     * The revision timestamp
102     *
103     * @return int
104     */
105    public function getRev()
106    {
107        return $this->rev;
108    }
109
110    /**
111     * Get the current status of this revision
112     *
113     * @return string
114     */
115    public function getStatus()
116    {
117        return $this->status;
118    }
119
120    /**
121     * Set the current status of this revision
122     *
123     * @param string $status
124     */
125    public function setStatus($status)
126    {
127        $this->status = $status;
128    }
129
130    /**
131     * Get the user that changed the status of this revision
132     *
133     * Not available for drafts
134     *
135     * @return string|null
136     */
137    public function getUser()
138    {
139        return $this->user;
140    }
141
142    /**
143     * Set the user that changed the revision status
144     *
145     * @param string $user
146     */
147    public function setUser($user): void
148    {
149        $this->user = $user;
150    }
151
152    /**
153     * The datetime when the status of this revision was changed
154     *
155     * Uses ISO Format. Not available for drafts
156     *
157     * @return string|null
158     */
159    public function getDatetime()
160    {
161        return $this->datetime;
162    }
163
164    /**
165     * The timestamp of when the status of this revision was changed
166     *
167     * Not available for drafts
168     *
169     * @return int|null
170     */
171    public function getTimestamp()
172    {
173        if ($this->datetime === null) {
174            return null;
175        }
176        return strtotime($this->datetime);
177    }
178
179    /**
180     * Set the datetime when the status of this revision was changed
181     *
182     * Uses ISO Format
183     *
184     * @param string $time
185     */
186    public function setDatetime($time)
187    {
188        $this->datetime = $time;
189    }
190
191    /**
192     * Set the timestamp of when the status of this revision was changed
193     */
194    public function setTimestamp($timestamp)
195    {
196        $this->datetime = date('Y-m-d\TH:i', $timestamp);
197    }
198
199    /**
200     * The page ID this revision is for
201     *
202     * @return string
203     */
204    public function getId()
205    {
206        return $this->id;
207    }
208
209    /**
210     * Update publish status in the core table
211     *
212     * @param string $pid
213     * @param int $rid
214     */
215    protected function updateCoreData($pid, $rid = 0)
216    {
217        $data = [
218            'status' => $this->status,
219            'user' => $this->user,
220            'datetime' => $this->datetime,
221            'revision' => $this->rev,
222            'version' => $this->version,
223        ];
224        $schema = new Schema('structpublish', 0);
225        $access = new AccessTableStructpublish($schema, $pid, 0, $rid);
226        $access->setPublished($this->published);
227        $access->saveData($data);
228    }
229
230    /**
231     * Fetches data from the structpublish schema for the current page.
232     * Returns an array of struct Value objects, not literal values.
233     * $andFilters can be used to limit the search, e.g. by status or revision
234     *
235     * @see https://www.dokuwiki.org/plugin:struct:filters
236     *
237     * @param array $andFilters
238     * @return array|Value[]
239     */
240    public function getCoreData($andFilters = [])
241    {
242        $lines = [
243            'schema: structpublish',
244            'cols: *',
245            'sort: revision',
246            'filter: %pageid% = $ID$'
247        ];
248
249        if (!empty($andFilters)) {
250            foreach ($andFilters as $filter) {
251                $lines[] = 'filter: ' . $filter;
252            }
253        }
254
255        $parser = new ConfigParser($lines);
256        $config = $parser->getConfig();
257        $search = new SearchConfig($config);
258        // disable 'latest' flag in select query
259        $search->setSelectLatest(false);
260        $data = $search->execute();
261        if (!empty($data)) {
262            return array_pop($data);
263        }
264        return [];
265    }
266
267    /**
268     * Return "latest" published revision of a given page.
269     * If $rev is specified, "latest" means relative to the $rev revision.
270     *
271     * @param int|null $rev
272     * @return Revision|null
273     */
274    public function getLatestPublishedRevision($rev = null)
275    {
276        $andFilters[] = 'status=' . Constants::STATUS_PUBLISHED;
277        if ($rev) {
278            $andFilters[] .= 'revision<' . $rev;
279        }
280        $latestPublished = $this->getCoreData($andFilters);
281
282        if (empty($latestPublished)) {
283            return null;
284        }
285
286        $published = new Revision(
287            $this->id,
288            $latestPublished[$this->revisionCol->getColref() - 1]->getRawValue()
289        );
290
291        $published->setStatus($latestPublished[$this->statusCol->getColref() - 1]->getRawValue());
292        $published->setUser($latestPublished[$this->userCol->getColref() - 1]->getRawValue());
293        $published->setDatetime($latestPublished[$this->datetimeCol->getColref() - 1]->getRawValue());
294        $published->setVersion($latestPublished[$this->versionCol->getColref() - 1]->getRawValue());
295
296        return $published;
297    }
298}
299