Completed
Push — master ( 9b981d...ec0b2f )
by Mikael
02:21
created

CFileBasedContent::getMetaForRoute()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 3
Bugs 0 Features 1
Metric Value
c 3
b 0
f 1
dl 0
loc 7
ccs 0
cts 4
cp 0
rs 9.4285
cc 2
eloc 4
nc 2
nop 1
crap 6
1
<?php
2
3
namespace Anax\Content;
4
5
/**
6
 * Pages based on file content.
7
 */
8
class CFileBasedContent
9
{
10
    use \Anax\TConfigure,
11
        \Anax\DI\TInjectionAware;
12
13
14
15
    /**
16
     * Properties.
17
     */
18
    private $index = null;
19
    private $meta = null;
20
    private $ignoreCache = false;
21
    
22
    /**
23
     * File name pattern, all files must match this pattern and the first
24
     * numbered part is optional, the second part becomes the route.
25
     */
26
    private $filenamePattern = "#^(\d*)_*([^\.]+)\.md$#";
27
28
    /**
29
     * Internal routes that is marked as internal content routes and not
30
     * exposed as public routes.
31
     */
32
    private $internalRouteDirPattern = [
33
        "#block/#",
34
    ];
35
36
    private $internalRouteFilePattern = [
37
        "#^block[_-]{1}#",
38
        "#^_#",
39
    ];
40
41
    /**
42
     * Routes that should be used in toc.
43
     */
44
    private $allowedInTocPattern = "([\d]+_(\w)+)";
45
46
47
48
    /**
49
     * Create a breadcrumb, append slash / to all dirs.
50
     *
51
     * @param string $route      current route.
52
     *
53
     * @return array with values for the breadcrumb.
54
     */
55
    public function createBreadcrumb($route)
56
    {
57
        $breadcrumbs = [];
58
59
        while ($route !== "./") {
60
            $routeIndex = $this->mapRoute2IndexKey($route);
61
            $item["url"] = $route;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$item was never initialized. Although not strictly required by PHP, it is generally a good practice to add $item = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
62
            $item["text"] = $this->getTitle($this->index[$routeIndex]["file"]);
0 ignored issues
show
Bug introduced by
The variable $item 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...
Deprecated Code introduced by
The method Anax\Content\CFileBasedContent::getTitle() has been deprecated with message: in favor of getFrontmatter

This method has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
63
            $breadcrumbs[] = $item;
64
            $route = dirname($route) . "/";
65
        }
66
67
        krsort($breadcrumbs);
68
        return $breadcrumbs;
69
    }
70
71
72
73
/**
74
 * Get time when the content was last updated.
75
 *
76
 * @return string with the time.
77
 */
78
/*public function PublishTime() {
79
  if(!empty($this['published'])) {
80
    return $this['published'];
81
  } else if(isset($this['updated'])) {
82
    return $this['updated'];
83
  } else {
84
    return $this['created'];
85
  } 
86
}
87
*/
88
/**
89
 * Get the action for latest updated of the content.
90
 *
91
 * @return string with the time.
92
 */
93
/*public function PublishAction() {
94
  if(!empty($this['published'])) {
95
    //return t('Published');
96
    return t('Last updated');
97
  } else if(isset($this['updated'])) {
98
    return t('Updated');
99
  } else {
100
    return t('Created');
101
  } 
102
}
103
*/
104
105
106
107
    /**
108
     * Set default values from configuration.
109
     *
110
     * @return this.
0 ignored issues
show
Documentation introduced by
The doc-type this. could not be parsed: Unknown type name "this." at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
111
     */
112
    public function setDefaultsFromConfiguration()
113
    {
114
        $this->ignoreCache = isset($this->config["ignoreCache"])
115
            ? $this->config["ignoreCache"]
116
            : $this->ignoreCache;
117
118
        return $this;
119
    }
120
121
122
123
    /**
124
     * Should the cache be used or ignored.
125
     *
126
     * @param boolean $use true to use the cache or false to ignore the cache
127
     *
128
     * @return this.
0 ignored issues
show
Documentation introduced by
The doc-type this. could not be parsed: Unknown type name "this." at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
129
     */
130
    public function useCache($use)
131
    {
132
        $this->ignoreCache = !$use;
133
134
        return $this;
135
    }
136
137
138
139
    /**
140
     * Get the index as an array.
141
     *
142
     * @return array as index.
143
     */
144
    public function getIndex()
145
    {
146
        return $this->loadIndex();
147
    }
148
149
150
151
    /**
152
     * Create the index of all content into an array.
153
     *
154
     * @return array as index.
155
     */
156 View Code Duplication
    private function loadIndex()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
157
    {
158
        if ($this->index) {
159
            return $this->index;
160
        }
161
162
        $key = $this->di->cache->createKey(__CLASS__, "index");
163
        $this->index = $this->di->cache->get($key);
164
165
        if (!$this->index || $this->ignoreCache) {
166
            $this->index = $this->createIndex();
167
            $this->di->cache->put($key, $this->index);
168
        }
169
170
        return $this->index;
171
    }
172
173
174
175
    /**
176
     * Check if a filename is to be marked as an internal route..
177
     *
178
     * @param string $filepath as the basepath (routepart) to the file.
179
     *
180
     * @return boolean true if the route content is internal, else false
181
     */
182
    private function isInternalRoute($filepath)
183
    {
184
        foreach ($this->internalRouteDirPattern as $pattern) {
185
            if (preg_match($pattern, $filepath)) {
186
                return true;
187
            }
188
        }
189
190
        $filename = basename($filepath);
191
        foreach ($this->internalRouteFilePattern as $pattern) {
192
            if (preg_match($pattern, $filename)) {
193
                return true;
194
            }
195
        }
196
197
        return false;
198
    }
199
200
201
202
    /**
203
     * Check if filepath should be used as part of toc.
204
     *
205
     * @param string $filepath as the basepath (routepart) to the file.
206
     *
207
     * @return boolean true if the route content shoul dbe in toc, else false
208
     */
209
    private function allowInToc($filepath)
210
    {
211
        return (boolean) preg_match($this->allowedInTocPattern, $filepath);
212
    }
213
214
215
216
    /**
217
     * Generate an index from the directory structure.
218
     *
219
     * @return array as index for all content files.
220
     */
221
    private function createIndex()
222
    {
223
        $basepath   = $this->config["basepath"];
224
        $pattern    = $this->config["pattern"];
225
        $path       = "$basepath/$pattern";
226
227
        $index = [];
228
        foreach (glob_recursive($path) as $file) {
229
            $filepath = substr($file, strlen($basepath) + 1);
230
231
            // Find content files
232
            $matches = [];
233
            preg_match($this->filenamePattern, basename($filepath), $matches);
234
            $dirpart = dirname($filepath) . "/";
235
            if ($dirpart === "./") {
236
                $dirpart = null;
237
            }
238
            $key = $dirpart . $matches[2];
239
            
240
            // Create level depending on the file id
241
            $id = $matches[1];
242
            $level = 2;
243 View Code Duplication
            if ($id % 100 === 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
244
                $level = 0;
245
            } elseif ($id % 10 === 0) {
246
                $level = 1;
247
            }
248
249
            $index[$key] = [
250
                "file"     => $filepath,
251
                "section"  => $matches[1],
252
                "level"    => $level,
253
                "internal" => $this->isInternalRoute($filepath),
254
                "tocable"  => $this->allowInToc($filepath),
255
            ];
256
        }
257
258
        return $index;
259
    }
260
261
262
263
    /**
264
     * Create the index of all meta content into an array.
265
     *
266
     * @return array as index.
267
     */
268 View Code Duplication
    private function loadMetaIndex()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
269
    {
270
        if ($this->meta) {
271
            return $this->meta;
272
        }
273
274
        $key = $this->di->cache->createKey(__CLASS__, "meta");
275
        $this->meta = $this->di->cache->get($key);
276
277
        if (!$this->meta || $this->ignoreCache) {
278
            $this->meta = $this->createMetaIndex();
279
            $this->di->cache->put($key, $this->meta);
280
        }
281
282
        return $this->meta;
283
    }
284
285
286
287
    /**
288
     * Generate an index for meta files.
289
     *
290
     * @return array as table of content.
291
     */
292
    private function createMetaIndex()
293
    {
294
        $basepath = $this->config["basepath"];
295
        $filter   = $this->config["metafilter"];
296
        $meta     = $this->config["meta"];
297
        $path     = "$basepath/$meta";
298
299
        $meta = [];
300
        foreach (glob_recursive($path) as $file) {
301
            $filepath = substr($file, strlen($basepath) + 1);
302
            
303
            $src = file_get_contents($file);
304
            $filtered = $this->di->textFilter->parse($src, $filter);
305
306
            $key = dirname($filepath);
307
            $meta[$key] = $filtered->frontmatter;
308
309
            // Add Toc to the data array
310
            $meta[$key]["__toc__"] = $this->createBaseRouteToc(dirname($filepath));
311
        }
312
313
        return $meta;
314
    }
315
316
317
318
    /**
319
     * Get a reference to meta data for specific route.
320
     *
321
     * @param string $route current route used to access page.
322
     *
323
     * @return array as table of content.
324
     */
325
    private function getMetaForRoute($route)
326
    {
327
        $base = dirname($route);
328
        return isset($this->meta[$base])
329
            ? $this->meta[$base]
330
            : [];
331
    }
332
333
334
335
    /**
336
     * Get the title of a document.
337
     *
338
     * @deprecated in favor of getFrontmatter
339
     *
340
     * @param string $file to get title from.
341
     *
342
     * @return string as the title.
343
     */
344 View Code Duplication
    private function getTitle($file)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
345
    {
346
        $basepath = $this->config["basepath"];
347
        $filter   = $this->config["textfilter"];
348
349
        $path = $basepath . "/" . $file;
350
        $src = file_get_contents($path);
351
        $filtered = $this->di->textFilter->parse($src, $filter);
352
        return $filtered->frontmatter["title"];
353
    }
354
355
356
357
    /**
358
     * Get the frontmatter of a document.
359
     *
360
     * @param string $file to get frontmatter from.
361
     *
362
     * @return array as frontmatter.
363
     */
364 View Code Duplication
    private function getFrontmatter($file)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
365
    {
366
        $basepath = $this->config["basepath"];
367
        $filter   = $this->config["textfilter"];
368
369
        $path = $basepath . "/" . $file;
370
        $src = file_get_contents($path);
371
        $filtered = $this->di->textFilter->parse($src, $filter);
372
        return $filtered->frontmatter;
373
    }
374
375
376
377
    /**
378
     * Create a table of content for routes at particular level.
379
     *
380
     * @param string $route base route to use.
381
     *
382
     * @return array as the toc.
383
     */
384
    private function createBaseRouteToc($route)
385
    {
386
        $toc = [];
387
        $len = strlen($route);
388
389
        foreach ($this->index as $key => $value) {
390
            if (substr($key, 0, $len) === $route) {
391
                if ($value["internal"] === false
392
                    && $value["tocable"] === true) {
393
                    $toc[$key] = $value;
394
                    
395
                    $frontm = $this->getFrontmatter($value["file"]);
396
                    $toc[$key]["title"] = $frontm["title"];
397
                    $toc[$key]["sectionHeader"] = isset($frontm["sectionHeader"]) ? $frontm["sectionHeader"] : null;
398
                    $toc[$key]["linkable"] = isset($frontm["linkable"]) ? $frontm["linkable"] : null;
399
                }
400
            }
401
        };
402
403
        return $toc;
404
    }
405
406
407
408
    /**
409
     * Map the route to the correct key in the index.
410
     *
411
     * @param string $route current route used to access page.
412
     *
413
     * @return string as key or false if no match.
414
     */
415
    private function mapRoute2IndexKey($route)
416
    {
417
        $route = rtrim($route, "/");
418
419
        if (key_exists($route, $this->index)) {
420
            return $route;
421
        } elseif (empty($route) && key_exists("index", $this->index)) {
422
            return "index";
423
        } elseif (key_exists($route . "/index", $this->index)) {
424
            return "$route/index";
425
        }
426
427
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by Anax\Content\CFileBasedContent::mapRoute2IndexKey of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
428
    }
429
430
431
432
    /**
433
     * Map the route to the correct entry in the index.
434
     *
435
     * @param string $route current route used to access page.
436
     *
437
     * @return array as the matched route.
438
     */
439
    private function mapRoute2Index($route)
440
    {
441
        $routeIndex = $this->mapRoute2IndexKey($route);
442
443
        if ($routeIndex) {
444
            return [$routeIndex, $this->index[$routeIndex]];
445
        }
446
447
        throw new \Anax\Exception\NotFoundException(t("The route '!ROUTE' does not exists in the index.", ["!ROUTE" => $route]));
448
    }
449
450
451
452
    /**
453
     * Get view by mergin information from meta and frontmatter.
454
     *
455
     * @param string $route       current route used to access page.
456
     * @param array  $frontmatter for the content.
457
     * @param string $key         for the view to retrive.
458
     * @param string $distinct    how to merge the array.
459
     *
460
     * @return array with data to add as view.
461
     */
462
    private function getView($route, $frontmatter, $key, $distinct = true)
463
    {
464
        $view = [];
465
466
        // From meta frontmatter
467
        $meta = $this->getMetaForRoute($route);
468
        if (isset($meta[$key])) {
469
            $view = $meta[$key];
470
        }
471
472
        // From document frontmatter
473
        if (isset($frontmatter[$key])) {
474
            if ($distinct) {
475
                $view = array_merge_recursive_distinct($view, $frontmatter[$key]);
476
            } else {
477
                $view = array_merge($view, $frontmatter[$key]);
478
            }
479
        }
480
481
        return $view;
482
    }
483
484
485
486
    /**
487
     * Get details on extra views.
488
     *
489
     * @param string $route       current route used to access page.
490
     * @param array  $frontmatter for the content.
491
     *
492
     * @return array with page data to send to view.
493
     */
494
    private function getViews($route, $frontmatter)
495
    {
496
        // Arrange data into views
497
        $views = $this->getView($route, $frontmatter, "views", false);
498
499
        // Set defaults
500
        if (!isset($views["main"]["template"])) {
501
            $views["main"]["template"] = $this->config["template"];
502
        }
503
        if (!isset($views["main"]["data"])) {
504
            $views["main"]["data"] = [];
505
        }
506
507
        // Merge remaining frontmatter into view main data.
508
        $data = $this->getMetaForRoute($route);
509
        unset($data["__toc__"]);
510
        unset($data["views"]);
511
        unset($frontmatter["views"]);
512
        $data = array_merge_recursive_distinct($data, $frontmatter);
513
        $views["main"]["data"] = array_merge_recursive_distinct($views["main"]["data"], $data);
514
515
        return $views;
516
    }
517
518
519
520
    /**
521
     * Load extra info intro views based of meta information provided in each
522
     * view.
523
     *
524
     * @param string $view  with current settings.
525
     * @param string $route to load view from.
526
     *
527
     * @return array with view details.
528
     */
529
    private function getAdditionalViewDataForRoute($view, $route)
530
    {
531
        // Get filtered content from route
532
        list(, , $filtered) =
533
            $this->mapRoute2Content($route);
534
535
        // From document frontmatter
536
        $view["data"] = array_merge_recursive_distinct($view["data"], $filtered->frontmatter);
0 ignored issues
show
Documentation introduced by
$view['data'] is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
537
        $view["data"]["content"] = $filtered->text;
538
539
        return $view;
540
541
    }
542
543
544
545
    /**
546
     * Order and limit toc items.
547
     *
548
     * @param string &$toc  array with current toc.
549
     * @param string &$meta on how to order and limit toc.
550
     *
551
     * @return void.
0 ignored issues
show
Documentation introduced by
The doc-type void. could not be parsed: Unknown type name "void." at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
552
     */
553
    private function orderAndlimitToc(&$toc, &$meta)
554
    {
555
        $defaults = [
556
            "items" => 7,
557
            "offset" => 0,
558
            "orderby" => "section",
559
            "orderorder" => "asc",
560
        ];
561
        $options = array_merge($defaults, $meta);
562
        $orderby = $options["orderby"];
563
        $order   = $options["orderorder"];
564
565
        $meta["totalItems"] = count($toc);
566
567
        // TODO support pagination by adding entries to $meta
568
569
        uksort($toc, function ($a, $b) use ($toc, $orderby, $order) {
570
            $a = $toc[$a][$orderby];
571
            $b = $toc[$b][$orderby];
572
573
            if ($order == "asc") {
574
                return strcmp($a, $b);
575
            }
576
            return strcmp($b, $a);
577
        });
578
579
        $toc = array_slice($toc, $options["offset"], $options["items"]);
580
        $meta["displayedItems"] = count($toc);
581
    }
582
583
584
585
    /**
586
     * Find next and previous links of current content.
587
     *
588
     * @param string $routeIndex target route to find next and previous for.
589
     *
590
     * @return array with next and previous if found.
591
     */
592
    private function findNextAndPrevious($routeIndex)
593
    {
594
        $key = dirname($routeIndex);
595
        if (!isset($this->meta[$key]["__toc__"])) {
596
            return [null, null];
597
        }
598
599
        $toc = $this->meta[$key]["__toc__"];
600
        if (!isset($toc[$routeIndex])) {
601
            return [null, null];
602
        }
603
604
        $index2Key = array_keys($toc);
605
        $keys = array_flip($index2Key);
606
        $values = array_values($toc);
607
        $count = count($keys);
608
609
        $current = $keys[$routeIndex];
610
        $previous = null;
611 View Code Duplication
        for ($i = $current - 1; $i >= 0; $i--) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
612
            $isSectionHeader = $values[$i]["sectionHeader"];
613
            $isInternal = $values[$i]["internal"];
614
            if ($isSectionHeader || $isInternal) {
615
                continue;
616
            }
617
            $previous = $values[$i];
618
            $previous["route"] = $index2Key[$i];
619
            break;
620
        }
621
        
622
        $next = null;
623 View Code Duplication
        for ($i = $current + 1; $i < $count; $i++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
624
            $isSectionHeader = $values[$i]["sectionHeader"];
625
            $isInternal = $values[$i]["internal"];
626
            if ($isSectionHeader || $isInternal) {
627
                continue;
628
            }
629
            $next = $values[$i];
630
            $next["route"] = $index2Key[$i];
631
            break;
632
        }
633
634
        return [$next, $previous];
635
    }
636
637
638
639
    /**
640
     * Load extra info into views based of meta information provided in each
641
     * view.
642
     *
643
     * @param array  &$views     with all views.
644
     * @param string $route      current route
645
     * @param string $routeIndex route with appended /index
646
     *
647
     * @throws NotFoundException when mapping can not be done.
648
     *
649
     * @return void.
0 ignored issues
show
Documentation introduced by
The doc-type void. could not be parsed: Unknown type name "void." at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
650
     */
651
    private function loadAdditionalContent(&$views, $route, $routeIndex)
652
    {
653
        foreach ($views as $id => $view) {
654
            $meta = isset($view["data"]["meta"])
655
                ? $view["data"]["meta"]
656
                : null;
657
658
            if (is_array($meta)) {
659
                switch ($meta["type"]) {
660
                    case "article-toc":
661
                        $content = $views["main"]["data"]["content"];
662
                        $views[$id]["data"]["articleToc"] = $this->di->textFilter->createToc($content);
663
                        break;
664
665
                    case "breadcrumb":
666
                        $views[$id]["data"]["breadcrumb"] = $this->createBreadcrumb($route);
667
                        break;
668
669 View Code Duplication
                    case "next-previous":
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
670
                        $baseRoute = dirname($routeIndex);
0 ignored issues
show
Unused Code introduced by
$baseRoute is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
671
                        list($next, $previous) = $this->findNextAndPrevious($routeIndex);
672
                        $views[$id]["data"]["next"] = $next;
673
                        $views[$id]["data"]["previous"] = $previous;
674
                        break;
675
676
                    case "single":
677
                        $views[$id] = $this->getAdditionalViewDataForRoute($view, $meta["route"]);
678
                        break;
679
680 View Code Duplication
                    case "toc":
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
681
                        $baseRoute = dirname($routeIndex);
682
                        $toc = $this->meta[$baseRoute]["__toc__"];
683
                        $this->orderAndlimitToc($toc, $meta);
684
                        $views[$id]["data"]["toc"] = $toc;
685
                        $views[$id]["data"]["meta"] = $meta;
686
                        break;
687
688
                    default:
689
                        throw new Exception(t("Unsupported data/meta/type for additional content."));
690
                }
691
            }
692
        }
693
    }
694
695
696
697
    /**
698
     * Load extra info intro views based of meta information provided in each
699
     * view.
700
     *
701
     * @param string $key     array with all views.
702
     * @param string $content array with all views.
703
     *
704
     * @throws NotFoundException when mapping can not be done.
705
     *
706
     * @return void.
0 ignored issues
show
Documentation introduced by
The doc-type void. could not be parsed: Unknown type name "void." at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
707
     */
708
    private function loadFileContent($key, $content)
709
    {
710
        // Settings from config
711
        $basepath = $this->config["basepath"];
712
        $filter   = $this->config["textfilter"];
713
714
        // Whole path to file
715
        $path = $basepath . "/" . $content["file"];
716
        $content["path"] = $path;
717
718
        // Load content from file
719
        if (!is_file($path)) {
720
            $msg = t("The content '!ROUTE' does not exists as a file '!FILE'.", ["!ROUTE" => $key, "!FILE" => $path]);
721
            throw new \Anax\Exception\NotFoundException($msg);
722
        }
723
724
        // Get filtered content
725
        $src = file_get_contents($path);
726
        $filtered = $this->di->textFilter->parse($src, $filter);
727
728
        return [$content, $filtered];
729
    }
730
731
732
733
    /**
734
     * Look up the route in the index and use that to retrieve the filtered
735
     * content.
736
     *
737
     * @param string $route to look up.
738
     *
739
     * @return array with content and filtered version.
740
     */
741
    public function mapRoute2Content($route)
742
    {
743
        // Look it up in the index
744
        list($keyIndex, $content) = $this->mapRoute2Index($route);
745
        list($content, $filtered) = $this->loadFileContent($keyIndex, $content);
746
747
        return [$keyIndex, $content, $filtered];
748
    }
749
750
751
752
    /**
753
     * Map url to content if such mapping can be done, exclude internal routes.
754
     *
755
     * @param string $route optional route to look up.
756
     *
757
     * @return object with content and filtered version.
758
     */
759
    public function contentForRoute($route = null)
760
    {
761
        $content = $this->contentForInternalRoute($route);
762
        if ($content->internal === true) {
763
            $msg = t("The content '!ROUTE' does not exists as a public route.", ["!ROUTE" => $route]);
764
            throw new \Anax\Exception\NotFoundException($msg);
765
        }
766
767
        return $content;
768
    }
769
770
771
772
    /**
773
     * Map url to content, even internal content, if such mapping can be done.
774
     *
775
     * @param string $route optional route to look up.
776
     *
777
     * @return object with content and filtered version.
778
     */
779
    public function contentForInternalRoute($route = null)
780
    {
781
        // Get the route
782
        if (is_null($route)) {
783
            $route = $this->di->request->getRoute();
784
        }
785
786
        // TODO cache route content.
787
788
        // Load index and map route to content
789
        $this->loadIndex();
790
        $this->loadMetaIndex();
791
        list($routeIndex, $content, $filtered) = $this->mapRoute2Content($route);
792
793
        // TODO Should not supply all frontmatter to theme, only the
794
        // parts valid to the index template. Separate that data into own
795
        // holder in frontmatter. Do not include whole frontmatter? Only
796
        // on debg?
797
        $content["frontmatter"] = $filtered->frontmatter;
798
799
        // Create and arrange the content as views
800
        $content["views"] = $this->getViews($routeIndex, $filtered->frontmatter);
801
802
        //
803
        // TODO Load content, pure or use data available
804
        // own functuion
805
        // perhaps load in separate view
806
        //
807
        $content["views"]["main"]["data"]["content"] = $filtered->text;
808
        $content["views"]["main"]["data"]["excerpt"] = $filtered->excerpt;
809
        $this->loadAdditionalContent($content["views"], $route, $routeIndex);
810
811
        return (object) $content;
812
    }
813
}
814