1 <?php
2
3 4 5 6 7 8 9 10
11
12 namespace Icybee\Element;
13
14 use ICanBoogie\Event;
15 use ICanBoogie\Exception;
16 use ICanBoogie\I18n;
17 use ICanBoogie\Module;
18 use ICanBoogie\PropertyNotDefined;
19 use ICanBoogie\Route;
20 use ICanBoogie\Routes;
21 use ICanBoogie\Routing\Pattern;
22
23 use Brickrouge\A;
24 use Brickrouge\Button;
25 use Brickrouge\Collector;
26 use Brickrouge\Element;
27 use Brickrouge\SplitButton;
28
29 class Actionbar extends Element
30 {
31 public function __construct(array $attributes=array())
32 {
33 parent::__construct
34 (
35 'div', $attributes + array
36 (
37 'class' => 'actionbar',
38 'data-display' => ''
39 )
40 );
41 }
42
43 protected function render_inner_html()
44 {
45 global $core;
46
47 $actionbar_new = null;
48 $actionbar_navigation = null;
49 $actionbar_search = null;
50 $actionbar_toolbar = null;
51
52 try
53 {
54
55
56
57
58 if (!$core->request)
59 {
60 throw new PropertyNotDefined();
61 }
62
63 $route = $core->request->route;
64
65 if (!$core->user->is_guest && !($core->user instanceof \Icybee\Modules\Members\Member))
66 {
67 $module_id = $route->module;
68
69 $actionbar_new = (string) new ActionbarNew
70 (
71 'New', array
72 (
73 ActionbarNew::PATTERN => "/admin/$module_id/new",
74 ActionbarNew::ROUTE => $route
75 )
76 );
77 }
78
79 $actionbar_navigation = (string) new ActionbarNav;
80 $actionbar_search = (string) new ActionbarSearch;
81 $actionbar_toolbar = (string) new ActionbarToolbar;
82 }
83 catch (PropertyNotDefined $e)
84 {
85
86
87
88
89
90 }
91
92 $actionbar_title = (string) new ActionbarTitle;
93
94 if (!$actionbar_title && !$actionbar_new && !$actionbar_navigation && !$actionbar_toolbar && !$actionbar_search)
95 {
96 throw new \Brickrouge\ElementIsEmpty;
97 }
98
99 $actionbar_contextual = (string) new ActionbarContextual();
100
101 return <<<EOT
102 <div class="actionbar-faces">
103 <div class="actionbar-front">
104 <div class="actionbar-brand pull-left">
105 {$actionbar_title}{$actionbar_new}{$actionbar_navigation}
106 </div>
107
108 <div class="pull-right">
109 <div class="actionbar-actions">{$actionbar_toolbar}</div>
110 <div class="actionbar-search">{$actionbar_search}</div>
111 </div>
112 </div>
113
114 <div class="actionbar-back">
115 $actionbar_contextual
116 </div>
117 </div>
118 EOT;
119 }
120 }
121
122 class ActionbarNav extends Element
123 {
124 public function __construct(array $attributes=array())
125 {
126 parent::__construct('div', $attributes + array('class' => 'actionbar-nav'));
127 }
128
129 protected function render_inner_html()
130 {
131 global $core;
132
133 $current_route = $core->request->route;
134 $collection = $this->collect_routes($current_route);
135
136 if (empty($collection))
137 {
138 throw new \Brickrouge\ElementIsEmpty;
139 }
140
141 $html = '';
142
143 foreach ($collection as $route)
144 {
145 $html .= $this->render_link($route, $current_route);
146 }
147
148 return $html . parent::render_inner_html();
149 }
150
151 protected function render_link(array $route, Route $current_route)
152 {
153 global $core;
154
155 $title = $route['title'];
156
157 if ($title{0} == '.')
158 {
159 $title = substr($title, 1);
160 }
161
162 $title = I18n\t($title, array(), array('scope' => 'block.title'));
163 $pattern = $pathname = $route['pattern'];
164
165 if (Pattern::is_pattern($pattern))
166 {
167 $pathname = Pattern::from($pattern)->format($core->request->path_params);
168 }
169
170 $link = new A($title, \ICanBoogie\Routing\contextualize($pathname), array('class' => 'actionbar-link'));
171
172 if ($pattern == $current_route->pattern)
173 {
174 $link->add_class('active');
175 }
176
177 return $link;
178 }
179
180 protected function collect_routes($current_route)
181 {
182 global $core;
183
184 $collection = array();
185 $pattern = $current_route->pattern;
186
187 if (!$current_route->module)
188 {
189 throw new \Brickrouge\ElementIsEmpty;
190 }
191
192 $module_id = $current_route->module;
193 $user = $core->user;
194
195 $index_pattern = "/admin/$module_id";
196 $new_pattern = "/admin/$module_id/new";
197
198 $skip = array
199 (
200
201 "/admin/$module_id/manage" => true
202 );
203
204 foreach (Routes::get() as $route)
205 {
206 $route_module = isset($route['module']) ? $route['module'] : null;
207
208 if (!$route_module || $route_module != $module_id || empty($route['title']))
209 {
210 continue;
211 }
212
213 $r_pattern = $route['pattern'];
214
215 if ($r_pattern == $index_pattern || $r_pattern == $new_pattern)
216 {
217 continue;
218 }
219
220 221 222 223 224 225
226
227 if ($r_pattern == $pattern)
228 {
229
230 }
231 else
232 {
233 if ((isset($route['visibility']) && $route['visibility'] == 'auto') || Pattern::is_pattern($r_pattern) || isset($skip[$r_pattern]))
234 {
235 continue;
236 }
237 }
238
239 $permission = isset($route['permission']) ? $route['permission'] : Module::PERMISSION_ACCESS;
240
241 if (!$user->has_permission($permission, $module_id))
242 {
243 continue;
244 }
245
246 $collection[$r_pattern] = $route;
247 }
248
249 return $collection;
250 }
251 }
252
253 class ActionbarNew extends SplitButton
254 {
255 const PATTERN = '#abn-pattern';
256 const ROUTE = '#abn-route';
257
258 public function __construct($label, array $attributes=array())
259 {
260 $options = $this->collect_routes();
261
262 parent::__construct
263 (
264 $label, $attributes + array
265 (
266 self::OPTIONS => $options
267 )
268 );
269
270 $route = $this[self::ROUTE];
271
272 if ($route->pattern == $this[self::PATTERN])
273 {
274 $this->add_class('btn-info');
275 }
276 else
277 {
278 $this->add_class('btn-danger');
279 }
280 }
281
282 private $render_as_button=false;
283
284 protected function render_splitbutton_label($label, $class)
285 {
286 if ($this->render_as_button)
287 {
288 return '';
289 }
290
291 return new A($label, \ICanBoogie\Routing\contextualize($this[self::PATTERN]), array('class' => 'btn ' . $class));
292 }
293
294 protected function render_splitbutton_toggle($class)
295 {
296 if ($this->render_as_button)
297 {
298 return <<<EOT
299 <a href="javascript:void()" class="btn dropdown-toggle $class" data-toggle="dropdown">$this->inner_html <span class="caret"></span></a>
300 EOT;
301 }
302
303 return parent::render_splitbutton_toggle($class);
304 }
305
306 public function __toString()
307 {
308 global $core;
309
310 $route = $core->request->route;
311 $module_id = $route->module;
312 $match = Routes::get()->find("/admin/$module_id/new");
313
314 $this->render_as_button = !$match;
315
316 if ($route->pattern != '/admin/dashboard' && !$match)
317 {
318 return '';
319 }
320
321 return parent::__toString();
322 }
323
324 protected function collect_routes()
325 {
326 global $core;
327
328 $collection = array();
329 $translations = array();
330
331 $routes = Routes::get();
332 $descriptors = $core->modules->descriptors;
333 $user = $core->user;
334
335 foreach ($routes as $route)
336 {
337 $pattern = $route['pattern'];
338
339 if (!preg_match('#/new$#', $pattern))
340 {
341 continue;
342 }
343
344 $module_id = $route['module'];
345
346 if (!isset($core->modules[$module_id]) || !$user->has_permission(Module::PERMISSION_CREATE, $module_id))
347 {
348 continue;
349 }
350
351 $collection[$pattern] = $module_id;
352
353 $flat_id = strtr($module_id, '.', '_');
354
355 $translations[$module_id] = I18n\t
356 (
357 $flat_id . '.name', array(':count' => 1), array
358 (
359 'default' => \ICanBoogie\singularize(I18n\t("module_title.$flat_id", array(), array('default' => $descriptors[$module_id][Module::T_TITLE])))
360 )
361 );
362 }
363
364 \ICanBoogie\stable_sort($collection, function($v) use ($translations) {
365
366 return \ICanBoogie\downcase(\ICanBoogie\remove_accents($translations[$v]));
367
368 });
369
370 array_walk($collection, function(&$v, $k) use ($translations) {
371
372 $label = $translations[$v];
373
374 $v = new A($label, \ICanBoogie\Routing\contextualize($k));
375
376 });
377
378 return $collection;
379 }
380 }