Completed
Push — master ( d4a8e6...69a81a )
by Andreas
16s
created

Ajax   F

Complexity

Total Complexity 65

Size/Duplication

Total Lines 429
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 2

Importance

Changes 0
Metric Value
dl 0
loc 429
rs 3.3333
c 0
b 0
f 0
wmc 65
lcom 0
cbo 2

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 14 3
C call_qsearch() 0 40 8
B call_suggestions() 0 30 4
B call_lock() 0 34 5
A call_draftdel() 0 11 3
A call_medians() 0 17 2
A call_medialist() 0 12 3
B call_mediadetails() 0 15 6
A call_mediadiff() 0 10 2
C call_mediaupload() 0 49 10
A call_index() 0 17 2
D call_linkwiz() 0 103 17

How to fix   Complexity   

Complex Class

Complex classes like Ajax often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Ajax, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace dokuwiki;
4
5
/**
6
 * Manage all builtin AJAX calls
7
 *
8
 * @todo The calls should be refactored out to their own proper classes
9
 * @package dokuwiki
10
 */
11
class Ajax {
12
13
    /**
14
     * Execute the given call
15
     *
16
     * @param string $call name of the ajax call
17
     */
18
    public function __construct($call) {
19
        $callfn = 'call_' . $call;
20
        if(method_exists($this, $callfn)) {
21
            $this->$callfn();
22
        } else {
23
            $evt = new \Doku_Event('AJAX_CALL_UNKNOWN', $call);
24
            if($evt->advise_before()) {
25
                print "AJAX call '" . hsc($call) . "' unknown!\n";
26
            } else {
27
                $evt->advise_after();
28
                unset($evt);
29
            }
30
        }
31
    }
32
33
    /**
34
     * Searches for matching pagenames
35
     *
36
     * @author Andreas Gohr <[email protected]>
37
     */
38
    protected function call_qsearch() {
39
        global $lang;
40
        global $INPUT;
41
42
        $maxnumbersuggestions = 50;
43
44
        $query = $INPUT->post->str('q');
45
        if(empty($query)) $query = $INPUT->get->str('q');
46
        if(empty($query)) return;
47
48
        $query = urldecode($query);
49
50
        $data = ft_pageLookup($query, true, useHeading('navigation'));
51
52
        if(!count($data)) return;
53
54
        print '<strong>' . $lang['quickhits'] . '</strong>';
55
        print '<ul>';
56
        $counter = 0;
57
        foreach($data as $id => $title) {
58
            if(useHeading('navigation')) {
59
                $name = $title;
60
            } else {
61
                $ns = getNS($id);
62
                if($ns) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $ns of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false 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...
63
                    $name = noNS($id) . ' (' . $ns . ')';
64
                } else {
65
                    $name = $id;
66
                }
67
            }
68
            echo '<li>' . html_wikilink(':' . $id, $name) . '</li>';
69
70
            $counter++;
71
            if($counter > $maxnumbersuggestions) {
72
                echo '<li>...</li>';
73
                break;
74
            }
75
        }
76
        print '</ul>';
77
    }
78
79
    /**
80
     * Support OpenSearch suggestions
81
     *
82
     * @link   http://www.opensearch.org/Specifications/OpenSearch/Extensions/Suggestions/1.0
83
     * @author Mike Frysinger <[email protected]>
84
     */
85
    protected function call_suggestions() {
86
        global $INPUT;
87
88
        $query = cleanID($INPUT->post->str('q'));
89
        if(empty($query)) $query = cleanID($INPUT->get->str('q'));
90
        if(empty($query)) return;
91
92
        $data = ft_pageLookup($query);
93
        if(!count($data)) return;
94
        $data = array_keys($data);
95
96
        // limit results to 15 hits
97
        $data = array_slice($data, 0, 15);
98
        $data = array_map('trim', $data);
99
        $data = array_map('noNS', $data);
100
        $data = array_unique($data);
101
        sort($data);
102
103
        /* now construct a json */
104
        $suggestions = array(
105
            $query,  // the original query
106
            $data,   // some suggestions
107
            array(), // no description
108
            array()  // no urls
109
        );
110
        $json = new \JSON();
111
112
        header('Content-Type: application/x-suggestions+json');
113
        print $json->encode($suggestions);
114
    }
115
116
    /**
117
     * Refresh a page lock and save draft
118
     *
119
     * Andreas Gohr <[email protected]>
120
     */
121
    protected function call_lock() {
122
        global $ID;
123
        global $INFO;
124
        global $INPUT;
125
126
        $ID = cleanID($INPUT->post->str('id'));
127
        if(empty($ID)) return;
128
129
        $INFO = pageinfo();
130
131
        $response = [
132
            'errors' => [],
133
            'lock' => '0',
134
            'draft' => '',
135
        ];
136
        if(!$INFO['writable']) {
137
            $response['errors'][] = 'Permission to write this page has been denied.';
138
            echo json_encode($response);
139
            return;
140
        }
141
142
        if(!checklock($ID)) {
143
            lock($ID);
144
            $response['lock'] = '1';
145
        }
146
147
        $draft = new Draft($ID, $INFO['client']);
148
        if ($draft->saveDraft()) {
149
            $response['draft'] = $draft->getDraftMessage();
150
        } else {
151
            $response['errors'] = array_merge($response['errors'], $draft->getErrors());
152
        }
153
        echo json_encode($response);
154
    }
155
156
    /**
157
     * Delete a draft
158
     *
159
     * @author Andreas Gohr <[email protected]>
160
     */
161
    protected function call_draftdel() {
162
        global $INPUT;
163
        $id = cleanID($INPUT->str('id'));
164
        if(empty($id)) return;
165
166
        $client = $_SERVER['REMOTE_USER'];
167
        if(!$client) $client = clientIP(true);
168
169
        $cname = getCacheName($client . $id, '.draft');
170
        @unlink($cname);
171
    }
172
173
    /**
174
     * Return subnamespaces for the Mediamanager
175
     *
176
     * @author Andreas Gohr <[email protected]>
177
     */
178
    protected function call_medians() {
179
        global $conf;
180
        global $INPUT;
181
182
        // wanted namespace
183
        $ns = cleanID($INPUT->post->str('ns'));
184
        $dir = utf8_encodeFN(str_replace(':', '/', $ns));
185
186
        $lvl = count(explode(':', $ns));
187
188
        $data = array();
189
        search($data, $conf['mediadir'], 'search_index', array('nofiles' => true), $dir);
190
        foreach(array_keys($data) as $item) {
191
            $data[$item]['level'] = $lvl + 1;
192
        }
193
        echo html_buildlist($data, 'idx', 'media_nstree_item', 'media_nstree_li');
194
    }
195
196
    /**
197
     * Return list of files for the Mediamanager
198
     *
199
     * @author Andreas Gohr <[email protected]>
200
     */
201
    protected function call_medialist() {
202
        global $NS;
203
        global $INPUT;
204
205
        $NS = cleanID($INPUT->post->str('ns'));
206
        $sort = $INPUT->post->bool('recent') ? 'date' : 'natural';
207
        if($INPUT->post->str('do') == 'media') {
208
            tpl_mediaFileList();
209
        } else {
210
            tpl_mediaContent(true, $sort);
211
        }
212
    }
213
214
    /**
215
     * Return the content of the right column
216
     * (image details) for the Mediamanager
217
     *
218
     * @author Kate Arzamastseva <[email protected]>
219
     */
220
    protected function call_mediadetails() {
221
        global $IMG, $JUMPTO, $REV, $fullscreen, $INPUT;
222
        $fullscreen = true;
223
        require_once(DOKU_INC . 'lib/exe/mediamanager.php');
224
225
        $image = '';
226
        if($INPUT->has('image')) $image = cleanID($INPUT->str('image'));
227
        if(isset($IMG)) $image = $IMG;
228
        if(isset($JUMPTO)) $image = $JUMPTO;
229
        $rev = false;
230
        if(isset($REV) && !$JUMPTO) $rev = $REV;
231
232
        html_msgarea();
233
        tpl_mediaFileDetails($image, $rev);
234
    }
235
236
    /**
237
     * Returns image diff representation for mediamanager
238
     *
239
     * @author Kate Arzamastseva <[email protected]>
240
     */
241
    protected function call_mediadiff() {
242
        global $NS;
243
        global $INPUT;
244
245
        $image = '';
246
        if($INPUT->has('image')) $image = cleanID($INPUT->str('image'));
247
        $NS = getNS($image);
248
        $auth = auth_quickaclcheck("$NS:*");
249
        media_diff($image, $NS, $auth, true);
0 ignored issues
show
Security Bug introduced by
It seems like $NS defined by getNS($image) on line 247 can also be of type false; however, media_diff() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
250
    }
251
252
    /**
253
     * Manages file uploads
254
     *
255
     * @author Kate Arzamastseva <[email protected]>
256
     */
257
    protected function call_mediaupload() {
258
        global $NS, $MSG, $INPUT;
259
260
        $id = '';
261
        if($_FILES['qqfile']['tmp_name']) {
262
            $id = $INPUT->post->str('mediaid', $_FILES['qqfile']['name']);
263
        } elseif($INPUT->get->has('qqfile')) {
264
            $id = $INPUT->get->str('qqfile');
265
        }
266
267
        $id = cleanID($id);
268
269
        $NS = $INPUT->str('ns');
270
        $ns = $NS . ':' . getNS($id);
271
272
        $AUTH = auth_quickaclcheck("$ns:*");
273
        if($AUTH >= AUTH_UPLOAD) {
274
            io_createNamespace("$ns:xxx", 'media');
275
        }
276
277
        if($_FILES['qqfile']['error']) unset($_FILES['qqfile']);
278
279
        $res = false;
280
        if($_FILES['qqfile']['tmp_name']) $res = media_upload($NS, $AUTH, $_FILES['qqfile']);
281
        if($INPUT->get->has('qqfile')) $res = media_upload_xhr($NS, $AUTH);
282
283
        if($res) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $res of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false 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...
284
            $result = array(
285
                'success' => true,
286
                'link' => media_managerURL(array('ns' => $ns, 'image' => $NS . ':' . $id), '&'),
287
                'id' => $NS . ':' . $id,
288
                'ns' => $NS
289
            );
290
        } else {
291
            $error = '';
292
            if(isset($MSG)) {
293
                foreach($MSG as $msg) {
294
                    $error .= $msg['msg'];
295
                }
296
            }
297
            $result = array(
298
                'error' => $error,
299
                'ns' => $NS
300
            );
301
        }
302
        $json = new \JSON;
303
        header('Content-Type: application/json');
304
        echo $json->encode($result);
305
    }
306
307
    /**
308
     * Return sub index for index view
309
     *
310
     * @author Andreas Gohr <[email protected]>
311
     */
312
    protected function call_index() {
313
        global $conf;
314
        global $INPUT;
315
316
        // wanted namespace
317
        $ns = cleanID($INPUT->post->str('idx'));
318
        $dir = utf8_encodeFN(str_replace(':', '/', $ns));
319
320
        $lvl = count(explode(':', $ns));
321
322
        $data = array();
323
        search($data, $conf['datadir'], 'search_index', array('ns' => $ns), $dir);
324
        foreach(array_keys($data) as $item) {
325
            $data[$item]['level'] = $lvl + 1;
326
        }
327
        echo html_buildlist($data, 'idx', 'html_list_index', 'html_li_index');
328
    }
329
330
    /**
331
     * List matching namespaces and pages for the link wizard
332
     *
333
     * @author Andreas Gohr <[email protected]>
334
     */
335
    protected function call_linkwiz() {
336
        global $conf;
337
        global $lang;
338
        global $INPUT;
339
340
        $q = ltrim(trim($INPUT->post->str('q')), ':');
341
        $id = noNS($q);
342
        $ns = getNS($q);
343
344
        $ns = cleanID($ns);
0 ignored issues
show
Security Bug introduced by
It seems like $ns can also be of type false; however, cleanID() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
345
        $id = cleanID($id);
346
347
        $nsd = utf8_encodeFN(str_replace(':', '/', $ns));
348
349
        $data = array();
350
        if($q && !$ns) {
351
352
            // use index to lookup matching pages
353
            $pages = ft_pageLookup($id, true);
354
355
            // result contains matches in pages and namespaces
356
            // we now extract the matching namespaces to show
357
            // them seperately
358
            $dirs = array();
359
360
            foreach($pages as $pid => $title) {
361
                if(strpos(noNS($pid), $id) === false) {
362
                    // match was in the namespace
363
                    $dirs[getNS($pid)] = 1; // assoc array avoids dupes
364
                } else {
365
                    // it is a matching page, add it to the result
366
                    $data[] = array(
367
                        'id' => $pid,
368
                        'title' => $title,
369
                        'type' => 'f',
370
                    );
371
                }
372
                unset($pages[$pid]);
373
            }
374
            foreach($dirs as $dir => $junk) {
375
                $data[] = array(
376
                    'id' => $dir,
377
                    'type' => 'd',
378
                );
379
            }
380
381
        } else {
382
383
            $opts = array(
384
                'depth' => 1,
385
                'listfiles' => true,
386
                'listdirs' => true,
387
                'pagesonly' => true,
388
                'firsthead' => true,
389
                'sneakyacl' => $conf['sneaky_index'],
390
            );
391
            if($id) $opts['filematch'] = '^.*\/' . $id;
392
            if($id) $opts['dirmatch'] = '^.*\/' . $id;
393
            search($data, $conf['datadir'], 'search_universal', $opts, $nsd);
394
395
            // add back to upper
396
            if($ns) {
397
                array_unshift(
398
                    $data, array(
399
                             'id' => getNS($ns),
400
                             'type' => 'u',
401
                         )
402
                );
403
            }
404
        }
405
406
        // fixme sort results in a useful way ?
407
408
        if(!count($data)) {
409
            echo $lang['nothingfound'];
410
            exit;
411
        }
412
413
        // output the found data
414
        $even = 1;
415
        foreach($data as $item) {
416
            $even *= -1; //zebra
417
418
            if(($item['type'] == 'd' || $item['type'] == 'u') && $item['id']) $item['id'] .= ':';
419
            $link = wl($item['id']);
420
421
            echo '<div class="' . (($even > 0) ? 'even' : 'odd') . ' type_' . $item['type'] . '">';
422
423
            if($item['type'] == 'u') {
424
                $name = $lang['upperns'];
425
            } else {
426
                $name = hsc($item['id']);
427
            }
428
429
            echo '<a href="' . $link . '" title="' . hsc($item['id']) . '" class="wikilink1">' . $name . '</a>';
430
431
            if(!blank($item['title'])) {
432
                echo '<span>' . hsc($item['title']) . '</span>';
433
            }
434
            echo '</div>';
435
        }
436
437
    }
438
439
}
440