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