Failed Conditions
Push — stable ( 017e16...b83837 )
by
unknown
07:54 queued 02:55
created

inc/changelog.php (1 issue)

Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Changelog handling functions
4
 *
5
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6
 * @author     Andreas Gohr <[email protected]>
7
 */
8
9
// Constants for known core changelog line types.
10
// Use these in place of string literals for more readable code.
11
define('DOKU_CHANGE_TYPE_CREATE',       'C');
12
define('DOKU_CHANGE_TYPE_EDIT',         'E');
13
define('DOKU_CHANGE_TYPE_MINOR_EDIT',   'e');
14
define('DOKU_CHANGE_TYPE_DELETE',       'D');
15
define('DOKU_CHANGE_TYPE_REVERT',       'R');
16
17
/**
18
 * parses a changelog line into it's components
19
 *
20
 * @author Ben Coburn <[email protected]>
21
 *
22
 * @param string $line changelog line
23
 * @return array|bool parsed line or false
24
 */
25
function parseChangelogLine($line) {
26
    $line = rtrim($line, "\n");
27
    $tmp = explode("\t", $line);
28
    if ($tmp!==false && count($tmp)>1) {
29
        $info = array();
30
        $info['date']  = (int)$tmp[0]; // unix timestamp
31
        $info['ip']    = $tmp[1]; // IPv4 address (127.0.0.1)
32
        $info['type']  = $tmp[2]; // log line type
33
        $info['id']    = $tmp[3]; // page id
34
        $info['user']  = $tmp[4]; // user name
35
        $info['sum']   = $tmp[5]; // edit summary (or action reason)
36
        $info['extra'] = $tmp[6]; // extra data (varies by line type)
37
        if(isset($tmp[7]) && $tmp[7] !== '') { //last item has line-end||
38
            $info['sizechange'] = (int) $tmp[7];
39
        } else {
40
            $info['sizechange'] = null;
41
        }
42
        return $info;
43
    } else {
44
        return false;
45
    }
46
}
47
48
/**
49
 * Add's an entry to the changelog and saves the metadata for the page
50
 *
51
 * @param int    $date      Timestamp of the change
52
 * @param String $id        Name of the affected page
53
 * @param String $type      Type of the change see DOKU_CHANGE_TYPE_*
54
 * @param String $summary   Summary of the change
55
 * @param mixed  $extra     In case of a revert the revision (timestmp) of the reverted page
56
 * @param array  $flags     Additional flags in a key value array.
57
 *                             Available flags:
58
 *                             - ExternalEdit - mark as an external edit.
59
 * @param null|int $sizechange Change of filesize
60
 *
61
 * @author Andreas Gohr <[email protected]>
62
 * @author Esther Brunner <[email protected]>
63
 * @author Ben Coburn <[email protected]>
64
 */
65
function addLogEntry($date, $id, $type=DOKU_CHANGE_TYPE_EDIT, $summary='', $extra='', $flags=null, $sizechange = null){
66
    global $conf, $INFO;
67
    /** @var Input $INPUT */
68
    global $INPUT;
69
70
    // check for special flags as keys
71
    if (!is_array($flags)) { $flags = array(); }
72
    $flagExternalEdit = isset($flags['ExternalEdit']);
73
74
    $id = cleanid($id);
75
    $file = wikiFN($id);
76
    $created = @filectime($file);
77
    $minor = ($type===DOKU_CHANGE_TYPE_MINOR_EDIT);
78
    $wasRemoved = ($type===DOKU_CHANGE_TYPE_DELETE);
79
80
    if(!$date) $date = time(); //use current time if none supplied
81
    $remote = (!$flagExternalEdit)?clientIP(true):'127.0.0.1';
82
    $user   = (!$flagExternalEdit)?$INPUT->server->str('REMOTE_USER'):'';
83
    if($sizechange === null) {
84
        $sizechange = '';
85
    } else {
86
        $sizechange = (int) $sizechange;
87
    }
88
89
    $strip = array("\t", "\n");
90
    $logline = array(
91
        'date'       => $date,
92
        'ip'         => $remote,
93
        'type'       => str_replace($strip, '', $type),
94
        'id'         => $id,
95
        'user'       => $user,
96
        'sum'        => \dokuwiki\Utf8\PhpString::substr(str_replace($strip, '', $summary), 0, 255),
97
        'extra'      => str_replace($strip, '', $extra),
98
        'sizechange' => $sizechange
99
    );
100
101
    $wasCreated = ($type===DOKU_CHANGE_TYPE_CREATE);
102
    $wasReverted = ($type===DOKU_CHANGE_TYPE_REVERT);
103
    // update metadata
104
    if (!$wasRemoved) {
105
        $oldmeta = p_read_metadata($id);
106
        $meta    = array();
107
        if (
108
            $wasCreated && (
109
                empty($oldmeta['persistent']['date']['created']) ||
110
                $oldmeta['persistent']['date']['created'] === $created
111
            )
112
        ){
113
            // newly created
114
            $meta['date']['created'] = $created;
115
            if ($user){
116
                $meta['creator'] = isset($INFO) ? $INFO['userinfo']['name'] : null;
117
                $meta['user']    = $user;
118
            }
119
        } elseif (($wasCreated || $wasReverted) && !empty($oldmeta['persistent']['date']['created'])) {
120
            // re-created / restored
121
            $meta['date']['created']  = $oldmeta['persistent']['date']['created'];
122
            $meta['date']['modified'] = $created; // use the files ctime here
123
            $meta['creator'] = $oldmeta['persistent']['creator'];
124
            if ($user) $meta['contributor'][$user] = isset($INFO) ? $INFO['userinfo']['name'] : null;
125
        } elseif (!$minor) {   // non-minor modification
126
            $meta['date']['modified'] = $date;
127
            if ($user) $meta['contributor'][$user] = isset($INFO) ? $INFO['userinfo']['name'] : null;
128
        }
129
        $meta['last_change'] = $logline;
130
        p_set_metadata($id, $meta);
131
    }
132
133
    // add changelog lines
134
    $logline = implode("\t", $logline)."\n";
135
    io_saveFile(metaFN($id,'.changes'),$logline,true); //page changelog
136
    io_saveFile($conf['changelog'],$logline,true); //global changelog cache
137
}
138
139
/**
140
 * Add's an entry to the media changelog
141
 *
142
 * @author Michael Hamann <[email protected]>
143
 * @author Andreas Gohr <[email protected]>
144
 * @author Esther Brunner <[email protected]>
145
 * @author Ben Coburn <[email protected]>
146
 *
147
 * @param int    $date      Timestamp of the change
148
 * @param String $id        Name of the affected page
149
 * @param String $type      Type of the change see DOKU_CHANGE_TYPE_*
150
 * @param String $summary   Summary of the change
151
 * @param mixed  $extra     In case of a revert the revision (timestmp) of the reverted page
152
 * @param array  $flags     Additional flags in a key value array.
153
 *                             Available flags:
154
 *                             - (none, so far)
155
 * @param null|int $sizechange Change of filesize
156
 */
157
function addMediaLogEntry(
158
    $date,
159
    $id,
160
    $type=DOKU_CHANGE_TYPE_EDIT,
161
    $summary='',
162
    $extra='',
163
    $flags=null,
0 ignored issues
show
The parameter $flags is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
164
    $sizechange = null)
165
{
166
    global $conf;
167
    /** @var Input $INPUT */
168
    global $INPUT;
169
170
    $id = cleanid($id);
171
172
    if(!$date) $date = time(); //use current time if none supplied
173
    $remote = clientIP(true);
174
    $user   = $INPUT->server->str('REMOTE_USER');
175
    if($sizechange === null) {
176
        $sizechange = '';
177
    } else {
178
        $sizechange = (int) $sizechange;
179
    }
180
181
    $strip = array("\t", "\n");
182
    $logline = array(
183
        'date'       => $date,
184
        'ip'         => $remote,
185
        'type'       => str_replace($strip, '', $type),
186
        'id'         => $id,
187
        'user'       => $user,
188
        'sum'        => \dokuwiki\Utf8\PhpString::substr(str_replace($strip, '', $summary), 0, 255),
189
        'extra'      => str_replace($strip, '', $extra),
190
        'sizechange' => $sizechange
191
    );
192
193
    // add changelog lines
194
    $logline = implode("\t", $logline)."\n";
195
    io_saveFile($conf['media_changelog'],$logline,true); //global media changelog cache
196
    io_saveFile(mediaMetaFN($id,'.changes'),$logline,true); //media file's changelog
197
}
198
199
/**
200
 * returns an array of recently changed files using the
201
 * changelog
202
 *
203
 * The following constants can be used to control which changes are
204
 * included. Add them together as needed.
205
 *
206
 * RECENTS_SKIP_DELETED   - don't include deleted pages
207
 * RECENTS_SKIP_MINORS    - don't include minor changes
208
 * RECENTS_ONLY_CREATION  - only include new created pages and media
209
 * RECENTS_SKIP_SUBSPACES - don't include subspaces
210
 * RECENTS_MEDIA_CHANGES  - return media changes instead of page changes
211
 * RECENTS_MEDIA_PAGES_MIXED  - return both media changes and page changes
212
 *
213
 * @param int    $first   number of first entry returned (for paginating
214
 * @param int    $num     return $num entries
215
 * @param string $ns      restrict to given namespace
216
 * @param int    $flags   see above
217
 * @return array recently changed files
218
 *
219
 * @author Ben Coburn <[email protected]>
220
 * @author Kate Arzamastseva <[email protected]>
221
 */
222
function getRecents($first,$num,$ns='',$flags=0){
223
    global $conf;
224
    $recent = array();
225
    $count  = 0;
226
227
    if(!$num)
228
        return $recent;
229
230
    // read all recent changes. (kept short)
231
    if ($flags & RECENTS_MEDIA_CHANGES) {
232
        $lines = @file($conf['media_changelog']) ?: [];
233
    } else {
234
        $lines = @file($conf['changelog']) ?: [];
235
    }
236
    if (!is_array($lines)) {
237
        $lines = array();
238
    }
239
    $lines_position = count($lines)-1;
240
    $media_lines_position = 0;
241
    $media_lines = array();
242
243
    if ($flags & RECENTS_MEDIA_PAGES_MIXED) {
244
        $media_lines = @file($conf['media_changelog']) ?: [];
245
        if (!is_array($media_lines)) {
246
            $media_lines = array();
247
        }
248
        $media_lines_position = count($media_lines)-1;
249
    }
250
251
    $seen = array(); // caches seen lines, _handleRecent() skips them
252
253
    // handle lines
254
    while ($lines_position >= 0 || (($flags & RECENTS_MEDIA_PAGES_MIXED) && $media_lines_position >=0)) {
255
        if (empty($rec) && $lines_position >= 0) {
256
            $rec = _handleRecent(@$lines[$lines_position], $ns, $flags, $seen);
257
            if (!$rec) {
258
                $lines_position --;
259
                continue;
260
            }
261
        }
262
        if (($flags & RECENTS_MEDIA_PAGES_MIXED) && empty($media_rec) && $media_lines_position >= 0) {
263
            $media_rec = _handleRecent(
264
                @$media_lines[$media_lines_position],
265
                $ns,
266
                $flags | RECENTS_MEDIA_CHANGES,
267
                $seen
268
            );
269
            if (!$media_rec) {
270
                $media_lines_position --;
271
                continue;
272
            }
273
        }
274
        if (($flags & RECENTS_MEDIA_PAGES_MIXED) && @$media_rec['date'] >= @$rec['date']) {
275
            $media_lines_position--;
276
            $x = $media_rec;
277
            $x['media'] = true;
278
            $media_rec = false;
279
        } else {
280
            $lines_position--;
281
            $x = $rec;
282
            if ($flags & RECENTS_MEDIA_CHANGES) $x['media'] = true;
283
            $rec = false;
284
        }
285
        if(--$first >= 0) continue; // skip first entries
286
        $recent[] = $x;
287
        $count++;
288
        // break when we have enough entries
289
        if($count >= $num){ break; }
290
    }
291
    return $recent;
292
}
293
294
/**
295
 * returns an array of files changed since a given time using the
296
 * changelog
297
 *
298
 * The following constants can be used to control which changes are
299
 * included. Add them together as needed.
300
 *
301
 * RECENTS_SKIP_DELETED   - don't include deleted pages
302
 * RECENTS_SKIP_MINORS    - don't include minor changes
303
 * RECENTS_ONLY_CREATION  - only include new created pages and media
304
 * RECENTS_SKIP_SUBSPACES - don't include subspaces
305
 * RECENTS_MEDIA_CHANGES  - return media changes instead of page changes
306
 *
307
 * @param int    $from    date of the oldest entry to return
308
 * @param int    $to      date of the newest entry to return (for pagination, optional)
309
 * @param string $ns      restrict to given namespace (optional)
310
 * @param int    $flags   see above (optional)
311
 * @return array of files
312
 *
313
 * @author Michael Hamann <[email protected]>
314
 * @author Ben Coburn <[email protected]>
315
 */
316
function getRecentsSince($from,$to=null,$ns='',$flags=0){
317
    global $conf;
318
    $recent = array();
319
320
    if($to && $to < $from)
321
        return $recent;
322
323
    // read all recent changes. (kept short)
324
    if ($flags & RECENTS_MEDIA_CHANGES) {
325
        $lines = @file($conf['media_changelog']);
326
    } else {
327
        $lines = @file($conf['changelog']);
328
    }
329
    if(!$lines) return $recent;
330
331
    // we start searching at the end of the list
332
    $lines = array_reverse($lines);
333
334
    // handle lines
335
    $seen = array(); // caches seen lines, _handleRecent() skips them
336
337
    foreach($lines as $line){
338
        $rec = _handleRecent($line, $ns, $flags, $seen);
339
        if($rec !== false) {
340
            if ($rec['date'] >= $from) {
341
                if (!$to || $rec['date'] <= $to) {
342
                    $recent[] = $rec;
343
                }
344
            } else {
345
                break;
346
            }
347
        }
348
    }
349
350
    return array_reverse($recent);
351
}
352
353
/**
354
 * Internal function used by getRecents
355
 *
356
 * don't call directly
357
 *
358
 * @see getRecents()
359
 * @author Andreas Gohr <[email protected]>
360
 * @author Ben Coburn <[email protected]>
361
 *
362
 * @param string $line   changelog line
363
 * @param string $ns     restrict to given namespace
364
 * @param int    $flags  flags to control which changes are included
365
 * @param array  $seen   listing of seen pages
366
 * @return array|bool    false or array with info about a change
367
 */
368
function _handleRecent($line,$ns,$flags,&$seen){
369
    if(empty($line)) return false;   //skip empty lines
370
371
    // split the line into parts
372
    $recent = parseChangelogLine($line);
373
    if ($recent===false) { return false; }
374
375
    // skip seen ones
376
    if(isset($seen[$recent['id']])) return false;
377
378
    // skip changes, of only new items are requested
379
    if($recent['type']!==DOKU_CHANGE_TYPE_CREATE && ($flags & RECENTS_ONLY_CREATION)) return false;
380
381
    // skip minors
382
    if($recent['type']===DOKU_CHANGE_TYPE_MINOR_EDIT && ($flags & RECENTS_SKIP_MINORS)) return false;
383
384
    // remember in seen to skip additional sights
385
    $seen[$recent['id']] = 1;
386
387
    // check if it's a hidden page
388
    if(isHiddenPage($recent['id'])) return false;
389
390
    // filter namespace
391
    if (($ns) && (strpos($recent['id'],$ns.':') !== 0)) return false;
392
393
    // exclude subnamespaces
394
    if (($flags & RECENTS_SKIP_SUBSPACES) && (getNS($recent['id']) != $ns)) return false;
395
396
    // check ACL
397
    if ($flags & RECENTS_MEDIA_CHANGES) {
398
        $recent['perms'] = auth_quickaclcheck(getNS($recent['id']).':*');
399
    } else {
400
        $recent['perms'] = auth_quickaclcheck($recent['id']);
401
    }
402
    if ($recent['perms'] < AUTH_READ) return false;
403
404
    // check existance
405
    if($flags & RECENTS_SKIP_DELETED){
406
        $fn = (($flags & RECENTS_MEDIA_CHANGES) ? mediaFN($recent['id']) : wikiFN($recent['id']));
407
        if(!file_exists($fn)) return false;
408
    }
409
410
    return $recent;
411
}
412