Completed
Push — master ( b28ef5...58dbbf )
by Mikael
02:32
created

CFileBasedContent::useCache()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 2
Bugs 0 Features 2
Metric Value
c 2
b 0
f 2
dl 0
loc 6
ccs 0
cts 6
cp 0
rs 9.4285
cc 1
eloc 3
nc 1
nop 1
crap 2
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
        TFBCBreadcrumb,
13
        TFBCLoadAdditionalContent,
14
        TFBCUtilities;
15
16
17
18
    /**
19
     * All routes.
20
     */
21
    private $index = null;
22
23
    /**
24
     * All authors.
25
     */
26
    private $author = null;
27
28
    /**
29
     * All categories.
30
     */
31
    private $category = null;
32
33
    /**
34
     * All routes having meta.
35
     */
36
    private $meta = null;
37
38
    /**
39
     * Use cache or recreate each time.
40
     */
41
    private $ignoreCache = false;
42
    
43
    /**
44
     * File name pattern, all files must match this pattern and the first
45
     * numbered part is optional, the second part becomes the route.
46
     */
47
    private $filenamePattern = "#^(\d*)_*([^\.]+)\.md$#";
48
49
    /**
50
     * Internal routes that is marked as internal content routes and not
51
     * exposed as public routes.
52
     */
53
    private $internalRouteDirPattern = [
54
        "#block/#",
55
    ];
56
57
    private $internalRouteFilePattern = [
58
        "#^block[_-]{1}#",
59
        "#^_#",
60
    ];
61
62
    /**
63
     * Routes that should be used in toc.
64
     */
65
    private $allowedInTocPattern = "([\d]+_(\w)+)";
66
67
68
69
    /**
70
     * Set default values from configuration.
71
     *
72
     * @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...
73
     */
74
    public function setDefaultsFromConfiguration()
75
    {
76
        $this->ignoreCache = isset($this->config["ignoreCache"])
77
            ? $this->config["ignoreCache"]
78
            : $this->ignoreCache;
79
80
        return $this;
81
    }
82
83
84
85
    /**
86
     * Should the cache be used or ignored.
87
     *
88
     * @param boolean $use true to use the cache or false to ignore the cache
89
     *
90
     * @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...
91
     */
92
    public function useCache($use)
93
    {
94
        $this->ignoreCache = !$use;
95
96
        return $this;
97
    }
98
99
100
101
    /**
102
     * Create the index of all content into an array.
103
     *
104
     * @param string $type of index to load.
105
     *
106
     * @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...
107
     */
108
    private function load($type)
109
    {
110
        $index = $this->$type;
111
        if ($index) {
112
            return;
113
        }
114
115
        $cache = $this->di->get("cache");
116
        $key = $cache->createKey(__CLASS__, $type);
117
        $index = $cache->get($key);
118
119
        if (is_null($index) || $this->ignoreCache) {
120
            $createMethod = "create$type";
121
            $index = $this->$createMethod();
122
            $cache->put($key, $index);
123
        }
124
125
        $this->$type = $index;
126
    }
127
128
129
130
131
    // = Create and manage index ==================================
132
133
    /**
134
     * Generate an index from the directory structure.
135
     *
136
     * @return array as index for all content files.
137
     */
138
    private function createIndex()
139
    {
140
        $basepath   = $this->config["basepath"];
141
        $pattern    = $this->config["pattern"];
142
        $path       = "$basepath/$pattern";
143
144
        $index = [];
145
        foreach (glob_recursive($path) as $file) {
146
            $filepath = substr($file, strlen($basepath) + 1);
147
148
            // Find content files
149
            $matches = [];
150
            preg_match($this->filenamePattern, basename($filepath), $matches);
151
            $dirpart = dirname($filepath) . "/";
152
            if ($dirpart === "./") {
153
                $dirpart = null;
154
            }
155
            $key = $dirpart . $matches[2];
156
            
157
            // Create level depending on the file id
158
            // TODO ciamge doc, can be replaced by __toc__ in meta?
159
            $id = $matches[1];
160
            $level = 2;
161 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...
162
                $level = 0;
163
            } elseif ($id % 10 === 0) {
164
                $level = 1;
165
            }
166
167
            $index[$key] = [
168
                "file"     => $filepath,
169
                "section"  => $matches[1],
170
                "level"    => $level,  // TODO ?
171
                "internal" => $this->isInternalRoute($filepath),
172
                "tocable"  => $this->allowInToc($filepath),
173
            ];
174
        }
175
176
        return $index;
177
    }
178
179
180
181
    /**
182
     * Check if a filename is to be marked as an internal route..
183
     *
184
     * @param string $filepath as the basepath (routepart) to the file.
185
     *
186
     * @return boolean true if the route content is internal, else false
187
     */
188
    private function isInternalRoute($filepath)
189
    {
190
        foreach ($this->internalRouteDirPattern as $pattern) {
191
            if (preg_match($pattern, $filepath)) {
192
                return true;
193
            }
194
        }
195
196
        $filename = basename($filepath);
197
        foreach ($this->internalRouteFilePattern as $pattern) {
198
            if (preg_match($pattern, $filename)) {
199
                return true;
200
            }
201
        }
202
203
        return false;
204
    }
205
206
207
208
    /**
209
     * Check if filepath should be used as part of toc.
210
     *
211
     * @param string $filepath as the basepath (routepart) to the file.
212
     *
213
     * @return boolean true if the route content shoul dbe in toc, else false
214
     */
215
    private function allowInToc($filepath)
216
    {
217
        return (boolean) preg_match($this->allowedInTocPattern, $filepath);
218
    }
219
220
221
222
    // = Create and manage meta ==================================
223
224
    /**
225
     * Generate an index for meta files.
226
     *
227
     * @return array as meta index.
228
     */
229
    private function createMeta()
230
    {
231
        $basepath = $this->config["basepath"];
232
        $filter   = $this->config["textfilter-frontmatter"];
233
        $pattern  = $this->config["meta"];
234
        $path     = "$basepath/$pattern";
235
        $textfilter = $this->di->get("textFilter");
236
237
        $index = [];
238
        foreach (glob_recursive($path) as $file) {
239
            // The key entry to index
240
            $key = dirname(substr($file, strlen($basepath) + 1));
241
242
            // Get info from base document
243
            $src = file_get_contents($file);
244
            $filtered = $textfilter->parse($src, $filter);
245
            $index[$key] = $filtered->frontmatter;
246
247
            // Add Toc to the data array
248
            $index[$key]["__toc__"] = $this->createBaseRouteToc($key);
249
        }
250
251
        // Add author details
252
        $this->meta = $index;
253
        $this->createAuthor();
254
255
        return $this->meta;
256
    }
257
258
259
260
    /**
261
     * Get a reference to meta data for specific route.
262
     *
263
     * @param string $route current route used to access page.
264
     *
265
     * @return array as table of content.
266
     */
267
    private function getMetaForRoute($route)
268
    {
269
        $base = dirname($route);
270
        return isset($this->meta[$base])
271
            ? $this->meta[$base]
272
            : [];
273
    }
274
275
276
277
    /**
278
     * Create a table of content for routes at particular level.
279
     *
280
     * @param string $route base route to use.
281
     *
282
     * @return array as the toc.
283
     */
284
    private function createBaseRouteToc($route)
285
    {
286
        $toc = [];
287
        $len = strlen($route);
288
289
        foreach ($this->index as $key => $value) {
290
            if (substr($key, 0, $len) === $route) {
291
                if ($value["internal"] === false
292
                    && $value["tocable"] === true) {
293
                    $toc[$key] = $value;
294
                    
295
                    $frontm = $this->getFrontmatter($value["file"]);
296
                    $toc[$key]["title"] = $frontm["title"];
297
                    $toc[$key]["sectionHeader"] = isset($frontm["sectionHeader"]) ? $frontm["sectionHeader"] : null;
298
                    $toc[$key]["linkable"] = isset($frontm["linkable"]) ? $frontm["linkable"] : null;
299
                }
300
            }
301
        };
302
303
        return $toc;
304
    }
305
306
307
308
    // = Deal with authors ====================================
309
    
310
    /**
311
     * Generate a lookup index for authors that maps into the meta entry
312
     * for the author.
313
     *
314
     * @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...
315
     */
316
    private function createAuthor()
317
    {
318
        $pattern = $this->config["author"];
319
320
        $index = [];
321
        $matches = [];
322
        foreach ($this->meta as $key => $entry) {
323
            if (preg_match($pattern, $key, $matches)) {
324
                $acronym = $matches[1];
325
                $index[$acronym] = $key;
326
                $this->meta[$key]["acronym"] = $acronym;
327
                
328
                // Get content for byline
329
                $route = "$key/byline";
330
                $data = $this->getDataForAdditionalRoute($route);
331
                $this->meta[$key]["byline"] = $data["content"];
332
            }
333
        }
334
335
        return $index;
336
    }
337
338
339
340
    /**
341
     * Find next and previous links of current content.
342
     *
343
     * @param array|string $author with details on the author(s).
344
     *
345
     * @return array with more details on the authors(s).
346
     */
347
    private function loadAuthorDetails($author)
348
    {
349
        if (is_array($author) && is_array(array_values($author)[0])) {
350
            return $author;
351
        }
352
353
        if (!is_array($author)) {
354
            $tmp = $author;
355
            $author = [];
356
            $author[] = $tmp;
357
        }
358
359
        $authors = [];
360
        foreach ($author as $acronym) {
361
            if (isset($this->author[$acronym])) {
362
                $key = $this->author[$acronym];
363
                $authors[$acronym] = $this->meta[$key];
364
                unset($authors[$acronym]["__toc__"]);
365
            }
366
        }
367
368
        return $authors;
369
    }
370
371
372
373
    // == Used by meta and breadcrumb (to get title) ===========================
374
    // TODO REFACTOR THIS?
375
    // Support getting only frontmatter.
376
    // Merge with function that retrieves whole filtered since getting
377
    // frontmatter will involve full parsing of document.
378
    // Title is retrieved from the HTML code.
379
    // Also do cacheing of each retrieved and parsed document
380
    // in this cycle, to gather code that loads and parses a individual
381
    // document. 
382
    
383
    /**
384
     * Get the frontmatter of a document.
385
     *
386
     * @param string $file to get frontmatter from.
387
     *
388
     * @return array as frontmatter.
389
     */
390
    private function getFrontmatter($file)
391
    {
392
        $basepath = $this->config["basepath"];
393
        $filter1  = $this->config["textfilter-frontmatter"];
394
        $filter2  = $this->config["textfilter"];
395
        $filter = array_merge($filter1, $filter2);
396
        
397
        $path = $basepath . "/" . $file;
398
        $src = file_get_contents($path);
399
        $filtered = $this->di->get("textFilter")->parse($src, $filter);
400
        return $filtered->frontmatter;
401
    }
402
403
404
405
    // = Section X to be labeled  ==================================
406
407
    /**
408
     * Load the content from filtered and parse it step two.
409
     *
410
     * @param string $file to get content from.
411
     *
412
     * @return object as filtered content.
413
     */
414
/*
415
    private function loadPureContentPhase2($filtered)
416
    {
417
        $filter = $this->config["textfilter"];
418
        $text = $filtered->text;
419
420
        // Get new filtered content
421
        $new = $this->di->get("textFilter")->parse($text, $filter);
422
        $filtered->text = $new->text;
423
424
        // Update all anchor urls to use baseurl, needs info about baseurl
425
        // from merged frontmatter
426
        //$baseurl = $this->getBaseurl($content["views"]);
427
        //$this->addBaseurl2AnchorUrls($filtered, $baseurl);
428
429
        return $filtered;
430
    }
431
432
*/
433
434
435
    // == Look up route in index ===================================
436
    
437
    /**
438
     * Map the route to the correct key in the index.
439
     *
440
     * @param string $route current route used to access page.
441
     *
442
     * @return string as key or false if no match.
443
     */
444
    private function mapRoute2IndexKey($route)
445
    {
446
        $route = rtrim($route, "/");
447
448
        if (key_exists($route, $this->index)) {
449
            return $route;
450
        } elseif (empty($route) && key_exists("index", $this->index)) {
451
            return "index";
452
        } elseif (key_exists($route . "/index", $this->index)) {
453
            return "$route/index";
454
        }
455
456
        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...
457
    }
458
459
460
461
    /**
462
     * Map the route to the correct entry in the index.
463
     *
464
     * @param string $route current route used to access page.
465
     *
466
     * @return array as the matched route.
467
     */
468
    private function mapRoute2Index($route)
469
    {
470
        $routeIndex = $this->mapRoute2IndexKey($route);
471
472
        if ($routeIndex) {
473
            return [$routeIndex, $this->index[$routeIndex]];
474
        }
475
476
        throw new \Anax\Exception\NotFoundException(t("The route '!ROUTE' does not exists in the index.", ["!ROUTE" => $route]));
477
    }
478
479
480
481
    // = Get view data by merging from meta and current frontmatter =========
482
    
483
    /**
484
     * Get view by mergin information from meta and frontmatter.
485
     *
486
     * @param string $route       current route used to access page.
487
     * @param array  $frontmatter for the content.
488
     * @param string $key         for the view to retrive.
489
     * @param string $distinct    how to merge the array.
490
     *
491
     * @return array with data to add as view.
492
     */
493
    private function getView($route, $frontmatter, $key, $distinct = true)
494
    {
495
        $view = [];
496
497
        // From meta frontmatter
498
        $meta = $this->getMetaForRoute($route);
499
        if (isset($meta[$key])) {
500
            $view = $meta[$key];
501
        }
502
503
        // From document frontmatter
504
        if (isset($frontmatter[$key])) {
505
            if ($distinct) {
506
                $view = array_merge_recursive_distinct($view, $frontmatter[$key]);
507
            } else {
508
                $view = array_merge($view, $frontmatter[$key]);
509
            }
510
        }
511
512
        return $view;
513
    }
514
515
516
517
    /**
518
     * Get details on extra views.
519
     *
520
     * @param string $route       current route used to access page.
521
     * @param array  $frontmatter for the content.
522
     *
523
     * @return array with page data to send to view.
524
     */
525
    private function getViews($route, $frontmatter)
526
    {
527
        // Arrange data into views
528
        $views = $this->getView($route, $frontmatter, "views", false);
529
530
        // Set defaults
531
        if (!isset($views["main"]["template"])) {
532
            $views["main"]["template"] = $this->config["template"];
533
        }
534
        if (!isset($views["main"]["data"])) {
535
            $views["main"]["data"] = [];
536
        }
537
538
        // Merge remaining frontmatter into view main data.
539
        $data = $this->getMetaForRoute($route);
540
        unset($data["__toc__"]);
541
        unset($data["views"]);
542
        unset($frontmatter["views"]);
543
544
        if ($frontmatter) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $frontmatter of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
545
            $data = array_merge_recursive_distinct($data, $frontmatter);
546
        }
547
        $views["main"]["data"] = array_merge_recursive_distinct($views["main"]["data"], $data);
548
549
        return $views;
550
    }
551
552
553
554
    // == Create and load content ===================================
555
556
    /**
557
     * Map url to content, even internal content, if such mapping can be done.
558
     *
559
     * @param string $route route to look up.
560
     *
561
     * @return object with content and filtered version.
562
     */
563
    private function createContentForInternalRoute($route)
564
    {
565
        // Load index and map route to content
566
        $this->load("index");
567
        $this->load("meta");
568
        $this->load("author");
569
        list($routeIndex, $content, $filtered) = $this->mapRoute2Content($route);
570
571
        // Create and arrange the content as views, merge with .meta,
572
        // frontmatter is complete.
573
        $content["views"] = $this->getViews($routeIndex, $filtered->frontmatter);
574
575
        // Do process content step two when all frontmatter is included.
576
        $this->processMainContentPhaseTwo($content, $filtered);
577
        
578
        // Set details of content
579
        $content["views"]["main"]["data"]["content"] = $filtered->text;
580
        $content["views"]["main"]["data"]["excerpt"] = $filtered->excerpt;
581
        $this->loadAdditionalContent($content["views"], $route, $routeIndex);
582
583
        // TODO Should not supply all frontmatter to theme, only the
584
        // parts valid to the index template. Separate that data into own
585
        // holder in frontmatter. Do not include whole frontmatter? Only
586
        // on debg?
587
        $content["frontmatter"] = $filtered->frontmatter;
588
589
        return (object) $content;
590
    }
591
592
593
594
    /**
595
     * Look up the route in the index and use that to retrieve the filtered
596
     * content.
597
     *
598
     * @param string $route to look up.
599
     *
600
     * @return array with content and filtered version.
601
     */
602
    private function mapRoute2Content($route)
603
    {
604
        // Look it up in the index
605
        list($keyIndex, $content) = $this->mapRoute2Index($route);
606
        $filtered = $this->loadFileContentPhaseOne($keyIndex);
607
608
        return [$keyIndex, $content, $filtered];
609
    }
610
611
612
613
    /**
614
     * Load content file and frontmatter, this is the first time we process
615
     * the content.
616
     *
617
     * @param string $key     to index with details on the route.
618
     *
619
     * @throws NotFoundException when mapping can not be done.
620
     *
621
     * @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...
622
     */
623
    private function loadFileContentPhaseOne($key)
624
    {
625
        // Settings from config
626
        $basepath = $this->config["basepath"];
627
        $filter   = $this->config["textfilter-frontmatter"];
628
629
        // Whole path to file
630
        $path = $basepath . "/" . $this->index[$key]["file"];
631
632
        // Load content from file
633
        if (!is_file($path)) {
634
            $msg = t("The content '!ROUTE' does not exists as a file '!FILE'.", ["!ROUTE" => $key, "!FILE" => $path]);
635
            throw new \Anax\Exception\NotFoundException($msg);
636
        }
637
638
        // Get filtered content
639
        $src = file_get_contents($path);
640
        $filtered = $this->di->get("textFilter")->parse($src, $filter);
641
642
        return $filtered;
643
    }
644
645
646
647
    // == Process content phase 2 ===================================
648
    // TODO REFACTOR THIS?
649
    
650
    /**
651
     * Look up the route in the index and use that to retrieve the filtered
652
     * content.
653
     *
654
     * @param array  &$content   to process.
655
     * @param object &$filtered to use for settings.
656
     *
657
     * @return array with content and filtered version.
658
     */
659
     private function processMainContentPhaseTwo(&$content, &$filtered)
660
     {
661
        // From configuration
662
         $filter = $this->config["textfilter"];
663
         $revisionStart = $this->config["revision-history"]["start"];
664
         $revisionEnd   = $this->config["revision-history"]["end"];
665
         $revisionClass = $this->config["revision-history"]["class"];
666
         
667
         $textFilter = $this->di->get("textFilter");
668
         $text = $filtered->text;
669
670
         // Check if revision history is to be included
671
         if (isset($content["views"]["main"]["data"]["revision"])) {
672
             $text = $textFilter->addRevisionHistory(
673
                 $text,
674
                 $content["views"]["main"]["data"]["revision"],
675
                 $revisionStart,
676
                 $revisionEnd,
677
                 $revisionClass
678
             );
679
         }
680
681
         // Get new filtered content (and updated frontmatter)
682
         $new = $textFilter->parse($text, $filter);
683
         $filtered->text = $new->text;
684
         if ($filtered->frontmatter) {
685
             $filtered->frontmatter = array_merge_recursive_distinct($filtered->frontmatter, $new->frontmatter);
686
         } else {
687
             $filtered->frontmatter = $new->frontmatter;
688
         }
689
690
        // Load details on author if set.
691
        if (isset($content["views"]["main"]["data"]["author"])) {
692
            $content["views"]["main"]["data"]["author"] = $this->loadAuthorDetails($content["views"]["main"]["data"]["author"]);
693
        }
694
695
         // Update all anchor urls to use baseurl, needs info about baseurl
696
         // from merged frontmatter
697
         $baseurl = isset($content["views"]["main"]["baseurl"])
698
            ? $content["views"]["main"]["baseurl"]
699
            : null;
700
         $this->addBaseurl2AnchorUrls($filtered, $baseurl);
701
702
         // Add excerpt and hasMore, if available
703
         $textFilter->addExcerpt($filtered);
704
    }
705
706
707
708
    // == Public methods ============================================
709
    
710
    /**
711
     * Map url to content, even internal content, if such mapping can be done.
712
     *
713
     * @param string $route optional route to look up.
714
     *
715
     * @return object with content and filtered version.
716
     */
717
    public function contentForInternalRoute($route = null)
718
    {
719
        // Get the route
720
        if (is_null($route)) {
721
            $route = $this->di->get("request")->getRoute();
722
        }
723
724
        // Check cache for content or create cached version of content
725
        $slug = $this->di->get("url")->slugify($route);
726
        $key = $this->di->cache->createKey(__CLASS__, "route-$slug");
727
        $content = $this->di->cache->get($key);
728
729
        if (!$content || $this->ignoreCache) {
730
            $content = $this->createContentForInternalRoute($route);
731
            $this->di->cache->put($key, $content);
732
        }
733
734
        return $content;
735
    }
736
737
738
739
    /**
740
     * Map url to content if such mapping can be done, exclude internal routes.
741
     *
742
     * @param string $route optional route to look up.
743
     *
744
     * @return object with content and filtered version.
745
     */
746
    public function contentForRoute($route = null)
747
    {
748
        $content = $this->contentForInternalRoute($route);
749
        if ($content->internal === true) {
750
            $msg = t("The content '!ROUTE' does not exists as a public route.", ["!ROUTE" => $route]);
751
            throw new \Anax\Exception\NotFoundException($msg);
752
        }
753
754
        return $content;
755
    }
756
}
757