Passed
Push — master ( 9eea33...054d19 )
by Mikael
05:44
created

src/Content/FBCLoadAdditionalContentTrait.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Anax\Content;
4
5
/**
6
 * File Based Content, code for loading additional content into view through
7
 * data["meta"].
8
 */
9
trait FBCLoadAdditionalContentTrait
10
{
11
    /**
12
     * Load extra info into views based of meta information provided in each
13
     * view.
14
     *
15
     * @param array  &$views     with all views.
16
     * @param string $route      current route
17
     * @param string $routeIndex route with appended /index
18
     *
19
     * @throws NotFoundException when mapping can not be done.
20
     *
21
     * @return void.
22
     */
23
    private function loadAdditionalContent(&$views, $route, $routeIndex)
24
    {
25
        foreach ($views as $id => $view) {
26
            $meta = isset($view["data"]["meta"])
27
                ? $view["data"]["meta"]
28
                : null;
29
30
            if (is_array($meta)) {
31
                switch ($meta["type"]) {
32
                    case "article-toc":
33
                        $content = $views["main"]["data"]["content"];
34
                        $views[$id]["data"]["articleToc"] = $this->di->textFilter->createToc($content);
35
                        break;
36
37
                    case "breadcrumb":
38
                        $views[$id]["data"]["breadcrumb"] = $this->createBreadcrumb($route);
39
                        break;
40
41 View Code Duplication
                    case "next-previous":
0 ignored issues
show
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...
42
                        $baseRoute = dirname($routeIndex);
43
                        list($next, $previous) = $this->findNextAndPrevious($routeIndex);
44
                        $views[$id]["data"]["next"] = $next;
45
                        $views[$id]["data"]["previous"] = $previous;
46
                        break;
47
48
                    case "single": // OBSOLETE
49
                    case "content":
50
                        $route = $this->getActiveRoute($meta["route"], $routeIndex);
51
52
                        // Load and parse route as view. Load meta view
53
                        // if any.
54
                        // Current view details preceds the loaded once.
55
                        $view = $this->loadAndParseRoute($route);
56
                        $views[$id] = array_merge_recursive_distinct($view, $views[$id]);
57
                        break;
58
59
                    case "columns":
60
                        // Each column is an own view set with details
61
                        // Process as meta view and load additional content
62
                        $template = isset($meta["template"])
63
                            ? $meta["template"]
64
                            : null;
65
                        $columns = $meta["columns"];
66
                        foreach ($columns as $key => $view) {
67
                            $views2 = [ "main" => $view ];
68
                            $this->loadAdditionalContent($views2, $route, $routeIndex);
69
                            $columns[$key] = $views2["main"];
70
                            
71
                            if ($template) {
72
                                $columns[$key]["template"] = $template;
73
                            }
74
                        }
75
                        $views[$id]["data"]["columns"] = $columns;
76
                        break;
77
78
                    case "toc-sort":
79
                        $baseRoute = dirname($routeIndex);
80
                        $this->orderToc($baseRoute, $meta);
81
                        break;
82
83 View Code Duplication
                    case "toc":
0 ignored issues
show
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...
84
                        $baseRoute = dirname($routeIndex);
85
                        $toc = $this->meta[$baseRoute]["__toc__"];
86
                        $this->limitToc($toc, $meta);
87
                        $views[$id]["data"]["toc"] = $toc;
88
                        $views[$id]["data"]["meta"] = $meta;
89
                        break;
90
91
                    case "toc-route":
92
                        // Get the toc for a specific route
93
                        $route = $this->getActiveRoute($meta["route"], $routeIndex);
94
                        $routeIndex2 = $this->mapRoute2IndexKey($route);
95
                        $baseRoute = dirname($routeIndex2);
96
97
                        // Include support for ordering
98
                        if (isset($meta["orderby"])
99
                            || isset($meta["orderorder"])) {
100
                            $this->orderToc($baseRoute, $meta);
101
                        }
102
103
                        // Same as toc
104
                        $toc = $this->meta[$baseRoute]["__toc__"];
105
                        $this->limitToc($toc, $meta, $baseRoute);
106
                        $views[$id]["data"]["toc"] = $toc;
107
                        $views[$id]["data"]["meta"] = $meta;
108
                        break;
109
110
                    case "book-toc":
111
                        $toc = $this->meta[$baseRoute]["__toc__"];
112
                        $views[$id]["data"]["toc"] = $toc;
113
                        break;
114
115
                    case "author":
116
                        if (isset($views["main"]["data"]["author"])) {
117
                            $views[$id]["data"]["author"] = $this->loadAuthorDetails($views["main"]["data"]["author"]);
118
                        }
119
                        break;
120
121
                    case "copy":
122
                        $viewToCopy = $views[$id]["data"]["meta"]["view"];
123
                        $views[$id]["data"] = array_merge_recursive_distinct(
124
                            $views[$viewToCopy]["data"],
125
                            $views[$id]["data"]
126
                        );
127
                        break;
128
129
                    default:
130
                        $msg = t("Unsupported data/meta/type '!TYPE' for additional content.", [
131
                            "!TYPE" => $meta["type"]
132
                        ]);
133
                        throw new Exception($msg);
134
                }
135
            }
136
        }
137
    }
138
139
140
141
    /**
142
     * Find next and previous links of current content.
143
     *
144
     * @param string $routeIndex target route to find next and previous for.
145
     *
146
     * @return array with next and previous if found.
147
     */
148
    private function findNextAndPrevious($routeIndex)
149
    {
150
        $key = dirname($routeIndex);
151
        if (!isset($this->meta[$key]["__toc__"])) {
152
            return [null, null];
153
        }
154
155
        $toc = $this->meta[$key]["__toc__"];
156
        if (!isset($toc[$routeIndex])) {
157
            return [null, null];
158
        }
159
160
        $index2Key = array_keys($toc);
161
        $keys = array_flip($index2Key);
162
        $values = array_values($toc);
163
        $count = count($keys);
164
165
        $current = $keys[$routeIndex];
166
        $previous = null;
167 View Code Duplication
        for ($i = $current - 1; $i >= 0; $i--) {
168
            $isSectionHeader = $values[$i]["sectionHeader"];
169
            $isInternal = $values[$i]["internal"];
170
            if ($isSectionHeader || $isInternal) {
171
                continue;
172
            }
173
            $previous = $values[$i];
174
            $previous["route"] = $index2Key[$i];
175
            break;
176
        }
177
        
178
        $next = null;
179 View Code Duplication
        for ($i = $current + 1; $i < $count; $i++) {
180
            $isSectionHeader = $values[$i]["sectionHeader"];
181
            $isInternal = $values[$i]["internal"];
182
            if ($isSectionHeader || $isInternal) {
183
                continue;
184
            }
185
            $next = $values[$i];
186
            $next["route"] = $index2Key[$i];
187
            break;
188
        }
189
190
        return [$next, $previous];
191
    }
192
193
194
195
    /**
196
     * Order toc items.
197
     *
198
     * @param string $baseRoute route to use to find __toc__.
199
     * @param string $meta on how to order toc.
200
     *
201
     * @return void.
202
     */
203
    private function orderToc($baseRoute, $meta)
204
    {
205
        $defaults = [
206
            "orderby" => "section",
207
            "orderorder" => "asc",
208
        ];
209
        $options = array_merge($defaults, $meta);
210
        $orderby = $options["orderby"];
211
        $order   = $options["orderorder"];
212
        $toc = $this->meta[$baseRoute]["__toc__"];
213
214
        uksort($toc, function ($a, $b) use ($toc, $orderby, $order) {
215
                $a = $toc[$a][$orderby];
216
                $b = $toc[$b][$orderby];
217
218
                $asc = $order == "asc" ? 1 : -1;
219
                
220
            if ($a == $b) {
221
                return 0;
222
            } elseif ($a > $b) {
223
                return $asc;
224
            }
225
                return -$asc;
226
        });
227
        
228
        $this->meta[$baseRoute]["__toc__"] = $toc;
229
    }
230
231
232
    /**
233
     * Limit and paginate toc items.
234
     *
235
     * @param string &$toc      array with current toc.
236
     * @param string &$meta     on how to order and limit toc.
237
     * @param string $baseRoute prepend to next & previous urls.
238
     *
239
     * @return void.
240
     */
241
    private function limitToc(&$toc, &$meta, $baseRoute = null)
242
    {
243
        $defaults = [
244
            "items" => 7,
245
            "offset" => 0,
246
        ];
247
        $options = array_merge($defaults, $meta);
248
249
        // Check if pagination is currently used
250
        if ($this->currentPage) {
251
            $options["offset"] = ($this->currentPage - 1) * $options["items"];
252
        }
253
254
        $meta["totalItems"] = count($toc);
255
        $meta["currentPage"] = (int) floor($options["offset"] / $options["items"]) + 1;
256
        $meta["totalPages"] = (int) floor($meta["totalItems"] / $options["items"] + 1);
257
258
        // Next and previous page
259
        $pagination = $this->config["pagination"];
260
        $meta["nextPageUrl"] = null;
261
        $meta["previousPageUrl"] = null;
262
        $baseRoute = isset($baseRoute)
263
            ? $baseRoute
264
            : $this->baseRoute;
265
266
        if ($meta["currentPage"] > 1 && $meta["totalPages"] > 1) {
267
            $previousPage = $meta["currentPage"] - 1;
268
            $previous = "";
269
            if ($previousPage != 1) {
270
                $previous = "$pagination/$previousPage";
271
            }
272
            $meta["previousPageUrl"] = "$baseRoute/$previous";
273
        }
274
275
        if ($meta["currentPage"] < $meta["totalPages"]) {
276
            $nextPage = $meta["currentPage"] + 1;
277
            $meta["nextPageUrl"] = "$baseRoute/$pagination/$nextPage";
278
        }
279
280
281
        // Only use slice of toc
282
        $startSlice = ($meta["currentPage"] - 1) * $options["items"];
283
        $toc = array_slice($toc, $startSlice, $options["items"]);
284
        $meta["displayedItems"] = count($toc);
285
    }
286
}
287