Failed Conditions
Push — psr2-config ( c6639e )
by Andreas
06:39 queued 03:33
created

html.php ➔ html_btn()   B

Complexity

Conditions 9
Paths 96

Size

Total Lines 54
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 36
nc 96
nop 8
dl 0
loc 54
rs 7.255
c 0
b 0
f 0

How to fix   Long Method    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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
if (!defined('SEC_EDIT_PATTERN')) {
10
    define('SEC_EDIT_PATTERN', '#<!-- EDIT({.*?}) -->#');
11
}
12
13
14
/**
15
 * Convenience function to quickly build a wikilink
16
 *
17
 * @author Andreas Gohr <[email protected]>
18
 * @param string  $id      id of the target page
19
 * @param string  $name    the name of the link, i.e. the text that is displayed
20
 * @param string|array  $search  search string(s) that shall be highlighted in the target page
21
 * @return string the HTML code of the link
22
 */
23
function html_wikilink($id,$name=null,$search=''){
24
    /** @var Doku_Renderer_xhtml $xhtml_renderer */
25
    static $xhtml_renderer = null;
26
    if(is_null($xhtml_renderer)){
27
        $xhtml_renderer = p_get_renderer('xhtml');
28
    }
29
30
    return $xhtml_renderer->internallink($id,$name,$search,true,'navigation');
31
}
32
33
/**
34
 * The loginform
35
 *
36
 * @author   Andreas Gohr <[email protected]>
37
 *
38
 * @param bool $svg Whether to show svg icons in the register and resendpwd links or not
39
 */
40
function html_login($svg = false){
41
    global $lang;
42
    global $conf;
43
    global $ID;
44
    global $INPUT;
45
46
    print p_locale_xhtml('login');
47
    print '<div class="centeralign">'.NL;
48
    $form = new Doku_Form(array('id' => 'dw__login'));
49
    $form->startFieldset($lang['btn_login']);
50
    $form->addHidden('id', $ID);
51
    $form->addHidden('do', 'login');
52
    $form->addElement(form_makeTextField(
53
        'u',
54
        ((!$INPUT->bool('http_credentials')) ? $INPUT->str('u') : ''),
55
        $lang['user'],
56
        'focus__this',
57
        'block')
58
    );
59
    $form->addElement(form_makePasswordField('p', $lang['pass'], '', 'block'));
60
    if($conf['rememberme']) {
61
        $form->addElement(form_makeCheckboxField('r', '1', $lang['remember'], 'remember__me', 'simple'));
62
    }
63
    $form->addElement(form_makeButton('submit', '', $lang['btn_login']));
64
    $form->endFieldset();
65
66
    if(actionOK('register')){
67
        $registerLink = (new \dokuwiki\Menu\Item\Register())->asHtmlLink('', $svg);
68
        $form->addElement('<p>'.$lang['reghere'].': '. $registerLink .'</p>');
69
    }
70
71
    if (actionOK('resendpwd')) {
72
        $resendPwLink = (new \dokuwiki\Menu\Item\Resendpwd())->asHtmlLink('', $svg);
73
        $form->addElement('<p>'.$lang['pwdforget'].': '. $resendPwLink .'</p>');
74
    }
75
76
    html_form('login', $form);
77
    print '</div>'.NL;
78
}
79
80
81
/**
82
 * Denied page content
83
 *
84
 * @return string html
85
 */
86
function html_denied() {
87
    print p_locale_xhtml('denied');
88
89
    if(empty($_SERVER['REMOTE_USER'])){
90
        html_login();
91
    }
92
}
93
94
/**
95
 * inserts section edit buttons if wanted or removes the markers
96
 *
97
 * @author Andreas Gohr <[email protected]>
98
 *
99
 * @param string $text
100
 * @param bool   $show show section edit buttons?
101
 * @return string
102
 */
103
function html_secedit($text,$show=true){
104
    global $INFO;
105
106
    if(!$INFO['writable'] || !$show || $INFO['rev']){
107
        return preg_replace(SEC_EDIT_PATTERN,'',$text);
108
    }
109
110
    return preg_replace_callback(SEC_EDIT_PATTERN,
111
                'html_secedit_button', $text);
112
}
113
114
/**
115
 * prepares section edit button data for event triggering
116
 * used as a callback in html_secedit
117
 *
118
 * @author Andreas Gohr <[email protected]>
119
 *
120
 * @param array $matches matches with regexp
121
 * @return string
122
 * @triggers HTML_SECEDIT_BUTTON
123
 */
124
function html_secedit_button($matches){
125
    $json = htmlspecialchars_decode($matches[1], ENT_QUOTES);
126
    $data = json_decode($json, true);
127
    if ($data == NULL) {
128
        return;
129
    }
130
    $data ['target'] = strtolower($data['target']);
131
    $data ['hid'] = strtolower($data['hid']);
132
133
    return trigger_event('HTML_SECEDIT_BUTTON', $data,
134
                         'html_secedit_get_button');
135
}
136
137
/**
138
 * prints a section editing button
139
 * used as default action form HTML_SECEDIT_BUTTON
140
 *
141
 * @author Adrian Lang <[email protected]>
142
 *
143
 * @param array $data name, section id and target
144
 * @return string html
145
 */
146
function html_secedit_get_button($data) {
147
    global $ID;
148
    global $INFO;
149
150
    if (!isset($data['name']) || $data['name'] === '') return '';
151
152
    $name = $data['name'];
153
    unset($data['name']);
154
155
    $secid = $data['secid'];
156
    unset($data['secid']);
157
158
    return "<div class='secedit editbutton_" . $data['target'] .
159
                       " editbutton_" . $secid . "'>" .
160
           html_btn('secedit', $ID, '',
161
                    array_merge(array('do'  => 'edit',
162
                                      'rev' => $INFO['lastmod'],
163
                                      'summary' => '['.$name.'] '), $data),
164
                    'post', $name) . '</div>';
165
}
166
167
/**
168
 * Just the back to top button (in its own form)
169
 *
170
 * @author Andreas Gohr <[email protected]>
171
 *
172
 * @return string html
173
 */
174
function html_topbtn(){
175
    global $lang;
176
177
    $ret = '<a class="nolink" href="#dokuwiki__top">' .
178
        '<button class="button" onclick="window.scrollTo(0, 0)" title="' . $lang['btn_top'] . '">' .
179
        $lang['btn_top'] .
180
        '</button></a>';
181
182
    return $ret;
183
}
184
185
/**
186
 * Displays a button (using its own form)
187
 * If tooltip exists, the access key tooltip is replaced.
188
 *
189
 * @author Andreas Gohr <[email protected]>
190
 *
191
 * @param string         $name
192
 * @param string         $id
193
 * @param string         $akey   access key
194
 * @param string[] $params key-value pairs added as hidden inputs
195
 * @param string         $method
196
 * @param string         $tooltip
197
 * @param bool|string    $label  label text, false: lookup btn_$name in localization
198
 * @param string         $svg (optional) svg code, inserted into the button
199
 * @return string
200
 */
201
function html_btn($name, $id, $akey, $params, $method='get', $tooltip='', $label=false, $svg=null){
202
    global $conf;
203
    global $lang;
204
205
    if (!$label)
206
        $label = $lang['btn_'.$name];
207
208
    $ret = '';
209
210
    //filter id (without urlencoding)
211
    $id = idfilter($id,false);
212
213
    //make nice URLs even for buttons
214
    if($conf['userewrite'] == 2){
215
        $script = DOKU_BASE.DOKU_SCRIPT.'/'.$id;
216
    }elseif($conf['userewrite']){
217
        $script = DOKU_BASE.$id;
218
    }else{
219
        $script = DOKU_BASE.DOKU_SCRIPT;
220
        $params['id'] = $id;
221
    }
222
223
    $ret .= '<form class="button btn_'.$name.'" method="'.$method.'" action="'.$script.'"><div class="no">';
224
225
    if(is_array($params)){
226
        foreach($params as $key => $val) {
227
            $ret .= '<input type="hidden" name="'.$key.'" ';
228
            $ret .= 'value="'.hsc($val).'" />';
229
        }
230
    }
231
232
    if ($tooltip!='') {
233
        $tip = hsc($tooltip);
234
    }else{
235
        $tip = hsc($label);
236
    }
237
238
    $ret .= '<button type="submit" ';
239
    if($akey){
240
        $tip .= ' ['.strtoupper($akey).']';
241
        $ret .= 'accesskey="'.$akey.'" ';
242
    }
243
    $ret .= 'title="'.$tip.'">';
244
    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...
245
        $ret .= '<span>' . hsc($label) . '</span>';
246
        $ret .= inlineSVG($svg);
247
    } else {
248
        $ret .= hsc($label);
249
    }
250
    $ret .= '</button>';
251
    $ret .= '</div></form>';
252
253
    return $ret;
254
}
255
/**
256
 * show a revision warning
257
 *
258
 * @author Szymon Olewniczak <[email protected]>
259
 */
260
function html_showrev() {
261
    print p_locale_xhtml('showrev');
262
}
263
264
/**
265
 * Show a wiki page
266
 *
267
 * @author Andreas Gohr <[email protected]>
268
 *
269
 * @param null|string $txt wiki text or null for showing $ID
270
 */
271
function html_show($txt=null){
272
    global $ID;
273
    global $REV;
274
    global $HIGH;
275
    global $INFO;
276
    global $DATE_AT;
277
    //disable section editing for old revisions or in preview
278
    if($txt || $REV){
0 ignored issues
show
Bug Best Practice introduced by
The expression $txt of type null|string 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...
279
        $secedit = false;
280
    }else{
281
        $secedit = true;
282
    }
283
284
    if (!is_null($txt)){
285
        //PreviewHeader
286
        echo '<br id="scroll__here" />';
287
        echo p_locale_xhtml('preview');
288
        echo '<div class="preview"><div class="pad">';
289
        $html = html_secedit(p_render('xhtml',p_get_instructions($txt),$info),$secedit);
290
        if($INFO['prependTOC']) $html = tpl_toc(true).$html;
291
        echo $html;
292
        echo '<div class="clearer"></div>';
293
        echo '</div></div>';
294
295
    }else{
296
        if ($REV||$DATE_AT){
297
            $data = array('rev' => &$REV, 'date_at' => &$DATE_AT);
298
            trigger_event('HTML_SHOWREV_OUTPUT', $data, 'html_showrev');
299
        }
300
        $html = p_wiki_xhtml($ID,$REV,true,$DATE_AT);
301
        $html = html_secedit($html,$secedit);
0 ignored issues
show
Bug introduced by
It seems like $html can also be of type boolean or null; however, html_secedit() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
302
        if($INFO['prependTOC']) $html = tpl_toc(true).$html;
303
        $html = html_hilight($html,$HIGH);
304
        echo $html;
305
    }
306
}
307
308
/**
309
 * ask the user about how to handle an exisiting draft
310
 *
311
 * @author Andreas Gohr <[email protected]>
312
 */
313
function html_draft(){
314
    global $INFO;
315
    global $ID;
316
    global $lang;
317
    $draft = unserialize(io_readFile($INFO['draft'],false));
318
    $text  = cleanText(con($draft['prefix'],$draft['text'],$draft['suffix'],true));
319
320
    print p_locale_xhtml('draft');
321
    html_diff($text, false);
322
    $form = new Doku_Form(array('id' => 'dw__editform'));
323
    $form->addHidden('id', $ID);
324
    $form->addHidden('date', $draft['date']);
325
    $form->addHidden('wikitext', $text);
326
    $form->addElement(form_makeOpenTag('div', array('id'=>'draft__status')));
327
    $form->addElement($lang['draftdate'].' '. dformat(filemtime($INFO['draft'])));
328
    $form->addElement(form_makeCloseTag('div'));
329
    $form->addElement(form_makeButton('submit', 'recover', $lang['btn_recover'], array('tabindex'=>'1')));
330
    $form->addElement(form_makeButton('submit', 'draftdel', $lang['btn_draftdel'], array('tabindex'=>'2')));
331
    $form->addElement(form_makeButton('submit', 'show', $lang['btn_cancel'], array('tabindex'=>'3')));
332
    html_form('draft', $form);
333
}
334
335
/**
336
 * Highlights searchqueries in HTML code
337
 *
338
 * @author Andreas Gohr <[email protected]>
339
 * @author Harry Fuecks <[email protected]>
340
 *
341
 * @param string $html
342
 * @param array|string $phrases
343
 * @return string html
344
 */
345
function html_hilight($html,$phrases){
346
    $phrases = (array) $phrases;
347
    $phrases = array_map('preg_quote_cb', $phrases);
348
    $phrases = array_map('ft_snippet_re_preprocess', $phrases);
349
    $phrases = array_filter($phrases);
350
    $regex = join('|',$phrases);
351
352
    if ($regex === '') return $html;
353
    if (!utf8_check($regex)) return $html;
354
    $html = @preg_replace_callback("/((<[^>]*)|$regex)/ui",'html_hilight_callback',$html);
355
    return $html;
356
}
357
358
/**
359
 * Callback used by html_hilight()
360
 *
361
 * @author Harry Fuecks <[email protected]>
362
 *
363
 * @param array $m matches
364
 * @return string html
365
 */
366
function html_hilight_callback($m) {
367
    $hlight = unslash($m[0]);
368
    if ( !isset($m[2])) {
369
        $hlight = '<span class="search_hit">'.$hlight.'</span>';
370
    }
371
    return $hlight;
372
}
373
374
/**
375
 * Display error on locked pages
376
 *
377
 * @author Andreas Gohr <[email protected]>
378
 */
379
function html_locked(){
380
    global $ID;
381
    global $conf;
382
    global $lang;
383
    global $INFO;
384
385
    $locktime = filemtime(wikiLockFN($ID));
386
    $expire = dformat($locktime + $conf['locktime']);
387
    $min    = round(($conf['locktime'] - (time() - $locktime) )/60);
388
389
    print p_locale_xhtml('locked');
390
    print '<ul>';
391
    print '<li><div class="li"><strong>'.$lang['lockedby'].'</strong> '.editorinfo($INFO['locked']).'</div></li>';
392
    print '<li><div class="li"><strong>'.$lang['lockexpire'].'</strong> '.$expire.' ('.$min.' min)</div></li>';
393
    print '</ul>';
394
}
395
396
/**
397
 * list old revisions
398
 *
399
 * @author Andreas Gohr <[email protected]>
400
 * @author Ben Coburn <[email protected]>
401
 * @author Kate Arzamastseva <[email protected]>
402
 *
403
 * @param int $first skip the first n changelog lines
404
 * @param bool|string $media_id id of media, or false for current page
405
 */
406
function html_revisions($first=0, $media_id = false){
407
    global $ID;
408
    global $INFO;
409
    global $conf;
410
    global $lang;
411
    $id = $ID;
412
    if ($media_id) {
413
        $id = $media_id;
414
        $changelog = new MediaChangeLog($id);
415
    } else {
416
        $changelog = new PageChangeLog($id);
417
    }
418
419
    /* we need to get one additional log entry to be able to
420
     * decide if this is the last page or is there another one.
421
     * see html_recent()
422
     */
423
424
    $revisions = $changelog->getRevisions($first, $conf['recent']+1);
425
426
    if(count($revisions)==0 && $first!=0){
427
        $first=0;
428
        $revisions = $changelog->getRevisions($first, $conf['recent']+1);
429
    }
430
    $hasNext = false;
431
    if (count($revisions)>$conf['recent']) {
432
        $hasNext = true;
433
        array_pop($revisions); // remove extra log entry
434
    }
435
436
    if (!$media_id) print p_locale_xhtml('revisions');
437
438
    $params = array('id' => 'page__revisions', 'class' => 'changes');
439
    if($media_id) {
440
        $params['action'] = media_managerURL(array('image' => $media_id), '&');
441
    }
442
443
    if(!$media_id) {
444
        $exists = $INFO['exists'];
445
        $display_name = useHeading('navigation') ? hsc(p_get_first_heading($id)) : $id;
446
        if(!$display_name) {
447
            $display_name = $id;
448
        }
449
    } else {
450
        $exists = file_exists(mediaFN($id));
451
        $display_name = $id;
452
    }
453
454
    $form = new Doku_Form($params);
455
    $form->addElement(form_makeOpenTag('ul'));
456
457
    if($exists && $first == 0) {
458
        $minor = false;
459
        if($media_id) {
460
            $date = dformat(@filemtime(mediaFN($id)));
461
            $href = media_managerURL(array('image' => $id, 'tab_details' => 'view'), '&');
462
463
            $changelog->setChunkSize(1024);
464
            $revinfo = $changelog->getRevisionInfo(@filemtime(fullpath(mediaFN($id))));
465
466
            $summary = $revinfo['sum'];
467
            if($revinfo['user']) {
468
                $editor = $revinfo['user'];
469
            } else {
470
                $editor = $revinfo['ip'];
471
            }
472
            $sizechange = $revinfo['sizechange'];
473
        } else {
474
            $date = dformat($INFO['lastmod']);
475
            if(isset($INFO['meta']) && isset($INFO['meta']['last_change'])) {
476
                if($INFO['meta']['last_change']['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT) {
477
                    $minor = true;
478
                }
479
                if(isset($INFO['meta']['last_change']['sizechange'])) {
480
                    $sizechange = $INFO['meta']['last_change']['sizechange'];
481
                } else {
482
                    $sizechange = null;
483
                }
484
            }
485
            $pagelog = new PageChangeLog($ID);
486
            $latestrev = $pagelog->getRevisions(-1, 1);
487
            $latestrev = array_pop($latestrev);
488
            $href = wl($id,"rev=$latestrev",false,'&');
489
            $summary = $INFO['sum'];
490
            $editor = $INFO['editor'];
491
        }
492
493
        $form->addElement(form_makeOpenTag('li', array('class' => ($minor ? 'minor' : ''))));
494
        $form->addElement(form_makeOpenTag('div', array('class' => 'li')));
495
        $form->addElement(form_makeTag('input', array(
496
                        'type' => 'checkbox',
497
                        'name' => 'rev2[]',
498
                        'value' => 'current')));
499
500
        $form->addElement(form_makeOpenTag('span', array('class' => 'date')));
501
        $form->addElement($date);
502
        $form->addElement(form_makeCloseTag('span'));
503
504
        $form->addElement('<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />');
505
506
        $form->addElement(form_makeOpenTag('a', array(
507
                        'class' => 'wikilink1',
508
                        'href'  => $href)));
509
        $form->addElement($display_name);
510
        $form->addElement(form_makeCloseTag('a'));
511
512
        if ($media_id) $form->addElement(form_makeOpenTag('div'));
513
514
        if($summary) {
515
            $form->addElement(form_makeOpenTag('span', array('class' => 'sum')));
516
            if(!$media_id) $form->addElement(' – ');
517
            $form->addElement('<bdi>' . hsc($summary) . '</bdi>');
518
            $form->addElement(form_makeCloseTag('span'));
519
        }
520
521
        $form->addElement(form_makeOpenTag('span', array('class' => 'user')));
522
        $form->addElement((empty($editor))?('('.$lang['external_edit'].')'):'<bdi>'.editorinfo($editor).'</bdi>');
523
        $form->addElement(form_makeCloseTag('span'));
524
525
        html_sizechange($sizechange, $form);
0 ignored issues
show
Bug introduced by
The variable $sizechange does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
526
527
        $form->addElement('('.$lang['current'].')');
528
529
        if ($media_id) $form->addElement(form_makeCloseTag('div'));
530
531
        $form->addElement(form_makeCloseTag('div'));
532
        $form->addElement(form_makeCloseTag('li'));
533
    }
534
535
    foreach($revisions as $rev) {
536
        $date = dformat($rev);
537
        $info = $changelog->getRevisionInfo($rev);
538
        if($media_id) {
539
            $exists = file_exists(mediaFN($id, $rev));
540
        } else {
541
            $exists = page_exists($id, $rev);
542
        }
543
544
        $class = '';
545
        if($info['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT) {
546
            $class = 'minor';
547
        }
548
        $form->addElement(form_makeOpenTag('li', array('class' => $class)));
549
        $form->addElement(form_makeOpenTag('div', array('class' => 'li')));
550
        if($exists){
551
            $form->addElement(form_makeTag('input', array(
552
                            'type' => 'checkbox',
553
                            'name' => 'rev2[]',
554
                            'value' => $rev)));
555
        }else{
556
            $form->addElement('<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />');
557
        }
558
559
        $form->addElement(form_makeOpenTag('span', array('class' => 'date')));
560
        $form->addElement($date);
561
        $form->addElement(form_makeCloseTag('span'));
562
563
        if($exists){
564
            if (!$media_id) {
565
                $href = wl($id,"rev=$rev,do=diff", false, '&');
566
            } else {
567
                $href = media_managerURL(array('image' => $id, 'rev' => $rev, 'mediado' => 'diff'), '&');
568
            }
569
            $form->addElement(form_makeOpenTag('a', array(
570
                            'class' => 'diff_link',
571
                            'href' => $href)));
572
            $form->addElement(form_makeTag('img', array(
573
                            'src'    => DOKU_BASE.'lib/images/diff.png',
574
                            'width'  => 15,
575
                            'height' => 11,
576
                            'title'  => $lang['diff'],
577
                            'alt'    => $lang['diff'])));
578
            $form->addElement(form_makeCloseTag('a'));
579
580
            if (!$media_id) {
581
                $href = wl($id,"rev=$rev",false,'&');
582
            } else {
583
                $href = media_managerURL(array('image' => $id, 'tab_details' => 'view', 'rev' => $rev), '&');
584
            }
585
            $form->addElement(form_makeOpenTag('a', array(
586
                            'class' => 'wikilink1',
587
                            'href' => $href)));
588
            $form->addElement($display_name);
589
            $form->addElement(form_makeCloseTag('a'));
590
        }else{
591
            $form->addElement('<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />');
592
            $form->addElement($display_name);
593
        }
594
595
        if ($media_id) $form->addElement(form_makeOpenTag('div'));
596
597
        if ($info['sum']) {
598
            $form->addElement(form_makeOpenTag('span', array('class' => 'sum')));
599
            if(!$media_id) $form->addElement(' – ');
600
            $form->addElement('<bdi>'.hsc($info['sum']).'</bdi>');
601
            $form->addElement(form_makeCloseTag('span'));
602
        }
603
604
        $form->addElement(form_makeOpenTag('span', array('class' => 'user')));
605
        if($info['user']){
606
            $form->addElement('<bdi>'.editorinfo($info['user']).'</bdi>');
607
            if(auth_ismanager()){
608
                $form->addElement(' <bdo dir="ltr">('.$info['ip'].')</bdo>');
609
            }
610
        }else{
611
            $form->addElement('<bdo dir="ltr">'.$info['ip'].'</bdo>');
612
        }
613
        $form->addElement(form_makeCloseTag('span'));
614
615
        html_sizechange($info['sizechange'], $form);
616
617
        if ($media_id) $form->addElement(form_makeCloseTag('div'));
618
619
        $form->addElement(form_makeCloseTag('div'));
620
        $form->addElement(form_makeCloseTag('li'));
621
    }
622
    $form->addElement(form_makeCloseTag('ul'));
623
    if (!$media_id) {
624
        $form->addElement(form_makeButton('submit', 'diff', $lang['diff2']));
625
    } else {
626
        $form->addHidden('mediado', 'diff');
627
        $form->addElement(form_makeButton('submit', '', $lang['diff2']));
628
    }
629
    html_form('revisions', $form);
630
631
    print '<div class="pagenav">';
632
    $last = $first + $conf['recent'];
633
    if ($first > 0) {
634
        $first -= $conf['recent'];
635
        if ($first < 0) $first = 0;
636
        print '<div class="pagenav-prev">';
637
        if ($media_id) {
638
            print html_btn('newer',$media_id,"p",media_managerURL(array('first' => $first), '&amp;', false, true));
639
        } else {
640
            print html_btn('newer',$id,"p",array('do' => 'revisions', 'first' => $first));
641
        }
642
        print '</div>';
643
    }
644
    if ($hasNext) {
645
        print '<div class="pagenav-next">';
646
        if ($media_id) {
647
            print html_btn('older',$media_id,"n",media_managerURL(array('first' => $last), '&amp;', false, true));
648
        } else {
649
            print html_btn('older',$id,"n",array('do' => 'revisions', 'first' => $last));
650
        }
651
        print '</div>';
652
    }
653
    print '</div>';
654
655
}
656
657
/**
658
 * display recent changes
659
 *
660
 * @author Andreas Gohr <[email protected]>
661
 * @author Matthias Grimm <[email protected]>
662
 * @author Ben Coburn <[email protected]>
663
 * @author Kate Arzamastseva <[email protected]>
664
 *
665
 * @param int $first
666
 * @param string $show_changes
667
 */
668
function html_recent($first = 0, $show_changes = 'both') {
669
    global $conf;
670
    global $lang;
671
    global $ID;
672
    /* we need to get one additionally log entry to be able to
673
     * decide if this is the last page or is there another one.
674
     * This is the cheapest solution to get this information.
675
     */
676
    $flags = 0;
677
    if($show_changes == 'mediafiles' && $conf['mediarevisions']) {
678
        $flags = RECENTS_MEDIA_CHANGES;
679
    } elseif($show_changes == 'pages') {
680
        $flags = 0;
681
    } elseif($conf['mediarevisions']) {
682
        $show_changes = 'both';
683
        $flags = RECENTS_MEDIA_PAGES_MIXED;
684
    }
685
686
    $recents = getRecents($first, $conf['recent'] + 1, getNS($ID), $flags);
0 ignored issues
show
Security Bug introduced by
It seems like getNS($ID) targeting getNS() can also be of type false; however, getRecents() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
687
    if(count($recents) == 0 && $first != 0) {
688
        $first = 0;
689
        $recents = getRecents($first, $conf['recent'] + 1, getNS($ID), $flags);
0 ignored issues
show
Security Bug introduced by
It seems like getNS($ID) targeting getNS() can also be of type false; however, getRecents() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
690
    }
691
    $hasNext = false;
692
    if(count($recents) > $conf['recent']) {
693
        $hasNext = true;
694
        array_pop($recents); // remove extra log entry
695
    }
696
697
    print p_locale_xhtml('recent');
698
699
    if(getNS($ID) != '') {
700
        print '<div class="level1"><p>' .
701
            sprintf($lang['recent_global'], getNS($ID), wl('', 'do=recent')) .
702
            '</p></div>';
703
    }
704
705
    $form = new Doku_Form(array('id' => 'dw__recent', 'method' => 'GET', 'class' => 'changes'));
706
    $form->addHidden('sectok', null);
707
    $form->addHidden('do', 'recent');
708
    $form->addHidden('id', $ID);
709
710
    if($conf['mediarevisions']) {
711
        $form->addElement('<div class="changeType">');
712
        $form->addElement(form_makeListboxField(
713
                    'show_changes',
714
                    array(
715
                        'pages'      => $lang['pages_changes'],
716
                        'mediafiles' => $lang['media_changes'],
717
                        'both'       => $lang['both_changes']
718
                    ),
719
                    $show_changes,
720
                    $lang['changes_type'],
721
                    '', '',
722
                    array('class' => 'quickselect')));
723
724
        $form->addElement(form_makeButton('submit', 'recent', $lang['btn_apply']));
725
        $form->addElement('</div>');
726
    }
727
728
    $form->addElement(form_makeOpenTag('ul'));
729
730
    foreach($recents as $recent) {
731
        $date = dformat($recent['date']);
732
733
        $class = '';
734
        if($recent['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT) {
735
            $class = 'minor';
736
        }
737
        $form->addElement(form_makeOpenTag('li', array('class' => $class)));
738
        $form->addElement(form_makeOpenTag('div', array('class' => 'li')));
739
740
        if(!empty($recent['media'])) {
741
            $form->addElement(media_printicon($recent['id']));
742
        } else {
743
            $icon = DOKU_BASE . 'lib/images/fileicons/file.png';
744
            $form->addElement('<img src="' . $icon . '" alt="' . $recent['id'] . '" class="icon" />');
745
        }
746
747
        $form->addElement(form_makeOpenTag('span', array('class' => 'date')));
748
        $form->addElement($date);
749
        $form->addElement(form_makeCloseTag('span'));
750
751
        $diff = false;
752
        $href = '';
753
754
        if(!empty($recent['media'])) {
755
            $changelog = new MediaChangeLog($recent['id']);
756
            $revs = $changelog->getRevisions(0, 1);
757
            $diff = (count($revs) && file_exists(mediaFN($recent['id'])));
758
            if($diff) {
759
                $href = media_managerURL(array(
760
                                            'tab_details' => 'history',
761
                                            'mediado' => 'diff',
762
                                            'image' => $recent['id'],
763
                                            'ns' => getNS($recent['id'])
764
                                        ), '&');
765
            }
766
        } else {
767
            $href = wl($recent['id'], "do=diff", false, '&');
768
        }
769
770
        if(!empty($recent['media']) && !$diff) {
771
            $form->addElement('<img src="' . DOKU_BASE . 'lib/images/blank.gif" width="15" height="11" alt="" />');
772
        } else {
773
            $form->addElement(form_makeOpenTag('a', array('class' => 'diff_link', 'href' => $href)));
774
            $form->addElement(form_makeTag('img', array(
775
                            'src'    => DOKU_BASE . 'lib/images/diff.png',
776
                            'width'  => 15,
777
                            'height' => 11,
778
                            'title'  => $lang['diff'],
779
                            'alt'    => $lang['diff']
780
                        )));
781
            $form->addElement(form_makeCloseTag('a'));
782
        }
783
784
        if(!empty($recent['media'])) {
785
            $href = media_managerURL(
786
                array(
787
                    'tab_details' => 'history',
788
                    'image' => $recent['id'],
789
                    'ns' => getNS($recent['id'])
790
                ),
791
                '&'
792
            );
793
        } else {
794
            $href = wl($recent['id'], "do=revisions", false, '&');
795
        }
796
        $form->addElement(form_makeOpenTag('a', array(
797
                        'class' => 'revisions_link',
798
                        'href'  => $href)));
799
        $form->addElement(form_makeTag('img', array(
800
                        'src'    => DOKU_BASE . 'lib/images/history.png',
801
                        'width'  => 12,
802
                        'height' => 14,
803
                        'title'  => $lang['btn_revs'],
804
                        'alt'    => $lang['btn_revs']
805
                    )));
806
        $form->addElement(form_makeCloseTag('a'));
807
808
        if(!empty($recent['media'])) {
809
            $href = media_managerURL(
810
                array(
811
                    'tab_details' => 'view',
812
                    'image' => $recent['id'],
813
                    'ns' => getNS($recent['id'])
814
                ),
815
                '&'
816
            );
817
            $class = file_exists(mediaFN($recent['id'])) ? 'wikilink1' : 'wikilink2';
818
            $form->addElement(form_makeOpenTag('a', array(
819
                        'class' => $class,
820
                        'href'  => $href)));
821
            $form->addElement($recent['id']);
822
            $form->addElement(form_makeCloseTag('a'));
823
        } else {
824
            $form->addElement(html_wikilink(':' . $recent['id'], useHeading('navigation') ? null : $recent['id']));
825
        }
826
        $form->addElement(form_makeOpenTag('span', array('class' => 'sum')));
827
        $form->addElement(' – ' . hsc($recent['sum']));
828
        $form->addElement(form_makeCloseTag('span'));
829
830
        $form->addElement(form_makeOpenTag('span', array('class' => 'user')));
831
        if($recent['user']) {
832
            $form->addElement('<bdi>' . editorinfo($recent['user']) . '</bdi>');
833
            if(auth_ismanager()) {
834
                $form->addElement(' <bdo dir="ltr">(' . $recent['ip'] . ')</bdo>');
835
            }
836
        } else {
837
            $form->addElement('<bdo dir="ltr">' . $recent['ip'] . '</bdo>');
838
        }
839
        $form->addElement(form_makeCloseTag('span'));
840
841
        html_sizechange($recent['sizechange'], $form);
842
843
        $form->addElement(form_makeCloseTag('div'));
844
        $form->addElement(form_makeCloseTag('li'));
845
    }
846
    $form->addElement(form_makeCloseTag('ul'));
847
848
    $form->addElement(form_makeOpenTag('div', array('class' => 'pagenav')));
849
    $last = $first + $conf['recent'];
850
    if($first > 0) {
851
        $first -= $conf['recent'];
852
        if($first < 0) $first = 0;
853
        $form->addElement(form_makeOpenTag('div', array('class' => 'pagenav-prev')));
854
        $form->addElement(form_makeOpenTag('button', array(
855
                        'type'      => 'submit',
856
                        'name'      => 'first[' . $first . ']',
857
                        'accesskey' => 'n',
858
                        'title'     => $lang['btn_newer'] . ' [N]',
859
                        'class'     => 'button show'
860
                    )));
861
        $form->addElement($lang['btn_newer']);
862
        $form->addElement(form_makeCloseTag('button'));
863
        $form->addElement(form_makeCloseTag('div'));
864
    }
865
    if($hasNext) {
866
        $form->addElement(form_makeOpenTag('div', array('class' => 'pagenav-next')));
867
        $form->addElement(form_makeOpenTag('button', array(
868
                        'type'      => 'submit',
869
                        'name'      => 'first[' . $last . ']',
870
                        'accesskey' => 'p',
871
                        'title'     => $lang['btn_older'] . ' [P]',
872
                        'class'     => 'button show'
873
                    )));
874
        $form->addElement($lang['btn_older']);
875
        $form->addElement(form_makeCloseTag('button'));
876
        $form->addElement(form_makeCloseTag('div'));
877
    }
878
    $form->addElement(form_makeCloseTag('div'));
879
    html_form('recent', $form);
880
}
881
882
/**
883
 * Display page index
884
 *
885
 * @author Andreas Gohr <[email protected]>
886
 *
887
 * @param string $ns
888
 */
889
function html_index($ns){
890
    global $conf;
891
    global $ID;
892
    $ns  = cleanID($ns);
893
    if(empty($ns)){
894
        $ns = getNS($ID);
895
        if($ns === false) $ns ='';
896
    }
897
    $ns  = utf8_encodeFN(str_replace(':','/',$ns));
898
899
    echo p_locale_xhtml('index');
900
    echo '<div id="index__tree" class="index__tree">';
901
902
    $data = array();
903
    search($data,$conf['datadir'],'search_index',array('ns' => $ns));
904
    echo html_buildlist($data,'idx','html_list_index','html_li_index');
905
906
    echo '</div>';
907
}
908
909
/**
910
 * Index item formatter
911
 *
912
 * User function for html_buildlist()
913
 *
914
 * @author Andreas Gohr <[email protected]>
915
 *
916
 * @param array $item
917
 * @return string
918
 */
919
function html_list_index($item){
920
    global $ID, $conf;
921
922
    // prevent searchbots needlessly following links
923
    $nofollow = ($ID != $conf['start'] || $conf['sitemap']) ? 'rel="nofollow"' : '';
924
925
    $ret = '';
926
    $base = ':'.$item['id'];
927
    $base = substr($base,strrpos($base,':')+1);
928
    if($item['type']=='d'){
929
        // FS#2766, no need for search bots to follow namespace links in the index
930
        $link = wl($ID, 'idx=' . rawurlencode($item['id']));
931
        $ret .= '<a href="' . $link . '" title="' . $item['id'] . '" class="idx_dir" ' . $nofollow . '><strong>';
932
        $ret .= $base;
933
        $ret .= '</strong></a>';
934
    }else{
935
        // default is noNSorNS($id), but we want noNS($id) when useheading is off FS#2605
936
        $ret .= html_wikilink(':'.$item['id'], useHeading('navigation') ? null : noNS($item['id']));
937
    }
938
    return $ret;
939
}
940
941
/**
942
 * Index List item
943
 *
944
 * This user function is used in html_buildlist to build the
945
 * <li> tags for namespaces when displaying the page index
946
 * it gives different classes to opened or closed "folders"
947
 *
948
 * @author Andreas Gohr <[email protected]>
949
 *
950
 * @param array $item
951
 * @return string html
952
 */
953
function html_li_index($item){
954
    global $INFO;
955
    global $ACT;
956
957
    $class = '';
958
    $id = '';
959
960
    if($item['type'] == "f"){
961
        // scroll to the current item
962
        if($item['id'] == $INFO['id'] && $ACT == 'index') {
963
            $id = ' id="scroll__here"';
964
            $class = ' bounce';
965
        }
966
        return '<li class="level'.$item['level'].$class.'" '.$id.'>';
967
    }elseif($item['open']){
968
        return '<li class="open">';
969
    }else{
970
        return '<li class="closed">';
971
    }
972
}
973
974
/**
975
 * Default List item
976
 *
977
 * @author Andreas Gohr <[email protected]>
978
 *
979
 * @param array $item
980
 * @return string html
981
 */
982
function html_li_default($item){
983
    return '<li class="level'.$item['level'].'">';
984
}
985
986
/**
987
 * Build an unordered list
988
 *
989
 * Build an unordered list from the given $data array
990
 * Each item in the array has to have a 'level' property
991
 * the item itself gets printed by the given $func user
992
 * function. The second and optional function is used to
993
 * print the <li> tag. Both user function need to accept
994
 * a single item.
995
 *
996
 * Both user functions can be given as array to point to
997
 * a member of an object.
998
 *
999
 * @author Andreas Gohr <[email protected]>
1000
 *
1001
 * @param array    $data  array with item arrays
1002
 * @param string   $class class of ul wrapper
1003
 * @param callable $func  callback to print an list item
1004
 * @param callable $lifunc callback to the opening li tag
1005
 * @param bool     $forcewrapper Trigger building a wrapper ul if the first level is
1006
 *                               0 (we have a root object) or 1 (just the root content)
1007
 * @return string html of an unordered list
1008
 */
1009
function html_buildlist($data,$class,$func,$lifunc='html_li_default',$forcewrapper=false){
1010
    if (count($data) === 0) {
1011
        return '';
1012
    }
1013
1014
    $firstElement = reset($data);
1015
    $start_level = $firstElement['level'];
1016
    $level = $start_level;
1017
    $ret   = '';
1018
    $open  = 0;
1019
1020
    foreach ($data as $item){
1021
1022
        if( $item['level'] > $level ){
1023
            //open new list
1024
            for($i=0; $i<($item['level'] - $level); $i++){
1025
                if ($i) $ret .= "<li class=\"clear\">";
1026
                $ret .= "\n<ul class=\"$class\">\n";
1027
                $open++;
1028
            }
1029
            $level = $item['level'];
1030
1031
        }elseif( $item['level'] < $level ){
1032
            //close last item
1033
            $ret .= "</li>\n";
1034
            while( $level > $item['level'] && $open > 0 ){
1035
                //close higher lists
1036
                $ret .= "</ul>\n</li>\n";
1037
                $level--;
1038
                $open--;
1039
            }
1040
        } elseif ($ret !== '') {
1041
            //close previous item
1042
            $ret .= "</li>\n";
1043
        }
1044
1045
        //print item
1046
        $ret .= call_user_func($lifunc,$item);
1047
        $ret .= '<div class="li">';
1048
1049
        $ret .= call_user_func($func,$item);
1050
        $ret .= '</div>';
1051
    }
1052
1053
    //close remaining items and lists
1054
    $ret .= "</li>\n";
1055
    while($open-- > 0) {
1056
        $ret .= "</ul></li>\n";
1057
    }
1058
1059
    if ($forcewrapper || $start_level < 2) {
1060
        // Trigger building a wrapper ul if the first level is
1061
        // 0 (we have a root object) or 1 (just the root content)
1062
        $ret = "\n<ul class=\"$class\">\n".$ret."</ul>\n";
1063
    }
1064
1065
    return $ret;
1066
}
1067
1068
/**
1069
 * display backlinks
1070
 *
1071
 * @author Andreas Gohr <[email protected]>
1072
 * @author Michael Klier <[email protected]>
1073
 */
1074
function html_backlinks(){
1075
    global $ID;
1076
    global $lang;
1077
1078
    print p_locale_xhtml('backlinks');
1079
1080
    $data = ft_backlinks($ID);
1081
1082
    if(!empty($data)) {
1083
        print '<ul class="idx">';
1084
        foreach($data as $blink){
1085
            print '<li><div class="li">';
1086
            print html_wikilink(':'.$blink,useHeading('navigation')?null:$blink);
1087
            print '</div></li>';
1088
        }
1089
        print '</ul>';
1090
    } else {
1091
        print '<div class="level1"><p>' . $lang['nothingfound'] . '</p></div>';
1092
    }
1093
}
1094
1095
/**
1096
 * Get header of diff HTML
1097
 *
1098
 * @param string $l_rev   Left revisions
1099
 * @param string $r_rev   Right revision
1100
 * @param string $id      Page id, if null $ID is used
1101
 * @param bool   $media   If it is for media files
1102
 * @param bool   $inline  Return the header on a single line
1103
 * @return string[] HTML snippets for diff header
1104
 */
1105
function html_diff_head($l_rev, $r_rev, $id = null, $media = false, $inline = false) {
1106
    global $lang;
1107
    if ($id === null) {
1108
        global $ID;
1109
        $id = $ID;
1110
    }
1111
    $head_separator = $inline ? ' ' : '<br />';
1112
    $media_or_wikiFN = $media ? 'mediaFN' : 'wikiFN';
1113
    $ml_or_wl = $media ? 'ml' : 'wl';
1114
    $l_minor = $r_minor = '';
1115
1116
    if($media) {
1117
        $changelog = new MediaChangeLog($id);
1118
    } else {
1119
        $changelog = new PageChangeLog($id);
1120
    }
1121
    if(!$l_rev){
1122
        $l_head = '&mdash;';
1123
    }else{
1124
        $l_info   = $changelog->getRevisionInfo($l_rev);
1125
        if($l_info['user']){
1126
            $l_user = '<bdi>'.editorinfo($l_info['user']).'</bdi>';
1127
            if(auth_ismanager()) $l_user .= ' <bdo dir="ltr">('.$l_info['ip'].')</bdo>';
1128
        } else {
1129
            $l_user = '<bdo dir="ltr">'.$l_info['ip'].'</bdo>';
1130
        }
1131
        $l_user  = '<span class="user">'.$l_user.'</span>';
1132
        $l_sum   = ($l_info['sum']) ? '<span class="sum"><bdi>'.hsc($l_info['sum']).'</bdi></span>' : '';
1133
        if ($l_info['type']===DOKU_CHANGE_TYPE_MINOR_EDIT) $l_minor = 'class="minor"';
1134
1135
        $l_head_title = ($media) ? dformat($l_rev) : $id.' ['.dformat($l_rev).']';
1136
        $l_head = '<bdi><a class="wikilink1" href="'.$ml_or_wl($id,"rev=$l_rev").'">'.
1137
        $l_head_title.'</a></bdi>'.
1138
        $head_separator.$l_user.' '.$l_sum;
1139
    }
1140
1141
    if($r_rev){
1142
        $r_info   = $changelog->getRevisionInfo($r_rev);
1143
        if($r_info['user']){
1144
            $r_user = '<bdi>'.editorinfo($r_info['user']).'</bdi>';
1145
            if(auth_ismanager()) $r_user .= ' <bdo dir="ltr">('.$r_info['ip'].')</bdo>';
1146
        } else {
1147
            $r_user = '<bdo dir="ltr">'.$r_info['ip'].'</bdo>';
1148
        }
1149
        $r_user = '<span class="user">'.$r_user.'</span>';
1150
        $r_sum  = ($r_info['sum']) ? '<span class="sum"><bdi>'.hsc($r_info['sum']).'</bdi></span>' : '';
1151
        if ($r_info['type']===DOKU_CHANGE_TYPE_MINOR_EDIT) $r_minor = 'class="minor"';
1152
1153
        $r_head_title = ($media) ? dformat($r_rev) : $id.' ['.dformat($r_rev).']';
1154
        $r_head = '<bdi><a class="wikilink1" href="'.$ml_or_wl($id,"rev=$r_rev").'">'.
1155
        $r_head_title.'</a></bdi>'.
1156
        $head_separator.$r_user.' '.$r_sum;
1157
    }elseif($_rev = @filemtime($media_or_wikiFN($id))){
1158
        $_info   = $changelog->getRevisionInfo($_rev);
1159
        if($_info['user']){
1160
            $_user = '<bdi>'.editorinfo($_info['user']).'</bdi>';
1161
            if(auth_ismanager()) $_user .= ' <bdo dir="ltr">('.$_info['ip'].')</bdo>';
1162
        } else {
1163
            $_user = '<bdo dir="ltr">'.$_info['ip'].'</bdo>';
1164
        }
1165
        $_user = '<span class="user">'.$_user.'</span>';
1166
        $_sum  = ($_info['sum']) ? '<span class="sum"><bdi>'.hsc($_info['sum']).'</span></bdi>' : '';
1167
        if ($_info['type']===DOKU_CHANGE_TYPE_MINOR_EDIT) $r_minor = 'class="minor"';
1168
1169
        $r_head_title = ($media) ? dformat($_rev) : $id.' ['.dformat($_rev).']';
1170
        $r_head  = '<bdi><a class="wikilink1" href="'.$ml_or_wl($id).'">'.
1171
        $r_head_title.'</a></bdi> '.
1172
        '('.$lang['current'].')'.
1173
        $head_separator.$_user.' '.$_sum;
1174
    }else{
1175
        $r_head = '&mdash; ('.$lang['current'].')';
1176
    }
1177
1178
    return array($l_head, $r_head, $l_minor, $r_minor);
1179
}
1180
1181
/**
1182
 * Show diff
1183
 * between current page version and provided $text
1184
 * or between the revisions provided via GET or POST
1185
 *
1186
 * @author Andreas Gohr <[email protected]>
1187
 * @param  string $text  when non-empty: compare with this text with most current version
1188
 * @param  bool   $intro display the intro text
1189
 * @param  string $type  type of the diff (inline or sidebyside)
1190
 */
1191
function html_diff($text = '', $intro = true, $type = null) {
1192
    global $ID;
1193
    global $REV;
1194
    global $lang;
1195
    global $INPUT;
1196
    global $INFO;
1197
    $pagelog = new PageChangeLog($ID);
1198
1199
    /*
1200
     * Determine diff type
1201
     */
1202
    if(!$type) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $type of type string|null is loosely compared to false; 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...
1203
        $type = $INPUT->str('difftype');
1204
        if(empty($type)) {
1205
            $type = get_doku_pref('difftype', $type);
1206
            if(empty($type) && $INFO['ismobile']) {
1207
                $type = 'inline';
1208
            }
1209
        }
1210
    }
1211
    if($type != 'inline') $type = 'sidebyside';
1212
1213
    /*
1214
     * Determine requested revision(s)
1215
     */
1216
    // we're trying to be clever here, revisions to compare can be either
1217
    // given as rev and rev2 parameters, with rev2 being optional. Or in an
1218
    // array in rev2.
1219
    $rev1 = $REV;
1220
1221
    $rev2 = $INPUT->ref('rev2');
1222
    if(is_array($rev2)) {
1223
        $rev1 = (int) $rev2[0];
1224
        $rev2 = (int) $rev2[1];
1225
1226
        if(!$rev1) {
1227
            $rev1 = $rev2;
1228
            unset($rev2);
1229
        }
1230
    } else {
1231
        $rev2 = $INPUT->int('rev2');
1232
    }
1233
1234
    /*
1235
     * Determine left and right revision, its texts and the header
1236
     */
1237
    $r_minor = '';
1238
    $l_minor = '';
1239
1240
    if($text) { // compare text to the most current revision
1241
        $l_rev = '';
1242
        $l_text = rawWiki($ID, '');
1243
        $l_head = '<a class="wikilink1" href="' . wl($ID) . '">' .
1244
            $ID . ' ' . dformat((int) @filemtime(wikiFN($ID))) . '</a> ' .
1245
            $lang['current'];
1246
1247
        $r_rev = '';
1248
        $r_text = cleanText($text);
1249
        $r_head = $lang['yours'];
1250
    } else {
1251
        if($rev1 && isset($rev2) && $rev2) { // two specific revisions wanted
1252
            // make sure order is correct (older on the left)
1253
            if($rev1 < $rev2) {
1254
                $l_rev = $rev1;
1255
                $r_rev = $rev2;
1256
            } else {
1257
                $l_rev = $rev2;
1258
                $r_rev = $rev1;
1259
            }
1260
        } elseif($rev1) { // single revision given, compare to current
1261
            $r_rev = '';
1262
            $l_rev = $rev1;
1263
        } else { // no revision was given, compare previous to current
1264
            $r_rev = '';
1265
            $revs = $pagelog->getRevisions(0, 1);
1266
            $l_rev = $revs[0];
1267
            $REV = $l_rev; // store revision back in $REV
1268
        }
1269
1270
        // when both revisions are empty then the page was created just now
1271
        if(!$l_rev && !$r_rev) {
1272
            $l_text = '';
1273
        } else {
1274
            $l_text = rawWiki($ID, $l_rev);
1275
        }
1276
        $r_text = rawWiki($ID, $r_rev);
1277
1278
        list($l_head, $r_head, $l_minor, $r_minor) = html_diff_head($l_rev, $r_rev, null, false, $type == 'inline');
1279
    }
1280
1281
    /*
1282
     * Build navigation
1283
     */
1284
    $l_nav = '';
1285
    $r_nav = '';
1286
    if(!$text) {
1287
        list($l_nav, $r_nav) = html_diff_navigation($pagelog, $type, $l_rev, $r_rev);
1288
    }
1289
    /*
1290
     * Create diff object and the formatter
1291
     */
1292
    $diff = new Diff(explode("\n", $l_text), explode("\n", $r_text));
1293
1294
    if($type == 'inline') {
1295
        $diffformatter = new InlineDiffFormatter();
1296
    } else {
1297
        $diffformatter = new TableDiffFormatter();
1298
    }
1299
    /*
1300
     * Display intro
1301
     */
1302
    if($intro) print p_locale_xhtml('diff');
1303
1304
    /*
1305
     * Display type and exact reference
1306
     */
1307
    if(!$text) {
1308
        ptln('<div class="diffoptions group">');
1309
1310
1311
        $form = new Doku_Form(array('action' => wl()));
1312
        $form->addHidden('id', $ID);
1313
        $form->addHidden('rev2[0]', $l_rev);
1314
        $form->addHidden('rev2[1]', $r_rev);
1315
        $form->addHidden('do', 'diff');
1316
        $form->addElement(
1317
             form_makeListboxField(
1318
                 'difftype',
1319
                 array(
1320
                     'sidebyside' => $lang['diff_side'],
1321
                     'inline' => $lang['diff_inline']
1322
                 ),
1323
                 $type,
1324
                 $lang['diff_type'],
1325
                 '', '',
1326
                 array('class' => 'quickselect')
1327
             )
1328
        );
1329
        $form->addElement(form_makeButton('submit', 'diff', 'Go'));
1330
        $form->printForm();
1331
1332
        ptln('<p>');
1333
        // link to exactly this view FS#2835
1334
        echo html_diff_navigationlink($type, 'difflink', $l_rev, $r_rev ? $r_rev : $INFO['currentrev']);
1335
        ptln('</p>');
1336
1337
        ptln('</div>'); // .diffoptions
1338
    }
1339
1340
    /*
1341
     * Display diff view table
1342
     */
1343
    ?>
1344
    <div class="table">
1345
    <table class="diff diff_<?php echo $type ?>">
1346
1347
        <?php
1348
        //navigation and header
1349
        if($type == 'inline') {
1350
            if(!$text) { ?>
1351
                <tr>
1352
                    <td class="diff-lineheader">-</td>
1353
                    <td class="diffnav"><?php echo $l_nav ?></td>
1354
                </tr>
1355
                <tr>
1356
                    <th class="diff-lineheader">-</th>
1357
                    <th <?php echo $l_minor ?>>
1358
                        <?php echo $l_head ?>
1359
                    </th>
1360
                </tr>
1361
            <?php } ?>
1362
            <tr>
1363
                <td class="diff-lineheader">+</td>
1364
                <td class="diffnav"><?php echo $r_nav ?></td>
1365
            </tr>
1366
            <tr>
1367
                <th class="diff-lineheader">+</th>
1368
                <th <?php echo $r_minor ?>>
1369
                    <?php echo $r_head ?>
1370
                </th>
1371
            </tr>
1372
        <?php } else {
1373
            if(!$text) { ?>
1374
                <tr>
1375
                    <td colspan="2" class="diffnav"><?php echo $l_nav ?></td>
1376
                    <td colspan="2" class="diffnav"><?php echo $r_nav ?></td>
1377
                </tr>
1378
            <?php } ?>
1379
            <tr>
1380
                <th colspan="2" <?php echo $l_minor ?>>
1381
                    <?php echo $l_head ?>
1382
                </th>
1383
                <th colspan="2" <?php echo $r_minor ?>>
1384
                    <?php echo $r_head ?>
1385
                </th>
1386
            </tr>
1387
        <?php }
1388
1389
        //diff view
1390
        echo html_insert_softbreaks($diffformatter->format($diff)); ?>
1391
1392
    </table>
1393
    </div>
1394
<?php
1395
}
1396
1397
/**
1398
 * Create html for revision navigation
1399
 *
1400
 * @param PageChangeLog $pagelog changelog object of current page
1401
 * @param string        $type    inline vs sidebyside
1402
 * @param int           $l_rev   left revision timestamp
1403
 * @param int           $r_rev   right revision timestamp
1404
 * @return string[] html of left and right navigation elements
1405
 */
1406
function html_diff_navigation($pagelog, $type, $l_rev, $r_rev) {
1407
    global $INFO, $ID;
1408
1409
    // last timestamp is not in changelog, retrieve timestamp from metadata
1410
    // note: when page is removed, the metadata timestamp is zero
1411
    if(!$r_rev) {
1412
        if(isset($INFO['meta']['last_change']['date'])) {
1413
            $r_rev = $INFO['meta']['last_change']['date'];
1414
        } else {
1415
            $r_rev = 0;
1416
        }
1417
    }
1418
1419
    //retrieve revisions with additional info
1420
    list($l_revs, $r_revs) = $pagelog->getRevisionsAround($l_rev, $r_rev);
1421
    $l_revisions = array();
1422
    if(!$l_rev) {
1423
        $l_revisions[0] = array(0, "", false); //no left revision given, add dummy
1424
    }
1425
    foreach($l_revs as $rev) {
1426
        $info = $pagelog->getRevisionInfo($rev);
1427
        $l_revisions[$rev] = array(
1428
            $rev,
1429
            dformat($info['date']) . ' ' . editorinfo($info['user'], true) . ' ' . $info['sum'],
1430
            $r_rev ? $rev >= $r_rev : false //disable?
1431
        );
1432
    }
1433
    $r_revisions = array();
1434
    if(!$r_rev) {
1435
        $r_revisions[0] = array(0, "", false); //no right revision given, add dummy
1436
    }
1437
    foreach($r_revs as $rev) {
1438
        $info = $pagelog->getRevisionInfo($rev);
1439
        $r_revisions[$rev] = array(
1440
            $rev,
1441
            dformat($info['date']) . ' ' . editorinfo($info['user'], true) . ' ' . $info['sum'],
1442
            $rev <= $l_rev //disable?
1443
        );
1444
    }
1445
1446
    //determine previous/next revisions
1447
    $l_index = array_search($l_rev, $l_revs);
1448
    $l_prev = $l_revs[$l_index + 1];
1449
    $l_next = $l_revs[$l_index - 1];
1450
    if($r_rev) {
1451
        $r_index = array_search($r_rev, $r_revs);
1452
        $r_prev = $r_revs[$r_index + 1];
1453
        $r_next = $r_revs[$r_index - 1];
1454
    } else {
1455
        //removed page
1456
        if($l_next) {
1457
            $r_prev = $r_revs[0];
1458
        } else {
1459
            $r_prev = null;
1460
        }
1461
        $r_next = null;
1462
    }
1463
1464
    /*
1465
     * Left side:
1466
     */
1467
    $l_nav = '';
1468
    //move back
1469
    if($l_prev) {
1470
        $l_nav .= html_diff_navigationlink($type, 'diffbothprevrev', $l_prev, $r_prev);
1471
        $l_nav .= html_diff_navigationlink($type, 'diffprevrev', $l_prev, $r_rev);
1472
    }
1473
    //dropdown
1474
    $form = new Doku_Form(array('action' => wl()));
1475
    $form->addHidden('id', $ID);
1476
    $form->addHidden('difftype', $type);
1477
    $form->addHidden('rev2[1]', $r_rev);
1478
    $form->addHidden('do', 'diff');
1479
    $form->addElement(
1480
         form_makeListboxField(
1481
             'rev2[0]',
1482
             $l_revisions,
1483
             $l_rev,
1484
             '', '', '',
1485
             array('class' => 'quickselect')
1486
         )
1487
    );
1488
    $form->addElement(form_makeButton('submit', 'diff', 'Go'));
1489
    $l_nav .= $form->getForm();
1490
    //move forward
1491
    if($l_next && ($l_next < $r_rev || !$r_rev)) {
1492
        $l_nav .= html_diff_navigationlink($type, 'diffnextrev', $l_next, $r_rev);
1493
    }
1494
1495
    /*
1496
     * Right side:
1497
     */
1498
    $r_nav = '';
1499
    //move back
1500
    if($l_rev < $r_prev) {
1501
        $r_nav .= html_diff_navigationlink($type, 'diffprevrev', $l_rev, $r_prev);
1502
    }
1503
    //dropdown
1504
    $form = new Doku_Form(array('action' => wl()));
1505
    $form->addHidden('id', $ID);
1506
    $form->addHidden('rev2[0]', $l_rev);
1507
    $form->addHidden('difftype', $type);
1508
    $form->addHidden('do', 'diff');
1509
    $form->addElement(
1510
         form_makeListboxField(
1511
             'rev2[1]',
1512
             $r_revisions,
1513
             $r_rev,
1514
             '', '', '',
1515
             array('class' => 'quickselect')
1516
         )
1517
    );
1518
    $form->addElement(form_makeButton('submit', 'diff', 'Go'));
1519
    $r_nav .= $form->getForm();
1520
    //move forward
1521
    if($r_next) {
1522
        if($pagelog->isCurrentRevision($r_next)) {
1523
            $r_nav .= html_diff_navigationlink($type, 'difflastrev', $l_rev); //last revision is diff with current page
1524
        } else {
1525
            $r_nav .= html_diff_navigationlink($type, 'diffnextrev', $l_rev, $r_next);
1526
        }
1527
        $r_nav .= html_diff_navigationlink($type, 'diffbothnextrev', $l_next, $r_next);
1528
    }
1529
    return array($l_nav, $r_nav);
1530
}
1531
1532
/**
1533
 * Create html link to a diff defined by two revisions
1534
 *
1535
 * @param string $difftype display type
1536
 * @param string $linktype
1537
 * @param int $lrev oldest revision
1538
 * @param int $rrev newest revision or null for diff with current revision
1539
 * @return string html of link to a diff
1540
 */
1541
function html_diff_navigationlink($difftype, $linktype, $lrev, $rrev = null) {
1542
    global $ID, $lang;
1543
    if(!$rrev) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $rrev of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. 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 integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1544
        $urlparam = array(
1545
            'do' => 'diff',
1546
            'rev' => $lrev,
1547
            'difftype' => $difftype,
1548
        );
1549
    } else {
1550
        $urlparam = array(
1551
            'do' => 'diff',
1552
            'rev2[0]' => $lrev,
1553
            'rev2[1]' => $rrev,
1554
            'difftype' => $difftype,
1555
        );
1556
    }
1557
    return  '<a class="' . $linktype . '" href="' . wl($ID, $urlparam) . '" title="' . $lang[$linktype] . '">' .
1558
                '<span>' . $lang[$linktype] . '</span>' .
1559
            '</a>' . "\n";
1560
}
1561
1562
/**
1563
 * Insert soft breaks in diff html
1564
 *
1565
 * @param string $diffhtml
1566
 * @return string
1567
 */
1568
function html_insert_softbreaks($diffhtml) {
1569
    // search the diff html string for both:
1570
    // - html tags, so these can be ignored
1571
    // - long strings of characters without breaking characters
1572
    return preg_replace_callback('/<[^>]*>|[^<> ]{12,}/','html_softbreak_callback',$diffhtml);
1573
}
1574
1575
/**
1576
 * callback which adds softbreaks
1577
 *
1578
 * @param array $match array with first the complete match
1579
 * @return string the replacement
1580
 */
1581
function html_softbreak_callback($match){
1582
    // if match is an html tag, return it intact
1583
    if ($match[0]{0} == '<') return $match[0];
1584
1585
    // its a long string without a breaking character,
1586
    // make certain characters into breaking characters by inserting a
1587
    // breaking character (zero length space, U+200B / #8203) in front them.
1588
    $regex = <<< REGEX
1589
(?(?=              # start a conditional expression with a positive look ahead ...
1590
&\#?\\w{1,6};)     # ... for html entities - we don't want to split them (ok to catch some invalid combinations)
1591
&\#?\\w{1,6};      # yes pattern - a quicker match for the html entity, since we know we have one
1592
|
1593
[?/,&\#;:]         # no pattern - any other group of 'special' characters to insert a breaking character after
1594
)+                 # end conditional expression
1595
REGEX;
1596
1597
    return preg_replace('<'.$regex.'>xu','\0&#8203;',$match[0]);
1598
}
1599
1600
/**
1601
 * show warning on conflict detection
1602
 *
1603
 * @author Andreas Gohr <[email protected]>
1604
 *
1605
 * @param string $text
1606
 * @param string $summary
1607
 */
1608
function html_conflict($text,$summary){
1609
    global $ID;
1610
    global $lang;
1611
1612
    print p_locale_xhtml('conflict');
1613
    $form = new Doku_Form(array('id' => 'dw__editform'));
1614
    $form->addHidden('id', $ID);
1615
    $form->addHidden('wikitext', $text);
1616
    $form->addHidden('summary', $summary);
1617
    $form->addElement(form_makeButton('submit', 'save', $lang['btn_save'], array('accesskey'=>'s')));
1618
    $form->addElement(form_makeButton('submit', 'cancel', $lang['btn_cancel']));
1619
    html_form('conflict', $form);
1620
    print '<br /><br /><br /><br />'.NL;
1621
}
1622
1623
/**
1624
 * Prints the global message array
1625
 *
1626
 * @author Andreas Gohr <[email protected]>
1627
 */
1628
function html_msgarea(){
1629
    global $MSG, $MSG_shown;
1630
    /** @var array $MSG */
1631
    // store if the global $MSG has already been shown and thus HTML output has been started
1632
    $MSG_shown = true;
1633
1634
    if(!isset($MSG)) return;
1635
1636
    $shown = array();
1637
    foreach($MSG as $msg){
1638
        $hash = md5($msg['msg']);
1639
        if(isset($shown[$hash])) continue; // skip double messages
1640
        if(info_msg_allowed($msg)){
1641
            print '<div class="'.$msg['lvl'].'">';
1642
            print $msg['msg'];
1643
            print '</div>';
1644
        }
1645
        $shown[$hash] = 1;
1646
    }
1647
1648
    unset($GLOBALS['MSG']);
1649
}
1650
1651
/**
1652
 * Prints the registration form
1653
 *
1654
 * @author Andreas Gohr <[email protected]>
1655
 */
1656
function html_register(){
1657
    global $lang;
1658
    global $conf;
1659
    global $INPUT;
1660
1661
    $base_attrs = array('size'=>50,'required'=>'required');
1662
    $email_attrs = $base_attrs + array('type'=>'email','class'=>'edit');
1663
1664
    print p_locale_xhtml('register');
1665
    print '<div class="centeralign">'.NL;
1666
    $form = new Doku_Form(array('id' => 'dw__register'));
1667
    $form->startFieldset($lang['btn_register']);
1668
    $form->addHidden('do', 'register');
1669
    $form->addHidden('save', '1');
1670
    $form->addElement(
1671
        form_makeTextField(
1672
            'login',
1673
            $INPUT->post->str('login'),
1674
            $lang['user'],
1675
            '',
1676
            'block',
1677
            $base_attrs
1678
        )
1679
    );
1680
    if (!$conf['autopasswd']) {
1681
        $form->addElement(form_makePasswordField('pass', $lang['pass'], '', 'block', $base_attrs));
1682
        $form->addElement(form_makePasswordField('passchk', $lang['passchk'], '', 'block', $base_attrs));
1683
    }
1684
    $form->addElement(
1685
        form_makeTextField(
1686
            'fullname',
1687
            $INPUT->post->str('fullname'),
1688
            $lang['fullname'],
1689
            '',
1690
            'block',
1691
            $base_attrs
1692
        )
1693
    );
1694
    $form->addElement(
1695
        form_makeField(
1696
            'email',
1697
            'email',
1698
            $INPUT->post->str('email'),
1699
            $lang['email'],
1700
            '',
1701
            'block',
1702
            $email_attrs
1703
        )
1704
    );
1705
    $form->addElement(form_makeButton('submit', '', $lang['btn_register']));
1706
    $form->endFieldset();
1707
    html_form('register', $form);
1708
1709
    print '</div>'.NL;
1710
}
1711
1712
/**
1713
 * Print the update profile form
1714
 *
1715
 * @author Christopher Smith <[email protected]>
1716
 * @author Andreas Gohr <[email protected]>
1717
 */
1718
function html_updateprofile(){
1719
    global $lang;
1720
    global $conf;
1721
    global $INPUT;
1722
    global $INFO;
1723
    /** @var DokuWiki_Auth_Plugin $auth */
1724
    global $auth;
1725
1726
    print p_locale_xhtml('updateprofile');
1727
    print '<div class="centeralign">'.NL;
1728
1729
    $fullname = $INPUT->post->str('fullname', $INFO['userinfo']['name'], true);
1730
    $email = $INPUT->post->str('email', $INFO['userinfo']['mail'], true);
1731
    $form = new Doku_Form(array('id' => 'dw__register'));
1732
    $form->startFieldset($lang['profile']);
1733
    $form->addHidden('do', 'profile');
1734
    $form->addHidden('save', '1');
1735
    $form->addElement(
1736
        form_makeTextField(
1737
            'login',
1738
            $_SERVER['REMOTE_USER'],
1739
            $lang['user'],
1740
            '',
1741
            'block',
1742
            array('size' => '50', 'disabled' => 'disabled')
1743
        )
1744
    );
1745
    $attr = array('size'=>'50');
1746
    if (!$auth->canDo('modName')) $attr['disabled'] = 'disabled';
1747
    $form->addElement(form_makeTextField('fullname', $fullname, $lang['fullname'], '', 'block', $attr));
1748
    $attr = array('size'=>'50', 'class'=>'edit');
1749
    if (!$auth->canDo('modMail')) $attr['disabled'] = 'disabled';
1750
    $form->addElement(form_makeField('email','email', $email, $lang['email'], '', 'block', $attr));
1751
    $form->addElement(form_makeTag('br'));
1752
    if ($auth->canDo('modPass')) {
1753
        $form->addElement(form_makePasswordField('newpass', $lang['newpass'], '', 'block', array('size'=>'50')));
1754
        $form->addElement(form_makePasswordField('passchk', $lang['passchk'], '', 'block', array('size'=>'50')));
1755
    }
1756
    if ($conf['profileconfirm']) {
1757
        $form->addElement(form_makeTag('br'));
1758
        $form->addElement(
1759
            form_makePasswordField(
1760
                'oldpass',
1761
                $lang['oldpass'],
1762
                '',
1763
                'block',
1764
                array('size' => '50', 'required' => 'required')
1765
            )
1766
        );
1767
    }
1768
    $form->addElement(form_makeButton('submit', '', $lang['btn_save']));
1769
    $form->addElement(form_makeButton('reset', '', $lang['btn_reset']));
1770
1771
    $form->endFieldset();
1772
    html_form('updateprofile', $form);
1773
1774
    if ($auth->canDo('delUser') && actionOK('profile_delete')) {
1775
        $form_profiledelete = new Doku_Form(array('id' => 'dw__profiledelete'));
1776
        $form_profiledelete->startFieldset($lang['profdeleteuser']);
1777
        $form_profiledelete->addHidden('do', 'profile_delete');
1778
        $form_profiledelete->addHidden('delete', '1');
1779
        $form_profiledelete->addElement(
1780
            form_makeCheckboxField(
1781
                'confirm_delete',
1782
                '1',
1783
                $lang['profconfdelete'],
1784
                'dw__confirmdelete',
1785
                '',
1786
                array('required' => 'required')
1787
            )
1788
        );
1789
        if ($conf['profileconfirm']) {
1790
            $form_profiledelete->addElement(form_makeTag('br'));
1791
            $form_profiledelete->addElement(
1792
                form_makePasswordField(
1793
                    'oldpass',
1794
                    $lang['oldpass'],
1795
                    '',
1796
                    'block',
1797
                    array('size' => '50', 'required' => 'required')
1798
                )
1799
            );
1800
        }
1801
        $form_profiledelete->addElement(form_makeButton('submit', '', $lang['btn_deleteuser']));
1802
        $form_profiledelete->endFieldset();
1803
1804
        html_form('profiledelete', $form_profiledelete);
1805
    }
1806
1807
    print '</div>'.NL;
1808
}
1809
1810
/**
1811
 * Preprocess edit form data
1812
 *
1813
 * @author   Andreas Gohr <[email protected]>
1814
 *
1815
 * @triggers HTML_EDITFORM_OUTPUT
1816
 */
1817
function html_edit(){
1818
    global $INPUT;
1819
    global $ID;
1820
    global $REV;
1821
    global $DATE;
1822
    global $PRE;
1823
    global $SUF;
1824
    global $INFO;
1825
    global $SUM;
1826
    global $lang;
1827
    global $conf;
1828
    global $TEXT;
1829
1830
    if ($INPUT->has('changecheck')) {
1831
        $check = $INPUT->str('changecheck');
1832
    } elseif(!$INFO['exists']){
1833
        // $TEXT has been loaded from page template
1834
        $check = md5('');
1835
    } else {
1836
        $check = md5($TEXT);
1837
    }
1838
    $mod = md5($TEXT) !== $check;
1839
1840
    $wr = $INFO['writable'] && !$INFO['locked'];
1841
    $include = 'edit';
1842
    if($wr){
1843
        if ($REV) $include = 'editrev';
1844
    }else{
1845
        // check pseudo action 'source'
1846
        if(!actionOK('source')){
1847
            msg('Command disabled: source',-1);
1848
            return;
1849
        }
1850
        $include = 'read';
1851
    }
1852
1853
    global $license;
1854
1855
    $form = new Doku_Form(array('id' => 'dw__editform'));
1856
    $form->addHidden('id', $ID);
1857
    $form->addHidden('rev', $REV);
1858
    $form->addHidden('date', $DATE);
1859
    $form->addHidden('prefix', $PRE . '.');
1860
    $form->addHidden('suffix', $SUF);
1861
    $form->addHidden('changecheck', $check);
1862
1863
    $data = array('form' => $form,
1864
                  'wr'   => $wr,
1865
                  'media_manager' => true,
1866
                  'target' => ($INPUT->has('target') && $wr) ? $INPUT->str('target') : 'section',
1867
                  'intro_locale' => $include);
1868
1869
    if ($data['target'] !== 'section') {
1870
        // Only emit event if page is writable, section edit data is valid and
1871
        // edit target is not section.
1872
        trigger_event('HTML_EDIT_FORMSELECTION', $data, 'html_edit_form', true);
1873
    } else {
1874
        html_edit_form($data);
1875
    }
1876
    if (isset($data['intro_locale'])) {
1877
        echo p_locale_xhtml($data['intro_locale']);
1878
    }
1879
1880
    $form->addHidden('target', $data['target']);
1881
    if ($INPUT->has('hid')) {
1882
        $form->addHidden('hid', $INPUT->str('hid'));
1883
    }
1884
    if ($INPUT->has('codeblockOffset')) {
1885
        $form->addHidden('codeblockOffset', $INPUT->str('codeblockOffset'));
1886
    }
1887
    $form->addElement(form_makeOpenTag('div', array('id'=>'wiki__editbar', 'class'=>'editBar')));
1888
    $form->addElement(form_makeOpenTag('div', array('id'=>'size__ctl')));
1889
    $form->addElement(form_makeCloseTag('div'));
1890
    if ($wr) {
1891
        $form->addElement(form_makeOpenTag('div', array('class'=>'editButtons')));
1892
        $form->addElement(
1893
            form_makeButton(
1894
                'submit',
1895
                'save',
1896
                $lang['btn_save'],
1897
                array('id' => 'edbtn__save', 'accesskey' => 's', 'tabindex' => '4')
1898
            )
1899
        );
1900
        $form->addElement(
1901
            form_makeButton(
1902
                'submit',
1903
                'preview',
1904
                $lang['btn_preview'],
1905
                array('id' => 'edbtn__preview', 'accesskey' => 'p', 'tabindex' => '5')
1906
            )
1907
        );
1908
        $form->addElement(form_makeButton('submit', 'cancel', $lang['btn_cancel'], array('tabindex'=>'6')));
1909
        $form->addElement(form_makeCloseTag('div'));
1910
        $form->addElement(form_makeOpenTag('div', array('class'=>'summary')));
1911
        $form->addElement(
1912
            form_makeTextField(
1913
                'summary',
1914
                $SUM,
1915
                $lang['summary'],
1916
                'edit__summary',
1917
                'nowrap',
1918
                array('size' => '50', 'tabindex' => '2')
1919
            )
1920
        );
1921
        $elem = html_minoredit();
1922
        if ($elem) $form->addElement($elem);
1923
        $form->addElement(form_makeCloseTag('div'));
1924
    }
1925
    $form->addElement(form_makeCloseTag('div'));
1926
    if($wr && $conf['license']){
1927
        $form->addElement(form_makeOpenTag('div', array('class'=>'license')));
1928
        $out  = $lang['licenseok'];
1929
        $out .= ' <a href="'.$license[$conf['license']]['url'].'" rel="license" class="urlextern"';
1930
        if($conf['target']['extern']) $out .= ' target="'.$conf['target']['extern'].'"';
1931
        $out .= '>'.$license[$conf['license']]['name'].'</a>';
1932
        $form->addElement($out);
1933
        $form->addElement(form_makeCloseTag('div'));
1934
    }
1935
1936
    if ($wr) {
1937
        // sets changed to true when previewed
1938
        echo '<script type="text/javascript">/*<![CDATA[*/'. NL;
1939
        echo 'textChanged = ' . ($mod ? 'true' : 'false');
1940
        echo '/*!]]>*/</script>' . NL;
1941
    } ?>
1942
    <div class="editBox" role="application">
1943
1944
    <div class="toolbar group">
1945
        <div id="draft__status" class="draft__status"><?php
1946
            if(!empty($INFO['draft'])) echo $lang['draftdate'].' '.dformat();
1947
            ?></div>
1948
        <div id="tool__bar" class="tool__bar"><?php
1949
            if ($wr && $data['media_manager']){
1950
                ?><a href="<?php echo DOKU_BASE?>lib/exe/mediamanager.php?ns=<?php echo $INFO['namespace']?>"
1951
                target="_blank"><?php echo $lang['mediaselect'] ?></a><?php
1952
            }?></div>
1953
    </div>
1954
    <?php
1955
1956
    html_form('edit', $form);
1957
    print '</div>'.NL;
1958
}
1959
1960
/**
1961
 * Display the default edit form
1962
 *
1963
 * Is the default action for HTML_EDIT_FORMSELECTION.
1964
 *
1965
 * @param mixed[] $param
1966
 */
1967
function html_edit_form($param) {
1968
    global $TEXT;
1969
1970
    if ($param['target'] !== 'section') {
1971
        msg('No editor for edit target ' . hsc($param['target']) . ' found.', -1);
1972
    }
1973
1974
    $attr = array('tabindex'=>'1');
1975
    if (!$param['wr']) $attr['readonly'] = 'readonly';
1976
1977
    $param['form']->addElement(form_makeWikiText($TEXT, $attr));
1978
}
1979
1980
/**
1981
 * Adds a checkbox for minor edits for logged in users
1982
 *
1983
 * @author Andreas Gohr <[email protected]>
1984
 *
1985
 * @return array|bool
1986
 */
1987
function html_minoredit(){
1988
    global $conf;
1989
    global $lang;
1990
    global $INPUT;
1991
    // minor edits are for logged in users only
1992
    if(!$conf['useacl'] || !$_SERVER['REMOTE_USER']){
1993
        return false;
1994
    }
1995
1996
    $p = array();
1997
    $p['tabindex'] = 3;
1998
    if($INPUT->bool('minor')) $p['checked']='checked';
1999
    return form_makeCheckboxField('minor', '1', $lang['minoredit'], 'minoredit', 'nowrap', $p);
2000
}
2001
2002
/**
2003
 * prints some debug info
2004
 *
2005
 * @author Andreas Gohr <[email protected]>
2006
 */
2007
function html_debug(){
2008
    global $conf;
2009
    global $lang;
2010
    /** @var DokuWiki_Auth_Plugin $auth */
2011
    global $auth;
2012
    global $INFO;
2013
2014
    //remove sensitive data
2015
    $cnf = $conf;
2016
    debug_guard($cnf);
2017
    $nfo = $INFO;
2018
    debug_guard($nfo);
2019
    $ses = $_SESSION;
2020
    debug_guard($ses);
2021
2022
    print '<html><body>';
2023
2024
    print '<p>When reporting bugs please send all the following ';
2025
    print 'output as a mail to [email protected] ';
2026
    print 'The best way to do this is to save this page in your browser</p>';
2027
2028
    print '<b>$INFO:</b><pre>';
2029
    print_r($nfo);
2030
    print '</pre>';
2031
2032
    print '<b>$_SERVER:</b><pre>';
2033
    print_r($_SERVER);
2034
    print '</pre>';
2035
2036
    print '<b>$conf:</b><pre>';
2037
    print_r($cnf);
2038
    print '</pre>';
2039
2040
    print '<b>DOKU_BASE:</b><pre>';
2041
    print DOKU_BASE;
2042
    print '</pre>';
2043
2044
    print '<b>abs DOKU_BASE:</b><pre>';
2045
    print DOKU_URL;
2046
    print '</pre>';
2047
2048
    print '<b>rel DOKU_BASE:</b><pre>';
2049
    print dirname($_SERVER['PHP_SELF']).'/';
2050
    print '</pre>';
2051
2052
    print '<b>PHP Version:</b><pre>';
2053
    print phpversion();
2054
    print '</pre>';
2055
2056
    print '<b>locale:</b><pre>';
2057
    print setlocale(LC_ALL,0);
2058
    print '</pre>';
2059
2060
    print '<b>encoding:</b><pre>';
2061
    print $lang['encoding'];
2062
    print '</pre>';
2063
2064
    if($auth){
2065
        print '<b>Auth backend capabilities:</b><pre>';
2066
        foreach ($auth->getCapabilities() as $cando){
2067
            print '   '.str_pad($cando,16) . ' => ' . (int)$auth->canDo($cando) . NL;
2068
        }
2069
        print '</pre>';
2070
    }
2071
2072
    print '<b>$_SESSION:</b><pre>';
2073
    print_r($ses);
2074
    print '</pre>';
2075
2076
    print '<b>Environment:</b><pre>';
2077
    print_r($_ENV);
2078
    print '</pre>';
2079
2080
    print '<b>PHP settings:</b><pre>';
2081
    $inis = ini_get_all();
2082
    print_r($inis);
2083
    print '</pre>';
2084
2085
    if (function_exists('apache_get_version')) {
2086
        $apache = array();
2087
        $apache['version'] = apache_get_version();
2088
2089
        if (function_exists('apache_get_modules')) {
2090
            $apache['modules'] = apache_get_modules();
2091
        }
2092
        print '<b>Apache</b><pre>';
2093
        print_r($apache);
2094
        print '</pre>';
2095
    }
2096
2097
    print '</body></html>';
2098
}
2099
2100
/**
2101
 * Form to request a new password for an existing account
2102
 *
2103
 * @author Benoit Chesneau <[email protected]>
2104
 * @author Andreas Gohr <[email protected]>
2105
 */
2106
function html_resendpwd() {
2107
    global $lang;
2108
    global $conf;
2109
    global $INPUT;
2110
2111
    $token = preg_replace('/[^a-f0-9]+/','',$INPUT->str('pwauth'));
2112
2113
    if(!$conf['autopasswd'] && $token){
2114
        print p_locale_xhtml('resetpwd');
2115
        print '<div class="centeralign">'.NL;
2116
        $form = new Doku_Form(array('id' => 'dw__resendpwd'));
2117
        $form->startFieldset($lang['btn_resendpwd']);
2118
        $form->addHidden('token', $token);
2119
        $form->addHidden('do', 'resendpwd');
2120
2121
        $form->addElement(form_makePasswordField('pass', $lang['pass'], '', 'block', array('size'=>'50')));
2122
        $form->addElement(form_makePasswordField('passchk', $lang['passchk'], '', 'block', array('size'=>'50')));
2123
2124
        $form->addElement(form_makeButton('submit', '', $lang['btn_resendpwd']));
2125
        $form->endFieldset();
2126
        html_form('resendpwd', $form);
2127
        print '</div>'.NL;
2128
    }else{
2129
        print p_locale_xhtml('resendpwd');
2130
        print '<div class="centeralign">'.NL;
2131
        $form = new Doku_Form(array('id' => 'dw__resendpwd'));
2132
        $form->startFieldset($lang['resendpwd']);
2133
        $form->addHidden('do', 'resendpwd');
2134
        $form->addHidden('save', '1');
2135
        $form->addElement(form_makeTag('br'));
2136
        $form->addElement(form_makeTextField('login', $INPUT->post->str('login'), $lang['user'], '', 'block'));
2137
        $form->addElement(form_makeTag('br'));
2138
        $form->addElement(form_makeTag('br'));
2139
        $form->addElement(form_makeButton('submit', '', $lang['btn_resendpwd']));
2140
        $form->endFieldset();
2141
        html_form('resendpwd', $form);
2142
        print '</div>'.NL;
2143
    }
2144
}
2145
2146
/**
2147
 * Return the TOC rendered to XHTML
2148
 *
2149
 * @author Andreas Gohr <[email protected]>
2150
 *
2151
 * @param array $toc
2152
 * @return string html
2153
 */
2154
function html_TOC($toc){
2155
    if(!count($toc)) return '';
2156
    global $lang;
2157
    $out  = '<!-- TOC START -->'.DOKU_LF;
2158
    $out .= '<div id="dw__toc" class="dw__toc">'.DOKU_LF;
2159
    $out .= '<h3 class="toggle">';
2160
    $out .= $lang['toc'];
2161
    $out .= '</h3>'.DOKU_LF;
2162
    $out .= '<div>'.DOKU_LF;
2163
    $out .= html_buildlist($toc,'toc','html_list_toc','html_li_default',true);
2164
    $out .= '</div>'.DOKU_LF.'</div>'.DOKU_LF;
2165
    $out .= '<!-- TOC END -->'.DOKU_LF;
2166
    return $out;
2167
}
2168
2169
/**
2170
 * Callback for html_buildlist
2171
 *
2172
 * @param array $item
2173
 * @return string html
2174
 */
2175
function html_list_toc($item){
2176
    if(isset($item['hid'])){
2177
        $link = '#'.$item['hid'];
2178
    }else{
2179
        $link = $item['link'];
2180
    }
2181
2182
    return '<a href="'.$link.'">'.hsc($item['title']).'</a>';
2183
}
2184
2185
/**
2186
 * Helper function to build TOC items
2187
 *
2188
 * Returns an array ready to be added to a TOC array
2189
 *
2190
 * @param string $link  - where to link (if $hash set to '#' it's a local anchor)
2191
 * @param string $text  - what to display in the TOC
2192
 * @param int    $level - nesting level
2193
 * @param string $hash  - is prepended to the given $link, set blank if you want full links
2194
 * @return array the toc item
2195
 */
2196
function html_mktocitem($link, $text, $level, $hash='#'){
2197
    return  array( 'link'  => $hash.$link,
2198
            'title' => $text,
2199
            'type'  => 'ul',
2200
            'level' => $level);
2201
}
2202
2203
/**
2204
 * Output a Doku_Form object.
2205
 * Triggers an event with the form name: HTML_{$name}FORM_OUTPUT
2206
 *
2207
 * @author Tom N Harris <[email protected]>
2208
 *
2209
 * @param string     $name The name of the form
2210
 * @param Doku_Form  $form The form
2211
 */
2212
function html_form($name, &$form) {
2213
    // Safety check in case the caller forgets.
2214
    $form->endFieldset();
2215
    trigger_event('HTML_'.strtoupper($name).'FORM_OUTPUT', $form, 'html_form_output', false);
2216
}
2217
2218
/**
2219
 * Form print function.
2220
 * Just calls printForm() on the data object.
2221
 *
2222
 * @param Doku_Form $data The form
2223
 */
2224
function html_form_output($data) {
2225
    $data->printForm();
2226
}
2227
2228
/**
2229
 * Embed a flash object in HTML
2230
 *
2231
 * This will create the needed HTML to embed a flash movie in a cross browser
2232
 * compatble way using valid XHTML
2233
 *
2234
 * The parameters $params, $flashvars and $atts need to be associative arrays.
2235
 * No escaping needs to be done for them. The alternative content *has* to be
2236
 * escaped because it is used as is. If no alternative content is given
2237
 * $lang['noflash'] is used.
2238
 *
2239
 * @author Andreas Gohr <[email protected]>
2240
 * @link   http://latrine.dgx.cz/how-to-correctly-insert-a-flash-into-xhtml
2241
 *
2242
 * @param string $swf      - the SWF movie to embed
2243
 * @param int $width       - width of the flash movie in pixels
2244
 * @param int $height      - height of the flash movie in pixels
2245
 * @param array $params    - additional parameters (<param>)
2246
 * @param array $flashvars - parameters to be passed in the flashvar parameter
2247
 * @param array $atts      - additional attributes for the <object> tag
2248
 * @param string $alt      - alternative content (is NOT automatically escaped!)
2249
 * @return string         - the XHTML markup
2250
 */
2251
function html_flashobject($swf,$width,$height,$params=null,$flashvars=null,$atts=null,$alt=''){
2252
    global $lang;
2253
2254
    $out = '';
2255
2256
    // prepare the object attributes
2257
    if(is_null($atts)) $atts = array();
2258
    $atts['width']  = (int) $width;
2259
    $atts['height'] = (int) $height;
2260
    if(!$atts['width'])  $atts['width']  = 425;
2261
    if(!$atts['height']) $atts['height'] = 350;
2262
2263
    // add object attributes for standard compliant browsers
2264
    $std = $atts;
2265
    $std['type'] = 'application/x-shockwave-flash';
2266
    $std['data'] = $swf;
2267
2268
    // add object attributes for IE
2269
    $ie  = $atts;
2270
    $ie['classid'] = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000';
2271
2272
    // open object (with conditional comments)
2273
    $out .= '<!--[if !IE]> -->'.NL;
2274
    $out .= '<object '.buildAttributes($std).'>'.NL;
2275
    $out .= '<!-- <![endif]-->'.NL;
2276
    $out .= '<!--[if IE]>'.NL;
2277
    $out .= '<object '.buildAttributes($ie).'>'.NL;
2278
    $out .= '    <param name="movie" value="'.hsc($swf).'" />'.NL;
2279
    $out .= '<!--><!-- -->'.NL;
2280
2281
    // print params
2282
    if(is_array($params)) foreach($params as $key => $val){
2283
        $out .= '  <param name="'.hsc($key).'" value="'.hsc($val).'" />'.NL;
2284
    }
2285
2286
    // add flashvars
2287
    if(is_array($flashvars)){
2288
        $out .= '  <param name="FlashVars" value="'.buildURLparams($flashvars).'" />'.NL;
2289
    }
2290
2291
    // alternative content
2292
    if($alt){
2293
        $out .= $alt.NL;
2294
    }else{
2295
        $out .= $lang['noflash'].NL;
2296
    }
2297
2298
    // finish
2299
    $out .= '</object>'.NL;
2300
    $out .= '<!-- <![endif]-->'.NL;
2301
2302
    return $out;
2303
}
2304
2305
/**
2306
 * Prints HTML code for the given tab structure
2307
 *
2308
 * @param array  $tabs        tab structure
2309
 * @param string $current_tab the current tab id
2310
 */
2311
function html_tabs($tabs, $current_tab = null) {
2312
    echo '<ul class="tabs">'.NL;
2313
2314
    foreach($tabs as $id => $tab) {
2315
        html_tab($tab['href'], $tab['caption'], $id === $current_tab);
2316
    }
2317
2318
    echo '</ul>'.NL;
2319
}
2320
2321
/**
2322
 * Prints a single tab
2323
 *
2324
 * @author Kate Arzamastseva <[email protected]>
2325
 * @author Adrian Lang <[email protected]>
2326
 *
2327
 * @param string $href - tab href
2328
 * @param string $caption - tab caption
2329
 * @param boolean $selected - is tab selected
2330
 */
2331
2332
function html_tab($href, $caption, $selected=false) {
2333
    $tab = '<li>';
2334
    if ($selected) {
2335
        $tab .= '<strong>';
2336
    } else {
2337
        $tab .= '<a href="' . hsc($href) . '">';
2338
    }
2339
    $tab .= hsc($caption)
2340
         .  '</' . ($selected ? 'strong' : 'a') . '>'
2341
         .  '</li>'.NL;
2342
    echo $tab;
2343
}
2344
2345
/**
2346
 * Display size change
2347
 *
2348
 * @param int $sizechange - size of change in Bytes
2349
 * @param Doku_Form $form - form to add elements to
2350
 */
2351
2352
function html_sizechange($sizechange, Doku_Form $form) {
2353
    if(isset($sizechange)) {
2354
        $class = 'sizechange';
2355
        $value = filesize_h(abs($sizechange));
2356
        if($sizechange > 0) {
2357
            $class .= ' positive';
2358
            $value = '+' . $value;
2359
        } elseif($sizechange < 0) {
2360
            $class .= ' negative';
2361
            $value = '-' . $value;
2362
        } else {
2363
            $value = '±' . $value;
2364
        }
2365
        $form->addElement(form_makeOpenTag('span', array('class' => $class)));
2366
        $form->addElement($value);
2367
        $form->addElement(form_makeCloseTag('span'));
2368
    }
2369
}
2370