1<?php
2
3namespace dokuwiki;
4
5use dokuwiki\Extension\Event;
6
7/**
8 * Class Draft
9 *
10 * @package dokuwiki
11 */
12class Draft
13{
14    protected $errors = [];
15    protected $cname;
16    protected $id;
17    protected $client;
18
19    /**
20     * Draft constructor.
21     *
22     * @param string $ID the page id for this draft
23     * @param string $client the client identification (username or ip or similar) for this draft
24     */
25    public function __construct($ID, $client)
26    {
27        $this->id = $ID;
28        $this->client = $client;
29        $this->cname = getCacheName("$client\n$ID", '.draft');
30        if (file_exists($this->cname) && file_exists(wikiFN($ID))) {
31            if (filemtime($this->cname) < filemtime(wikiFN($ID))) {
32                // remove stale draft
33                $this->deleteDraft();
34            }
35        }
36    }
37
38    /**
39     * Get the filename for this draft (whether or not it exists)
40     *
41     * @return string
42     */
43    public function getDraftFilename()
44    {
45        return $this->cname;
46    }
47
48    /**
49     * Checks if this draft exists on the filesystem
50     *
51     * @return bool
52     */
53    public function isDraftAvailable()
54    {
55        return file_exists($this->cname);
56    }
57
58    /**
59     * Save a draft of a current edit session
60     *
61     * The draft will not be saved if
62     *   - drafts are deactivated in the config
63     *   - or the editarea is empty and there are no event handlers registered
64     *   - or the event is prevented
65     *
66     * @triggers DRAFT_SAVE
67     *
68     * @return bool whether has the draft been saved
69     */
70    public function saveDraft()
71    {
72        global $INPUT, $INFO, $EVENT_HANDLER, $conf;
73        if (!$conf['usedraft']) {
74            return false;
75        }
76        if (
77            !$INPUT->post->has('wikitext') &&
78            !$EVENT_HANDLER->hasHandlerForEvent('DRAFT_SAVE')
79        ) {
80            return false;
81        }
82        $draft = [
83            'id' => $this->id,
84            'prefix' => substr($INPUT->post->str('prefix'), 0, -1),
85            'text' => $INPUT->post->str('wikitext'),
86            'suffix' => $INPUT->post->str('suffix'),
87            'date' => $INPUT->post->int('date'),
88            'client' => $this->client,
89            'cname' => $this->cname,
90            'errors' => [],
91        ];
92        $event = new Event('DRAFT_SAVE', $draft);
93        if ($event->advise_before()) {
94            $draft['hasBeenSaved'] = io_saveFile($draft['cname'], serialize($draft));
95            if ($draft['hasBeenSaved']) {
96                $INFO['draft'] = $draft['cname'];
97            }
98        } else {
99            $draft['hasBeenSaved'] = false;
100        }
101        $event->advise_after();
102
103        $this->errors = $draft['errors'];
104
105        return $draft['hasBeenSaved'];
106    }
107
108    /**
109     * Get the text from the draft file
110     *
111     * @throws \RuntimeException if the draft file doesn't exist
112     *
113     * @return string
114     */
115    public function getDraftText()
116    {
117        if (!file_exists($this->cname)) {
118            throw new \RuntimeException(
119                "Draft for page $this->id and user $this->client doesn't exist at $this->cname."
120            );
121        }
122        $draft = unserialize(io_readFile($this->cname, false));
123        return cleanText(con($draft['prefix'], $draft['text'], $draft['suffix'], true));
124    }
125
126    /**
127     * Remove the draft from the filesystem
128     *
129     * Also sets $INFO['draft'] to null
130     */
131    public function deleteDraft()
132    {
133        global $INFO;
134        @unlink($this->cname);
135        $INFO['draft'] = null;
136    }
137
138    /**
139     * Get a formatted message stating when the draft was saved
140     *
141     * @return string
142     */
143    public function getDraftMessage()
144    {
145        global $lang;
146        return $lang['draftdate'] . ' ' . dformat(filemtime($this->cname));
147    }
148
149    /**
150     * Retrieve the errors that occured when saving the draft
151     *
152     * @return array
153     */
154    public function getErrors()
155    {
156        return $this->errors;
157    }
158
159    /**
160     * Get the timestamp when this draft was saved
161     *
162     * @return int
163     */
164    public function getDraftDate()
165    {
166        return filemtime($this->cname);
167    }
168}
169