Failed Conditions
Pull Request — master (#2943)
by Andreas
03:19
created

inc/html.php (12 issues)

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
 * HTML output functions
4
 *
5
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6
 * @author     Andreas Gohr <[email protected]>
7
 */
8
9
use dokuwiki\ChangeLog\MediaChangeLog;
10
use dokuwiki\ChangeLog\PageChangeLog;
11
use dokuwiki\Extension\AuthPlugin;
12
use dokuwiki\Extension\Event;
13
use dokuwiki\Search\FulltextSearch;
14
use dokuwiki\Search\MetadataIndex;
15
use dokuwiki\Utf8;
16
17
if (!defined('SEC_EDIT_PATTERN')) {
18
    define('SEC_EDIT_PATTERN', '#<!-- EDIT({.*?}) -->#');
19
}
20
21
22
/**
23
 * Convenience function to quickly build a wikilink
24
 *
25
 * @author Andreas Gohr <[email protected]>
26
 * @param string  $id      id of the target page
27
 * @param string  $name    the name of the link, i.e. the text that is displayed
28
 * @param string|array  $search  search string(s) that shall be highlighted in the target page
29
 * @return string the HTML code of the link
30
 */
31
function html_wikilink($id,$name=null,$search=''){
32
    /** @var Doku_Renderer_xhtml $xhtml_renderer */
33
    static $xhtml_renderer = null;
34
    if(is_null($xhtml_renderer)){
35
        $xhtml_renderer = p_get_renderer('xhtml');
36
    }
37
38
    return $xhtml_renderer->internallink($id,$name,$search,true,'navigation');
39
}
40
41
/**
42
 * The loginform
43
 *
44
 * @author   Andreas Gohr <[email protected]>
45
 *
46
 * @param bool $svg Whether to show svg icons in the register and resendpwd links or not
47
 */
48
function html_login($svg = false){
49
    global $lang;
50
    global $conf;
51
    global $ID;
52
    global $INPUT;
53
54
    print p_locale_xhtml('login');
55
    print '<div class="centeralign">'.NL;
56
    $form = new Doku_Form(array('id' => 'dw__login', 'action'=>wl($ID)));
57
    $form->startFieldset($lang['btn_login']);
58
    $form->addHidden('id', $ID);
59
    $form->addHidden('do', 'login');
60
    $form->addElement(form_makeTextField(
61
        'u',
62
        ((!$INPUT->bool('http_credentials')) ? $INPUT->str('u') : ''),
63
        $lang['user'],
64
        'focus__this',
65
        'block')
66
    );
67
    $form->addElement(form_makePasswordField('p', $lang['pass'], '', 'block'));
68
    if($conf['rememberme']) {
69
        $form->addElement(form_makeCheckboxField('r', '1', $lang['remember'], 'remember__me', 'simple'));
70
    }
71
    $form->addElement(form_makeButton('submit', '', $lang['btn_login']));
72
    $form->endFieldset();
73
74
    if(actionOK('register')){
75
        $registerLink = (new \dokuwiki\Menu\Item\Register())->asHtmlLink('', $svg);
76
        $form->addElement('<p>'.$lang['reghere'].': '. $registerLink .'</p>');
77
    }
78
79
    if (actionOK('resendpwd')) {
80
        $resendPwLink = (new \dokuwiki\Menu\Item\Resendpwd())->asHtmlLink('', $svg);
81
        $form->addElement('<p>'.$lang['pwdforget'].': '. $resendPwLink .'</p>');
82
    }
83
84
    html_form('login', $form);
85
    print '</div>'.NL;
86
}
87
88
89
/**
90
 * Denied page content
91
 *
92
 * @return string html
93
 */
94
function html_denied() {
95
    print p_locale_xhtml('denied');
96
97
    if(empty($_SERVER['REMOTE_USER']) && actionOK('login')){
98
        html_login();
99
    }
100
}
101
102
/**
103
 * inserts section edit buttons if wanted or removes the markers
104
 *
105
 * @author Andreas Gohr <[email protected]>
106
 *
107
 * @param string $text
108
 * @param bool   $show show section edit buttons?
109
 * @return string
110
 */
111
function html_secedit($text,$show=true){
112
    global $INFO;
113
114
    if((isset($INFO) && !$INFO['writable']) || !$show || (isset($INFO) && $INFO['rev'])){
115
        return preg_replace(SEC_EDIT_PATTERN,'',$text);
116
    }
117
118
    return preg_replace_callback(SEC_EDIT_PATTERN,
119
                'html_secedit_button', $text);
120
}
121
122
/**
123
 * prepares section edit button data for event triggering
124
 * used as a callback in html_secedit
125
 *
126
 * @author Andreas Gohr <[email protected]>
127
 *
128
 * @param array $matches matches with regexp
129
 * @return string
130
 * @triggers HTML_SECEDIT_BUTTON
131
 */
132
function html_secedit_button($matches){
133
    $json = htmlspecialchars_decode($matches[1], ENT_QUOTES);
134
    $data = json_decode($json, true);
135
    if ($data == NULL) {
136
        return;
137
    }
138
    $data ['target'] = strtolower($data['target']);
139
    $data ['hid'] = strtolower($data['hid']);
140
141
    return Event::createAndTrigger('HTML_SECEDIT_BUTTON', $data,
142
                         'html_secedit_get_button');
143
}
144
145
/**
146
 * prints a section editing button
147
 * used as default action form HTML_SECEDIT_BUTTON
148
 *
149
 * @author Adrian Lang <[email protected]>
150
 *
151
 * @param array $data name, section id and target
152
 * @return string html
153
 */
154
function html_secedit_get_button($data) {
155
    global $ID;
156
    global $INFO;
157
158
    if (!isset($data['name']) || $data['name'] === '') return '';
159
160
    $name = $data['name'];
161
    unset($data['name']);
162
163
    $secid = $data['secid'];
164
    unset($data['secid']);
165
166
    return "<div class='secedit editbutton_" . $data['target'] .
167
                       " editbutton_" . $secid . "'>" .
168
           html_btn('secedit', $ID, '',
169
                    array_merge(array('do'  => 'edit',
170
                                      'rev' => $INFO['lastmod'],
171
                                      'summary' => '['.$name.'] '), $data),
172
                    'post', $name) . '</div>';
173
}
174
175
/**
176
 * Just the back to top button (in its own form)
177
 *
178
 * @author Andreas Gohr <[email protected]>
179
 *
180
 * @return string html
181
 */
182
function html_topbtn(){
183
    global $lang;
184
185
    $ret = '<a class="nolink" href="#dokuwiki__top">' .
186
        '<button class="button" onclick="window.scrollTo(0, 0)" title="' . $lang['btn_top'] . '">' .
187
        $lang['btn_top'] .
188
        '</button></a>';
189
190
    return $ret;
191
}
192
193
/**
194
 * Displays a button (using its own form)
195
 * If tooltip exists, the access key tooltip is replaced.
196
 *
197
 * @author Andreas Gohr <[email protected]>
198
 *
199
 * @param string         $name
200
 * @param string         $id
201
 * @param string         $akey   access key
202
 * @param string[] $params key-value pairs added as hidden inputs
203
 * @param string         $method
204
 * @param string         $tooltip
205
 * @param bool|string    $label  label text, false: lookup btn_$name in localization
206
 * @param string         $svg (optional) svg code, inserted into the button
207
 * @return string
208
 */
209
function html_btn($name, $id, $akey, $params, $method='get', $tooltip='', $label=false, $svg=null){
210
    global $conf;
211
    global $lang;
212
213
    if (!$label)
214
        $label = $lang['btn_'.$name];
215
216
    $ret = '';
217
218
    //filter id (without urlencoding)
219
    $id = idfilter($id,false);
220
221
    //make nice URLs even for buttons
222
    if($conf['userewrite'] == 2){
223
        $script = DOKU_BASE.DOKU_SCRIPT.'/'.$id;
224
    }elseif($conf['userewrite']){
225
        $script = DOKU_BASE.$id;
226
    }else{
227
        $script = DOKU_BASE.DOKU_SCRIPT;
228
        $params['id'] = $id;
229
    }
230
231
    $ret .= '<form class="button btn_'.$name.'" method="'.$method.'" action="'.$script.'"><div class="no">';
232
233
    if(is_array($params)){
234
        foreach($params as $key => $val) {
235
            $ret .= '<input type="hidden" name="'.$key.'" ';
236
            $ret .= 'value="'.hsc($val).'" />';
237
        }
238
    }
239
240
    if ($tooltip!='') {
241
        $tip = hsc($tooltip);
242
    }else{
243
        $tip = hsc($label);
244
    }
245
246
    $ret .= '<button type="submit" ';
247
    if($akey){
248
        $tip .= ' ['.strtoupper($akey).']';
249
        $ret .= 'accesskey="'.$akey.'" ';
250
    }
251
    $ret .= 'title="'.$tip.'">';
252
    if ($svg) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $svg of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
253
        $ret .= '<span>' . hsc($label) . '</span>';
254
        $ret .= inlineSVG($svg);
255
    } else {
256
        $ret .= hsc($label);
257
    }
258
    $ret .= '</button>';
259
    $ret .= '</div></form>';
260
261
    return $ret;
262
}
263
/**
264
 * show a revision warning
265
 *
266
 * @author Szymon Olewniczak <[email protected]>
267
 */
268
function html_showrev() {
269
    print p_locale_xhtml('showrev');
270
}
271
272
/**
273
 * Show a wiki page
274
 *
275
 * @author Andreas Gohr <[email protected]>
276
 *
277
 * @param null|string $txt wiki text or null for showing $ID
278
 */
279
function html_show($txt=null){
280
    global $ID;
281
    global $REV;
282
    global $HIGH;
283
    global $INFO;
284
    global $DATE_AT;
285
    //disable section editing for old revisions or in preview
286
    if($txt || $REV){
287
        $secedit = false;
288
    }else{
289
        $secedit = true;
290
    }
291
292
    if (!is_null($txt)){
293
        //PreviewHeader
294
        echo '<br id="scroll__here" />';
295
        echo p_locale_xhtml('preview');
296
        echo '<div class="preview"><div class="pad">';
297
        $html = html_secedit(p_render('xhtml',p_get_instructions($txt),$info),$secedit);
298
        if($INFO['prependTOC']) $html = tpl_toc(true).$html;
299
        echo $html;
300
        echo '<div class="clearer"></div>';
301
        echo '</div></div>';
302
303
    }else{
304
        if ($REV||$DATE_AT){
305
            $data = array('rev' => &$REV, 'date_at' => &$DATE_AT);
306
            Event::createAndTrigger('HTML_SHOWREV_OUTPUT', $data, 'html_showrev');
307
        }
308
        $html = p_wiki_xhtml($ID,$REV,true,$DATE_AT);
309
        $html = html_secedit($html,$secedit);
310
        if($INFO['prependTOC']) $html = tpl_toc(true).$html;
311
        $html = html_hilight($html,$HIGH);
312
        echo $html;
313
    }
314
}
315
316
/**
317
 * ask the user about how to handle an exisiting draft
318
 *
319
 * @author Andreas Gohr <[email protected]>
320
 */
321
function html_draft(){
322
    global $INFO;
323
    global $ID;
324
    global $lang;
325
    $draft = new \dokuwiki\Draft($ID, $INFO['client']);
326
    $text  = $draft->getDraftText();
327
328
    print p_locale_xhtml('draft');
329
    html_diff($text, false);
330
    $form = new Doku_Form(array('id' => 'dw__editform'));
0 ignored issues
show
Deprecated Code introduced by
The class Doku_Form has been deprecated with message: 2019-07-14

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
331
    $form->addHidden('id', $ID);
332
    $form->addHidden('date', $draft->getDraftDate());
333
    $form->addHidden('wikitext', $text);
334
    $form->addElement(form_makeOpenTag('div', array('id'=>'draft__status')));
335
    $form->addElement($draft->getDraftMessage());
336
    $form->addElement(form_makeCloseTag('div'));
337
    $form->addElement(form_makeButton('submit', 'recover', $lang['btn_recover'], array('tabindex'=>'1')));
338
    $form->addElement(form_makeButton('submit', 'draftdel', $lang['btn_draftdel'], array('tabindex'=>'2')));
339
    $form->addElement(form_makeButton('submit', 'show', $lang['btn_cancel'], array('tabindex'=>'3')));
340
    html_form('draft', $form);
341
}
342
343
/**
344
 * Highlights searchqueries in HTML code
345
 *
346
 * @author Andreas Gohr <[email protected]>
347
 * @author Harry Fuecks <[email protected]>
348
 *
349
 * @param string $html
350
 * @param array|string $phrases
351
 * @return string html
352
 */
353
function html_hilight($html, $phrases) {
354
    $FulltextSearch = FulltextSearch::getInstance();
355
    $phrases = (array) $phrases;
356
    $phrases = array_map('preg_quote_cb', $phrases);
357
    $phrases = array_map([$FulltextSearch, 'snippetRePreprocess'], $phrases);
358
    $phrases = array_filter($phrases);
359
    $regex = implode('|', $phrases);
360
361
    if ($regex === '') return $html;
362
    if (!Utf8\Clean::isUtf8($regex)) return $html;
363
    $html = @preg_replace_callback("/((<[^>]*)|$regex)/ui",'html_hilight_callback', $html);
364
    return $html;
365
}
366
367
/**
368
 * Callback used by html_hilight()
369
 *
370
 * @author Harry Fuecks <[email protected]>
371
 *
372
 * @param array $m matches
373
 * @return string html
374
 */
375
function html_hilight_callback($m) {
376
    $hlight = unslash($m[0]);
377
    if ( !isset($m[2])) {
378
        $hlight = '<span class="search_hit">'.$hlight.'</span>';
379
    }
380
    return $hlight;
381
}
382
383
/**
384
 * Display error on locked pages
385
 *
386
 * @author Andreas Gohr <[email protected]>
387
 */
388
function html_locked(){
389
    global $ID;
390
    global $conf;
391
    global $lang;
392
    global $INFO;
393
394
    $locktime = filemtime(wikiLockFN($ID));
395
    $expire = dformat($locktime + $conf['locktime']);
396
    $min    = round(($conf['locktime'] - (time() - $locktime) )/60);
397
398
    print p_locale_xhtml('locked');
399
    print '<ul>';
400
    print '<li><div class="li"><strong>'.$lang['lockedby'].'</strong> '.editorinfo($INFO['locked']).'</div></li>';
401
    print '<li><div class="li"><strong>'.$lang['lockexpire'].'</strong> '.$expire.' ('.$min.' min)</div></li>';
402
    print '</ul>';
403
}
404
405
/**
406
 * list old revisions
407
 *
408
 * @author Andreas Gohr <[email protected]>
409
 * @author Ben Coburn <[email protected]>
410
 * @author Kate Arzamastseva <[email protected]>
411
 *
412
 * @param int $first skip the first n changelog lines
413
 * @param bool|string $media_id id of media, or false for current page
414
 */
415
function html_revisions($first=0, $media_id = false){
416
    global $ID;
417
    global $INFO;
418
    global $conf;
419
    global $lang;
420
    $id = $ID;
421
    if ($media_id) {
422
        $id = $media_id;
423
        $changelog = new MediaChangeLog($id);
424
    } else {
425
        $changelog = new PageChangeLog($id);
426
    }
427
428
    /* we need to get one additional log entry to be able to
429
     * decide if this is the last page or is there another one.
430
     * see html_recent()
431
     */
432
433
    $revisions = $changelog->getRevisions($first, $conf['recent']+1);
434
435
    if(count($revisions)==0 && $first!=0){
436
        $first=0;
437
        $revisions = $changelog->getRevisions($first, $conf['recent']+1);
438
    }
439
    $hasNext = false;
440
    if (count($revisions)>$conf['recent']) {
441
        $hasNext = true;
442
        array_pop($revisions); // remove extra log entry
443
    }
444
445
    if (!$media_id) print p_locale_xhtml('revisions');
446
447
    $params = array('id' => 'page__revisions', 'class' => 'changes');
448
    if($media_id) {
449
        $params['action'] = media_managerURL(array('image' => $media_id), '&');
450
    }
451
452
    if(!$media_id) {
453
        $exists = $INFO['exists'];
454
        $display_name = useHeading('navigation') ? hsc(p_get_first_heading($id)) : $id;
455
        if(!$display_name) {
456
            $display_name = $id;
457
        }
458
    } else {
459
        $exists = file_exists(mediaFN($id));
460
        $display_name = $id;
461
    }
462
463
    $form = new Doku_Form($params);
0 ignored issues
show
Deprecated Code introduced by
The class Doku_Form has been deprecated with message: 2019-07-14

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
464
    $form->addElement(form_makeOpenTag('ul'));
465
466
    if($exists && $first == 0) {
467
        $minor = false;
468
        if($media_id) {
469
            $date = dformat(@filemtime(mediaFN($id)));
470
            $href = media_managerURL(array('image' => $id, 'tab_details' => 'view'), '&');
471
472
            $changelog->setChunkSize(1024);
473
            $revinfo = $changelog->getRevisionInfo(@filemtime(fullpath(mediaFN($id))));
474
475
            $summary = $revinfo['sum'];
476
            if($revinfo['user']) {
477
                $editor = $revinfo['user'];
478
            } else {
479
                $editor = $revinfo['ip'];
480
            }
481
            $sizechange = $revinfo['sizechange'];
482
        } else {
483
            $date = dformat($INFO['lastmod']);
484
            if(isset($INFO['meta']) && isset($INFO['meta']['last_change'])) {
485
                if($INFO['meta']['last_change']['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT) {
486
                    $minor = true;
487
                }
488
                if(isset($INFO['meta']['last_change']['sizechange'])) {
489
                    $sizechange = $INFO['meta']['last_change']['sizechange'];
490
                } else {
491
                    $sizechange = null;
492
                }
493
            }
494
            $pagelog = new PageChangeLog($ID);
495
            $latestrev = $pagelog->getRevisions(-1, 1);
496
            $latestrev = array_pop($latestrev);
497
            $href = wl($id,"rev=$latestrev",false,'&');
498
            $summary = $INFO['sum'];
499
            $editor = $INFO['editor'];
500
        }
501
502
        $form->addElement(form_makeOpenTag('li', array('class' => ($minor ? 'minor' : ''))));
503
        $form->addElement(form_makeOpenTag('div', array('class' => 'li')));
504
        $form->addElement(form_makeTag('input', array(
505
                        'type' => 'checkbox',
506
                        'name' => 'rev2[]',
507
                        'value' => 'current')));
508
509
        $form->addElement(form_makeOpenTag('span', array('class' => 'date')));
510
        $form->addElement($date);
511
        $form->addElement(form_makeCloseTag('span'));
512
513
        $form->addElement('<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />');
514
515
        $form->addElement(form_makeOpenTag('a', array(
516
                        'class' => 'wikilink1',
517
                        'href'  => $href)));
518
        $form->addElement($display_name);
519
        $form->addElement(form_makeCloseTag('a'));
520
521
        if ($media_id) $form->addElement(form_makeOpenTag('div'));
522
523
        if($summary) {
524
            $form->addElement(form_makeOpenTag('span', array('class' => 'sum')));
525
            if(!$media_id) $form->addElement(' – ');
526
            $form->addElement('<bdi>' . hsc($summary) . '</bdi>');
527
            $form->addElement(form_makeCloseTag('span'));
528
        }
529
530
        $form->addElement(form_makeOpenTag('span', array('class' => 'user')));
531
        $form->addElement((empty($editor))?('('.$lang['external_edit'].')'):'<bdi>'.editorinfo($editor).'</bdi>');
532
        $form->addElement(form_makeCloseTag('span'));
533
534
        html_sizechange($sizechange, $form);
535
536
        $form->addElement('('.$lang['current'].')');
537
538
        if ($media_id) $form->addElement(form_makeCloseTag('div'));
539
540
        $form->addElement(form_makeCloseTag('div'));
541
        $form->addElement(form_makeCloseTag('li'));
542
    }
543
544
    foreach($revisions as $rev) {
545
        $date = dformat($rev);
546
        $info = $changelog->getRevisionInfo($rev);
547
        if($media_id) {
548
            $exists = file_exists(mediaFN($id, $rev));
549
        } else {
550
            $exists = page_exists($id, $rev);
551
        }
552
553
        $class = '';
554
        if($info['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT) {
555
            $class = 'minor';
556
        }
557
        $form->addElement(form_makeOpenTag('li', array('class' => $class)));
558
        $form->addElement(form_makeOpenTag('div', array('class' => 'li')));
559
        if($exists){
560
            $form->addElement(form_makeTag('input', array(
561
                            'type' => 'checkbox',
562
                            'name' => 'rev2[]',
563
                            'value' => $rev)));
564
        }else{
565
            $form->addElement('<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />');
566
        }
567
568
        $form->addElement(form_makeOpenTag('span', array('class' => 'date')));
569
        $form->addElement($date);
570
        $form->addElement(form_makeCloseTag('span'));
571
572
        if($exists){
573
            if (!$media_id) {
574
                $href = wl($id,"rev=$rev,do=diff", false, '&');
575
            } else {
576
                $href = media_managerURL(array('image' => $id, 'rev' => $rev, 'mediado' => 'diff'), '&');
577
            }
578
            $form->addElement(form_makeOpenTag('a', array(
579
                            'class' => 'diff_link',
580
                            'href' => $href)));
581
            $form->addElement(form_makeTag('img', array(
582
                            'src'    => DOKU_BASE.'lib/images/diff.png',
583
                            'width'  => 15,
584
                            'height' => 11,
585
                            'title'  => $lang['diff'],
586
                            'alt'    => $lang['diff'])));
587
            $form->addElement(form_makeCloseTag('a'));
588
589
            if (!$media_id) {
590
                $href = wl($id,"rev=$rev",false,'&');
591
            } else {
592
                $href = media_managerURL(array('image' => $id, 'tab_details' => 'view', 'rev' => $rev), '&');
593
            }
594
            $form->addElement(form_makeOpenTag('a', array(
595
                            'class' => 'wikilink1',
596
                            'href' => $href)));
597
            $form->addElement($display_name);
598
            $form->addElement(form_makeCloseTag('a'));
599
        }else{
600
            $form->addElement('<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />');
601
            $form->addElement($display_name);
602
        }
603
604
        if ($media_id) $form->addElement(form_makeOpenTag('div'));
605
606
        if ($info['sum']) {
607
            $form->addElement(form_makeOpenTag('span', array('class' => 'sum')));
608
            if(!$media_id) $form->addElement(' – ');
609
            $form->addElement('<bdi>'.hsc($info['sum']).'</bdi>');
610
            $form->addElement(form_makeCloseTag('span'));
611
        }
612
613
        $form->addElement(form_makeOpenTag('span', array('class' => 'user')));
614
        if($info['user']){
615
            $form->addElement('<bdi>'.editorinfo($info['user']).'</bdi>');
616
            if(auth_ismanager()){
617
                $form->addElement(' <bdo dir="ltr">('.$info['ip'].')</bdo>');
618
            }
619
        }else{
620
            $form->addElement('<bdo dir="ltr">'.$info['ip'].'</bdo>');
621
        }
622
        $form->addElement(form_makeCloseTag('span'));
623
624
        html_sizechange($info['sizechange'], $form);
625
626
        if ($media_id) $form->addElement(form_makeCloseTag('div'));
627
628
        $form->addElement(form_makeCloseTag('div'));
629
        $form->addElement(form_makeCloseTag('li'));
630
    }
631
    $form->addElement(form_makeCloseTag('ul'));
632
    if (!$media_id) {
633
        $form->addElement(form_makeButton('submit', 'diff', $lang['diff2']));
634
    } else {
635
        $form->addHidden('mediado', 'diff');
636
        $form->addElement(form_makeButton('submit', '', $lang['diff2']));
637
    }
638
    html_form('revisions', $form);
639
640
    print '<div class="pagenav">';
641
    $last = $first + $conf['recent'];
642
    if ($first > 0) {
643
        $first -= $conf['recent'];
644
        if ($first < 0) $first = 0;
645
        print '<div class="pagenav-prev">';
646
        if ($media_id) {
647
            print html_btn('newer',$media_id,"p",media_managerURL(array('first' => $first), '&amp;', false, true));
648
        } else {
649
            print html_btn('newer',$id,"p",array('do' => 'revisions', 'first' => $first));
650
        }
651
        print '</div>';
652
    }
653
    if ($hasNext) {
654
        print '<div class="pagenav-next">';
655
        if ($media_id) {
656
            print html_btn('older',$media_id,"n",media_managerURL(array('first' => $last), '&amp;', false, true));
657
        } else {
658
            print html_btn('older',$id,"n",array('do' => 'revisions', 'first' => $last));
659
        }
660
        print '</div>';
661
    }
662
    print '</div>';
663
664
}
665
666
/**
667
 * display recent changes
668
 *
669
 * @author Andreas Gohr <[email protected]>
670
 * @author Matthias Grimm <[email protected]>
671
 * @author Ben Coburn <[email protected]>
672
 * @author Kate Arzamastseva <[email protected]>
673
 *
674
 * @param int $first
675
 * @param string $show_changes
676
 */
677
function html_recent($first = 0, $show_changes = 'both') {
678
    global $conf;
679
    global $lang;
680
    global $ID;
681
    /* we need to get one additionally log entry to be able to
682
     * decide if this is the last page or is there another one.
683
     * This is the cheapest solution to get this information.
684
     */
685
    $flags = 0;
686
    if($show_changes == 'mediafiles' && $conf['mediarevisions']) {
687
        $flags = RECENTS_MEDIA_CHANGES;
688
    } elseif($show_changes == 'pages') {
689
        $flags = 0;
690
    } elseif($conf['mediarevisions']) {
691
        $show_changes = 'both';
692
        $flags = RECENTS_MEDIA_PAGES_MIXED;
693
    }
694
695
    $recents = getRecents($first, $conf['recent'] + 1, getNS($ID), $flags);
696
    if(count($recents) == 0 && $first != 0) {
697
        $first = 0;
698
        $recents = getRecents($first, $conf['recent'] + 1, getNS($ID), $flags);
699
    }
700
    $hasNext = false;
701
    if(count($recents) > $conf['recent']) {
702
        $hasNext = true;
703
        array_pop($recents); // remove extra log entry
704
    }
705
706
    print p_locale_xhtml('recent');
707
708
    if(getNS($ID) != '') {
709
        print '<div class="level1"><p>' .
710
            sprintf($lang['recent_global'], getNS($ID), wl('', 'do=recent')) .
711
            '</p></div>';
712
    }
713
714
    $form = new Doku_Form(array('id' => 'dw__recent', 'method' => 'GET', 'class' => 'changes', 'action'=>wl($ID)));
715
    $form->addHidden('sectok', null);
716
    $form->addHidden('do', 'recent');
717
    $form->addHidden('id', $ID);
718
719
    if($conf['mediarevisions']) {
720
        $form->addElement('<div class="changeType">');
721
        $form->addElement(form_makeListboxField(
722
                    'show_changes',
723
                    array(
724
                        'pages'      => $lang['pages_changes'],
725
                        'mediafiles' => $lang['media_changes'],
726
                        'both'       => $lang['both_changes']
727
                    ),
728
                    $show_changes,
729
                    $lang['changes_type'],
730
                    '', '',
731
                    array('class' => 'quickselect')));
732
733
        $form->addElement(form_makeButton('submit', 'recent', $lang['btn_apply']));
734
        $form->addElement('</div>');
735
    }
736
737
    $form->addElement(form_makeOpenTag('ul'));
738
739
    foreach($recents as $recent) {
740
        $date = dformat($recent['date']);
741
742
        $class = '';
743
        if($recent['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT) {
744
            $class = 'minor';
745
        }
746
        $form->addElement(form_makeOpenTag('li', array('class' => $class)));
747
        $form->addElement(form_makeOpenTag('div', array('class' => 'li')));
748
749
        if(!empty($recent['media'])) {
750
            $form->addElement(media_printicon($recent['id']));
751
        } else {
752
            $icon = DOKU_BASE . 'lib/images/fileicons/file.png';
753
            $form->addElement('<img src="' . $icon . '" alt="' . $recent['id'] . '" class="icon" />');
754
        }
755
756
        $form->addElement(form_makeOpenTag('span', array('class' => 'date')));
757
        $form->addElement($date);
758
        $form->addElement(form_makeCloseTag('span'));
759
760
        $diff = false;
761
        $href = '';
762
763
        if(!empty($recent['media'])) {
764
            $changelog = new MediaChangeLog($recent['id']);
765
            $revs = $changelog->getRevisions(0, 1);
766
            $diff = (count($revs) && file_exists(mediaFN($recent['id'])));
767
            if($diff) {
768
                $href = media_managerURL(array(
769
                                            'tab_details' => 'history',
770
                                            'mediado' => 'diff',
771
                                            'image' => $recent['id'],
772
                                            'ns' => getNS($recent['id'])
773
                                        ), '&');
774
            }
775
        } else {
776
            $href = wl($recent['id'], "do=diff", false, '&');
777
        }
778
779
        if(!empty($recent['media']) && !$diff) {
780
            $form->addElement('<img src="' . DOKU_BASE . 'lib/images/blank.gif" width="15" height="11" alt="" />');
781
        } else {
782
            $form->addElement(form_makeOpenTag('a', array('class' => 'diff_link', 'href' => $href)));
783
            $form->addElement(form_makeTag('img', array(
784
                            'src'    => DOKU_BASE . 'lib/images/diff.png',
785
                            'width'  => 15,
786
                            'height' => 11,
787
                            'title'  => $lang['diff'],
788
                            'alt'    => $lang['diff']
789
                        )));
790
            $form->addElement(form_makeCloseTag('a'));
791
        }
792
793
        if(!empty($recent['media'])) {
794
            $href = media_managerURL(
795
                array(
796
                    'tab_details' => 'history',
797
                    'image' => $recent['id'],
798
                    'ns' => getNS($recent['id'])
799
                ),
800
                '&'
801
            );
802
        } else {
803
            $href = wl($recent['id'], "do=revisions", false, '&');
804
        }
805
        $form->addElement(form_makeOpenTag('a', array(
806
                        'class' => 'revisions_link',
807
                        'href'  => $href)));
808
        $form->addElement(form_makeTag('img', array(
809
                        'src'    => DOKU_BASE . 'lib/images/history.png',
810
                        'width'  => 12,
811
                        'height' => 14,
812
                        'title'  => $lang['btn_revs'],
813
                        'alt'    => $lang['btn_revs']
814
                    )));
815
        $form->addElement(form_makeCloseTag('a'));
816
817
        if(!empty($recent['media'])) {
818
            $href = media_managerURL(
819
                array(
820
                    'tab_details' => 'view',
821
                    'image' => $recent['id'],
822
                    'ns' => getNS($recent['id'])
823
                ),
824
                '&'
825
            );
826
            $class = file_exists(mediaFN($recent['id'])) ? 'wikilink1' : 'wikilink2';
827
            $form->addElement(form_makeOpenTag('a', array(
828
                        'class' => $class,
829
                        'href'  => $href)));
830
            $form->addElement($recent['id']);
831
            $form->addElement(form_makeCloseTag('a'));
832
        } else {
833
            $form->addElement(html_wikilink(':' . $recent['id'], useHeading('navigation') ? null : $recent['id']));
834
        }
835
        $form->addElement(form_makeOpenTag('span', array('class' => 'sum')));
836
        $form->addElement(' – ' . hsc($recent['sum']));
837
        $form->addElement(form_makeCloseTag('span'));
838
839
        $form->addElement(form_makeOpenTag('span', array('class' => 'user')));
840
        if($recent['user']) {
841
            $form->addElement('<bdi>' . editorinfo($recent['user']) . '</bdi>');
842
            if(auth_ismanager()) {
843
                $form->addElement(' <bdo dir="ltr">(' . $recent['ip'] . ')</bdo>');
844
            }
845
        } else {
846
            $form->addElement('<bdo dir="ltr">' . $recent['ip'] . '</bdo>');
847
        }
848
        $form->addElement(form_makeCloseTag('span'));
849
850
        html_sizechange($recent['sizechange'], $form);
851
852
        $form->addElement(form_makeCloseTag('div'));
853
        $form->addElement(form_makeCloseTag('li'));
854
    }
855
    $form->addElement(form_makeCloseTag('ul'));
856
857
    $form->addElement(form_makeOpenTag('div', array('class' => 'pagenav')));
858
    $last = $first + $conf['recent'];
859
    if($first > 0) {
860
        $first -= $conf['recent'];
861
        if($first < 0) $first = 0;
862
        $form->addElement(form_makeOpenTag('div', array('class' => 'pagenav-prev')));
863
        $form->addElement(form_makeOpenTag('button', array(
864
                        'type'      => 'submit',
865
                        'name'      => 'first[' . $first . ']',
866
                        'accesskey' => 'n',
867
                        'title'     => $lang['btn_newer'] . ' [N]',
868
                        'class'     => 'button show'
869
                    )));
870
        $form->addElement($lang['btn_newer']);
871
        $form->addElement(form_makeCloseTag('button'));
872
        $form->addElement(form_makeCloseTag('div'));
873
    }
874
    if($hasNext) {
875
        $form->addElement(form_makeOpenTag('div', array('class' => 'pagenav-next')));
876
        $form->addElement(form_makeOpenTag('button', array(
877
                        'type'      => 'submit',
878
                        'name'      => 'first[' . $last . ']',
879
                        'accesskey' => 'p',
880
                        'title'     => $lang['btn_older'] . ' [P]',
881
                        'class'     => 'button show'
882
                    )));
883
        $form->addElement($lang['btn_older']);
884
        $form->addElement(form_makeCloseTag('button'));
885
        $form->addElement(form_makeCloseTag('div'));
886
    }
887
    $form->addElement(form_makeCloseTag('div'));
888
    html_form('recent', $form);
889
}
890
891
/**
892
 * Display page index
893
 *
894
 * @author Andreas Gohr <[email protected]>
895
 *
896
 * @param string $ns
897
 */
898
function html_index($ns){
899
    global $conf;
900
    global $ID;
901
    $ns  = cleanID($ns);
902
    if(empty($ns)){
903
        $ns = getNS($ID);
904
        if($ns === false) $ns ='';
905
    }
906
    $ns  = utf8_encodeFN(str_replace(':','/',$ns));
907
908
    echo p_locale_xhtml('index');
909
    echo '<div id="index__tree" class="index__tree">';
910
911
    $data = array();
912
    search($data,$conf['datadir'],'search_index',array('ns' => $ns));
913
    echo html_buildlist($data,'idx','html_list_index','html_li_index');
914
915
    echo '</div>';
916
}
917
918
/**
919
 * Index item formatter
920
 *
921
 * User function for html_buildlist()
922
 *
923
 * @author Andreas Gohr <[email protected]>
924
 *
925
 * @param array $item
926
 * @return string
927
 */
928
function html_list_index($item){
929
    global $ID, $conf;
930
931
    // prevent searchbots needlessly following links
932
    $nofollow = ($ID != $conf['start'] || $conf['sitemap']) ? 'rel="nofollow"' : '';
933
934
    $ret = '';
935
    $base = ':'.$item['id'];
936
    $base = substr($base,strrpos($base,':')+1);
937
    if($item['type']=='d'){
938
        // FS#2766, no need for search bots to follow namespace links in the index
939
        $link = wl($ID, 'idx=' . rawurlencode($item['id']));
940
        $ret .= '<a href="' . $link . '" title="' . $item['id'] . '" class="idx_dir" ' . $nofollow . '><strong>';
941
        $ret .= $base;
942
        $ret .= '</strong></a>';
943
    }else{
944
        // default is noNSorNS($id), but we want noNS($id) when useheading is off FS#2605
945
        $ret .= html_wikilink(':'.$item['id'], useHeading('navigation') ? null : noNS($item['id']));
946
    }
947
    return $ret;
948
}
949
950
/**
951
 * Index List item
952
 *
953
 * This user function is used in html_buildlist to build the
954
 * <li> tags for namespaces when displaying the page index
955
 * it gives different classes to opened or closed "folders"
956
 *
957
 * @author Andreas Gohr <[email protected]>
958
 *
959
 * @param array $item
960
 * @return string html
961
 */
962
function html_li_index($item){
963
    global $INFO;
964
    global $ACT;
965
966
    $class = '';
967
    $id = '';
968
969
    if($item['type'] == "f"){
970
        // scroll to the current item
971
        if(isset($INFO) && $item['id'] == $INFO['id'] && $ACT == 'index') {
972
            $id = ' id="scroll__here"';
973
            $class = ' bounce';
974
        }
975
        return '<li class="level'.$item['level'].$class.'" '.$id.'>';
976
    }elseif($item['open']){
977
        return '<li class="open">';
978
    }else{
979
        return '<li class="closed">';
980
    }
981
}
982
983
/**
984
 * Default List item
985
 *
986
 * @author Andreas Gohr <[email protected]>
987
 *
988
 * @param array $item
989
 * @return string html
990
 */
991
function html_li_default($item){
992
    return '<li class="level'.$item['level'].'">';
993
}
994
995
/**
996
 * Build an unordered list
997
 *
998
 * Build an unordered list from the given $data array
999
 * Each item in the array has to have a 'level' property
1000
 * the item itself gets printed by the given $func user
1001
 * function. The second and optional function is used to
1002
 * print the <li> tag. Both user function need to accept
1003
 * a single item.
1004
 *
1005
 * Both user functions can be given as array to point to
1006
 * a member of an object.
1007
 *
1008
 * @author Andreas Gohr <[email protected]>
1009
 *
1010
 * @param array    $data  array with item arrays
1011
 * @param string   $class class of ul wrapper
1012
 * @param callable $func  callback to print an list item
1013
 * @param callable $lifunc callback to the opening li tag
1014
 * @param bool     $forcewrapper Trigger building a wrapper ul if the first level is
1015
 *                               0 (we have a root object) or 1 (just the root content)
1016
 * @return string html of an unordered list
1017
 */
1018
function html_buildlist($data,$class,$func,$lifunc='html_li_default',$forcewrapper=false){
1019
    if (count($data) === 0) {
1020
        return '';
1021
    }
1022
1023
    $firstElement = reset($data);
1024
    $start_level = $firstElement['level'];
1025
    $level = $start_level;
1026
    $ret   = '';
1027
    $open  = 0;
1028
1029
    foreach ($data as $item){
1030
1031
        if( $item['level'] > $level ){
1032
            //open new list
1033
            for($i=0; $i<($item['level'] - $level); $i++){
1034
                if ($i) $ret .= "<li class=\"clear\">";
1035
                $ret .= "\n<ul class=\"$class\">\n";
1036
                $open++;
1037
            }
1038
            $level = $item['level'];
1039
1040
        }elseif( $item['level'] < $level ){
1041
            //close last item
1042
            $ret .= "</li>\n";
1043
            while( $level > $item['level'] && $open > 0 ){
1044
                //close higher lists
1045
                $ret .= "</ul>\n</li>\n";
1046
                $level--;
1047
                $open--;
1048
            }
1049
        } elseif ($ret !== '') {
1050
            //close previous item
1051
            $ret .= "</li>\n";
1052
        }
1053
1054
        //print item
1055
        $ret .= call_user_func($lifunc,$item);
1056
        $ret .= '<div class="li">';
1057
1058
        $ret .= call_user_func($func,$item);
1059
        $ret .= '</div>';
1060
    }
1061
1062
    //close remaining items and lists
1063
    $ret .= "</li>\n";
1064
    while($open-- > 0) {
1065
        $ret .= "</ul></li>\n";
1066
    }
1067
1068
    if ($forcewrapper || $start_level < 2) {
1069
        // Trigger building a wrapper ul if the first level is
1070
        // 0 (we have a root object) or 1 (just the root content)
1071
        $ret = "\n<ul class=\"$class\">\n".$ret."</ul>\n";
1072
    }
1073
1074
    return $ret;
1075
}
1076
1077
/**
1078
 * display backlinks
1079
 *
1080
 * @author Andreas Gohr <[email protected]>
1081
 * @author Michael Klier <[email protected]>
1082
 */
1083
function html_backlinks(){
1084
    global $ID;
1085
    global $lang;
1086
1087
    print p_locale_xhtml('backlinks');
1088
1089
    $MetadataIndex = MetadataIndex::getInstance();
1090
    $data = $MetadataIndex->backlinks($ID);
1091
1092
    if(!empty($data)) {
1093
        print '<ul class="idx">';
1094
        foreach($data as $blink){
1095
            print '<li><div class="li">';
1096
            print html_wikilink(':'.$blink,useHeading('navigation')?null:$blink);
1097
            print '</div></li>';
1098
        }
1099
        print '</ul>';
1100
    } else {
1101
        print '<div class="level1"><p>' . $lang['nothingfound'] . '</p></div>';
1102
    }
1103
}
1104
1105
/**
1106
 * Get header of diff HTML
1107
 *
1108
 * @param string $l_rev   Left revisions
1109
 * @param string $r_rev   Right revision
1110
 * @param string $id      Page id, if null $ID is used
1111
 * @param bool   $media   If it is for media files
1112
 * @param bool   $inline  Return the header on a single line
1113
 * @return string[] HTML snippets for diff header
1114
 */
1115
function html_diff_head($l_rev, $r_rev, $id = null, $media = false, $inline = false) {
1116
    global $lang;
1117
    if ($id === null) {
1118
        global $ID;
1119
        $id = $ID;
1120
    }
1121
    $head_separator = $inline ? ' ' : '<br />';
1122
    $media_or_wikiFN = $media ? 'mediaFN' : 'wikiFN';
1123
    $ml_or_wl = $media ? 'ml' : 'wl';
1124
    $l_minor = $r_minor = '';
1125
1126
    if($media) {
1127
        $changelog = new MediaChangeLog($id);
1128
    } else {
1129
        $changelog = new PageChangeLog($id);
1130
    }
1131
    if(!$l_rev){
1132
        $l_head = '&mdash;';
1133
    }else{
1134
        $l_info   = $changelog->getRevisionInfo($l_rev);
1135
        if($l_info['user']){
1136
            $l_user = '<bdi>'.editorinfo($l_info['user']).'</bdi>';
1137
            if(auth_ismanager()) $l_user .= ' <bdo dir="ltr">('.$l_info['ip'].')</bdo>';
1138
        } else {
1139
            $l_user = '<bdo dir="ltr">'.$l_info['ip'].'</bdo>';
1140
        }
1141
        $l_user  = '<span class="user">'.$l_user.'</span>';
1142
        $l_sum   = ($l_info['sum']) ? '<span class="sum"><bdi>'.hsc($l_info['sum']).'</bdi></span>' : '';
1143
        if ($l_info['type']===DOKU_CHANGE_TYPE_MINOR_EDIT) $l_minor = 'class="minor"';
1144
1145
        $l_head_title = ($media) ? dformat($l_rev) : $id.' ['.dformat($l_rev).']';
1146
        $l_head = '<bdi><a class="wikilink1" href="'.$ml_or_wl($id,"rev=$l_rev").'">'.
1147
        $l_head_title.'</a></bdi>'.
1148
        $head_separator.$l_user.' '.$l_sum;
1149
    }
1150
1151
    if($r_rev){
1152
        $r_info   = $changelog->getRevisionInfo($r_rev);
1153
        if($r_info['user']){
1154
            $r_user = '<bdi>'.editorinfo($r_info['user']).'</bdi>';
1155
            if(auth_ismanager()) $r_user .= ' <bdo dir="ltr">('.$r_info['ip'].')</bdo>';
1156
        } else {
1157
            $r_user = '<bdo dir="ltr">'.$r_info['ip'].'</bdo>';
1158
        }
1159
        $r_user = '<span class="user">'.$r_user.'</span>';
1160
        $r_sum  = ($r_info['sum']) ? '<span class="sum"><bdi>'.hsc($r_info['sum']).'</bdi></span>' : '';
1161
        if ($r_info['type']===DOKU_CHANGE_TYPE_MINOR_EDIT) $r_minor = 'class="minor"';
1162
1163
        $r_head_title = ($media) ? dformat($r_rev) : $id.' ['.dformat($r_rev).']';
1164
        $r_head = '<bdi><a class="wikilink1" href="'.$ml_or_wl($id,"rev=$r_rev").'">'.
1165
        $r_head_title.'</a></bdi>'.
1166
        $head_separator.$r_user.' '.$r_sum;
1167
    }elseif($_rev = @filemtime($media_or_wikiFN($id))){
1168
        $_info   = $changelog->getRevisionInfo($_rev);
1169
        if($_info['user']){
1170
            $_user = '<bdi>'.editorinfo($_info['user']).'</bdi>';
1171
            if(auth_ismanager()) $_user .= ' <bdo dir="ltr">('.$_info['ip'].')</bdo>';
1172
        } else {
1173
            $_user = '<bdo dir="ltr">'.$_info['ip'].'</bdo>';
1174
        }
1175
        $_user = '<span class="user">'.$_user.'</span>';
1176
        $_sum  = ($_info['sum']) ? '<span class="sum"><bdi>'.hsc($_info['sum']).'</span></bdi>' : '';
1177
        if ($_info['type']===DOKU_CHANGE_TYPE_MINOR_EDIT) $r_minor = 'class="minor"';
1178
1179
        $r_head_title = ($media) ? dformat($_rev) : $id.' ['.dformat($_rev).']';
1180
        $r_head  = '<bdi><a class="wikilink1" href="'.$ml_or_wl($id).'">'.
1181
        $r_head_title.'</a></bdi> '.
1182
        '('.$lang['current'].')'.
1183
        $head_separator.$_user.' '.$_sum;
1184
    }else{
1185
        $r_head = '&mdash; ('.$lang['current'].')';
1186
    }
1187
1188
    return array($l_head, $r_head, $l_minor, $r_minor);
1189
}
1190
1191
/**
1192
 * Show diff
1193
 * between current page version and provided $text
1194
 * or between the revisions provided via GET or POST
1195
 *
1196
 * @author Andreas Gohr <[email protected]>
1197
 * @param  string $text  when non-empty: compare with this text with most current version
1198
 * @param  bool   $intro display the intro text
1199
 * @param  string $type  type of the diff (inline or sidebyside)
1200
 */
1201
function html_diff($text = '', $intro = true, $type = null) {
1202
    global $ID;
1203
    global $REV;
1204
    global $lang;
1205
    global $INPUT;
1206
    global $INFO;
1207
    $pagelog = new PageChangeLog($ID);
1208
1209
    /*
1210
     * Determine diff type
1211
     */
1212
    if(!$type) {
1213
        $type = $INPUT->str('difftype');
1214
        if(empty($type)) {
1215
            $type = get_doku_pref('difftype', $type);
1216
            if(empty($type) && $INFO['ismobile']) {
1217
                $type = 'inline';
1218
            }
1219
        }
1220
    }
1221
    if($type != 'inline') $type = 'sidebyside';
1222
1223
    /*
1224
     * Determine requested revision(s)
1225
     */
1226
    // we're trying to be clever here, revisions to compare can be either
1227
    // given as rev and rev2 parameters, with rev2 being optional. Or in an
1228
    // array in rev2.
1229
    $rev1 = $REV;
1230
1231
    $rev2 = $INPUT->ref('rev2');
1232
    if(is_array($rev2)) {
1233
        $rev1 = (int) $rev2[0];
1234
        $rev2 = (int) $rev2[1];
1235
1236
        if(!$rev1) {
1237
            $rev1 = $rev2;
1238
            unset($rev2);
1239
        }
1240
    } else {
1241
        $rev2 = $INPUT->int('rev2');
1242
    }
1243
1244
    /*
1245
     * Determine left and right revision, its texts and the header
1246
     */
1247
    $r_minor = '';
1248
    $l_minor = '';
1249
1250
    if($text) { // compare text to the most current revision
1251
        $l_rev = '';
1252
        $l_text = rawWiki($ID, '');
1253
        $l_head = '<a class="wikilink1" href="' . wl($ID) . '">' .
1254
            $ID . ' ' . dformat((int) @filemtime(wikiFN($ID))) . '</a> ' .
1255
            $lang['current'];
1256
1257
        $r_rev = '';
1258
        $r_text = cleanText($text);
1259
        $r_head = $lang['yours'];
1260
    } else {
1261
        if($rev1 && isset($rev2) && $rev2) { // two specific revisions wanted
1262
            // make sure order is correct (older on the left)
1263
            if($rev1 < $rev2) {
1264
                $l_rev = $rev1;
1265
                $r_rev = $rev2;
1266
            } else {
1267
                $l_rev = $rev2;
1268
                $r_rev = $rev1;
1269
            }
1270
        } elseif($rev1) { // single revision given, compare to current
1271
            $r_rev = '';
1272
            $l_rev = $rev1;
1273
        } else { // no revision was given, compare previous to current
1274
            $r_rev = '';
1275
            $revs = $pagelog->getRevisions(0, 1);
1276
            $l_rev = $revs[0];
1277
            $REV = $l_rev; // store revision back in $REV
1278
        }
1279
1280
        // when both revisions are empty then the page was created just now
1281
        if(!$l_rev && !$r_rev) {
1282
            $l_text = '';
1283
        } else {
1284
            $l_text = rawWiki($ID, $l_rev);
1285
        }
1286
        $r_text = rawWiki($ID, $r_rev);
1287
1288
        list($l_head, $r_head, $l_minor, $r_minor) = html_diff_head($l_rev, $r_rev, null, false, $type == 'inline');
1289
    }
1290
1291
    /*
1292
     * Build navigation
1293
     */
1294
    $l_nav = '';
1295
    $r_nav = '';
1296
    if(!$text) {
1297
        list($l_nav, $r_nav) = html_diff_navigation($pagelog, $type, $l_rev, $r_rev);
1298
    }
1299
    /*
1300
     * Create diff object and the formatter
1301
     */
1302
    $diff = new Diff(explode("\n", $l_text), explode("\n", $r_text));
1303
1304
    if($type == 'inline') {
1305
        $diffformatter = new InlineDiffFormatter();
1306
    } else {
1307
        $diffformatter = new TableDiffFormatter();
1308
    }
1309
    /*
1310
     * Display intro
1311
     */
1312
    if($intro) print p_locale_xhtml('diff');
1313
1314
    /*
1315
     * Display type and exact reference
1316
     */
1317
    if(!$text) {
1318
        ptln('<div class="diffoptions group">');
1319
1320
1321
        $form = new Doku_Form(array('action' => wl()));
0 ignored issues
show
Deprecated Code introduced by
The class Doku_Form has been deprecated with message: 2019-07-14

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
1322
        $form->addHidden('id', $ID);
1323
        $form->addHidden('rev2[0]', $l_rev);
1324
        $form->addHidden('rev2[1]', $r_rev);
1325
        $form->addHidden('do', 'diff');
1326
        $form->addElement(
1327
             form_makeListboxField(
1328
                 'difftype',
1329
                 array(
1330
                     'sidebyside' => $lang['diff_side'],
1331
                     'inline' => $lang['diff_inline']
1332
                 ),
1333
                 $type,
1334
                 $lang['diff_type'],
1335
                 '', '',
1336
                 array('class' => 'quickselect')
1337
             )
1338
        );
1339
        $form->addElement(form_makeButton('submit', 'diff', 'Go'));
1340
        $form->printForm();
1341
1342
        ptln('<p>');
1343
        // link to exactly this view FS#2835
1344
        echo html_diff_navigationlink($type, 'difflink', $l_rev, $r_rev ? $r_rev : $INFO['currentrev']);
1345
        ptln('</p>');
1346
1347
        ptln('</div>'); // .diffoptions
1348
    }
1349
1350
    /*
1351
     * Display diff view table
1352
     */
1353
    ?>
1354
    <div class="table">
1355
    <table class="diff diff_<?php echo $type ?>">
1356
1357
        <?php
1358
        //navigation and header
1359
        if($type == 'inline') {
1360
            if(!$text) { ?>
1361
                <tr>
1362
                    <td class="diff-lineheader">-</td>
1363
                    <td class="diffnav"><?php echo $l_nav ?></td>
1364
                </tr>
1365
                <tr>
1366
                    <th class="diff-lineheader">-</th>
1367
                    <th <?php echo $l_minor ?>>
1368
                        <?php echo $l_head ?>
1369
                    </th>
1370
                </tr>
1371
            <?php } ?>
1372
            <tr>
1373
                <td class="diff-lineheader">+</td>
1374
                <td class="diffnav"><?php echo $r_nav ?></td>
1375
            </tr>
1376
            <tr>
1377
                <th class="diff-lineheader">+</th>
1378
                <th <?php echo $r_minor ?>>
1379
                    <?php echo $r_head ?>
1380
                </th>
1381
            </tr>
1382
        <?php } else {
1383
            if(!$text) { ?>
1384
                <tr>
1385
                    <td colspan="2" class="diffnav"><?php echo $l_nav ?></td>
1386
                    <td colspan="2" class="diffnav"><?php echo $r_nav ?></td>
1387
                </tr>
1388
            <?php } ?>
1389
            <tr>
1390
                <th colspan="2" <?php echo $l_minor ?>>
1391
                    <?php echo $l_head ?>
1392
                </th>
1393
                <th colspan="2" <?php echo $r_minor ?>>
1394
                    <?php echo $r_head ?>
1395
                </th>
1396
            </tr>
1397
        <?php }
1398
1399
        //diff view
1400
        echo html_insert_softbreaks($diffformatter->format($diff)); ?>
1401
1402
    </table>
1403
    </div>
1404
<?php
1405
}
1406
1407
/**
1408
 * Create html for revision navigation
1409
 *
1410
 * @param PageChangeLog $pagelog changelog object of current page
1411
 * @param string        $type    inline vs sidebyside
1412
 * @param int           $l_rev   left revision timestamp
1413
 * @param int           $r_rev   right revision timestamp
1414
 * @return string[] html of left and right navigation elements
1415
 */
1416
function html_diff_navigation($pagelog, $type, $l_rev, $r_rev) {
1417
    global $INFO, $ID;
1418
1419
    // last timestamp is not in changelog, retrieve timestamp from metadata
1420
    // note: when page is removed, the metadata timestamp is zero
1421
    if(!$r_rev) {
1422
        if(isset($INFO['meta']['last_change']['date'])) {
1423
            $r_rev = $INFO['meta']['last_change']['date'];
1424
        } else {
1425
            $r_rev = 0;
1426
        }
1427
    }
1428
1429
    //retrieve revisions with additional info
1430
    list($l_revs, $r_revs) = $pagelog->getRevisionsAround($l_rev, $r_rev);
1431
    $l_revisions = array();
1432
    if(!$l_rev) {
1433
        $l_revisions[0] = array(0, "", false); //no left revision given, add dummy
1434
    }
1435
    foreach($l_revs as $rev) {
1436
        $info = $pagelog->getRevisionInfo($rev);
1437
        $l_revisions[$rev] = array(
1438
            $rev,
1439
            dformat($info['date']) . ' ' . editorinfo($info['user'], true) . ' ' . $info['sum'],
1440
            $r_rev ? $rev >= $r_rev : false //disable?
1441
        );
1442
    }
1443
    $r_revisions = array();
1444
    if(!$r_rev) {
1445
        $r_revisions[0] = array(0, "", false); //no right revision given, add dummy
1446
    }
1447
    foreach($r_revs as $rev) {
1448
        $info = $pagelog->getRevisionInfo($rev);
1449
        $r_revisions[$rev] = array(
1450
            $rev,
1451
            dformat($info['date']) . ' ' . editorinfo($info['user'], true) . ' ' . $info['sum'],
1452
            $rev <= $l_rev //disable?
1453
        );
1454
    }
1455
1456
    //determine previous/next revisions
1457
    $l_index = array_search($l_rev, $l_revs);
1458
    $l_prev = $l_revs[$l_index + 1];
1459
    $l_next = $l_revs[$l_index - 1];
1460
    if($r_rev) {
1461
        $r_index = array_search($r_rev, $r_revs);
1462
        $r_prev = $r_revs[$r_index + 1];
1463
        $r_next = $r_revs[$r_index - 1];
1464
    } else {
1465
        //removed page
1466
        if($l_next) {
1467
            $r_prev = $r_revs[0];
1468
        } else {
1469
            $r_prev = null;
1470
        }
1471
        $r_next = null;
1472
    }
1473
1474
    /*
1475
     * Left side:
1476
     */
1477
    $l_nav = '';
1478
    //move back
1479
    if($l_prev) {
1480
        $l_nav .= html_diff_navigationlink($type, 'diffbothprevrev', $l_prev, $r_prev);
1481
        $l_nav .= html_diff_navigationlink($type, 'diffprevrev', $l_prev, $r_rev);
1482
    }
1483
    //dropdown
1484
    $form = new Doku_Form(array('action' => wl()));
0 ignored issues
show
Deprecated Code introduced by
The class Doku_Form has been deprecated with message: 2019-07-14

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
1485
    $form->addHidden('id', $ID);
1486
    $form->addHidden('difftype', $type);
1487
    $form->addHidden('rev2[1]', $r_rev);
1488
    $form->addHidden('do', 'diff');
1489
    $form->addElement(
1490
         form_makeListboxField(
1491
             'rev2[0]',
1492
             $l_revisions,
1493
             $l_rev,
1494
             '', '', '',
1495
             array('class' => 'quickselect')
1496
         )
1497
    );
1498
    $form->addElement(form_makeButton('submit', 'diff', 'Go'));
1499
    $l_nav .= $form->getForm();
1500
    //move forward
1501
    if($l_next && ($l_next < $r_rev || !$r_rev)) {
1502
        $l_nav .= html_diff_navigationlink($type, 'diffnextrev', $l_next, $r_rev);
1503
    }
1504
1505
    /*
1506
     * Right side:
1507
     */
1508
    $r_nav = '';
1509
    //move back
1510
    if($l_rev < $r_prev) {
1511
        $r_nav .= html_diff_navigationlink($type, 'diffprevrev', $l_rev, $r_prev);
1512
    }
1513
    //dropdown
1514
    $form = new Doku_Form(array('action' => wl()));
0 ignored issues
show
Deprecated Code introduced by
The class Doku_Form has been deprecated with message: 2019-07-14

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
1515
    $form->addHidden('id', $ID);
1516
    $form->addHidden('rev2[0]', $l_rev);
1517
    $form->addHidden('difftype', $type);
1518
    $form->addHidden('do', 'diff');
1519
    $form->addElement(
1520
         form_makeListboxField(
1521
             'rev2[1]',
1522
             $r_revisions,
1523
             $r_rev,
1524
             '', '', '',
1525
             array('class' => 'quickselect')
1526
         )
1527
    );
1528
    $form->addElement(form_makeButton('submit', 'diff', 'Go'));
1529
    $r_nav .= $form->getForm();
1530
    //move forward
1531
    if($r_next) {
1532
        if($pagelog->isCurrentRevision($r_next)) {
1533
            $r_nav .= html_diff_navigationlink($type, 'difflastrev', $l_rev); //last revision is diff with current page
1534
        } else {
1535
            $r_nav .= html_diff_navigationlink($type, 'diffnextrev', $l_rev, $r_next);
1536
        }
1537
        $r_nav .= html_diff_navigationlink($type, 'diffbothnextrev', $l_next, $r_next);
1538
    }
1539
    return array($l_nav, $r_nav);
1540
}
1541
1542
/**
1543
 * Create html link to a diff defined by two revisions
1544
 *
1545
 * @param string $difftype display type
1546
 * @param string $linktype
1547
 * @param int $lrev oldest revision
1548
 * @param int $rrev newest revision or null for diff with current revision
1549
 * @return string html of link to a diff
1550
 */
1551
function html_diff_navigationlink($difftype, $linktype, $lrev, $rrev = null) {
1552
    global $ID, $lang;
1553
    if(!$rrev) {
1554
        $urlparam = array(
1555
            'do' => 'diff',
1556
            'rev' => $lrev,
1557
            'difftype' => $difftype,
1558
        );
1559
    } else {
1560
        $urlparam = array(
1561
            'do' => 'diff',
1562
            'rev2[0]' => $lrev,
1563
            'rev2[1]' => $rrev,
1564
            'difftype' => $difftype,
1565
        );
1566
    }
1567
    return  '<a class="' . $linktype . '" href="' . wl($ID, $urlparam) . '" title="' . $lang[$linktype] . '">' .
1568
                '<span>' . $lang[$linktype] . '</span>' .
1569
            '</a>' . "\n";
1570
}
1571
1572
/**
1573
 * Insert soft breaks in diff html
1574
 *
1575
 * @param string $diffhtml
1576
 * @return string
1577
 */
1578
function html_insert_softbreaks($diffhtml) {
1579
    // search the diff html string for both:
1580
    // - html tags, so these can be ignored
1581
    // - long strings of characters without breaking characters
1582
    return preg_replace_callback('/<[^>]*>|[^<> ]{12,}/','html_softbreak_callback',$diffhtml);
1583
}
1584
1585
/**
1586
 * callback which adds softbreaks
1587
 *
1588
 * @param array $match array with first the complete match
1589
 * @return string the replacement
1590
 */
1591
function html_softbreak_callback($match){
1592
    // if match is an html tag, return it intact
1593
    if ($match[0][0] == '<') return $match[0];
1594
1595
    // its a long string without a breaking character,
1596
    // make certain characters into breaking characters by inserting a
1597
    // word break opportunity (<wbr> tag) in front of them.
1598
    $regex = <<< REGEX
1599
(?(?=              # start a conditional expression with a positive look ahead ...
1600
&\#?\\w{1,6};)     # ... for html entities - we don't want to split them (ok to catch some invalid combinations)
1601
&\#?\\w{1,6};      # yes pattern - a quicker match for the html entity, since we know we have one
1602
|
1603
[?/,&\#;:]         # no pattern - any other group of 'special' characters to insert a breaking character after
1604
)+                 # end conditional expression
1605
REGEX;
1606
1607
    return preg_replace('<'.$regex.'>xu','\0<wbr>',$match[0]);
1608
}
1609
1610
/**
1611
 * show warning on conflict detection
1612
 *
1613
 * @author Andreas Gohr <[email protected]>
1614
 *
1615
 * @param string $text
1616
 * @param string $summary
1617
 */
1618
function html_conflict($text,$summary){
1619
    global $ID;
1620
    global $lang;
1621
1622
    print p_locale_xhtml('conflict');
1623
    $form = new Doku_Form(array('id' => 'dw__editform'));
0 ignored issues
show
Deprecated Code introduced by
The class Doku_Form has been deprecated with message: 2019-07-14

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
1624
    $form->addHidden('id', $ID);
1625
    $form->addHidden('wikitext', $text);
1626
    $form->addHidden('summary', $summary);
1627
    $form->addElement(form_makeButton('submit', 'save', $lang['btn_save'], array('accesskey'=>'s')));
1628
    $form->addElement(form_makeButton('submit', 'cancel', $lang['btn_cancel']));
1629
    html_form('conflict', $form);
1630
    print '<br /><br /><br /><br />'.NL;
1631
}
1632
1633
/**
1634
 * Prints the global message array
1635
 *
1636
 * @author Andreas Gohr <[email protected]>
1637
 */
1638
function html_msgarea(){
1639
    global $MSG, $MSG_shown;
1640
    /** @var array $MSG */
1641
    // store if the global $MSG has already been shown and thus HTML output has been started
1642
    $MSG_shown = true;
1643
1644
    if(!isset($MSG)) return;
1645
1646
    $shown = array();
1647
    foreach($MSG as $msg){
1648
        $hash = md5($msg['msg']);
1649
        if(isset($shown[$hash])) continue; // skip double messages
1650
        if(info_msg_allowed($msg)){
1651
            print '<div class="'.$msg['lvl'].'">';
1652
            print $msg['msg'];
1653
            print '</div>';
1654
        }
1655
        $shown[$hash] = 1;
1656
    }
1657
1658
    unset($GLOBALS['MSG']);
1659
}
1660
1661
/**
1662
 * Prints the registration form
1663
 *
1664
 * @author Andreas Gohr <[email protected]>
1665
 */
1666
function html_register(){
1667
    global $lang;
1668
    global $conf;
1669
    global $INPUT;
1670
1671
    $base_attrs = array('size'=>50,'required'=>'required');
1672
    $email_attrs = $base_attrs + array('type'=>'email','class'=>'edit');
1673
1674
    print p_locale_xhtml('register');
1675
    print '<div class="centeralign">'.NL;
1676
    $form = new Doku_Form(array('id' => 'dw__register'));
0 ignored issues
show
Deprecated Code introduced by
The class Doku_Form has been deprecated with message: 2019-07-14

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
1677
    $form->startFieldset($lang['btn_register']);
1678
    $form->addHidden('do', 'register');
1679
    $form->addHidden('save', '1');
1680
    $form->addElement(
1681
        form_makeTextField(
1682
            'login',
1683
            $INPUT->post->str('login'),
1684
            $lang['user'],
1685
            '',
1686
            'block',
1687
            $base_attrs
1688
        )
1689
    );
1690
    if (!$conf['autopasswd']) {
1691
        $form->addElement(form_makePasswordField('pass', $lang['pass'], '', 'block', $base_attrs));
1692
        $form->addElement(form_makePasswordField('passchk', $lang['passchk'], '', 'block', $base_attrs));
1693
    }
1694
    $form->addElement(
1695
        form_makeTextField(
1696
            'fullname',
1697
            $INPUT->post->str('fullname'),
1698
            $lang['fullname'],
1699
            '',
1700
            'block',
1701
            $base_attrs
1702
        )
1703
    );
1704
    $form->addElement(
1705
        form_makeField(
1706
            'email',
1707
            'email',
1708
            $INPUT->post->str('email'),
1709
            $lang['email'],
1710
            '',
1711
            'block',
1712
            $email_attrs
1713
        )
1714
    );
1715
    $form->addElement(form_makeButton('submit', '', $lang['btn_register']));
1716
    $form->endFieldset();
1717
    html_form('register', $form);
1718
1719
    print '</div>'.NL;
1720
}
1721
1722
/**
1723
 * Print the update profile form
1724
 *
1725
 * @author Christopher Smith <[email protected]>
1726
 * @author Andreas Gohr <[email protected]>
1727
 */
1728
function html_updateprofile(){
1729
    global $lang;
1730
    global $conf;
1731
    global $INPUT;
1732
    global $INFO;
1733
    /** @var AuthPlugin $auth */
1734
    global $auth;
1735
1736
    print p_locale_xhtml('updateprofile');
1737
    print '<div class="centeralign">'.NL;
1738
1739
    $fullname = $INPUT->post->str('fullname', $INFO['userinfo']['name'], true);
1740
    $email = $INPUT->post->str('email', $INFO['userinfo']['mail'], true);
1741
    $form = new Doku_Form(array('id' => 'dw__register'));
0 ignored issues
show
Deprecated Code introduced by
The class Doku_Form has been deprecated with message: 2019-07-14

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
1742
    $form->startFieldset($lang['profile']);
1743
    $form->addHidden('do', 'profile');
1744
    $form->addHidden('save', '1');
1745
    $form->addElement(
1746
        form_makeTextField(
1747
            'login',
1748
            $_SERVER['REMOTE_USER'],
1749
            $lang['user'],
1750
            '',
1751
            'block',
1752
            array('size' => '50', 'disabled' => 'disabled')
1753
        )
1754
    );
1755
    $attr = array('size'=>'50');
1756
    if (!$auth->canDo('modName')) $attr['disabled'] = 'disabled';
1757
    $form->addElement(form_makeTextField('fullname', $fullname, $lang['fullname'], '', 'block', $attr));
1758
    $attr = array('size'=>'50', 'class'=>'edit');
1759
    if (!$auth->canDo('modMail')) $attr['disabled'] = 'disabled';
1760
    $form->addElement(form_makeField('email','email', $email, $lang['email'], '', 'block', $attr));
1761
    $form->addElement(form_makeTag('br'));
1762
    if ($auth->canDo('modPass')) {
1763
        $form->addElement(form_makePasswordField('newpass', $lang['newpass'], '', 'block', array('size'=>'50')));
1764
        $form->addElement(form_makePasswordField('passchk', $lang['passchk'], '', 'block', array('size'=>'50')));
1765
    }
1766
    if ($conf['profileconfirm']) {
1767
        $form->addElement(form_makeTag('br'));
1768
        $form->addElement(
1769
            form_makePasswordField(
1770
                'oldpass',
1771
                $lang['oldpass'],
1772
                '',
1773
                'block',
1774
                array('size' => '50', 'required' => 'required')
1775
            )
1776
        );
1777
    }
1778
    $form->addElement(form_makeButton('submit', '', $lang['btn_save']));
1779
    $form->addElement(form_makeButton('reset', '', $lang['btn_reset']));
1780
1781
    $form->endFieldset();
1782
    html_form('updateprofile', $form);
1783
1784
    if ($auth->canDo('delUser') && actionOK('profile_delete')) {
1785
        $form_profiledelete = new Doku_Form(array('id' => 'dw__profiledelete'));
1786
        $form_profiledelete->startFieldset($lang['profdeleteuser']);
1787
        $form_profiledelete->addHidden('do', 'profile_delete');
1788
        $form_profiledelete->addHidden('delete', '1');
1789
        $form_profiledelete->addElement(
1790
            form_makeCheckboxField(
1791
                'confirm_delete',
1792
                '1',
1793
                $lang['profconfdelete'],
1794
                'dw__confirmdelete',
1795
                '',
1796
                array('required' => 'required')
1797
            )
1798
        );
1799
        if ($conf['profileconfirm']) {
1800
            $form_profiledelete->addElement(form_makeTag('br'));
1801
            $form_profiledelete->addElement(
1802
                form_makePasswordField(
1803
                    'oldpass',
1804
                    $lang['oldpass'],
1805
                    '',
1806
                    'block',
1807
                    array('size' => '50', 'required' => 'required')
1808
                )
1809
            );
1810
        }
1811
        $form_profiledelete->addElement(form_makeButton('submit', '', $lang['btn_deleteuser']));
1812
        $form_profiledelete->endFieldset();
1813
1814
        html_form('profiledelete', $form_profiledelete);
1815
    }
1816
1817
    print '</div>'.NL;
1818
}
1819
1820
/**
1821
 * Preprocess edit form data
1822
 *
1823
 * @author   Andreas Gohr <[email protected]>
1824
 *
1825
 * @triggers HTML_EDITFORM_OUTPUT
1826
 */
1827
function html_edit(){
1828
    global $INPUT;
1829
    global $ID;
1830
    global $REV;
1831
    global $DATE;
1832
    global $PRE;
1833
    global $SUF;
1834
    global $INFO;
1835
    global $SUM;
1836
    global $lang;
1837
    global $conf;
1838
    global $TEXT;
1839
1840
    if ($INPUT->has('changecheck')) {
1841
        $check = $INPUT->str('changecheck');
1842
    } elseif(!$INFO['exists']){
1843
        // $TEXT has been loaded from page template
1844
        $check = md5('');
1845
    } else {
1846
        $check = md5($TEXT);
1847
    }
1848
    $mod = md5($TEXT) !== $check;
1849
1850
    $wr = $INFO['writable'] && !$INFO['locked'];
1851
    $include = 'edit';
1852
    if($wr){
1853
        if ($REV) $include = 'editrev';
1854
    }else{
1855
        // check pseudo action 'source'
1856
        if(!actionOK('source')){
1857
            msg('Command disabled: source',-1);
1858
            return;
1859
        }
1860
        $include = 'read';
1861
    }
1862
1863
    global $license;
1864
1865
    $form = new Doku_Form(array('id' => 'dw__editform'));
0 ignored issues
show
Deprecated Code introduced by
The class Doku_Form has been deprecated with message: 2019-07-14

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
1866
    $form->addHidden('id', $ID);
1867
    $form->addHidden('rev', $REV);
1868
    $form->addHidden('date', $DATE);
1869
    $form->addHidden('prefix', $PRE . '.');
1870
    $form->addHidden('suffix', $SUF);
1871
    $form->addHidden('changecheck', $check);
1872
1873
    $data = array('form' => $form,
1874
                  'wr'   => $wr,
1875
                  'media_manager' => true,
1876
                  'target' => ($INPUT->has('target') && $wr) ? $INPUT->str('target') : 'section',
1877
                  'intro_locale' => $include);
1878
1879
    if ($data['target'] !== 'section') {
1880
        // Only emit event if page is writable, section edit data is valid and
1881
        // edit target is not section.
1882
        Event::createAndTrigger('HTML_EDIT_FORMSELECTION', $data, 'html_edit_form', true);
1883
    } else {
1884
        html_edit_form($data);
1885
    }
1886
    if (isset($data['intro_locale'])) {
1887
        echo p_locale_xhtml($data['intro_locale']);
1888
    }
1889
1890
    $form->addHidden('target', $data['target']);
1891
    if ($INPUT->has('hid')) {
1892
        $form->addHidden('hid', $INPUT->str('hid'));
1893
    }
1894
    if ($INPUT->has('codeblockOffset')) {
1895
        $form->addHidden('codeblockOffset', $INPUT->str('codeblockOffset'));
1896
    }
1897
    $form->addElement(form_makeOpenTag('div', array('id'=>'wiki__editbar', 'class'=>'editBar')));
1898
    $form->addElement(form_makeOpenTag('div', array('id'=>'size__ctl')));
1899
    $form->addElement(form_makeCloseTag('div'));
1900
    if ($wr) {
1901
        $form->addElement(form_makeOpenTag('div', array('class'=>'editButtons')));
1902
        $form->addElement(
1903
            form_makeButton(
1904
                'submit',
1905
                'save',
1906
                $lang['btn_save'],
1907
                array('id' => 'edbtn__save', 'accesskey' => 's', 'tabindex' => '4')
1908
            )
1909
        );
1910
        $form->addElement(
1911
            form_makeButton(
1912
                'submit',
1913
                'preview',
1914
                $lang['btn_preview'],
1915
                array('id' => 'edbtn__preview', 'accesskey' => 'p', 'tabindex' => '5')
1916
            )
1917
        );
1918
        $form->addElement(form_makeButton('submit', 'cancel', $lang['btn_cancel'], array('tabindex'=>'6')));
1919
        $form->addElement(form_makeCloseTag('div'));
1920
        $form->addElement(form_makeOpenTag('div', array('class'=>'summary')));
1921
        $form->addElement(
1922
            form_makeTextField(
1923
                'summary',
1924
                $SUM,
1925
                $lang['summary'],
1926
                'edit__summary',
1927
                'nowrap',
1928
                array('size' => '50', 'tabindex' => '2')
1929
            )
1930
        );
1931
        $elem = html_minoredit();
1932
        if ($elem) $form->addElement($elem);
1933
        $form->addElement(form_makeCloseTag('div'));
1934
    }
1935
    $form->addElement(form_makeCloseTag('div'));
1936
    if($wr && $conf['license']){
1937
        $form->addElement(form_makeOpenTag('div', array('class'=>'license')));
1938
        $out  = $lang['licenseok'];
1939
        $out .= ' <a href="'.$license[$conf['license']]['url'].'" rel="license" class="urlextern"';
1940
        if($conf['target']['extern']) $out .= ' target="'.$conf['target']['extern'].'"';
1941
        $out .= '>'.$license[$conf['license']]['name'].'</a>';
1942
        $form->addElement($out);
1943
        $form->addElement(form_makeCloseTag('div'));
1944
    }
1945
1946
    if ($wr) {
1947
        // sets changed to true when previewed
1948
        echo '<script>/*<![CDATA[*/'. NL;
1949
        echo 'textChanged = ' . ($mod ? 'true' : 'false');
1950
        echo '/*!]]>*/</script>' . NL;
1951
    } ?>
1952
    <div class="editBox" role="application">
1953
1954
    <div class="toolbar group">
1955
        <div id="tool__bar" class="tool__bar"><?php
1956
            if ($wr && $data['media_manager']){
1957
                ?><a href="<?php echo DOKU_BASE?>lib/exe/mediamanager.php?ns=<?php echo $INFO['namespace']?>"
1958
                target="_blank"><?php echo $lang['mediaselect'] ?></a><?php
1959
            }?>
1960
        </div>
1961
    </div>
1962
    <div id="draft__status" class="draft__status">
1963
        <?php
1964
        $draft = new \dokuwiki\Draft($ID, $INFO['client']);
1965
        if ($draft->isDraftAvailable()) {
1966
            echo $draft->getDraftMessage();
1967
        }
1968
        ?>
1969
    </div>
1970
    <?php
1971
1972
    html_form('edit', $form);
1973
    print '</div>'.NL;
1974
}
1975
1976
/**
1977
 * Display the default edit form
1978
 *
1979
 * Is the default action for HTML_EDIT_FORMSELECTION.
1980
 *
1981
 * @param mixed[] $param
1982
 */
1983
function html_edit_form($param) {
1984
    global $TEXT;
1985
1986
    if ($param['target'] !== 'section') {
1987
        msg('No editor for edit target ' . hsc($param['target']) . ' found.', -1);
1988
    }
1989
1990
    $attr = array('tabindex'=>'1');
1991
    if (!$param['wr']) $attr['readonly'] = 'readonly';
1992
1993
    $param['form']->addElement(form_makeWikiText($TEXT, $attr));
1994
}
1995
1996
/**
1997
 * Adds a checkbox for minor edits for logged in users
1998
 *
1999
 * @author Andreas Gohr <[email protected]>
2000
 *
2001
 * @return array|bool
2002
 */
2003
function html_minoredit(){
2004
    global $conf;
2005
    global $lang;
2006
    global $INPUT;
2007
    // minor edits are for logged in users only
2008
    if(!$conf['useacl'] || !$_SERVER['REMOTE_USER']){
2009
        return false;
2010
    }
2011
2012
    $p = array();
2013
    $p['tabindex'] = 3;
2014
    if($INPUT->bool('minor')) $p['checked']='checked';
2015
    return form_makeCheckboxField('minor', '1', $lang['minoredit'], 'minoredit', 'nowrap', $p);
2016
}
2017
2018
/**
2019
 * prints some debug info
2020
 *
2021
 * @author Andreas Gohr <[email protected]>
2022
 */
2023
function html_debug(){
2024
    global $conf;
2025
    global $lang;
2026
    /** @var AuthPlugin $auth */
2027
    global $auth;
2028
    global $INFO;
2029
2030
    //remove sensitive data
2031
    $cnf = $conf;
2032
    debug_guard($cnf);
2033
    $nfo = $INFO;
2034
    debug_guard($nfo);
2035
    $ses = $_SESSION;
2036
    debug_guard($ses);
2037
2038
    print '<html><body>';
2039
2040
    print '<p>When reporting bugs please send all the following ';
2041
    print 'output as a mail to [email protected] ';
2042
    print 'The best way to do this is to save this page in your browser</p>';
2043
2044
    print '<b>$INFO:</b><pre>';
2045
    print_r($nfo);
2046
    print '</pre>';
2047
2048
    print '<b>$_SERVER:</b><pre>';
2049
    print_r($_SERVER);
2050
    print '</pre>';
2051
2052
    print '<b>$conf:</b><pre>';
2053
    print_r($cnf);
2054
    print '</pre>';
2055
2056
    print '<b>DOKU_BASE:</b><pre>';
2057
    print DOKU_BASE;
2058
    print '</pre>';
2059
2060
    print '<b>abs DOKU_BASE:</b><pre>';
2061
    print DOKU_URL;
2062
    print '</pre>';
2063
2064
    print '<b>rel DOKU_BASE:</b><pre>';
2065
    print dirname($_SERVER['PHP_SELF']).'/';
2066
    print '</pre>';
2067
2068
    print '<b>PHP Version:</b><pre>';
2069
    print phpversion();
2070
    print '</pre>';
2071
2072
    print '<b>locale:</b><pre>';
2073
    print setlocale(LC_ALL,0);
2074
    print '</pre>';
2075
2076
    print '<b>encoding:</b><pre>';
2077
    print $lang['encoding'];
2078
    print '</pre>';
2079
2080
    if($auth){
2081
        print '<b>Auth backend capabilities:</b><pre>';
2082
        foreach ($auth->getCapabilities() as $cando){
2083
            print '   '.str_pad($cando,16) . ' => ' . (int)$auth->canDo($cando) . NL;
2084
        }
2085
        print '</pre>';
2086
    }
2087
2088
    print '<b>$_SESSION:</b><pre>';
2089
    print_r($ses);
2090
    print '</pre>';
2091
2092
    print '<b>Environment:</b><pre>';
2093
    print_r($_ENV);
2094
    print '</pre>';
2095
2096
    print '<b>PHP settings:</b><pre>';
2097
    $inis = ini_get_all();
2098
    print_r($inis);
2099
    print '</pre>';
2100
2101
    if (function_exists('apache_get_version')) {
2102
        $apache = array();
2103
        $apache['version'] = apache_get_version();
2104
2105
        if (function_exists('apache_get_modules')) {
2106
            $apache['modules'] = apache_get_modules();
2107
        }
2108
        print '<b>Apache</b><pre>';
2109
        print_r($apache);
2110
        print '</pre>';
2111
    }
2112
2113
    print '</body></html>';
2114
}
2115
2116
/**
2117
 * Form to request a new password for an existing account
2118
 *
2119
 * @author Benoit Chesneau <[email protected]>
2120
 * @author Andreas Gohr <[email protected]>
2121
 */
2122
function html_resendpwd() {
2123
    global $lang;
2124
    global $conf;
2125
    global $INPUT;
2126
2127
    $token = preg_replace('/[^a-f0-9]+/','',$INPUT->str('pwauth'));
2128
2129
    if(!$conf['autopasswd'] && $token){
2130
        print p_locale_xhtml('resetpwd');
2131
        print '<div class="centeralign">'.NL;
2132
        $form = new Doku_Form(array('id' => 'dw__resendpwd'));
0 ignored issues
show
Deprecated Code introduced by
The class Doku_Form has been deprecated with message: 2019-07-14

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
2133
        $form->startFieldset($lang['btn_resendpwd']);
2134
        $form->addHidden('token', $token);
2135
        $form->addHidden('do', 'resendpwd');
2136
2137
        $form->addElement(form_makePasswordField('pass', $lang['pass'], '', 'block', array('size'=>'50')));
2138
        $form->addElement(form_makePasswordField('passchk', $lang['passchk'], '', 'block', array('size'=>'50')));
2139
2140
        $form->addElement(form_makeButton('submit', '', $lang['btn_resendpwd']));
2141
        $form->endFieldset();
2142
        html_form('resendpwd', $form);
2143
        print '</div>'.NL;
2144
    }else{
2145
        print p_locale_xhtml('resendpwd');
2146
        print '<div class="centeralign">'.NL;
2147
        $form = new Doku_Form(array('id' => 'dw__resendpwd'));
0 ignored issues
show
Deprecated Code introduced by
The class Doku_Form has been deprecated with message: 2019-07-14

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
2148
        $form->startFieldset($lang['resendpwd']);
2149
        $form->addHidden('do', 'resendpwd');
2150
        $form->addHidden('save', '1');
2151
        $form->addElement(form_makeTag('br'));
2152
        $form->addElement(form_makeTextField('login', $INPUT->post->str('login'), $lang['user'], '', 'block'));
2153
        $form->addElement(form_makeTag('br'));
2154
        $form->addElement(form_makeTag('br'));
2155
        $form->addElement(form_makeButton('submit', '', $lang['btn_resendpwd']));
2156
        $form->endFieldset();
2157
        html_form('resendpwd', $form);
2158
        print '</div>'.NL;
2159
    }
2160
}
2161
2162
/**
2163
 * Return the TOC rendered to XHTML
2164
 *
2165
 * @author Andreas Gohr <[email protected]>
2166
 *
2167
 * @param array $toc
2168
 * @return string html
2169
 */
2170
function html_TOC($toc){
2171
    if(!count($toc)) return '';
2172
    global $lang;
2173
    $out  = '<!-- TOC START -->'.DOKU_LF;
2174
    $out .= '<div id="dw__toc" class="dw__toc">'.DOKU_LF;
2175
    $out .= '<h3 class="toggle">';
2176
    $out .= $lang['toc'];
2177
    $out .= '</h3>'.DOKU_LF;
2178
    $out .= '<div>'.DOKU_LF;
2179
    $out .= html_buildlist($toc,'toc','html_list_toc','html_li_default',true);
2180
    $out .= '</div>'.DOKU_LF.'</div>'.DOKU_LF;
2181
    $out .= '<!-- TOC END -->'.DOKU_LF;
2182
    return $out;
2183
}
2184
2185
/**
2186
 * Callback for html_buildlist
2187
 *
2188
 * @param array $item
2189
 * @return string html
2190
 */
2191
function html_list_toc($item){
2192
    if(isset($item['hid'])){
2193
        $link = '#'.$item['hid'];
2194
    }else{
2195
        $link = $item['link'];
2196
    }
2197
2198
    return '<a href="'.$link.'">'.hsc($item['title']).'</a>';
2199
}
2200
2201
/**
2202
 * Helper function to build TOC items
2203
 *
2204
 * Returns an array ready to be added to a TOC array
2205
 *
2206
 * @param string $link  - where to link (if $hash set to '#' it's a local anchor)
2207
 * @param string $text  - what to display in the TOC
2208
 * @param int    $level - nesting level
2209
 * @param string $hash  - is prepended to the given $link, set blank if you want full links
2210
 * @return array the toc item
2211
 */
2212
function html_mktocitem($link, $text, $level, $hash='#'){
2213
    return  array( 'link'  => $hash.$link,
2214
            'title' => $text,
2215
            'type'  => 'ul',
2216
            'level' => $level);
2217
}
2218
2219
/**
2220
 * Output a Doku_Form object.
2221
 * Triggers an event with the form name: HTML_{$name}FORM_OUTPUT
2222
 *
2223
 * @author Tom N Harris <[email protected]>
2224
 *
2225
 * @param string     $name The name of the form
2226
 * @param Doku_Form  $form The form
2227
 */
2228
function html_form($name, &$form) {
2229
    // Safety check in case the caller forgets.
2230
    $form->endFieldset();
2231
    Event::createAndTrigger('HTML_'.strtoupper($name).'FORM_OUTPUT', $form, 'html_form_output', false);
2232
}
2233
2234
/**
2235
 * Form print function.
2236
 * Just calls printForm() on the data object.
2237
 *
2238
 * @param Doku_Form $data The form
2239
 */
2240
function html_form_output($data) {
2241
    $data->printForm();
2242
}
2243
2244
/**
2245
 * Embed a flash object in HTML
2246
 *
2247
 * This will create the needed HTML to embed a flash movie in a cross browser
2248
 * compatble way using valid XHTML
2249
 *
2250
 * The parameters $params, $flashvars and $atts need to be associative arrays.
2251
 * No escaping needs to be done for them. The alternative content *has* to be
2252
 * escaped because it is used as is. If no alternative content is given
2253
 * $lang['noflash'] is used.
2254
 *
2255
 * @author Andreas Gohr <[email protected]>
2256
 * @link   http://latrine.dgx.cz/how-to-correctly-insert-a-flash-into-xhtml
2257
 *
2258
 * @param string $swf      - the SWF movie to embed
2259
 * @param int $width       - width of the flash movie in pixels
2260
 * @param int $height      - height of the flash movie in pixels
2261
 * @param array $params    - additional parameters (<param>)
2262
 * @param array $flashvars - parameters to be passed in the flashvar parameter
2263
 * @param array $atts      - additional attributes for the <object> tag
2264
 * @param string $alt      - alternative content (is NOT automatically escaped!)
2265
 * @return string         - the XHTML markup
2266
 */
2267
function html_flashobject($swf,$width,$height,$params=null,$flashvars=null,$atts=null,$alt=''){
2268
    global $lang;
2269
2270
    $out = '';
2271
2272
    // prepare the object attributes
2273
    if(is_null($atts)) $atts = array();
2274
    $atts['width']  = (int) $width;
2275
    $atts['height'] = (int) $height;
2276
    if(!$atts['width'])  $atts['width']  = 425;
2277
    if(!$atts['height']) $atts['height'] = 350;
2278
2279
    // add object attributes for standard compliant browsers
2280
    $std = $atts;
2281
    $std['type'] = 'application/x-shockwave-flash';
2282
    $std['data'] = $swf;
2283
2284
    // add object attributes for IE
2285
    $ie  = $atts;
2286
    $ie['classid'] = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000';
2287
2288
    // open object (with conditional comments)
2289
    $out .= '<!--[if !IE]> -->'.NL;
2290
    $out .= '<object '.buildAttributes($std).'>'.NL;
2291
    $out .= '<!-- <![endif]-->'.NL;
2292
    $out .= '<!--[if IE]>'.NL;
2293
    $out .= '<object '.buildAttributes($ie).'>'.NL;
2294
    $out .= '    <param name="movie" value="'.hsc($swf).'" />'.NL;
2295
    $out .= '<!--><!-- -->'.NL;
2296
2297
    // print params
2298
    if(is_array($params)) foreach($params as $key => $val){
2299
        $out .= '  <param name="'.hsc($key).'" value="'.hsc($val).'" />'.NL;
2300
    }
2301
2302
    // add flashvars
2303
    if(is_array($flashvars)){
2304
        $out .= '  <param name="FlashVars" value="'.buildURLparams($flashvars).'" />'.NL;
2305
    }
2306
2307
    // alternative content
2308
    if($alt){
2309
        $out .= $alt.NL;
2310
    }else{
2311
        $out .= $lang['noflash'].NL;
2312
    }
2313
2314
    // finish
2315
    $out .= '</object>'.NL;
2316
    $out .= '<!-- <![endif]-->'.NL;
2317
2318
    return $out;
2319
}
2320
2321
/**
2322
 * Prints HTML code for the given tab structure
2323
 *
2324
 * @param array  $tabs        tab structure
2325
 * @param string $current_tab the current tab id
2326
 */
2327
function html_tabs($tabs, $current_tab = null) {
2328
    echo '<ul class="tabs">'.NL;
2329
2330
    foreach($tabs as $id => $tab) {
2331
        html_tab($tab['href'], $tab['caption'], $id === $current_tab);
2332
    }
2333
2334
    echo '</ul>'.NL;
2335
}
2336
2337
/**
2338
 * Prints a single tab
2339
 *
2340
 * @author Kate Arzamastseva <[email protected]>
2341
 * @author Adrian Lang <[email protected]>
2342
 *
2343
 * @param string $href - tab href
2344
 * @param string $caption - tab caption
2345
 * @param boolean $selected - is tab selected
2346
 */
2347
2348
function html_tab($href, $caption, $selected=false) {
2349
    $tab = '<li>';
2350
    if ($selected) {
2351
        $tab .= '<strong>';
2352
    } else {
2353
        $tab .= '<a href="' . hsc($href) . '">';
2354
    }
2355
    $tab .= hsc($caption)
2356
         .  '</' . ($selected ? 'strong' : 'a') . '>'
2357
         .  '</li>'.NL;
2358
    echo $tab;
2359
}
2360
2361
/**
2362
 * Display size change
2363
 *
2364
 * @param int $sizechange - size of change in Bytes
2365
 * @param Doku_Form $form - form to add elements to
2366
 */
2367
2368
function html_sizechange($sizechange, Doku_Form $form) {
2369
    if(isset($sizechange)) {
2370
        $class = 'sizechange';
2371
        $value = filesize_h(abs($sizechange));
2372
        if($sizechange > 0) {
2373
            $class .= ' positive';
2374
            $value = '+' . $value;
2375
        } elseif($sizechange < 0) {
2376
            $class .= ' negative';
2377
            $value = '-' . $value;
2378
        } else {
2379
            $value = '±' . $value;
2380
        }
2381
        $form->addElement(form_makeOpenTag('span', array('class' => $class)));
2382
        $form->addElement($value);
2383
        $form->addElement(form_makeCloseTag('span'));
2384
    }
2385
}
2386