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