1 | <?php |
||||
2 | /** |
||||
3 | * BEdita, API-first content management framework |
||||
4 | * Copyright 2018 ChannelWeb Srl, Chialab Srl |
||||
5 | * |
||||
6 | * This file is part of BEdita: you can redistribute it and/or modify |
||||
7 | * it under the terms of the GNU Lesser General Public License as published |
||||
8 | * by the Free Software Foundation, either version 3 of the License, or |
||||
9 | * (at your option) any later version. |
||||
10 | * |
||||
11 | * See LICENSE.LGPL or <http://gnu.org/licenses/lgpl-3.0.html> for more details. |
||||
12 | */ |
||||
13 | namespace App\View\Helper; |
||||
14 | |||||
15 | use Cake\Core\Configure; |
||||
16 | use Cake\Core\Plugin; |
||||
17 | use Cake\Routing\Router; |
||||
18 | use Cake\Utility\Hash; |
||||
19 | use Cake\View\Helper; |
||||
20 | |||||
21 | /** |
||||
22 | * Helper class to generate links or link tags |
||||
23 | * |
||||
24 | * @property \Cake\View\Helper\HtmlHelper $Html |
||||
25 | */ |
||||
26 | class LinkHelper extends Helper |
||||
27 | { |
||||
28 | /** |
||||
29 | * List of helpers used by this helper |
||||
30 | * |
||||
31 | * @var array |
||||
32 | */ |
||||
33 | public $helpers = ['Html']; |
||||
34 | |||||
35 | /** |
||||
36 | * {@inheritDoc} |
||||
37 | * |
||||
38 | * Default configuration |
||||
39 | * |
||||
40 | * - 'apiBaseUrl': API base URL |
||||
41 | * - 'webBaseUrl': WebApp base URL |
||||
42 | * - 'query': Request Query params |
||||
43 | * - 'manifestPath': Manifest file path |
||||
44 | * - 'manifest': Manifest content (array) |
||||
45 | */ |
||||
46 | protected $_defaultConfig = [ |
||||
47 | 'apiBaseUrl' => '', |
||||
48 | 'webBaseUrl' => '', |
||||
49 | 'query' => [], |
||||
50 | 'manifestPath' => WWW_ROOT . 'manifest.json', |
||||
51 | 'manifest' => [], |
||||
52 | ]; |
||||
53 | |||||
54 | /** |
||||
55 | * {@inheritDoc} |
||||
56 | * |
||||
57 | * Init API and WebAPP base URL |
||||
58 | * |
||||
59 | * @codeCoverageIgnore |
||||
60 | */ |
||||
61 | public function initialize(array $config): void |
||||
62 | { |
||||
63 | $default = [ |
||||
64 | 'apiBaseUrl' => Configure::read('API.apiBaseUrl'), |
||||
65 | 'webBaseUrl' => Router::fullBaseUrl(), |
||||
66 | 'query' => $this->getView()->getRequest()->getQueryParams(), |
||||
67 | ]; |
||||
68 | $this->setConfig(array_merge($default, $config)); |
||||
69 | if (empty($this->getConfig('manifest')) && file_exists($this->getConfig('manifestPath'))) { |
||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||
70 | $content = (string)file_get_contents($this->getConfig('manifestPath')); |
||||
0 ignored issues
–
show
It seems like
$this->getConfig('manifestPath') can also be of type null ; however, parameter $filename of file_get_contents() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
71 | $this->setConfig('manifest', json_decode($content, true)); |
||||
72 | } |
||||
73 | } |
||||
74 | |||||
75 | /** |
||||
76 | * Return base url. |
||||
77 | * If it ends with ':80' (proxy case), remove it. |
||||
78 | * |
||||
79 | * @return string |
||||
80 | */ |
||||
81 | public function baseUrl(): string |
||||
82 | { |
||||
83 | $url = (string)$this->getConfig('webBaseUrl'); |
||||
84 | if (substr_compare($url, ':80', -strlen(':80')) === 0) { |
||||
85 | return substr($url, 0, strpos($url, ':80')); |
||||
86 | } |
||||
87 | |||||
88 | return $url; |
||||
89 | } |
||||
90 | |||||
91 | /** |
||||
92 | * Transform API url in web app URL, preserving path part. |
||||
93 | * Extremely simple for now |
||||
94 | * |
||||
95 | * @param string $apiUrl Api url |
||||
96 | * @return void |
||||
97 | */ |
||||
98 | public function fromAPI($apiUrl): void |
||||
99 | { |
||||
100 | echo str_replace($this->getConfig('apiBaseUrl'), $this->getConfig('webBaseUrl'), $apiUrl); |
||||
101 | } |
||||
102 | |||||
103 | /** |
||||
104 | * Returns sort url by field direction |
||||
105 | * |
||||
106 | * @param string $field the Field. |
||||
107 | * @param bool $resetPage flag to reset pagination. |
||||
108 | * @return string |
||||
109 | */ |
||||
110 | public function sortUrl($field, $resetPage = true): string |
||||
111 | { |
||||
112 | $sort = (string)Hash::get($this->getConfig('query'), 'sort'); |
||||
113 | $sort = $this->sortValue($field, $sort); |
||||
114 | $replace = compact('sort'); |
||||
115 | $currentPage = Hash::get($this->getConfig('query'), 'page'); |
||||
116 | if (isset($currentPage) && $resetPage) { |
||||
117 | $replace['page'] = 1; |
||||
118 | } |
||||
119 | |||||
120 | return $this->replaceQueryParams($replace); |
||||
121 | } |
||||
122 | |||||
123 | /** |
||||
124 | * Define sort query string value using sort string and current |
||||
125 | * 'sort' value |
||||
126 | * |
||||
127 | * @param string $field Field to sort. |
||||
128 | * @param string $currentSort Current sort value |
||||
129 | * @return string |
||||
130 | */ |
||||
131 | protected function sortValue(string $field, string $currentSort): string |
||||
132 | { |
||||
133 | $sort = $this->sortField($field); // <= ascendant order |
||||
134 | if ($currentSort === $sort) { // it was ascendant sort |
||||
135 | $sort = '-' . $sort; // <= descendant order |
||||
136 | } |
||||
137 | |||||
138 | return $sort; |
||||
139 | } |
||||
140 | |||||
141 | /** |
||||
142 | * Retrieve 'sort' field from field name. |
||||
143 | * |
||||
144 | * @param string $field Field name. |
||||
145 | * @return string |
||||
146 | */ |
||||
147 | protected function sortField(string $field): string |
||||
148 | { |
||||
149 | if ($field === 'date_ranges') { |
||||
150 | return 'date_ranges_min_start_date'; |
||||
151 | } |
||||
152 | |||||
153 | return $field; |
||||
154 | } |
||||
155 | |||||
156 | /** |
||||
157 | * Returns sort class by field direction |
||||
158 | * |
||||
159 | * @param string $field the Field. |
||||
160 | * @return string |
||||
161 | */ |
||||
162 | public function sortClass(string $field): string |
||||
163 | { |
||||
164 | $sort = (string)Hash::get($this->getConfig('query'), 'sort'); |
||||
165 | if (empty($sort)) { |
||||
166 | return ''; |
||||
167 | } |
||||
168 | $sortField = $this->sortField($field); |
||||
169 | if ($sort === $sortField) { // it was ascendant sort |
||||
170 | return 'sort down'; |
||||
171 | } |
||||
172 | if ($sort === '-' . $sortField) { // it was descendant sort |
||||
173 | return 'sort up'; |
||||
174 | } |
||||
175 | |||||
176 | return ''; |
||||
177 | } |
||||
178 | |||||
179 | /** |
||||
180 | * Pagination link for page |
||||
181 | * |
||||
182 | * @param int $page destination page. |
||||
183 | * @return void |
||||
184 | */ |
||||
185 | public function page($page): void |
||||
186 | { |
||||
187 | echo $this->replaceQueryParams(['page' => $page]); |
||||
188 | } |
||||
189 | |||||
190 | /** |
||||
191 | * Pagination link for page size |
||||
192 | * |
||||
193 | * @param int $pageSize new page size. |
||||
194 | * @return void |
||||
195 | */ |
||||
196 | public function pageSize($pageSize): void |
||||
197 | { |
||||
198 | echo $this->replaceQueryParams(['page_size' => $pageSize]); |
||||
199 | } |
||||
200 | |||||
201 | /** |
||||
202 | * Return url for current page |
||||
203 | * |
||||
204 | * @param array $options options for query |
||||
205 | * @return string url |
||||
206 | */ |
||||
207 | public function here($options = []): string |
||||
208 | { |
||||
209 | $url = (string)$this->getConfig('webBaseUrl'); |
||||
210 | $here = sprintf( |
||||
211 | '%s%s', |
||||
212 | $url, |
||||
213 | $this->getView()->getRequest()->getAttribute('here') |
||||
214 | ); |
||||
215 | $query = (array)$this->getConfig('query'); |
||||
216 | if (empty($query) || !empty($options['no-query'])) { |
||||
217 | return $here; |
||||
218 | } |
||||
219 | if (isset($options['exclude'])) { |
||||
220 | $key = sprintf('query.%s', $options['exclude']); |
||||
221 | $this->setConfig($key, null); |
||||
222 | $query = (array)$this->getConfig('query'); |
||||
223 | } |
||||
224 | $q = http_build_query($query); |
||||
225 | if (!empty($q)) { |
||||
226 | return $here . '?' . $q; |
||||
227 | } |
||||
228 | |||||
229 | return $here; |
||||
230 | } |
||||
231 | |||||
232 | /** |
||||
233 | * Replace query parameters on current request. |
||||
234 | * |
||||
235 | * @param array $queryParams list of query params to replace. |
||||
236 | * @return string new uri |
||||
237 | */ |
||||
238 | private function replaceQueryParams(array $queryParams): string |
||||
239 | { |
||||
240 | $request = $this->getView()->getRequest(); |
||||
241 | $query = array_merge((array)$this->getConfig('query'), $queryParams); |
||||
242 | |||||
243 | return (string)$request->getUri()->withQuery(http_build_query($query)); |
||||
244 | } |
||||
245 | |||||
246 | /** |
||||
247 | * Include plugin js and css bundles. |
||||
248 | * |
||||
249 | * @return void |
||||
250 | */ |
||||
251 | public function pluginsBundle(): void |
||||
252 | { |
||||
253 | $plugins = Configure::read('Plugins', []); |
||||
254 | foreach (['js', 'css'] as $extension) { |
||||
255 | foreach ($plugins as $plugin => $v) { |
||||
256 | echo $this->pluginAsset($plugin, $extension); |
||||
257 | } |
||||
258 | } |
||||
259 | } |
||||
260 | |||||
261 | /** |
||||
262 | * Return js/css plugin asset by plugin and extension |
||||
263 | * |
||||
264 | * @param string $plugin The plugin |
||||
265 | * @param string $extension the extension |
||||
266 | * @return string |
||||
267 | */ |
||||
268 | public function pluginAsset(string $plugin, string $extension): string |
||||
269 | { |
||||
270 | $path = sprintf('%swebroot%s%s%s%s.plugin.%s', Plugin::path($plugin), DS, $extension, DS, $plugin, $extension); |
||||
271 | if (!file_exists($path)) { |
||||
272 | return ''; |
||||
273 | } |
||||
274 | $method = $extension === 'js' ? 'script' : 'css'; |
||||
275 | |||||
276 | return $this->Html->{$method}(sprintf('%s.%s.plugin.%s', $plugin, $plugin, $extension)); |
||||
277 | } |
||||
278 | |||||
279 | /** |
||||
280 | * Include statically imported JS splitted vendors. |
||||
281 | * |
||||
282 | * @param array $filter list of file name filters |
||||
283 | * @return void |
||||
284 | */ |
||||
285 | public function jsBundle(array $filter = []): void |
||||
286 | { |
||||
287 | $jsFiles = $this->findFiles($filter, 'js'); |
||||
288 | foreach ($jsFiles as $jsFile) { |
||||
289 | echo $this->Html->script(sprintf('%s', $jsFile)); |
||||
290 | } |
||||
291 | } |
||||
292 | |||||
293 | /** |
||||
294 | * Include statically imported CSS splitted vendors. |
||||
295 | * |
||||
296 | * @param array $filter list of file name filters |
||||
297 | * @return void |
||||
298 | */ |
||||
299 | public function cssBundle(array $filter = []): void |
||||
300 | { |
||||
301 | $cssFiles = $this->findFiles($filter, 'css'); |
||||
302 | foreach ($cssFiles as $cssFile) { |
||||
303 | echo $this->Html->css(sprintf('%s', $cssFile)); |
||||
304 | } |
||||
305 | } |
||||
306 | |||||
307 | /** |
||||
308 | * find files under webroot directory specifing a ordered list of filters and the file type |
||||
309 | * to search for |
||||
310 | * |
||||
311 | * @param array $filter list of file name filters |
||||
312 | * @param string $type file type (js/css) |
||||
313 | * @return array files found |
||||
314 | */ |
||||
315 | public function findFiles(array $filter, string $type): array |
||||
316 | { |
||||
317 | $ext = '.' . $type; |
||||
318 | $len = strlen($ext); |
||||
319 | $prefixLen = strlen(sprintf('/%s/', $type)); |
||||
320 | $files = []; |
||||
321 | $manifest = (array)$this->getConfig('manifest'); |
||||
322 | foreach ($manifest as $key => $value) { |
||||
323 | // see if file name ends with extension |
||||
324 | if (substr_compare($key, $ext, -$len) !== 0) { |
||||
325 | continue; |
||||
326 | } |
||||
327 | foreach ($filter as $filterName) { |
||||
328 | if (strpos($key, $filterName) !== false) { |
||||
329 | // add file without prefix |
||||
330 | $files[] = substr($value, $prefixLen); |
||||
331 | } |
||||
332 | } |
||||
333 | } |
||||
334 | |||||
335 | return $files; |
||||
336 | } |
||||
337 | } |
||||
338 |