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

src/Content/FBCLoadAdditionalContentTrait.php (1 issue)

Severity

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
                    case "next-previous":
42
                        $baseRoute = dirname($routeIndex);
43
                        $this->orderToc($baseRoute, $meta);
44
                        list($next, $previous) = $this->findNextAndPrevious($routeIndex);
45
                        $views[$id]["data"]["next"] = $next;
46
                        $views[$id]["data"]["previous"] = $previous;
47
                        break;
48
49
                    case "single": // OBSOLETE
50
                    case "content":
51
                        $route = $this->getActiveRoute($meta["route"], $routeIndex);
52
53
                        // Load and parse route as view. Load meta view
54
                        // if any.
55
                        // Current view details preceds the loaded once.
56
                        $view = $this->loadAndParseRoute($route);
57
                        $views[$id] = array_merge_recursive_distinct($view, $views[$id]);
58
                        break;
59
60
                    case "columns":
61
                        // Each column is an own view set with details
62
                        // Process as meta view and load additional content
63
                        $template = isset($meta["template"])
64
                            ? $meta["template"]
65
                            : null;
66
                        $columns = $meta["columns"];
67
                        foreach ($columns as $key => $view) {
68
                            $views2 = [ "main" => $view ];
69
                            $this->loadAdditionalContent($views2, $route, $routeIndex);
70
                            $columns[$key] = $views2["main"];
71
                            
72
                            if ($template) {
73
                                $columns[$key]["template"] = $template;
74
                            }
75
                        }
76
                        $views[$id]["data"]["columns"] = $columns;
77
                        break;
78
79
                    case "toc-sort":
80
                        $baseRoute = dirname($routeIndex);
81
                        $this->orderToc($baseRoute, $meta);
0 ignored issues
show
$meta is of type array<string,?,{"type":"?"}>, but the function expects a string.

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...
82
                        break;
83
84
                    case "toc":
85
                        $baseRoute = dirname($routeIndex);
86
                        $toc = $this->meta[$baseRoute]["__toc__"];
87
                        $this->limitToc($toc, $meta);
88
                        $views[$id]["data"]["toc"] = $toc;
89
                        $views[$id]["data"]["meta"] = $meta;
90
                        break;
91
92
                    case "toc-route":
93
                        // Get the toc for a specific route
94
                        $route = $this->getActiveRoute($meta["route"], $routeIndex);
95
                        $routeIndex2 = $this->mapRoute2IndexKey($route);
96
                        $baseRoute = dirname($routeIndex2);
97
98
                        // Include support for ordering
99
                        if (isset($meta["orderby"])
100
                            || isset($meta["orderorder"])) {
101
                            $this->orderToc($baseRoute, $meta);
102
                        }
103
104
                        // Same as toc
105
                        $toc = $this->meta[$baseRoute]["__toc__"];
106
                        $this->limitToc($toc, $meta, $baseRoute);
107
                        $views[$id]["data"]["toc"] = $toc;
108
                        $views[$id]["data"]["meta"] = $meta;
109
                        break;
110
111
                    case "book-toc":
112
                        $toc = $this->meta[$baseRoute]["__toc__"];
113
                        $views[$id]["data"]["toc"] = $toc;
114
                        break;
115
116
                    case "author":
117
                        if (isset($views["main"]["data"]["author"])) {
118
                            $views[$id]["data"]["author"] = $this->loadAuthorDetails($views["main"]["data"]["author"]);
119
                        }
120
                        break;
121
122
                    case "copy":
123
                        $viewToCopy = $views[$id]["data"]["meta"]["view"];
124
                        $views[$id]["data"] = array_merge_recursive_distinct(
125
                            $views[$viewToCopy]["data"],
126
                            $views[$id]["data"]
127
                        );
128
                        break;
129
130
                    default:
131
                        $msg = t("Unsupported data/meta/type '!TYPE' for additional content.", [
132
                            "!TYPE" => $meta["type"]
133
                        ]);
134
                        throw new Exception($msg);
135
                }
136
            }
137
        }
138
    }
139
140
141
142
    /**
143
     * Find next and previous links of current content.
144
     *
145
     * @param string $routeIndex target route to find next and previous for.
146
     *
147
     * @return array with next and previous if found.
148
     */
149
    private function findNextAndPrevious($routeIndex)
150
    {
151
        $key = dirname($routeIndex);
152
        if (!isset($this->meta[$key]["__toc__"])) {
153
            return [null, null];
154
        }
155
156
        $toc = $this->meta[$key]["__toc__"];
157
        if (!isset($toc[$routeIndex])) {
158
            return [null, null];
159
        }
160
161
        $index2Key = array_keys($toc);
162
        $keys = array_flip($index2Key);
163
        $values = array_values($toc);
164
        $count = count($keys);
165
166
        $current = $keys[$routeIndex];
167
        $previous = null;
168 View Code Duplication
        for ($i = $current - 1; $i >= 0; $i--) {
169
            $isSectionHeader = $values[$i]["sectionHeader"];
170
            $isLinkable = $values[$i]["linkable"]; // ?? null;
171
            $isInternal = $values[$i]["internal"];
172
            if (($isSectionHeader && !$isLinkable) || $isInternal) {
173
                continue;
174
            }
175
            $previous = $values[$i];
176
            $previous["route"] = $index2Key[$i];
177
            break;
178
        }
179
        
180
        $next = null;
181 View Code Duplication
        for ($i = $current + 1; $i < $count; $i++) {
182
            $isSectionHeader = $values[$i]["sectionHeader"];
183
            $isLinkable = $values[$i]["linkable"]; // ?? null;
184
            $isInternal = $values[$i]["internal"];
185
            if (($isSectionHeader && !$isLinkable) || $isInternal) {
186
                continue;
187
            }
188
            $next = $values[$i];
189
            $next["route"] = $index2Key[$i];
190
            break;
191
        }
192
193
        return [$next, $previous];
194
    }
195
196
197
198
    /**
199
     * Order toc items.
200
     *
201
     * @param string $baseRoute route to use to find __toc__.
202
     * @param string $meta on how to order toc.
203
     *
204
     * @return void.
205
     */
206
    private function orderToc($baseRoute, $meta)
207
    {
208
        $defaults = [
209
            "orderby" => "section",
210
            "orderorder" => "asc",
211
        ];
212
        $options = array_merge($defaults, $meta);
213
        $orderby = $options["orderby"];
214
        $order   = $options["orderorder"];
215
        $toc = $this->meta[$baseRoute]["__toc__"];
216
217
        uksort($toc, function ($a, $b) use ($toc, $orderby, $order) {
218
                $a = $toc[$a][$orderby];
219
                $b = $toc[$b][$orderby];
220
221
                $asc = $order == "asc" ? 1 : -1;
222
                
223
            if ($a == $b) {
224
                return 0;
225
            } elseif ($a > $b) {
226
                return $asc;
227
            }
228
                return -$asc;
229
        });
230
231
        $this->meta[$baseRoute]["__toc__"] = $toc;
232
    }
233
234
235
    /**
236
     * Limit and paginate toc items.
237
     *
238
     * @param string &$toc      array with current toc.
239
     * @param string &$meta     on how to order and limit toc.
240
     * @param string $baseRoute prepend to next & previous urls.
241
     *
242
     * @return void.
243
     */
244
    private function limitToc(&$toc, &$meta, $baseRoute = null)
245
    {
246
        $defaults = [
247
            "items" => 7,
248
            "offset" => 0,
249
        ];
250
        $options = array_merge($defaults, $meta);
251
252
        // Check if pagination is currently used
253
        if ($this->currentPage) {
254
            $options["offset"] = ($this->currentPage - 1) * $options["items"];
255
        }
256
257
        $meta["totalItems"] = count($toc);
258
        $meta["currentPage"] = (int) floor($options["offset"] / $options["items"]) + 1;
259
        $meta["totalPages"] = (int) floor($meta["totalItems"] / $options["items"] + 1);
260
261
        // Next and previous page
262
        $pagination = $this->config["pagination"];
263
        $meta["nextPageUrl"] = null;
264
        $meta["previousPageUrl"] = null;
265
        $baseRoute = isset($baseRoute)
266
            ? $baseRoute
267
            : $this->baseRoute;
268
269
        if ($meta["currentPage"] > 1 && $meta["totalPages"] > 1) {
270
            $previousPage = $meta["currentPage"] - 1;
271
            $previous = "";
272
            if ($previousPage != 1) {
273
                $previous = "$pagination/$previousPage";
274
            }
275
            $meta["previousPageUrl"] = "$baseRoute/$previous";
276
        }
277
278
        if ($meta["currentPage"] < $meta["totalPages"]) {
279
            $nextPage = $meta["currentPage"] + 1;
280
            $meta["nextPageUrl"] = "$baseRoute/$pagination/$nextPage";
281
        }
282
283
284
        // Only use slice of toc
285
        $startSlice = ($meta["currentPage"] - 1) * $options["items"];
286
        $toc = array_slice($toc, $startSlice, $options["items"]);
287
        $meta["displayedItems"] = count($toc);
288
    }
289
}
290