Failed Conditions
Push — master ( a3965d...1362c8 )
by Andreas
04:15
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('DOKU_INC')) die('meh.');
10
if(!defined('NL')) define('NL',"\n");
11
if (!defined('SEC_EDIT_PATTERN')) {
12
    define('SEC_EDIT_PATTERN', '#<!-- EDIT({.*?}) -->#');
13
}
14
15
16
/**
17
 * Convenience function to quickly build a wikilink
18
 *
19
 * @author Andreas Gohr <[email protected]>
20
 * @param string  $id      id of the target page
21
 * @param string  $name    the name of the link, i.e. the text that is displayed
22
 * @param string|array  $search  search string(s) that shall be highlighted in the target page
23
 * @return string the HTML code of the link
24
 */
25
function html_wikilink($id,$name=null,$search=''){
26
    /** @var Doku_Renderer_xhtml $xhtml_renderer */
27
    static $xhtml_renderer = null;
28
    if(is_null($xhtml_renderer)){
29
        $xhtml_renderer = p_get_renderer('xhtml');
30
    }
31
32
    return $xhtml_renderer->internallink($id,$name,$search,true,'navigation');
33
}
34
35
/**
36
 * The loginform
37
 *
38
 * @author   Andreas Gohr <[email protected]>
39
 *
40
 * @param bool $svg Whether to show svg icons in the register and resendpwd links or not
41
 */
42
function html_login($svg = false){
43
    global $lang;
44
    global $conf;
45
    global $ID;
46
    global $INPUT;
47
48
    print p_locale_xhtml('login');
49
    print '<div class="centeralign">'.NL;
50
    $form = new Doku_Form(array('id' => 'dw__login'));
51
    $form->startFieldset($lang['btn_login']);
52
    $form->addHidden('id', $ID);
53
    $form->addHidden('do', 'login');
54
    $form->addElement(form_makeTextField('u', ((!$INPUT->bool('http_credentials')) ? $INPUT->str('u') : ''), $lang['user'], 'focus__this', 'block'));
55
    $form->addElement(form_makePasswordField('p', $lang['pass'], '', 'block'));
56
    if($conf['rememberme']) {
57
        $form->addElement(form_makeCheckboxField('r', '1', $lang['remember'], 'remember__me', 'simple'));
58
    }
59
    $form->addElement(form_makeButton('submit', '', $lang['btn_login']));
60
    $form->endFieldset();
61
62
    if(actionOK('register')){
63
        $registerLink = (new \dokuwiki\Menu\Item\Register())->asHtmlLink('', $svg);
64
        $form->addElement('<p>'.$lang['reghere'].': '. $registerLink .'</p>');
65
    }
66
67
    if (actionOK('resendpwd')) {
68
        $resendPwLink = (new \dokuwiki\Menu\Item\Resendpwd())->asHtmlLink('', $svg);
69
        $form->addElement('<p>'.$lang['pwdforget'].': '. $resendPwLink .'</p>');
70
    }
71
72
    html_form('login', $form);
73
    print '</div>'.NL;
74
}
75
76
77
/**
78
 * Denied page content
79
 *
80
 * @return string html
81
 */
82
function html_denied() {
83
    print p_locale_xhtml('denied');
84
85
    if(empty($_SERVER['REMOTE_USER'])){
86
        html_login();
87
    }
88
}
89
90
/**
91
 * inserts section edit buttons if wanted or removes the markers
92
 *
93
 * @author Andreas Gohr <[email protected]>
94
 *
95
 * @param string $text
96
 * @param bool   $show show section edit buttons?
97
 * @return string
98
 */
99
function html_secedit($text,$show=true){
100
    global $INFO;
101
102
    if(!$INFO['writable'] || !$show || $INFO['rev']){
103
        return preg_replace(SEC_EDIT_PATTERN,'',$text);
104
    }
105
106
    return preg_replace_callback(SEC_EDIT_PATTERN,
107
                'html_secedit_button', $text);
108
}
109
110
/**
111
 * prepares section edit button data for event triggering
112
 * used as a callback in html_secedit
113
 *
114
 * @author Andreas Gohr <[email protected]>
115
 *
116
 * @param array $matches matches with regexp
117
 * @return string
118
 * @triggers HTML_SECEDIT_BUTTON
119
 */
120
function html_secedit_button($matches){
121
    $json = htmlspecialchars_decode($matches[1], ENT_QUOTES);
122
    $data = json_decode($json, true);
123
    if ($data == NULL) {
124
        return;
125
    }
126
    $data ['target'] = strtolower($data['target']);
127
    $data ['hid'] = strtolower($data['hid']);
128
129
    return trigger_event('HTML_SECEDIT_BUTTON', $data,
130
                         'html_secedit_get_button');
131
}
132
133
/**
134
 * prints a section editing button
135
 * used as default action form HTML_SECEDIT_BUTTON
136
 *
137
 * @author Adrian Lang <[email protected]>
138
 *
139
 * @param array $data name, section id and target
140
 * @return string html
141
 */
142
function html_secedit_get_button($data) {
143
    global $ID;
144
    global $INFO;
145
146
    if (!isset($data['name']) || $data['name'] === '') return '';
147
148
    $name = $data['name'];
149
    unset($data['name']);
150
151
    $secid = $data['secid'];
152
    unset($data['secid']);
153
154
    return "<div class='secedit editbutton_" . $data['target'] .
155
                       " editbutton_" . $secid . "'>" .
156
           html_btn('secedit', $ID, '',
157
                    array_merge(array('do'  => 'edit',
158
                                      'rev' => $INFO['lastmod'],
159
                                      'summary' => '['.$name.'] '), $data),
160
                    'post', $name) . '</div>';
161
}
162
163
/**
164
 * Just the back to top button (in its own form)
165
 *
166
 * @author Andreas Gohr <[email protected]>
167
 *
168
 * @return string html
169
 */
170
function html_topbtn(){
171
    global $lang;
172
173
    $ret  = '<a class="nolink" href="#dokuwiki__top"><button class="button" onclick="window.scrollTo(0, 0)" title="'.$lang['btn_top'].'">'.$lang['btn_top'].'</button></a>';
174
175
    return $ret;
176
}
177
178
/**
179
 * Displays a button (using its own form)
180
 * If tooltip exists, the access key tooltip is replaced.
181
 *
182
 * @author Andreas Gohr <[email protected]>
183
 *
184
 * @param string         $name
185
 * @param string         $id
186
 * @param string         $akey   access key
187
 * @param string[] $params key-value pairs added as hidden inputs
188
 * @param string         $method
189
 * @param string         $tooltip
190
 * @param bool|string    $label  label text, false: lookup btn_$name in localization
191
 * @param string         $svg (optional) svg code, inserted into the button
192
 * @return string
193
 */
194
function html_btn($name, $id, $akey, $params, $method='get', $tooltip='', $label=false, $svg=null){
195
    global $conf;
196
    global $lang;
197
198
    if (!$label)
199
        $label = $lang['btn_'.$name];
200
201
    $ret = '';
202
203
    //filter id (without urlencoding)
204
    $id = idfilter($id,false);
205
206
    //make nice URLs even for buttons
207
    if($conf['userewrite'] == 2){
208
        $script = DOKU_BASE.DOKU_SCRIPT.'/'.$id;
209
    }elseif($conf['userewrite']){
210
        $script = DOKU_BASE.$id;
211
    }else{
212
        $script = DOKU_BASE.DOKU_SCRIPT;
213
        $params['id'] = $id;
214
    }
215
216
    $ret .= '<form class="button btn_'.$name.'" method="'.$method.'" action="'.$script.'"><div class="no">';
217
218
    if(is_array($params)){
219
        foreach($params as $key => $val) {
220
            $ret .= '<input type="hidden" name="'.$key.'" ';
221
            $ret .= 'value="'.hsc($val).'" />';
222
        }
223
    }
224
225
    if ($tooltip!='') {
226
        $tip = hsc($tooltip);
227
    }else{
228
        $tip = hsc($label);
229
    }
230
231
    $ret .= '<button type="submit" ';
232
    if($akey){
233
        $tip .= ' ['.strtoupper($akey).']';
234
        $ret .= 'accesskey="'.$akey.'" ';
235
    }
236
    $ret .= 'title="'.$tip.'">';
237
    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...
238
        $ret .= '<span>' . hsc($label) . '</span>';
239
        $ret .= inlineSVG($svg);
240
    } else {
241
        $ret .= hsc($label);
242
    }
243
    $ret .= '</button>';
244
    $ret .= '</div></form>';
245
246
    return $ret;
247
}
248
/**
249
 * show a revision warning
250
 *
251
 * @author Szymon Olewniczak <[email protected]>
252
 */
253
function html_showrev() {
254
    print p_locale_xhtml('showrev');
255
}
256
257
/**
258
 * Show a wiki page
259
 *
260
 * @author Andreas Gohr <[email protected]>
261
 *
262
 * @param null|string $txt wiki text or null for showing $ID
263
 */
264
function html_show($txt=null){
265
    global $ID;
266
    global $REV;
267
    global $HIGH;
268
    global $INFO;
269
    global $DATE_AT;
270
    //disable section editing for old revisions or in preview
271
    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...
272
        $secedit = false;
273
    }else{
274
        $secedit = true;
275
    }
276
277
    if (!is_null($txt)){
278
        //PreviewHeader
279
        echo '<br id="scroll__here" />';
280
        echo p_locale_xhtml('preview');
281
        echo '<div class="preview"><div class="pad">';
282
        $html = html_secedit(p_render('xhtml',p_get_instructions($txt),$info),$secedit);
283
        if($INFO['prependTOC']) $html = tpl_toc(true).$html;
284
        echo $html;
285
        echo '<div class="clearer"></div>';
286
        echo '</div></div>';
287
288
    }else{
289
        if ($REV||$DATE_AT){
290
            $data = array('rev' => &$REV, 'date_at' => &$DATE_AT);
291
            trigger_event('HTML_SHOWREV_OUTPUT', $data, 'html_showrev');
292
        }
293
        $html = p_wiki_xhtml($ID,$REV,true,$DATE_AT);
294
        $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...
295
        if($INFO['prependTOC']) $html = tpl_toc(true).$html;
296
        $html = html_hilight($html,$HIGH);
297
        echo $html;
298
    }
299
}
300
301
/**
302
 * ask the user about how to handle an exisiting draft
303
 *
304
 * @author Andreas Gohr <[email protected]>
305
 */
306
function html_draft(){
307
    global $INFO;
308
    global $ID;
309
    global $lang;
310
    $draft = unserialize(io_readFile($INFO['draft'],false));
311
    $text  = cleanText(con($draft['prefix'],$draft['text'],$draft['suffix'],true));
312
313
    print p_locale_xhtml('draft');
314
    html_diff($text, false);
315
    $form = new Doku_Form(array('id' => 'dw__editform'));
316
    $form->addHidden('id', $ID);
317
    $form->addHidden('date', $draft['date']);
318
    $form->addHidden('wikitext', $text);
319
    $form->addElement(form_makeOpenTag('div', array('id'=>'draft__status')));
320
    $form->addElement($lang['draftdate'].' '. dformat(filemtime($INFO['draft'])));
321
    $form->addElement(form_makeCloseTag('div'));
322
    $form->addElement(form_makeButton('submit', 'recover', $lang['btn_recover'], array('tabindex'=>'1')));
323
    $form->addElement(form_makeButton('submit', 'draftdel', $lang['btn_draftdel'], array('tabindex'=>'2')));
324
    $form->addElement(form_makeButton('submit', 'show', $lang['btn_cancel'], array('tabindex'=>'3')));
325
    html_form('draft', $form);
326
}
327
328
/**
329
 * Highlights searchqueries in HTML code
330
 *
331
 * @author Andreas Gohr <[email protected]>
332
 * @author Harry Fuecks <[email protected]>
333
 *
334
 * @param string $html
335
 * @param array|string $phrases
336
 * @return string html
337
 */
338
function html_hilight($html,$phrases){
339
    $phrases = (array) $phrases;
340
    $phrases = array_map('preg_quote_cb', $phrases);
341
    $phrases = array_map('ft_snippet_re_preprocess', $phrases);
342
    $phrases = array_filter($phrases);
343
    $regex = join('|',$phrases);
344
345
    if ($regex === '') return $html;
346
    if (!utf8_check($regex)) return $html;
347
    $html = @preg_replace_callback("/((<[^>]*)|$regex)/ui",'html_hilight_callback',$html);
348
    return $html;
349
}
350
351
/**
352
 * Callback used by html_hilight()
353
 *
354
 * @author Harry Fuecks <[email protected]>
355
 *
356
 * @param array $m matches
357
 * @return string html
358
 */
359
function html_hilight_callback($m) {
360
    $hlight = unslash($m[0]);
361
    if ( !isset($m[2])) {
362
        $hlight = '<span class="search_hit">'.$hlight.'</span>';
363
    }
364
    return $hlight;
365
}
366
367
/**
368
 * Display error on locked pages
369
 *
370
 * @author Andreas Gohr <[email protected]>
371
 */
372
function html_locked(){
373
    global $ID;
374
    global $conf;
375
    global $lang;
376
    global $INFO;
377
378
    $locktime = filemtime(wikiLockFN($ID));
379
    $expire = dformat($locktime + $conf['locktime']);
380
    $min    = round(($conf['locktime'] - (time() - $locktime) )/60);
381
382
    print p_locale_xhtml('locked');
383
    print '<ul>';
384
    print '<li><div class="li"><strong>'.$lang['lockedby'].'</strong> '.editorinfo($INFO['locked']).'</div></li>';
385
    print '<li><div class="li"><strong>'.$lang['lockexpire'].'</strong> '.$expire.' ('.$min.' min)</div></li>';
386
    print '</ul>';
387
}
388
389
/**
390
 * list old revisions
391
 *
392
 * @author Andreas Gohr <[email protected]>
393
 * @author Ben Coburn <[email protected]>
394
 * @author Kate Arzamastseva <[email protected]>
395
 *
396
 * @param int $first skip the first n changelog lines
397
 * @param bool|string $media_id id of media, or false for current page
398
 */
399
function html_revisions($first=0, $media_id = false){
400
    global $ID;
401
    global $INFO;
402
    global $conf;
403
    global $lang;
404
    $id = $ID;
405
    if ($media_id) {
406
        $id = $media_id;
407
        $changelog = new MediaChangeLog($id);
408
    } else {
409
        $changelog = new PageChangeLog($id);
410
    }
411
412
    /* we need to get one additional log entry to be able to
413
     * decide if this is the last page or is there another one.
414
     * see html_recent()
415
     */
416
417
    $revisions = $changelog->getRevisions($first, $conf['recent']+1);
418
419
    if(count($revisions)==0 && $first!=0){
420
        $first=0;
421
        $revisions = $changelog->getRevisions($first, $conf['recent']+1);
422
    }
423
    $hasNext = false;
424
    if (count($revisions)>$conf['recent']) {
425
        $hasNext = true;
426
        array_pop($revisions); // remove extra log entry
427
    }
428
429
    if (!$media_id) print p_locale_xhtml('revisions');
430
431
    $params = array('id' => 'page__revisions', 'class' => 'changes');
432
    if($media_id) {
433
        $params['action'] = media_managerURL(array('image' => $media_id), '&');
434
    }
435
436
    if(!$media_id) {
437
        $exists = $INFO['exists'];
438
        $display_name = useHeading('navigation') ? hsc(p_get_first_heading($id)) : $id;
439
        if(!$display_name) {
440
            $display_name = $id;
441
        }
442
    } else {
443
        $exists = file_exists(mediaFN($id));
444
        $display_name = $id;
445
    }
446
447
    $form = new Doku_Form($params);
448
    $form->addElement(form_makeOpenTag('ul'));
449
450
    if($exists && $first == 0) {
451
        $minor = false;
452
        if($media_id) {
453
            $date = dformat(@filemtime(mediaFN($id)));
454
            $href = media_managerURL(array('image' => $id, 'tab_details' => 'view'), '&');
455
456
            $changelog->setChunkSize(1024);
457
            $revinfo = $changelog->getRevisionInfo(@filemtime(fullpath(mediaFN($id))));
458
459
            $summary = $revinfo['sum'];
460
            if($revinfo['user']) {
461
                $editor = $revinfo['user'];
462
            } else {
463
                $editor = $revinfo['ip'];
464
            }
465
            $sizechange = $revinfo['sizechange'];
466
        } else {
467
            $date = dformat($INFO['lastmod']);
468
            if(isset($INFO['meta']) && isset($INFO['meta']['last_change'])) {
469
                if($INFO['meta']['last_change']['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT) {
470
                    $minor = true;
471
                }
472
                if(isset($INFO['meta']['last_change']['sizechange'])) {
473
                    $sizechange = $INFO['meta']['last_change']['sizechange'];
474
                } else {
475
                    $sizechange = null;
476
                }
477
            }
478
            $pagelog = new PageChangeLog($ID);
479
            $latestrev = $pagelog->getRevisions(-1, 1);
480
            $latestrev = array_pop($latestrev);
481
            $href = wl($id,"rev=$latestrev",false,'&');
482
            $summary = $INFO['sum'];
483
            $editor = $INFO['editor'];
484
        }
485
486
        $form->addElement(form_makeOpenTag('li', array('class' => ($minor ? 'minor' : ''))));
487
        $form->addElement(form_makeOpenTag('div', array('class' => 'li')));
488
        $form->addElement(form_makeTag('input', array(
489
                        'type' => 'checkbox',
490
                        'name' => 'rev2[]',
491
                        'value' => 'current')));
492
493
        $form->addElement(form_makeOpenTag('span', array('class' => 'date')));
494
        $form->addElement($date);
495
        $form->addElement(form_makeCloseTag('span'));
496
497
        $form->addElement('<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />');
498
499
        $form->addElement(form_makeOpenTag('a', array(
500
                        'class' => 'wikilink1',
501
                        'href'  => $href)));
502
        $form->addElement($display_name);
503
        $form->addElement(form_makeCloseTag('a'));
504
505
        if ($media_id) $form->addElement(form_makeOpenTag('div'));
506
507
        if($summary) {
508
            $form->addElement(form_makeOpenTag('span', array('class' => 'sum')));
509
            if(!$media_id) $form->addElement(' – ');
510
            $form->addElement('<bdi>' . hsc($summary) . '</bdi>');
511
            $form->addElement(form_makeCloseTag('span'));
512
        }
513
514
        $form->addElement(form_makeOpenTag('span', array('class' => 'user')));
515
        $form->addElement((empty($editor))?('('.$lang['external_edit'].')'):'<bdi>'.editorinfo($editor).'</bdi>');
516
        $form->addElement(form_makeCloseTag('span'));
517
518
        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...
519
520
        $form->addElement('('.$lang['current'].')');
521
522
        if ($media_id) $form->addElement(form_makeCloseTag('div'));
523
524
        $form->addElement(form_makeCloseTag('div'));
525
        $form->addElement(form_makeCloseTag('li'));
526
    }
527
528
    foreach($revisions as $rev) {
529
        $date = dformat($rev);
530
        $info = $changelog->getRevisionInfo($rev);
531
        if($media_id) {
532
            $exists = file_exists(mediaFN($id, $rev));
533
        } else {
534
            $exists = page_exists($id, $rev);
535
        }
536
537
        $class = '';
538
        if($info['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT) {
539
            $class = 'minor';
540
        }
541
        $form->addElement(form_makeOpenTag('li', array('class' => $class)));
542
        $form->addElement(form_makeOpenTag('div', array('class' => 'li')));
543
        if($exists){
544
            $form->addElement(form_makeTag('input', array(
545
                            'type' => 'checkbox',
546
                            'name' => 'rev2[]',
547
                            'value' => $rev)));
548
        }else{
549
            $form->addElement('<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />');
550
        }
551
552
        $form->addElement(form_makeOpenTag('span', array('class' => 'date')));
553
        $form->addElement($date);
554
        $form->addElement(form_makeCloseTag('span'));
555
556
        if($exists){
557
            if (!$media_id) {
558
                $href = wl($id,"rev=$rev,do=diff", false, '&');
559
            } else {
560
                $href = media_managerURL(array('image' => $id, 'rev' => $rev, 'mediado' => 'diff'), '&');
561
            }
562
            $form->addElement(form_makeOpenTag('a', array(
563
                            'class' => 'diff_link',
564
                            'href' => $href)));
565
            $form->addElement(form_makeTag('img', array(
566
                            'src'    => DOKU_BASE.'lib/images/diff.png',
567
                            'width'  => 15,
568
                            'height' => 11,
569
                            'title'  => $lang['diff'],
570
                            'alt'    => $lang['diff'])));
571
            $form->addElement(form_makeCloseTag('a'));
572
573
            if (!$media_id) {
574
                $href = wl($id,"rev=$rev",false,'&');
575
            } else {
576
                $href = media_managerURL(array('image' => $id, 'tab_details' => 'view', 'rev' => $rev), '&');
577
            }
578
            $form->addElement(form_makeOpenTag('a', array(
579
                            'class' => 'wikilink1',
580
                            'href' => $href)));
581
            $form->addElement($display_name);
582
            $form->addElement(form_makeCloseTag('a'));
583
        }else{
584
            $form->addElement('<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />');
585
            $form->addElement($display_name);
586
        }
587
588
        if ($media_id) $form->addElement(form_makeOpenTag('div'));
589
590
        if ($info['sum']) {
591
            $form->addElement(form_makeOpenTag('span', array('class' => 'sum')));
592
            if(!$media_id) $form->addElement(' – ');
593
            $form->addElement('<bdi>'.hsc($info['sum']).'</bdi>');
594
            $form->addElement(form_makeCloseTag('span'));
595
        }
596
597
        $form->addElement(form_makeOpenTag('span', array('class' => 'user')));
598
        if($info['user']){
599
            $form->addElement('<bdi>'.editorinfo($info['user']).'</bdi>');
600
            if(auth_ismanager()){
601
                $form->addElement(' <bdo dir="ltr">('.$info['ip'].')</bdo>');
602
            }
603
        }else{
604
            $form->addElement('<bdo dir="ltr">'.$info['ip'].'</bdo>');
605
        }
606
        $form->addElement(form_makeCloseTag('span'));
607
608
        html_sizechange($info['sizechange'], $form);
609
610
        if ($media_id) $form->addElement(form_makeCloseTag('div'));
611
612
        $form->addElement(form_makeCloseTag('div'));
613
        $form->addElement(form_makeCloseTag('li'));
614
    }
615
    $form->addElement(form_makeCloseTag('ul'));
616
    if (!$media_id) {
617
        $form->addElement(form_makeButton('submit', 'diff', $lang['diff2']));
618
    } else {
619
        $form->addHidden('mediado', 'diff');
620
        $form->addElement(form_makeButton('submit', '', $lang['diff2']));
621
    }
622
    html_form('revisions', $form);
623
624
    print '<div class="pagenav">';
625
    $last = $first + $conf['recent'];
626
    if ($first > 0) {
627
        $first -= $conf['recent'];
628
        if ($first < 0) $first = 0;
629
        print '<div class="pagenav-prev">';
630
        if ($media_id) {
631
            print html_btn('newer',$media_id,"p",media_managerURL(array('first' => $first), '&amp;', false, true));
632
        } else {
633
            print html_btn('newer',$id,"p",array('do' => 'revisions', 'first' => $first));
634
        }
635
        print '</div>';
636
    }
637
    if ($hasNext) {
638
        print '<div class="pagenav-next">';
639
        if ($media_id) {
640
            print html_btn('older',$media_id,"n",media_managerURL(array('first' => $last), '&amp;', false, true));
641
        } else {
642
            print html_btn('older',$id,"n",array('do' => 'revisions', 'first' => $last));
643
        }
644
        print '</div>';
645
    }
646
    print '</div>';
647
648
}
649
650
/**
651
 * display recent changes
652
 *
653
 * @author Andreas Gohr <[email protected]>
654
 * @author Matthias Grimm <[email protected]>
655
 * @author Ben Coburn <[email protected]>
656
 * @author Kate Arzamastseva <[email protected]>
657
 *
658
 * @param int $first
659
 * @param string $show_changes
660
 */
661
function html_recent($first = 0, $show_changes = 'both') {
662
    global $conf;
663
    global $lang;
664
    global $ID;
665
    /* we need to get one additionally log entry to be able to
666
     * decide if this is the last page or is there another one.
667
     * This is the cheapest solution to get this information.
668
     */
669
    $flags = 0;
670
    if($show_changes == 'mediafiles' && $conf['mediarevisions']) {
671
        $flags = RECENTS_MEDIA_CHANGES;
672
    } elseif($show_changes == 'pages') {
673
        $flags = 0;
674
    } elseif($conf['mediarevisions']) {
675
        $show_changes = 'both';
676
        $flags = RECENTS_MEDIA_PAGES_MIXED;
677
    }
678
679
    $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...
680
    if(count($recents) == 0 && $first != 0) {
681
        $first = 0;
682
        $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...
683
    }
684
    $hasNext = false;
685
    if(count($recents) > $conf['recent']) {
686
        $hasNext = true;
687
        array_pop($recents); // remove extra log entry
688
    }
689
690
    print p_locale_xhtml('recent');
691
692
    if(getNS($ID) != '') {
693
        print '<div class="level1"><p>' . sprintf($lang['recent_global'], getNS($ID), wl('', 'do=recent')) . '</p></div>';
694
    }
695
696
    $form = new Doku_Form(array('id' => 'dw__recent', 'method' => 'GET', 'class' => 'changes'));
697
    $form->addHidden('sectok', null);
698
    $form->addHidden('do', 'recent');
699
    $form->addHidden('id', $ID);
700
701
    if($conf['mediarevisions']) {
702
        $form->addElement('<div class="changeType">');
703
        $form->addElement(form_makeListboxField(
704
                    'show_changes',
705
                    array(
706
                        'pages'      => $lang['pages_changes'],
707
                        'mediafiles' => $lang['media_changes'],
708
                        'both'       => $lang['both_changes']
709
                    ),
710
                    $show_changes,
711
                    $lang['changes_type'],
712
                    '', '',
713
                    array('class' => 'quickselect')));
714
715
        $form->addElement(form_makeButton('submit', 'recent', $lang['btn_apply']));
716
        $form->addElement('</div>');
717
    }
718
719
    $form->addElement(form_makeOpenTag('ul'));
720
721
    foreach($recents as $recent) {
722
        $date = dformat($recent['date']);
723
724
        $class = '';
725
        if($recent['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT) {
726
            $class = 'minor';
727
        }
728
        $form->addElement(form_makeOpenTag('li', array('class' => $class)));
729
        $form->addElement(form_makeOpenTag('div', array('class' => 'li')));
730
731
        if(!empty($recent['media'])) {
732
            $form->addElement(media_printicon($recent['id']));
733
        } else {
734
            $icon = DOKU_BASE . 'lib/images/fileicons/file.png';
735
            $form->addElement('<img src="' . $icon . '" alt="' . $recent['id'] . '" class="icon" />');
736
        }
737
738
        $form->addElement(form_makeOpenTag('span', array('class' => 'date')));
739
        $form->addElement($date);
740
        $form->addElement(form_makeCloseTag('span'));
741
742
        $diff = false;
743
        $href = '';
744
745
        if(!empty($recent['media'])) {
746
            $changelog = new MediaChangeLog($recent['id']);
747
            $revs = $changelog->getRevisions(0, 1);
748
            $diff = (count($revs) && file_exists(mediaFN($recent['id'])));
749
            if($diff) {
750
                $href = media_managerURL(array(
751
                                            'tab_details' => 'history',
752
                                            'mediado' => 'diff',
753
                                            'image' => $recent['id'],
754
                                            'ns' => getNS($recent['id'])
755
                                        ), '&');
756
            }
757
        } else {
758
            $href = wl($recent['id'], "do=diff", false, '&');
759
        }
760
761
        if(!empty($recent['media']) && !$diff) {
762
            $form->addElement('<img src="' . DOKU_BASE . 'lib/images/blank.gif" width="15" height="11" alt="" />');
763
        } else {
764
            $form->addElement(form_makeOpenTag('a', array('class' => 'diff_link', 'href' => $href)));
765
            $form->addElement(form_makeTag('img', array(
766
                            'src'    => DOKU_BASE . 'lib/images/diff.png',
767
                            'width'  => 15,
768
                            'height' => 11,
769
                            'title'  => $lang['diff'],
770
                            'alt'    => $lang['diff']
771
                        )));
772
            $form->addElement(form_makeCloseTag('a'));
773
        }
774
775
        if(!empty($recent['media'])) {
776
            $href = media_managerURL(array('tab_details' => 'history', 'image' => $recent['id'], 'ns' => getNS($recent['id'])), '&');
777
        } else {
778
            $href = wl($recent['id'], "do=revisions", false, '&');
779
        }
780
        $form->addElement(form_makeOpenTag('a', array(
781
                        'class' => 'revisions_link',
782
                        'href'  => $href)));
783
        $form->addElement(form_makeTag('img', array(
784
                        'src'    => DOKU_BASE . 'lib/images/history.png',
785
                        'width'  => 12,
786
                        'height' => 14,
787
                        'title'  => $lang['btn_revs'],
788
                        'alt'    => $lang['btn_revs']
789
                    )));
790
        $form->addElement(form_makeCloseTag('a'));
791
792
        if(!empty($recent['media'])) {
793
            $href = media_managerURL(array('tab_details' => 'view', 'image' => $recent['id'], 'ns' => getNS($recent['id'])), '&');
794
            $class = file_exists(mediaFN($recent['id'])) ? 'wikilink1' : 'wikilink2';
795
            $form->addElement(form_makeOpenTag('a', array(
796
                        'class' => $class,
797
                        'href'  => $href)));
798
            $form->addElement($recent['id']);
799
            $form->addElement(form_makeCloseTag('a'));
800
        } else {
801
            $form->addElement(html_wikilink(':' . $recent['id'], useHeading('navigation') ? null : $recent['id']));
802
        }
803
        $form->addElement(form_makeOpenTag('span', array('class' => 'sum')));
804
        $form->addElement(' – ' . hsc($recent['sum']));
805
        $form->addElement(form_makeCloseTag('span'));
806
807
        $form->addElement(form_makeOpenTag('span', array('class' => 'user')));
808
        if($recent['user']) {
809
            $form->addElement('<bdi>' . editorinfo($recent['user']) . '</bdi>');
810
            if(auth_ismanager()) {
811
                $form->addElement(' <bdo dir="ltr">(' . $recent['ip'] . ')</bdo>');
812
            }
813
        } else {
814
            $form->addElement('<bdo dir="ltr">' . $recent['ip'] . '</bdo>');
815
        }
816
        $form->addElement(form_makeCloseTag('span'));
817
818
        html_sizechange($recent['sizechange'], $form);
819
820
        $form->addElement(form_makeCloseTag('div'));
821
        $form->addElement(form_makeCloseTag('li'));
822
    }
823
    $form->addElement(form_makeCloseTag('ul'));
824
825
    $form->addElement(form_makeOpenTag('div', array('class' => 'pagenav')));
826
    $last = $first + $conf['recent'];
827
    if($first > 0) {
828
        $first -= $conf['recent'];
829
        if($first < 0) $first = 0;
830
        $form->addElement(form_makeOpenTag('div', array('class' => 'pagenav-prev')));
831
        $form->addElement(form_makeOpenTag('button', array(
832
                        'type'      => 'submit',
833
                        'name'      => 'first[' . $first . ']',
834
                        'accesskey' => 'n',
835
                        'title'     => $lang['btn_newer'] . ' [N]',
836
                        'class'     => 'button show'
837
                    )));
838
        $form->addElement($lang['btn_newer']);
839
        $form->addElement(form_makeCloseTag('button'));
840
        $form->addElement(form_makeCloseTag('div'));
841
    }
842
    if($hasNext) {
843
        $form->addElement(form_makeOpenTag('div', array('class' => 'pagenav-next')));
844
        $form->addElement(form_makeOpenTag('button', array(
845
                        'type'      => 'submit',
846
                        'name'      => 'first[' . $last . ']',
847
                        'accesskey' => 'p',
848
                        'title'     => $lang['btn_older'] . ' [P]',
849
                        'class'     => 'button show'
850
                    )));
851
        $form->addElement($lang['btn_older']);
852
        $form->addElement(form_makeCloseTag('button'));
853
        $form->addElement(form_makeCloseTag('div'));
854
    }
855
    $form->addElement(form_makeCloseTag('div'));
856
    html_form('recent', $form);
857
}
858
859
/**
860
 * Display page index
861
 *
862
 * @author Andreas Gohr <[email protected]>
863
 *
864
 * @param string $ns
865
 */
866
function html_index($ns){
867
    global $conf;
868
    global $ID;
869
    $ns  = cleanID($ns);
870
    if(empty($ns)){
871
        $ns = getNS($ID);
872
        if($ns === false) $ns ='';
873
    }
874
    $ns  = utf8_encodeFN(str_replace(':','/',$ns));
875
876
    echo p_locale_xhtml('index');
877
    echo '<div id="index__tree" class="index__tree">';
878
879
    $data = array();
880
    search($data,$conf['datadir'],'search_index',array('ns' => $ns));
881
    echo html_buildlist($data,'idx','html_list_index','html_li_index');
882
883
    echo '</div>';
884
}
885
886
/**
887
 * Index item formatter
888
 *
889
 * User function for html_buildlist()
890
 *
891
 * @author Andreas Gohr <[email protected]>
892
 *
893
 * @param array $item
894
 * @return string
895
 */
896
function html_list_index($item){
897
    global $ID, $conf;
898
899
    // prevent searchbots needlessly following links
900
    $nofollow = ($ID != $conf['start'] || $conf['sitemap']) ? ' rel="nofollow"' : '';
901
902
    $ret = '';
903
    $base = ':'.$item['id'];
904
    $base = substr($base,strrpos($base,':')+1);
905
    if($item['type']=='d'){
906
        // FS#2766, no need for search bots to follow namespace links in the index
907
        $ret .= '<a href="'.wl($ID,'idx='.rawurlencode($item['id'])).'" title="' . $item['id'] . '" class="idx_dir"' . $nofollow . '><strong>';
908
        $ret .= $base;
909
        $ret .= '</strong></a>';
910
    }else{
911
        // default is noNSorNS($id), but we want noNS($id) when useheading is off FS#2605
912
        $ret .= html_wikilink(':'.$item['id'], useHeading('navigation') ? null : noNS($item['id']));
913
    }
914
    return $ret;
915
}
916
917
/**
918
 * Index List item
919
 *
920
 * This user function is used in html_buildlist to build the
921
 * <li> tags for namespaces when displaying the page index
922
 * it gives different classes to opened or closed "folders"
923
 *
924
 * @author Andreas Gohr <[email protected]>
925
 *
926
 * @param array $item
927
 * @return string html
928
 */
929
function html_li_index($item){
930
    global $INFO;
931
    global $ACT;
932
933
    $class = '';
934
    $id = '';
935
936
    if($item['type'] == "f"){
937
        // scroll to the current item
938
        if($item['id'] == $INFO['id'] && $ACT == 'index') {
939
            $id = ' id="scroll__here"';
940
            $class = ' bounce';
941
        }
942
        return '<li class="level'.$item['level'].$class.'" '.$id.'>';
943
    }elseif($item['open']){
944
        return '<li class="open">';
945
    }else{
946
        return '<li class="closed">';
947
    }
948
}
949
950
/**
951
 * Default List item
952
 *
953
 * @author Andreas Gohr <[email protected]>
954
 *
955
 * @param array $item
956
 * @return string html
957
 */
958
function html_li_default($item){
959
    return '<li class="level'.$item['level'].'">';
960
}
961
962
/**
963
 * Build an unordered list
964
 *
965
 * Build an unordered list from the given $data array
966
 * Each item in the array has to have a 'level' property
967
 * the item itself gets printed by the given $func user
968
 * function. The second and optional function is used to
969
 * print the <li> tag. Both user function need to accept
970
 * a single item.
971
 *
972
 * Both user functions can be given as array to point to
973
 * a member of an object.
974
 *
975
 * @author Andreas Gohr <[email protected]>
976
 *
977
 * @param array    $data  array with item arrays
978
 * @param string   $class class of ul wrapper
979
 * @param callable $func  callback to print an list item
980
 * @param callable $lifunc callback to the opening li tag
981
 * @param bool     $forcewrapper Trigger building a wrapper ul if the first level is
982
 *                               0 (we have a root object) or 1 (just the root content)
983
 * @return string html of an unordered list
984
 */
985
function html_buildlist($data,$class,$func,$lifunc='html_li_default',$forcewrapper=false){
986
    if (count($data) === 0) {
987
        return '';
988
    }
989
990
    $firstElement = reset($data);
991
    $start_level = $firstElement['level'];
992
    $level = $start_level;
993
    $ret   = '';
994
    $open  = 0;
995
996
    foreach ($data as $item){
997
998
        if( $item['level'] > $level ){
999
            //open new list
1000
            for($i=0; $i<($item['level'] - $level); $i++){
1001
                if ($i) $ret .= "<li class=\"clear\">";
1002
                $ret .= "\n<ul class=\"$class\">\n";
1003
                $open++;
1004
            }
1005
            $level = $item['level'];
1006
1007
        }elseif( $item['level'] < $level ){
1008
            //close last item
1009
            $ret .= "</li>\n";
1010
            while( $level > $item['level'] && $open > 0 ){
1011
                //close higher lists
1012
                $ret .= "</ul>\n</li>\n";
1013
                $level--;
1014
                $open--;
1015
            }
1016
        } elseif ($ret !== '') {
1017
            //close previous item
1018
            $ret .= "</li>\n";
1019
        }
1020
1021
        //print item
1022
        $ret .= call_user_func($lifunc,$item);
1023
        $ret .= '<div class="li">';
1024
1025
        $ret .= call_user_func($func,$item);
1026
        $ret .= '</div>';
1027
    }
1028
1029
    //close remaining items and lists
1030
    $ret .= "</li>\n";
1031
    while($open-- > 0) {
1032
        $ret .= "</ul></li>\n";
1033
    }
1034
1035
    if ($forcewrapper || $start_level < 2) {
1036
        // Trigger building a wrapper ul if the first level is
1037
        // 0 (we have a root object) or 1 (just the root content)
1038
        $ret = "\n<ul class=\"$class\">\n".$ret."</ul>\n";
1039
    }
1040
1041
    return $ret;
1042
}
1043
1044
/**
1045
 * display backlinks
1046
 *
1047
 * @author Andreas Gohr <[email protected]>
1048
 * @author Michael Klier <[email protected]>
1049
 */
1050
function html_backlinks(){
1051
    global $ID;
1052
    global $lang;
1053
1054
    print p_locale_xhtml('backlinks');
1055
1056
    $data = ft_backlinks($ID);
1057
1058
    if(!empty($data)) {
1059
        print '<ul class="idx">';
1060
        foreach($data as $blink){
1061
            print '<li><div class="li">';
1062
            print html_wikilink(':'.$blink,useHeading('navigation')?null:$blink);
1063
            print '</div></li>';
1064
        }
1065
        print '</ul>';
1066
    } else {
1067
        print '<div class="level1"><p>' . $lang['nothingfound'] . '</p></div>';
1068
    }
1069
}
1070
1071
/**
1072
 * Get header of diff HTML
1073
 *
1074
 * @param string $l_rev   Left revisions
1075
 * @param string $r_rev   Right revision
1076
 * @param string $id      Page id, if null $ID is used
1077
 * @param bool   $media   If it is for media files
1078
 * @param bool   $inline  Return the header on a single line
1079
 * @return string[] HTML snippets for diff header
1080
 */
1081
function html_diff_head($l_rev, $r_rev, $id = null, $media = false, $inline = false) {
1082
    global $lang;
1083
    if ($id === null) {
1084
        global $ID;
1085
        $id = $ID;
1086
    }
1087
    $head_separator = $inline ? ' ' : '<br />';
1088
    $media_or_wikiFN = $media ? 'mediaFN' : 'wikiFN';
1089
    $ml_or_wl = $media ? 'ml' : 'wl';
1090
    $l_minor = $r_minor = '';
1091
1092
    if($media) {
1093
        $changelog = new MediaChangeLog($id);
1094
    } else {
1095
        $changelog = new PageChangeLog($id);
1096
    }
1097
    if(!$l_rev){
1098
        $l_head = '&mdash;';
1099
    }else{
1100
        $l_info   = $changelog->getRevisionInfo($l_rev);
1101
        if($l_info['user']){
1102
            $l_user = '<bdi>'.editorinfo($l_info['user']).'</bdi>';
1103
            if(auth_ismanager()) $l_user .= ' <bdo dir="ltr">('.$l_info['ip'].')</bdo>';
1104
        } else {
1105
            $l_user = '<bdo dir="ltr">'.$l_info['ip'].'</bdo>';
1106
        }
1107
        $l_user  = '<span class="user">'.$l_user.'</span>';
1108
        $l_sum   = ($l_info['sum']) ? '<span class="sum"><bdi>'.hsc($l_info['sum']).'</bdi></span>' : '';
1109
        if ($l_info['type']===DOKU_CHANGE_TYPE_MINOR_EDIT) $l_minor = 'class="minor"';
1110
1111
        $l_head_title = ($media) ? dformat($l_rev) : $id.' ['.dformat($l_rev).']';
1112
        $l_head = '<bdi><a class="wikilink1" href="'.$ml_or_wl($id,"rev=$l_rev").'">'.
1113
        $l_head_title.'</a></bdi>'.
1114
        $head_separator.$l_user.' '.$l_sum;
1115
    }
1116
1117
    if($r_rev){
1118
        $r_info   = $changelog->getRevisionInfo($r_rev);
1119
        if($r_info['user']){
1120
            $r_user = '<bdi>'.editorinfo($r_info['user']).'</bdi>';
1121
            if(auth_ismanager()) $r_user .= ' <bdo dir="ltr">('.$r_info['ip'].')</bdo>';
1122
        } else {
1123
            $r_user = '<bdo dir="ltr">'.$r_info['ip'].'</bdo>';
1124
        }
1125
        $r_user = '<span class="user">'.$r_user.'</span>';
1126
        $r_sum  = ($r_info['sum']) ? '<span class="sum"><bdi>'.hsc($r_info['sum']).'</bdi></span>' : '';
1127
        if ($r_info['type']===DOKU_CHANGE_TYPE_MINOR_EDIT) $r_minor = 'class="minor"';
1128
1129
        $r_head_title = ($media) ? dformat($r_rev) : $id.' ['.dformat($r_rev).']';
1130
        $r_head = '<bdi><a class="wikilink1" href="'.$ml_or_wl($id,"rev=$r_rev").'">'.
1131
        $r_head_title.'</a></bdi>'.
1132
        $head_separator.$r_user.' '.$r_sum;
1133
    }elseif($_rev = @filemtime($media_or_wikiFN($id))){
1134
        $_info   = $changelog->getRevisionInfo($_rev);
1135
        if($_info['user']){
1136
            $_user = '<bdi>'.editorinfo($_info['user']).'</bdi>';
1137
            if(auth_ismanager()) $_user .= ' <bdo dir="ltr">('.$_info['ip'].')</bdo>';
1138
        } else {
1139
            $_user = '<bdo dir="ltr">'.$_info['ip'].'</bdo>';
1140
        }
1141
        $_user = '<span class="user">'.$_user.'</span>';
1142
        $_sum  = ($_info['sum']) ? '<span class="sum"><bdi>'.hsc($_info['sum']).'</span></bdi>' : '';
1143
        if ($_info['type']===DOKU_CHANGE_TYPE_MINOR_EDIT) $r_minor = 'class="minor"';
1144
1145
        $r_head_title = ($media) ? dformat($_rev) : $id.' ['.dformat($_rev).']';
1146
        $r_head  = '<bdi><a class="wikilink1" href="'.$ml_or_wl($id).'">'.
1147
        $r_head_title.'</a></bdi> '.
1148
        '('.$lang['current'].')'.
1149
        $head_separator.$_user.' '.$_sum;
1150
    }else{
1151
        $r_head = '&mdash; ('.$lang['current'].')';
1152
    }
1153
1154
    return array($l_head, $r_head, $l_minor, $r_minor);
1155
}
1156
1157
/**
1158
 * Show diff
1159
 * between current page version and provided $text
1160
 * or between the revisions provided via GET or POST
1161
 *
1162
 * @author Andreas Gohr <[email protected]>
1163
 * @param  string $text  when non-empty: compare with this text with most current version
1164
 * @param  bool   $intro display the intro text
1165
 * @param  string $type  type of the diff (inline or sidebyside)
1166
 */
1167
function html_diff($text = '', $intro = true, $type = null) {
1168
    global $ID;
1169
    global $REV;
1170
    global $lang;
1171
    global $INPUT;
1172
    global $INFO;
1173
    $pagelog = new PageChangeLog($ID);
1174
1175
    /*
1176
     * Determine diff type
1177
     */
1178
    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...
1179
        $type = $INPUT->str('difftype');
1180
        if(empty($type)) {
1181
            $type = get_doku_pref('difftype', $type);
1182
            if(empty($type) && $INFO['ismobile']) {
1183
                $type = 'inline';
1184
            }
1185
        }
1186
    }
1187
    if($type != 'inline') $type = 'sidebyside';
1188
1189
    /*
1190
     * Determine requested revision(s)
1191
     */
1192
    // we're trying to be clever here, revisions to compare can be either
1193
    // given as rev and rev2 parameters, with rev2 being optional. Or in an
1194
    // array in rev2.
1195
    $rev1 = $REV;
1196
1197
    $rev2 = $INPUT->ref('rev2');
1198
    if(is_array($rev2)) {
1199
        $rev1 = (int) $rev2[0];
1200
        $rev2 = (int) $rev2[1];
1201
1202
        if(!$rev1) {
1203
            $rev1 = $rev2;
1204
            unset($rev2);
1205
        }
1206
    } else {
1207
        $rev2 = $INPUT->int('rev2');
1208
    }
1209
1210
    /*
1211
     * Determine left and right revision, its texts and the header
1212
     */
1213
    $r_minor = '';
1214
    $l_minor = '';
1215
1216
    if($text) { // compare text to the most current revision
1217
        $l_rev = '';
1218
        $l_text = rawWiki($ID, '');
1219
        $l_head = '<a class="wikilink1" href="' . wl($ID) . '">' .
1220
            $ID . ' ' . dformat((int) @filemtime(wikiFN($ID))) . '</a> ' .
1221
            $lang['current'];
1222
1223
        $r_rev = '';
1224
        $r_text = cleanText($text);
1225
        $r_head = $lang['yours'];
1226
    } else {
1227
        if($rev1 && isset($rev2) && $rev2) { // two specific revisions wanted
1228
            // make sure order is correct (older on the left)
1229
            if($rev1 < $rev2) {
1230
                $l_rev = $rev1;
1231
                $r_rev = $rev2;
1232
            } else {
1233
                $l_rev = $rev2;
1234
                $r_rev = $rev1;
1235
            }
1236
        } elseif($rev1) { // single revision given, compare to current
1237
            $r_rev = '';
1238
            $l_rev = $rev1;
1239
        } else { // no revision was given, compare previous to current
1240
            $r_rev = '';
1241
            $revs = $pagelog->getRevisions(0, 1);
1242
            $l_rev = $revs[0];
1243
            $REV = $l_rev; // store revision back in $REV
1244
        }
1245
1246
        // when both revisions are empty then the page was created just now
1247
        if(!$l_rev && !$r_rev) {
1248
            $l_text = '';
1249
        } else {
1250
            $l_text = rawWiki($ID, $l_rev);
1251
        }
1252
        $r_text = rawWiki($ID, $r_rev);
1253
1254
        list($l_head, $r_head, $l_minor, $r_minor) = html_diff_head($l_rev, $r_rev, null, false, $type == 'inline');
1255
    }
1256
1257
    /*
1258
     * Build navigation
1259
     */
1260
    $l_nav = '';
1261
    $r_nav = '';
1262
    if(!$text) {
1263
        list($l_nav, $r_nav) = html_diff_navigation($pagelog, $type, $l_rev, $r_rev);
1264
    }
1265
    /*
1266
     * Create diff object and the formatter
1267
     */
1268
    $diff = new Diff(explode("\n", $l_text), explode("\n", $r_text));
1269
1270
    if($type == 'inline') {
1271
        $diffformatter = new InlineDiffFormatter();
1272
    } else {
1273
        $diffformatter = new TableDiffFormatter();
1274
    }
1275
    /*
1276
     * Display intro
1277
     */
1278
    if($intro) print p_locale_xhtml('diff');
1279
1280
    /*
1281
     * Display type and exact reference
1282
     */
1283
    if(!$text) {
1284
        ptln('<div class="diffoptions group">');
1285
1286
1287
        $form = new Doku_Form(array('action' => wl()));
1288
        $form->addHidden('id', $ID);
1289
        $form->addHidden('rev2[0]', $l_rev);
1290
        $form->addHidden('rev2[1]', $r_rev);
1291
        $form->addHidden('do', 'diff');
1292
        $form->addElement(
1293
             form_makeListboxField(
1294
                 'difftype',
1295
                 array(
1296
                     'sidebyside' => $lang['diff_side'],
1297
                     'inline' => $lang['diff_inline']
1298
                 ),
1299
                 $type,
1300
                 $lang['diff_type'],
1301
                 '', '',
1302
                 array('class' => 'quickselect')
1303
             )
1304
        );
1305
        $form->addElement(form_makeButton('submit', 'diff', 'Go'));
1306
        $form->printForm();
1307
1308
        ptln('<p>');
1309
        // link to exactly this view FS#2835
1310
        echo html_diff_navigationlink($type, 'difflink', $l_rev, $r_rev ? $r_rev : $INFO['currentrev']);
1311
        ptln('</p>');
1312
1313
        ptln('</div>'); // .diffoptions
1314
    }
1315
1316
    /*
1317
     * Display diff view table
1318
     */
1319
    ?>
1320
    <div class="table">
1321
    <table class="diff diff_<?php echo $type ?>">
1322
1323
        <?php
1324
        //navigation and header
1325
        if($type == 'inline') {
1326
            if(!$text) { ?>
1327
                <tr>
1328
                    <td class="diff-lineheader">-</td>
1329
                    <td class="diffnav"><?php echo $l_nav ?></td>
1330
                </tr>
1331
                <tr>
1332
                    <th class="diff-lineheader">-</th>
1333
                    <th <?php echo $l_minor ?>>
1334
                        <?php echo $l_head ?>
1335
                    </th>
1336
                </tr>
1337
            <?php } ?>
1338
            <tr>
1339
                <td class="diff-lineheader">+</td>
1340
                <td class="diffnav"><?php echo $r_nav ?></td>
1341
            </tr>
1342
            <tr>
1343
                <th class="diff-lineheader">+</th>
1344
                <th <?php echo $r_minor ?>>
1345
                    <?php echo $r_head ?>
1346
                </th>
1347
            </tr>
1348
        <?php } else {
1349
            if(!$text) { ?>
1350
                <tr>
1351
                    <td colspan="2" class="diffnav"><?php echo $l_nav ?></td>
1352
                    <td colspan="2" class="diffnav"><?php echo $r_nav ?></td>
1353
                </tr>
1354
            <?php } ?>
1355
            <tr>
1356
                <th colspan="2" <?php echo $l_minor ?>>
1357
                    <?php echo $l_head ?>
1358
                </th>
1359
                <th colspan="2" <?php echo $r_minor ?>>
1360
                    <?php echo $r_head ?>
1361
                </th>
1362
            </tr>
1363
        <?php }
1364
1365
        //diff view
1366
        echo html_insert_softbreaks($diffformatter->format($diff)); ?>
1367
1368
    </table>
1369
    </div>
1370
<?php
1371
}
1372
1373
/**
1374
 * Create html for revision navigation
1375
 *
1376
 * @param PageChangeLog $pagelog changelog object of current page
1377
 * @param string        $type    inline vs sidebyside
1378
 * @param int           $l_rev   left revision timestamp
1379
 * @param int           $r_rev   right revision timestamp
1380
 * @return string[] html of left and right navigation elements
1381
 */
1382
function html_diff_navigation($pagelog, $type, $l_rev, $r_rev) {
1383
    global $INFO, $ID;
1384
1385
    // last timestamp is not in changelog, retrieve timestamp from metadata
1386
    // note: when page is removed, the metadata timestamp is zero
1387
    if(!$r_rev) {
1388
        if(isset($INFO['meta']['last_change']['date'])) {
1389
            $r_rev = $INFO['meta']['last_change']['date'];
1390
        } else {
1391
            $r_rev = 0;
1392
        }
1393
    }
1394
1395
    //retrieve revisions with additional info
1396
    list($l_revs, $r_revs) = $pagelog->getRevisionsAround($l_rev, $r_rev);
1397
    $l_revisions = array();
1398
    if(!$l_rev) {
1399
        $l_revisions[0] = array(0, "", false); //no left revision given, add dummy
1400
    }
1401
    foreach($l_revs as $rev) {
1402
        $info = $pagelog->getRevisionInfo($rev);
1403
        $l_revisions[$rev] = array(
1404
            $rev,
1405
            dformat($info['date']) . ' ' . editorinfo($info['user'], true) . ' ' . $info['sum'],
1406
            $r_rev ? $rev >= $r_rev : false //disable?
1407
        );
1408
    }
1409
    $r_revisions = array();
1410
    if(!$r_rev) {
1411
        $r_revisions[0] = array(0, "", false); //no right revision given, add dummy
1412
    }
1413
    foreach($r_revs as $rev) {
1414
        $info = $pagelog->getRevisionInfo($rev);
1415
        $r_revisions[$rev] = array(
1416
            $rev,
1417
            dformat($info['date']) . ' ' . editorinfo($info['user'], true) . ' ' . $info['sum'],
1418
            $rev <= $l_rev //disable?
1419
        );
1420
    }
1421
1422
    //determine previous/next revisions
1423
    $l_index = array_search($l_rev, $l_revs);
1424
    $l_prev = $l_revs[$l_index + 1];
1425
    $l_next = $l_revs[$l_index - 1];
1426
    if($r_rev) {
1427
        $r_index = array_search($r_rev, $r_revs);
1428
        $r_prev = $r_revs[$r_index + 1];
1429
        $r_next = $r_revs[$r_index - 1];
1430
    } else {
1431
        //removed page
1432
        if($l_next) {
1433
            $r_prev = $r_revs[0];
1434
        } else {
1435
            $r_prev = null;
1436
        }
1437
        $r_next = null;
1438
    }
1439
1440
    /*
1441
     * Left side:
1442
     */
1443
    $l_nav = '';
1444
    //move back
1445
    if($l_prev) {
1446
        $l_nav .= html_diff_navigationlink($type, 'diffbothprevrev', $l_prev, $r_prev);
1447
        $l_nav .= html_diff_navigationlink($type, 'diffprevrev', $l_prev, $r_rev);
1448
    }
1449
    //dropdown
1450
    $form = new Doku_Form(array('action' => wl()));
1451
    $form->addHidden('id', $ID);
1452
    $form->addHidden('difftype', $type);
1453
    $form->addHidden('rev2[1]', $r_rev);
1454
    $form->addHidden('do', 'diff');
1455
    $form->addElement(
1456
         form_makeListboxField(
1457
             'rev2[0]',
1458
             $l_revisions,
1459
             $l_rev,
1460
             '', '', '',
1461
             array('class' => 'quickselect')
1462
         )
1463
    );
1464
    $form->addElement(form_makeButton('submit', 'diff', 'Go'));
1465
    $l_nav .= $form->getForm();
1466
    //move forward
1467
    if($l_next && ($l_next < $r_rev || !$r_rev)) {
1468
        $l_nav .= html_diff_navigationlink($type, 'diffnextrev', $l_next, $r_rev);
1469
    }
1470
1471
    /*
1472
     * Right side:
1473
     */
1474
    $r_nav = '';
1475
    //move back
1476
    if($l_rev < $r_prev) {
1477
        $r_nav .= html_diff_navigationlink($type, 'diffprevrev', $l_rev, $r_prev);
1478
    }
1479
    //dropdown
1480
    $form = new Doku_Form(array('action' => wl()));
1481
    $form->addHidden('id', $ID);
1482
    $form->addHidden('rev2[0]', $l_rev);
1483
    $form->addHidden('difftype', $type);
1484
    $form->addHidden('do', 'diff');
1485
    $form->addElement(
1486
         form_makeListboxField(
1487
             'rev2[1]',
1488
             $r_revisions,
1489
             $r_rev,
1490
             '', '', '',
1491
             array('class' => 'quickselect')
1492
         )
1493
    );
1494
    $form->addElement(form_makeButton('submit', 'diff', 'Go'));
1495
    $r_nav .= $form->getForm();
1496
    //move forward
1497
    if($r_next) {
1498
        if($pagelog->isCurrentRevision($r_next)) {
1499
            $r_nav .= html_diff_navigationlink($type, 'difflastrev', $l_rev); //last revision is diff with current page
1500
        } else {
1501
            $r_nav .= html_diff_navigationlink($type, 'diffnextrev', $l_rev, $r_next);
1502
        }
1503
        $r_nav .= html_diff_navigationlink($type, 'diffbothnextrev', $l_next, $r_next);
1504
    }
1505
    return array($l_nav, $r_nav);
1506
}
1507
1508
/**
1509
 * Create html link to a diff defined by two revisions
1510
 *
1511
 * @param string $difftype display type
1512
 * @param string $linktype
1513
 * @param int $lrev oldest revision
1514
 * @param int $rrev newest revision or null for diff with current revision
1515
 * @return string html of link to a diff
1516
 */
1517
function html_diff_navigationlink($difftype, $linktype, $lrev, $rrev = null) {
1518
    global $ID, $lang;
1519
    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...
1520
        $urlparam = array(
1521
            'do' => 'diff',
1522
            'rev' => $lrev,
1523
            'difftype' => $difftype,
1524
        );
1525
    } else {
1526
        $urlparam = array(
1527
            'do' => 'diff',
1528
            'rev2[0]' => $lrev,
1529
            'rev2[1]' => $rrev,
1530
            'difftype' => $difftype,
1531
        );
1532
    }
1533
    return  '<a class="' . $linktype . '" href="' . wl($ID, $urlparam) . '" title="' . $lang[$linktype] . '">' .
1534
                '<span>' . $lang[$linktype] . '</span>' .
1535
            '</a>' . "\n";
1536
}
1537
1538
/**
1539
 * Insert soft breaks in diff html
1540
 *
1541
 * @param string $diffhtml
1542
 * @return string
1543
 */
1544
function html_insert_softbreaks($diffhtml) {
1545
    // search the diff html string for both:
1546
    // - html tags, so these can be ignored
1547
    // - long strings of characters without breaking characters
1548
    return preg_replace_callback('/<[^>]*>|[^<> ]{12,}/','html_softbreak_callback',$diffhtml);
1549
}
1550
1551
/**
1552
 * callback which adds softbreaks
1553
 *
1554
 * @param array $match array with first the complete match
1555
 * @return string the replacement
1556
 */
1557
function html_softbreak_callback($match){
1558
    // if match is an html tag, return it intact
1559
    if ($match[0]{0} == '<') return $match[0];
1560
1561
    // its a long string without a breaking character,
1562
    // make certain characters into breaking characters by inserting a
1563
    // breaking character (zero length space, U+200B / #8203) in front them.
1564
    $regex = <<< REGEX
1565
(?(?=                                 # start a conditional expression with a positive look ahead ...
1566
&\#?\\w{1,6};)                        # ... for html entities - we don't want to split them (ok to catch some invalid combinations)
1567
&\#?\\w{1,6};                         # yes pattern - a quicker match for the html entity, since we know we have one
1568
|
1569
[?/,&\#;:]                            # no pattern - any other group of 'special' characters to insert a breaking character after
1570
)+                                    # end conditional expression
1571
REGEX;
1572
1573
    return preg_replace('<'.$regex.'>xu','\0&#8203;',$match[0]);
1574
}
1575
1576
/**
1577
 * show warning on conflict detection
1578
 *
1579
 * @author Andreas Gohr <[email protected]>
1580
 *
1581
 * @param string $text
1582
 * @param string $summary
1583
 */
1584
function html_conflict($text,$summary){
1585
    global $ID;
1586
    global $lang;
1587
1588
    print p_locale_xhtml('conflict');
1589
    $form = new Doku_Form(array('id' => 'dw__editform'));
1590
    $form->addHidden('id', $ID);
1591
    $form->addHidden('wikitext', $text);
1592
    $form->addHidden('summary', $summary);
1593
    $form->addElement(form_makeButton('submit', 'save', $lang['btn_save'], array('accesskey'=>'s')));
1594
    $form->addElement(form_makeButton('submit', 'cancel', $lang['btn_cancel']));
1595
    html_form('conflict', $form);
1596
    print '<br /><br /><br /><br />'.NL;
1597
}
1598
1599
/**
1600
 * Prints the global message array
1601
 *
1602
 * @author Andreas Gohr <[email protected]>
1603
 */
1604
function html_msgarea(){
1605
    global $MSG, $MSG_shown;
1606
    /** @var array $MSG */
1607
    // store if the global $MSG has already been shown and thus HTML output has been started
1608
    $MSG_shown = true;
1609
1610
    if(!isset($MSG)) return;
1611
1612
    $shown = array();
1613
    foreach($MSG as $msg){
1614
        $hash = md5($msg['msg']);
1615
        if(isset($shown[$hash])) continue; // skip double messages
1616
        if(info_msg_allowed($msg)){
1617
            print '<div class="'.$msg['lvl'].'">';
1618
            print $msg['msg'];
1619
            print '</div>';
1620
        }
1621
        $shown[$hash] = 1;
1622
    }
1623
1624
    unset($GLOBALS['MSG']);
1625
}
1626
1627
/**
1628
 * Prints the registration form
1629
 *
1630
 * @author Andreas Gohr <[email protected]>
1631
 */
1632
function html_register(){
1633
    global $lang;
1634
    global $conf;
1635
    global $INPUT;
1636
1637
    $base_attrs = array('size'=>50,'required'=>'required');
1638
    $email_attrs = $base_attrs + array('type'=>'email','class'=>'edit');
1639
1640
    print p_locale_xhtml('register');
1641
    print '<div class="centeralign">'.NL;
1642
    $form = new Doku_Form(array('id' => 'dw__register'));
1643
    $form->startFieldset($lang['btn_register']);
1644
    $form->addHidden('do', 'register');
1645
    $form->addHidden('save', '1');
1646
    $form->addElement(form_makeTextField('login', $INPUT->post->str('login'), $lang['user'], '', 'block', $base_attrs));
1647
    if (!$conf['autopasswd']) {
1648
        $form->addElement(form_makePasswordField('pass', $lang['pass'], '', 'block', $base_attrs));
1649
        $form->addElement(form_makePasswordField('passchk', $lang['passchk'], '', 'block', $base_attrs));
1650
    }
1651
    $form->addElement(form_makeTextField('fullname', $INPUT->post->str('fullname'), $lang['fullname'], '', 'block', $base_attrs));
1652
    $form->addElement(form_makeField('email','email', $INPUT->post->str('email'), $lang['email'], '', 'block', $email_attrs));
1653
    $form->addElement(form_makeButton('submit', '', $lang['btn_register']));
1654
    $form->endFieldset();
1655
    html_form('register', $form);
1656
1657
    print '</div>'.NL;
1658
}
1659
1660
/**
1661
 * Print the update profile form
1662
 *
1663
 * @author Christopher Smith <[email protected]>
1664
 * @author Andreas Gohr <[email protected]>
1665
 */
1666
function html_updateprofile(){
1667
    global $lang;
1668
    global $conf;
1669
    global $INPUT;
1670
    global $INFO;
1671
    /** @var DokuWiki_Auth_Plugin $auth */
1672
    global $auth;
1673
1674
    print p_locale_xhtml('updateprofile');
1675
    print '<div class="centeralign">'.NL;
1676
1677
    $fullname = $INPUT->post->str('fullname', $INFO['userinfo']['name'], true);
1678
    $email = $INPUT->post->str('email', $INFO['userinfo']['mail'], true);
1679
    $form = new Doku_Form(array('id' => 'dw__register'));
1680
    $form->startFieldset($lang['profile']);
1681
    $form->addHidden('do', 'profile');
1682
    $form->addHidden('save', '1');
1683
    $form->addElement(form_makeTextField('login', $_SERVER['REMOTE_USER'], $lang['user'], '', 'block', array('size'=>'50', 'disabled'=>'disabled')));
1684
    $attr = array('size'=>'50');
1685
    if (!$auth->canDo('modName')) $attr['disabled'] = 'disabled';
1686
    $form->addElement(form_makeTextField('fullname', $fullname, $lang['fullname'], '', 'block', $attr));
1687
    $attr = array('size'=>'50', 'class'=>'edit');
1688
    if (!$auth->canDo('modMail')) $attr['disabled'] = 'disabled';
1689
    $form->addElement(form_makeField('email','email', $email, $lang['email'], '', 'block', $attr));
1690
    $form->addElement(form_makeTag('br'));
1691
    if ($auth->canDo('modPass')) {
1692
        $form->addElement(form_makePasswordField('newpass', $lang['newpass'], '', 'block', array('size'=>'50')));
1693
        $form->addElement(form_makePasswordField('passchk', $lang['passchk'], '', 'block', array('size'=>'50')));
1694
    }
1695
    if ($conf['profileconfirm']) {
1696
        $form->addElement(form_makeTag('br'));
1697
        $form->addElement(form_makePasswordField('oldpass', $lang['oldpass'], '', 'block', array('size'=>'50', 'required' => 'required')));
1698
    }
1699
    $form->addElement(form_makeButton('submit', '', $lang['btn_save']));
1700
    $form->addElement(form_makeButton('reset', '', $lang['btn_reset']));
1701
1702
    $form->endFieldset();
1703
    html_form('updateprofile', $form);
1704
1705
    if ($auth->canDo('delUser') && actionOK('profile_delete')) {
1706
        $form_profiledelete = new Doku_Form(array('id' => 'dw__profiledelete'));
1707
        $form_profiledelete->startFieldset($lang['profdeleteuser']);
1708
        $form_profiledelete->addHidden('do', 'profile_delete');
1709
        $form_profiledelete->addHidden('delete', '1');
1710
        $form_profiledelete->addElement(form_makeCheckboxField('confirm_delete', '1', $lang['profconfdelete'],'dw__confirmdelete','', array('required' => 'required')));
1711
        if ($conf['profileconfirm']) {
1712
            $form_profiledelete->addElement(form_makeTag('br'));
1713
            $form_profiledelete->addElement(form_makePasswordField('oldpass', $lang['oldpass'], '', 'block', array('size'=>'50', 'required' => 'required')));
1714
        }
1715
        $form_profiledelete->addElement(form_makeButton('submit', '', $lang['btn_deleteuser']));
1716
        $form_profiledelete->endFieldset();
1717
1718
        html_form('profiledelete', $form_profiledelete);
1719
    }
1720
1721
    print '</div>'.NL;
1722
}
1723
1724
/**
1725
 * Preprocess edit form data
1726
 *
1727
 * @author   Andreas Gohr <[email protected]>
1728
 *
1729
 * @triggers HTML_EDITFORM_OUTPUT
1730
 */
1731
function html_edit(){
1732
    global $INPUT;
1733
    global $ID;
1734
    global $REV;
1735
    global $DATE;
1736
    global $PRE;
1737
    global $SUF;
1738
    global $INFO;
1739
    global $SUM;
1740
    global $lang;
1741
    global $conf;
1742
    global $TEXT;
1743
1744
    if ($INPUT->has('changecheck')) {
1745
        $check = $INPUT->str('changecheck');
1746
    } elseif(!$INFO['exists']){
1747
        // $TEXT has been loaded from page template
1748
        $check = md5('');
1749
    } else {
1750
        $check = md5($TEXT);
1751
    }
1752
    $mod = md5($TEXT) !== $check;
1753
1754
    $wr = $INFO['writable'] && !$INFO['locked'];
1755
    $include = 'edit';
1756
    if($wr){
1757
        if ($REV) $include = 'editrev';
1758
    }else{
1759
        // check pseudo action 'source'
1760
        if(!actionOK('source')){
1761
            msg('Command disabled: source',-1);
1762
            return;
1763
        }
1764
        $include = 'read';
1765
    }
1766
1767
    global $license;
1768
1769
    $form = new Doku_Form(array('id' => 'dw__editform'));
1770
    $form->addHidden('id', $ID);
1771
    $form->addHidden('rev', $REV);
1772
    $form->addHidden('date', $DATE);
1773
    $form->addHidden('prefix', $PRE . '.');
1774
    $form->addHidden('suffix', $SUF);
1775
    $form->addHidden('changecheck', $check);
1776
1777
    $data = array('form' => $form,
1778
                  'wr'   => $wr,
1779
                  'media_manager' => true,
1780
                  'target' => ($INPUT->has('target') && $wr) ? $INPUT->str('target') : 'section',
1781
                  'intro_locale' => $include);
1782
1783
    if ($data['target'] !== 'section') {
1784
        // Only emit event if page is writable, section edit data is valid and
1785
        // edit target is not section.
1786
        trigger_event('HTML_EDIT_FORMSELECTION', $data, 'html_edit_form', true);
1787
    } else {
1788
        html_edit_form($data);
1789
    }
1790
    if (isset($data['intro_locale'])) {
1791
        echo p_locale_xhtml($data['intro_locale']);
1792
    }
1793
1794
    $form->addHidden('target', $data['target']);
1795
    if ($INPUT->has('hid')) {
1796
        $form->addHidden('hid', $INPUT->str('hid'));
1797
    }
1798
    if ($INPUT->has('codeblockOffset')) {
1799
        $form->addHidden('codeblockOffset', $INPUT->str('codeblockOffset'));
1800
    }
1801
    $form->addElement(form_makeOpenTag('div', array('id'=>'wiki__editbar', 'class'=>'editBar')));
1802
    $form->addElement(form_makeOpenTag('div', array('id'=>'size__ctl')));
1803
    $form->addElement(form_makeCloseTag('div'));
1804
    if ($wr) {
1805
        $form->addElement(form_makeOpenTag('div', array('class'=>'editButtons')));
1806
        $form->addElement(form_makeButton('submit', 'save', $lang['btn_save'], array('id'=>'edbtn__save', 'accesskey'=>'s', 'tabindex'=>'4')));
1807
        $form->addElement(form_makeButton('submit', 'preview', $lang['btn_preview'], array('id'=>'edbtn__preview', 'accesskey'=>'p', 'tabindex'=>'5')));
1808
        $form->addElement(form_makeButton('submit', 'cancel', $lang['btn_cancel'], array('tabindex'=>'6')));
1809
        $form->addElement(form_makeCloseTag('div'));
1810
        $form->addElement(form_makeOpenTag('div', array('class'=>'summary')));
1811
        $form->addElement(form_makeTextField('summary', $SUM, $lang['summary'], 'edit__summary', 'nowrap', array('size'=>'50', 'tabindex'=>'2')));
1812
        $elem = html_minoredit();
1813
        if ($elem) $form->addElement($elem);
1814
        $form->addElement(form_makeCloseTag('div'));
1815
    }
1816
    $form->addElement(form_makeCloseTag('div'));
1817
    if($wr && $conf['license']){
1818
        $form->addElement(form_makeOpenTag('div', array('class'=>'license')));
1819
        $out  = $lang['licenseok'];
1820
        $out .= ' <a href="'.$license[$conf['license']]['url'].'" rel="license" class="urlextern"';
1821
        if($conf['target']['extern']) $out .= ' target="'.$conf['target']['extern'].'"';
1822
        $out .= '>'.$license[$conf['license']]['name'].'</a>';
1823
        $form->addElement($out);
1824
        $form->addElement(form_makeCloseTag('div'));
1825
    }
1826
1827
    if ($wr) {
1828
        // sets changed to true when previewed
1829
        echo '<script type="text/javascript">/*<![CDATA[*/'. NL;
1830
        echo 'textChanged = ' . ($mod ? 'true' : 'false');
1831
        echo '/*!]]>*/</script>' . NL;
1832
    } ?>
1833
    <div class="editBox" role="application">
1834
1835
    <div class="toolbar group">
1836
        <div id="draft__status" class="draft__status"><?php if(!empty($INFO['draft'])) echo $lang['draftdate'].' '.dformat();?></div>
1837
        <div id="tool__bar" class="tool__bar"><?php if ($wr && $data['media_manager']){?><a href="<?php echo DOKU_BASE?>lib/exe/mediamanager.php?ns=<?php echo $INFO['namespace']?>"
1838
            target="_blank"><?php echo $lang['mediaselect'] ?></a><?php }?></div>
1839
    </div>
1840
    <?php
1841
1842
    html_form('edit', $form);
1843
    print '</div>'.NL;
1844
}
1845
1846
/**
1847
 * Display the default edit form
1848
 *
1849
 * Is the default action for HTML_EDIT_FORMSELECTION.
1850
 *
1851
 * @param mixed[] $param
1852
 */
1853
function html_edit_form($param) {
1854
    global $TEXT;
1855
1856
    if ($param['target'] !== 'section') {
1857
        msg('No editor for edit target ' . hsc($param['target']) . ' found.', -1);
1858
    }
1859
1860
    $attr = array('tabindex'=>'1');
1861
    if (!$param['wr']) $attr['readonly'] = 'readonly';
1862
1863
    $param['form']->addElement(form_makeWikiText($TEXT, $attr));
1864
}
1865
1866
/**
1867
 * Adds a checkbox for minor edits for logged in users
1868
 *
1869
 * @author Andreas Gohr <[email protected]>
1870
 *
1871
 * @return array|bool
1872
 */
1873
function html_minoredit(){
1874
    global $conf;
1875
    global $lang;
1876
    global $INPUT;
1877
    // minor edits are for logged in users only
1878
    if(!$conf['useacl'] || !$_SERVER['REMOTE_USER']){
1879
        return false;
1880
    }
1881
1882
    $p = array();
1883
    $p['tabindex'] = 3;
1884
    if($INPUT->bool('minor')) $p['checked']='checked';
1885
    return form_makeCheckboxField('minor', '1', $lang['minoredit'], 'minoredit', 'nowrap', $p);
1886
}
1887
1888
/**
1889
 * prints some debug info
1890
 *
1891
 * @author Andreas Gohr <[email protected]>
1892
 */
1893
function html_debug(){
1894
    global $conf;
1895
    global $lang;
1896
    /** @var DokuWiki_Auth_Plugin $auth */
1897
    global $auth;
1898
    global $INFO;
1899
1900
    //remove sensitive data
1901
    $cnf = $conf;
1902
    debug_guard($cnf);
1903
    $nfo = $INFO;
1904
    debug_guard($nfo);
1905
    $ses = $_SESSION;
1906
    debug_guard($ses);
1907
1908
    print '<html><body>';
1909
1910
    print '<p>When reporting bugs please send all the following ';
1911
    print 'output as a mail to [email protected] ';
1912
    print 'The best way to do this is to save this page in your browser</p>';
1913
1914
    print '<b>$INFO:</b><pre>';
1915
    print_r($nfo);
1916
    print '</pre>';
1917
1918
    print '<b>$_SERVER:</b><pre>';
1919
    print_r($_SERVER);
1920
    print '</pre>';
1921
1922
    print '<b>$conf:</b><pre>';
1923
    print_r($cnf);
1924
    print '</pre>';
1925
1926
    print '<b>DOKU_BASE:</b><pre>';
1927
    print DOKU_BASE;
1928
    print '</pre>';
1929
1930
    print '<b>abs DOKU_BASE:</b><pre>';
1931
    print DOKU_URL;
1932
    print '</pre>';
1933
1934
    print '<b>rel DOKU_BASE:</b><pre>';
1935
    print dirname($_SERVER['PHP_SELF']).'/';
1936
    print '</pre>';
1937
1938
    print '<b>PHP Version:</b><pre>';
1939
    print phpversion();
1940
    print '</pre>';
1941
1942
    print '<b>locale:</b><pre>';
1943
    print setlocale(LC_ALL,0);
1944
    print '</pre>';
1945
1946
    print '<b>encoding:</b><pre>';
1947
    print $lang['encoding'];
1948
    print '</pre>';
1949
1950
    if($auth){
1951
        print '<b>Auth backend capabilities:</b><pre>';
1952
        foreach ($auth->getCapabilities() as $cando){
1953
            print '   '.str_pad($cando,16) . ' => ' . (int)$auth->canDo($cando) . NL;
1954
        }
1955
        print '</pre>';
1956
    }
1957
1958
    print '<b>$_SESSION:</b><pre>';
1959
    print_r($ses);
1960
    print '</pre>';
1961
1962
    print '<b>Environment:</b><pre>';
1963
    print_r($_ENV);
1964
    print '</pre>';
1965
1966
    print '<b>PHP settings:</b><pre>';
1967
    $inis = ini_get_all();
1968
    print_r($inis);
1969
    print '</pre>';
1970
1971
    if (function_exists('apache_get_version')) {
1972
        $apache = array();
1973
        $apache['version'] = apache_get_version();
1974
1975
        if (function_exists('apache_get_modules')) {
1976
            $apache['modules'] = apache_get_modules();
1977
        }
1978
        print '<b>Apache</b><pre>';
1979
        print_r($apache);
1980
        print '</pre>';
1981
    }
1982
1983
    print '</body></html>';
1984
}
1985
1986
/**
1987
 * Form to request a new password for an existing account
1988
 *
1989
 * @author Benoit Chesneau <[email protected]>
1990
 * @author Andreas Gohr <[email protected]>
1991
 */
1992
function html_resendpwd() {
1993
    global $lang;
1994
    global $conf;
1995
    global $INPUT;
1996
1997
    $token = preg_replace('/[^a-f0-9]+/','',$INPUT->str('pwauth'));
1998
1999
    if(!$conf['autopasswd'] && $token){
2000
        print p_locale_xhtml('resetpwd');
2001
        print '<div class="centeralign">'.NL;
2002
        $form = new Doku_Form(array('id' => 'dw__resendpwd'));
2003
        $form->startFieldset($lang['btn_resendpwd']);
2004
        $form->addHidden('token', $token);
2005
        $form->addHidden('do', 'resendpwd');
2006
2007
        $form->addElement(form_makePasswordField('pass', $lang['pass'], '', 'block', array('size'=>'50')));
2008
        $form->addElement(form_makePasswordField('passchk', $lang['passchk'], '', 'block', array('size'=>'50')));
2009
2010
        $form->addElement(form_makeButton('submit', '', $lang['btn_resendpwd']));
2011
        $form->endFieldset();
2012
        html_form('resendpwd', $form);
2013
        print '</div>'.NL;
2014
    }else{
2015
        print p_locale_xhtml('resendpwd');
2016
        print '<div class="centeralign">'.NL;
2017
        $form = new Doku_Form(array('id' => 'dw__resendpwd'));
2018
        $form->startFieldset($lang['resendpwd']);
2019
        $form->addHidden('do', 'resendpwd');
2020
        $form->addHidden('save', '1');
2021
        $form->addElement(form_makeTag('br'));
2022
        $form->addElement(form_makeTextField('login', $INPUT->post->str('login'), $lang['user'], '', 'block'));
2023
        $form->addElement(form_makeTag('br'));
2024
        $form->addElement(form_makeTag('br'));
2025
        $form->addElement(form_makeButton('submit', '', $lang['btn_resendpwd']));
2026
        $form->endFieldset();
2027
        html_form('resendpwd', $form);
2028
        print '</div>'.NL;
2029
    }
2030
}
2031
2032
/**
2033
 * Return the TOC rendered to XHTML
2034
 *
2035
 * @author Andreas Gohr <[email protected]>
2036
 *
2037
 * @param array $toc
2038
 * @return string html
2039
 */
2040
function html_TOC($toc){
2041
    if(!count($toc)) return '';
2042
    global $lang;
2043
    $out  = '<!-- TOC START -->'.DOKU_LF;
2044
    $out .= '<div id="dw__toc" class="dw__toc">'.DOKU_LF;
2045
    $out .= '<h3 class="toggle">';
2046
    $out .= $lang['toc'];
2047
    $out .= '</h3>'.DOKU_LF;
2048
    $out .= '<div>'.DOKU_LF;
2049
    $out .= html_buildlist($toc,'toc','html_list_toc','html_li_default',true);
2050
    $out .= '</div>'.DOKU_LF.'</div>'.DOKU_LF;
2051
    $out .= '<!-- TOC END -->'.DOKU_LF;
2052
    return $out;
2053
}
2054
2055
/**
2056
 * Callback for html_buildlist
2057
 *
2058
 * @param array $item
2059
 * @return string html
2060
 */
2061
function html_list_toc($item){
2062
    if(isset($item['hid'])){
2063
        $link = '#'.$item['hid'];
2064
    }else{
2065
        $link = $item['link'];
2066
    }
2067
2068
    return '<a href="'.$link.'">'.hsc($item['title']).'</a>';
2069
}
2070
2071
/**
2072
 * Helper function to build TOC items
2073
 *
2074
 * Returns an array ready to be added to a TOC array
2075
 *
2076
 * @param string $link  - where to link (if $hash set to '#' it's a local anchor)
2077
 * @param string $text  - what to display in the TOC
2078
 * @param int    $level - nesting level
2079
 * @param string $hash  - is prepended to the given $link, set blank if you want full links
2080
 * @return array the toc item
2081
 */
2082
function html_mktocitem($link, $text, $level, $hash='#'){
2083
    return  array( 'link'  => $hash.$link,
2084
            'title' => $text,
2085
            'type'  => 'ul',
2086
            'level' => $level);
2087
}
2088
2089
/**
2090
 * Output a Doku_Form object.
2091
 * Triggers an event with the form name: HTML_{$name}FORM_OUTPUT
2092
 *
2093
 * @author Tom N Harris <[email protected]>
2094
 *
2095
 * @param string     $name The name of the form
2096
 * @param Doku_Form  $form The form
2097
 */
2098
function html_form($name, &$form) {
2099
    // Safety check in case the caller forgets.
2100
    $form->endFieldset();
2101
    trigger_event('HTML_'.strtoupper($name).'FORM_OUTPUT', $form, 'html_form_output', false);
2102
}
2103
2104
/**
2105
 * Form print function.
2106
 * Just calls printForm() on the data object.
2107
 *
2108
 * @param Doku_Form $data The form
2109
 */
2110
function html_form_output($data) {
2111
    $data->printForm();
2112
}
2113
2114
/**
2115
 * Embed a flash object in HTML
2116
 *
2117
 * This will create the needed HTML to embed a flash movie in a cross browser
2118
 * compatble way using valid XHTML
2119
 *
2120
 * The parameters $params, $flashvars and $atts need to be associative arrays.
2121
 * No escaping needs to be done for them. The alternative content *has* to be
2122
 * escaped because it is used as is. If no alternative content is given
2123
 * $lang['noflash'] is used.
2124
 *
2125
 * @author Andreas Gohr <[email protected]>
2126
 * @link   http://latrine.dgx.cz/how-to-correctly-insert-a-flash-into-xhtml
2127
 *
2128
 * @param string $swf      - the SWF movie to embed
2129
 * @param int $width       - width of the flash movie in pixels
2130
 * @param int $height      - height of the flash movie in pixels
2131
 * @param array $params    - additional parameters (<param>)
2132
 * @param array $flashvars - parameters to be passed in the flashvar parameter
2133
 * @param array $atts      - additional attributes for the <object> tag
2134
 * @param string $alt      - alternative content (is NOT automatically escaped!)
2135
 * @return string         - the XHTML markup
2136
 */
2137
function html_flashobject($swf,$width,$height,$params=null,$flashvars=null,$atts=null,$alt=''){
2138
    global $lang;
2139
2140
    $out = '';
2141
2142
    // prepare the object attributes
2143
    if(is_null($atts)) $atts = array();
2144
    $atts['width']  = (int) $width;
2145
    $atts['height'] = (int) $height;
2146
    if(!$atts['width'])  $atts['width']  = 425;
2147
    if(!$atts['height']) $atts['height'] = 350;
2148
2149
    // add object attributes for standard compliant browsers
2150
    $std = $atts;
2151
    $std['type'] = 'application/x-shockwave-flash';
2152
    $std['data'] = $swf;
2153
2154
    // add object attributes for IE
2155
    $ie  = $atts;
2156
    $ie['classid'] = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000';
2157
2158
    // open object (with conditional comments)
2159
    $out .= '<!--[if !IE]> -->'.NL;
2160
    $out .= '<object '.buildAttributes($std).'>'.NL;
2161
    $out .= '<!-- <![endif]-->'.NL;
2162
    $out .= '<!--[if IE]>'.NL;
2163
    $out .= '<object '.buildAttributes($ie).'>'.NL;
2164
    $out .= '    <param name="movie" value="'.hsc($swf).'" />'.NL;
2165
    $out .= '<!--><!-- -->'.NL;
2166
2167
    // print params
2168
    if(is_array($params)) foreach($params as $key => $val){
2169
        $out .= '  <param name="'.hsc($key).'" value="'.hsc($val).'" />'.NL;
2170
    }
2171
2172
    // add flashvars
2173
    if(is_array($flashvars)){
2174
        $out .= '  <param name="FlashVars" value="'.buildURLparams($flashvars).'" />'.NL;
2175
    }
2176
2177
    // alternative content
2178
    if($alt){
2179
        $out .= $alt.NL;
2180
    }else{
2181
        $out .= $lang['noflash'].NL;
2182
    }
2183
2184
    // finish
2185
    $out .= '</object>'.NL;
2186
    $out .= '<!-- <![endif]-->'.NL;
2187
2188
    return $out;
2189
}
2190
2191
/**
2192
 * Prints HTML code for the given tab structure
2193
 *
2194
 * @param array  $tabs        tab structure
2195
 * @param string $current_tab the current tab id
2196
 */
2197
function html_tabs($tabs, $current_tab = null) {
2198
    echo '<ul class="tabs">'.NL;
2199
2200
    foreach($tabs as $id => $tab) {
2201
        html_tab($tab['href'], $tab['caption'], $id === $current_tab);
2202
    }
2203
2204
    echo '</ul>'.NL;
2205
}
2206
2207
/**
2208
 * Prints a single tab
2209
 *
2210
 * @author Kate Arzamastseva <[email protected]>
2211
 * @author Adrian Lang <[email protected]>
2212
 *
2213
 * @param string $href - tab href
2214
 * @param string $caption - tab caption
2215
 * @param boolean $selected - is tab selected
2216
 */
2217
2218
function html_tab($href, $caption, $selected=false) {
2219
    $tab = '<li>';
2220
    if ($selected) {
2221
        $tab .= '<strong>';
2222
    } else {
2223
        $tab .= '<a href="' . hsc($href) . '">';
2224
    }
2225
    $tab .= hsc($caption)
2226
         .  '</' . ($selected ? 'strong' : 'a') . '>'
2227
         .  '</li>'.NL;
2228
    echo $tab;
2229
}
2230
2231
/**
2232
 * Display size change
2233
 *
2234
 * @param int $sizechange - size of change in Bytes
2235
 * @param Doku_Form $form - form to add elements to
2236
 */
2237
2238
function html_sizechange($sizechange, Doku_Form $form) {
2239
    if(isset($sizechange)) {
2240
        $class = 'sizechange';
2241
        $value = filesize_h(abs($sizechange));
2242
        if($sizechange > 0) {
2243
            $class .= ' positive';
2244
            $value = '+' . $value;
2245
        } elseif($sizechange < 0) {
2246
            $class .= ' negative';
2247
            $value = '-' . $value;
2248
        } else {
2249
            $value = '±' . $value;
2250
        }
2251
        $form->addElement(form_makeOpenTag('span', array('class' => $class)));
2252
        $form->addElement($value);
2253
        $form->addElement(form_makeCloseTag('span'));
2254
    }
2255
}
2256