1 <?php
2 /**
3 * @package Joomla.Libraries
4 * @subpackage Component
5 *
6 * @copyright Copyright (C) 2005 - 2017 Open Source Matters, Inc. All rights reserved.
7 * @license GNU General Public License version 2 or later; see LICENSE.txt
8 */
9
10 defined('JPATH_PLATFORM') or die;
11
12 /**
13 * Rule for the standard handling of component routing
14 *
15 * @since 3.4
16 */
17 class JComponentRouterRulesStandard implements JComponentRouterRulesInterface
18 {
19 /**
20 * Router this rule belongs to
21 *
22 * @var JComponentRouterView
23 * @since 3.4
24 */
25 protected $router;
26
27 /**
28 * Class constructor.
29 *
30 * @param JComponentRouterView $router Router this rule belongs to
31 *
32 * @since 3.4
33 */
34 public function __construct(JComponentRouterView $router)
35 {
36 $this->router = $router;
37 }
38
39 /**
40 * Dummymethod to fullfill the interface requirements
41 *
42 * @param array &$query The query array to process
43 *
44 * @return void
45 *
46 * @since 3.4
47 */
48 public function preprocess(&$query)
49 {
50 }
51
52 /**
53 * Parse the URL
54 *
55 * @param array &$segments The URL segments to parse
56 * @param array &$vars The vars that result from the segments
57 *
58 * @return void
59 *
60 * @since 3.4
61 */
62 public function parse(&$segments, &$vars)
63 {
64 // Get the views and the currently active query vars
65 $views = $this->router->getViews();
66 $active = $this->router->menu->getActive();
67
68 if ($active)
69 {
70 $vars = array_merge($active->query, $vars);
71 }
72
73 // We don't have a view or its not a view of this component! We stop here
74 if (!isset($vars['view']) || !isset($views[$vars['view']]))
75 {
76 return;
77 }
78
79 // Copy the segments, so that we can iterate over all of them and at the same time modify the original segments
80 $tempSegments = $segments;
81
82 // Iterate over the segments as long as a segment fits
83 foreach ($tempSegments as $segment)
84 {
85 // Our current view is nestable. We need to check first if the segment fits to that
86 if ($views[$vars['view']]->nestable)
87 {
88 if (is_callable(array($this->router, 'get' . ucfirst($views[$vars['view']]->name) . 'Id')))
89 {
90 $key = call_user_func_array(array($this->router, 'get' . ucfirst($views[$vars['view']]->name) . 'Id'), array($segment, $vars));
91
92 // Did we get a proper key? If not, we need to look in the child-views
93 if ($key)
94 {
95 $vars[$views[$vars['view']]->key] = $key;
96
97 array_shift($segments);
98
99 continue;
100 }
101 }
102 else
103 {
104 // The router is not complete. The get<View>Key() method is missing.
105 return;
106 }
107 }
108
109 // Lets find the right view that belongs to this segment
110 $found = false;
111
112 foreach ($views[$vars['view']]->children as $view)
113 {
114 if (!$view->key)
115 {
116 if ($view->name === $segment)
117 {
118 // The segment is a view name
119 $parent = $views[$vars['view']];
120 $vars['view'] = $view->name;
121 $found = true;
122
123 if ($view->parent_key && isset($vars[$parent->key]))
124 {
125 $parent_key = $vars[$parent->key];
126 $vars[$view->parent_key] = $parent_key;
127
128 unset($vars[$parent->key]);
129 }
130
131 break;
132 }
133 }
134 elseif (is_callable(array($this->router, 'get' . ucfirst($view->name) . 'Id')))
135 {
136 // Hand the data over to the router specific method and see if there is a content item that fits
137 $key = call_user_func_array(array($this->router, 'get' . ucfirst($view->name) . 'Id'), array($segment, $vars));
138
139 if ($key)
140 {
141 // Found the right view and the right item
142 $parent = $views[$vars['view']];
143 $vars['view'] = $view->name;
144 $found = true;
145
146 if ($view->parent_key && isset($vars[$parent->key]))
147 {
148 $parent_key = $vars[$parent->key];
149 $vars[$view->parent_key] = $parent_key;
150
151 unset($vars[$parent->key]);
152 }
153
154 $vars[$view->key] = $key;
155
156 break;
157 }
158 }
159 }
160
161 if (!$found)
162 {
163 return;
164 }
165
166 array_shift($segments);
167 }
168 }
169
170 /**
171 * Build a standard URL
172 *
173 * @param array &$query The vars that should be converted
174 * @param array &$segments The URL segments to create
175 *
176 * @return void
177 *
178 * @since 3.4
179 */
180 public function build(&$query, &$segments)
181 {
182 // Get the menu item belonging to the Itemid that has been found
183 $item = $this->router->menu->getItem($query['Itemid']);
184
185 if (!isset($query['view']))
186 {
187 return;
188 }
189
190 // Get all views for this component
191 $views = $this->router->getViews();
192
193 // Return directly when the URL of the Itemid is identical with the URL to build
194 if (isset($item->query['view']) && $item->query['view'] === $query['view'])
195 {
196 $view = $views[$query['view']];
197
198 if (isset($item->query[$view->key]) && $item->query[$view->key] == (int) $query[$view->key])
199 {
200 unset($query[$view->key]);
201
202 while ($view)
203 {
204 unset($query[$view->parent_key]);
205
206 $view = $view->parent;
207 }
208
209 unset($query['view']);
210
211 if (isset($item->query['layout']) && isset($query['layout']) && $item->query['layout'] === $query['layout'])
212 {
213 unset($query['layout']);
214 }
215
216 return;
217 }
218
219 if (!$view->key)
220 {
221 if (isset($item->query['layout']) && isset($query['layout']) && $item->query['layout'] === $query['layout'])
222 {
223 unset($query['view'], $query['layout']);
224 return;
225 }
226 }
227 }
228
229 // Get the path from the view of the current URL and parse it to the menu item
230 $path = array_reverse($this->router->getPath($query), true);
231 $found = false;
232 $found2 = false;
233
234 for ($i = 0, $j = count($path); $i < $j; $i++)
235 {
236 reset($path);
237 $view = key($path);
238
239 if ($found)
240 {
241 $ids = array_shift($path);
242
243 if ($views[$view]->nestable)
244 {
245 foreach (array_reverse($ids, true) as $id => $segment)
246 {
247 if ($found2)
248 {
249 $segments[] = str_replace(':', '-', $segment);
250 }
251 elseif ((int) $item->query[$views[$view]->key] == (int) $id)
252 {
253 $found2 = true;
254 }
255 }
256 }
257 elseif (is_bool($ids))
258 {
259 $segments[] = $views[$view]->name;
260 }
261 else
262 {
263 $segments[] = str_replace(':', '-', array_shift($ids));
264 }
265 }
266 elseif ($item->query['view'] !== $view)
267 {
268 array_shift($path);
269 }
270 else
271 {
272 if (!$views[$view]->nestable)
273 {
274 array_shift($path);
275 }
276 else
277 {
278 $i--;
279 $found2 = false;
280 }
281
282 if (count($views[$view]->children))
283 {
284 $found = true;
285 }
286 }
287
288 unset($query[$views[$view]->parent_key]);
289 }
290
291 if ($found)
292 {
293 unset($query['layout'], $query[$views[$query['view']]->key], $query['view']);
294 }
295 }
296 }
297