-
Notifications
You must be signed in to change notification settings - Fork 0
/
action.php
180 lines (155 loc) · 5.81 KB
/
action.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
<?php
use dokuwiki\Extension\ActionPlugin;
use dokuwiki\Extension\Event;
use dokuwiki\Extension\EventHandler;
use dokuwiki\File\PageFile;
/**
* DokuWiki Plugin renderrevisions (Action Component)
*
* @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
* @author Andreas Gohr <dokuwiki@cosmocode.de>
*/
class action_plugin_renderrevisions extends ActionPlugin
{
/** @var array list of pages that are processed by the plugin */
protected $pages = [];
/** @var string|null the current page being saved, used to overwrite the contentchanged check */
protected $current = null;
/** @inheritDoc */
public function register(EventHandler $controller)
{
$controller->register_hook('PARSER_CACHE_USE', 'AFTER', $this, 'handleParserCacheUse');
$controller->register_hook(
'RENDERER_CONTENT_POSTPROCESS',
'AFTER',
$this,
'handleRenderContent',
null,
PHP_INT_MAX // other plugins might want to change the content before we see it
);
$controller->register_hook('COMMON_WIKIPAGE_SAVE', 'BEFORE', $this, 'handleCommonWikipageSave');
}
/**
* Event handler for PARSER_CACHE_USE
*
* @see https://www.dokuwiki.org/devel:event:PARSER_CACHE_USE
* @param Event $event Event object
* @param mixed $param optional parameter passed when event was registered
* @return void
*/
public function handleParserCacheUse(Event $event, $param)
{
$cacheObject = $event->data;
if (!$cacheObject->page) return;
if ($cacheObject->mode !== 'xhtml') return;
// only process pages that match both the skip and match regex
$page = $cacheObject->page;
try {
[$skipRE, $matchRE] = $this->getRegexps();
} catch (\Exception $e) {
msg(hsc($e->getMessage()), -1);
return;
}
if (
($skipRE && preg_match($skipRE, ":$page")) ||
($matchRE && !preg_match($matchRE, ":$page"))
) {
return;
}
// remember that this page was processed
// This is a somewhat ugly workaround for when text snippets are rendered within the same page.
// Those snippets will not have a page context set during cache use event and thus not be processed
// later on in the RENDERER_CONTENT_POSTPROCESS event
$this->pages[$page] = true;
}
/**
* Event handler for RENDERER_CONTENT_POSTPROCESS
*
* @see https://www.dokuwiki.org/devel:event:RENDERER_CONTENT_POSTPROCESS
* @param Event $event Event object
* @param mixed $param optional parameter passed when event was registered
* @return void
*/
public function handleRenderContent(Event $event, $param)
{
[$format, $xhtml] = $event->data;
if ($format !== 'xhtml') return;
// thanks to the $this->pages property we might be able to skip some of those checks, but they don't hurt
global $ACT;
global $REV;
global $DATE_AT;
global $ID;
global $INFO;
if ($ACT !== 'show') return;
if ($REV) return;
if ($DATE_AT) return;
if (!$INFO['exists']) return;
if (!$ID) return;
if (!isset($this->pages[$ID])) return;
$md5cache = getCacheName($ID, '.renderrevision');
// no or outdated MD5 cache, create new one
// this means a new revision of the page has been created naturally
// we store the new render result and are done
if (!file_exists($md5cache) || filemtime(wikiFN($ID)) > filemtime($md5cache)) {
file_put_contents($md5cache, md5($xhtml));
return;
}
// only act on pages that have not been changed very recently
if (time() - filemtime(wikiFN($ID)) < $this->getConf('maxfrequency')) {
return;
}
// get the render result as it were when the page was last changed
$oldMd5 = file_get_contents($md5cache);
// did the rendered content change?
if ($oldMd5 === md5($xhtml)) {
return;
}
// time to create a new revision
$this->current = $ID;
(new PageFile($ID))->saveWikiText(rawWiki($ID), 'Automatic revision due to content change');
$this->current = null;
}
/**
* Event handler for COMMON_WIKIPAGE_SAVE
*
* Overwrite the contentChanged flag to force a new revision even though the content did not change
*
* @see https://www.dokuwiki.org/devel:event:COMMON_WIKIPAGE_SAVE
* @param Event $event Event object
* @param mixed $param optional parameter passed when event was registered
* @return void
*/
public function handleCommonWikipageSave(Event $event, $param)
{
if ($this->current !== $event->data['id']) return;
$event->data['contentChanged'] = true;
}
/**
* Read the skip and match regex from the config
*
* Ensures the regular expressions are valid
*
* @return string[] [$skipRE, $matchRE]
* @throws \Exception if the regular expressions are invalid
*/
protected function getRegexps()
{
$skip = $this->getConf('skipRegex');
$skipRE = '';
$match = $this->getConf('matchRegex');
$matchRE = '';
if ($skip) {
$skipRE = '/' . $skip . '/';
if (@preg_match($skipRE, '') === false) {
throw new \Exception('Invalid regular expression in $conf[\'skipRegex\']. ' . preg_last_error_msg());
}
}
if ($match) {
$matchRE = '/' . $match . '/';
if (@preg_match($matchRE, '') === false) {
throw new \Exception('Invalid regular expression in $conf[\'matchRegex\']. ' . preg_last_error_msg());
}
}
return [$skipRE, $matchRE];
}
}