1: <?php
2:
3: /**
4: * TODO: License
5: */
6:
7: namespace Mapbender\CoreBundle\Component;
8:
9: use Assetic\Asset\AssetCollection;
10: use Assetic\Asset\AssetReference;
11: use Assetic\Asset\FileAsset;
12: use Assetic\FilterManager;
13: use Assetic\Asset\StringAsset;
14: use Assetic\AssetManager;
15: use Assetic\Factory\AssetFactory;
16:
17: use Mapbender\CoreBundle\Entity\Application as Entity;
18: use Symfony\Component\DependencyInjection\ContainerInterface;
19: use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
20:
21: /**
22: * Application is the main Mapbender3 class.
23: *
24: * This class is the controller for each application instance.
25: * The application class will not perform any access checks, this is due to
26: * the controller instantiating an application. The controller should check
27: * with the configuration entity to get a list of allowed roles and only then
28: * decide to instantiate a new application instance based on the configuration
29: * entity.
30: *
31: * @author Christian Wygoda
32: */
33: class Application
34: {
35:
36: /**
37: * @var ContainerInterface $container The container
38: */
39: protected $container;
40:
41: /**
42: * @var Template $template The application template class
43: */
44: protected $template;
45:
46: /**
47: * @var array $elements The elements, ordered by weight
48: */
49: protected $elements;
50:
51: /**
52: * @var array $layers The layers
53: */
54: protected $layers;
55:
56: /**
57: * @var array $urls Runtime URLs
58: */
59: protected $urls;
60:
61: /**
62: * @param ContainerInterface $container The container
63: * @param Entity $entity The configuration entity
64: * @param array $urls Array of runtime URLs
65: */
66: public function __construct(ContainerInterface $container, Entity $entity, array $urls)
67: {
68: $this->container = $container;
69: $this->entity = $entity;
70: $this->urls = $urls;
71: }
72:
73: /* * ***********************************************************************
74: * *
75: * Configuration entity handling *
76: * *
77: * *********************************************************************** */
78:
79: /**
80: * Get the configuration entity.
81: *
82: * @return object $entity
83: */
84: public function getEntity()
85: {
86: return $this->entity;
87: }
88:
89: /* * ***********************************************************************
90: * *
91: * Shortcut functions for leaner Twig templates *
92: * *
93: * *********************************************************************** */
94:
95: /**
96: * Get the application ID
97: *
98: * @return integer
99: */
100: public function getId()
101: {
102: return $this->entity->getId();
103: }
104:
105: /**
106: * Get the application slug
107: *
108: * @return string
109: */
110: public function getSlug()
111: {
112: return $this->entity->getSlug();
113: }
114:
115: /**
116: * Get the application title
117: *
118: * @return string
119: */
120: public function getTitle()
121: {
122: return $this->entity->getTitle();
123: }
124:
125: /**
126: * Get the application description
127: *
128: * @return string
129: */
130: public function getDescription()
131: {
132: return $this->entity->getDescription();
133: }
134:
135: /* * ***********************************************************************
136: * *
137: * Frontend stuff *
138: * *
139: * *********************************************************************** */
140:
141: /**
142: * Render the application
143: *
144: * @param string format Output format, defaults to HTML
145: * @param boolean $html Whether to render the HTML itself
146: * @param boolean $css Whether to include the CSS links
147: * @param boolean $js Whether to include the JavaScript
148: * @return string $html The rendered HTML
149: */
150: public function render($format = 'html', $html = true, $css = true, $js = true)
151: {
152: return $this->getTemplate()->render($format, $html, $css, $js);
153: }
154:
155: /**
156: * Get the assets as an AsseticCollection.
157: * Filters can be applied later on with the ensureFilter method.
158: *
159: * @param string $type Can be 'css' or 'js' to indicate which assets to dump
160: * @return AsseticFactory
161: */
162: public function getAssets($type)
163: {
164: if($type !== 'css' && $type !== 'js')
165: {
166: throw new \RuntimeException('Asset type \'' . $type .
167: '\' is unknown.');
168: }
169:
170: // Add all assets to an asset manager first to avoid duplication
171: $assets = new AssetManager();
172:
173: if($type === 'js')
174: {
175: // Mapbender API
176: $file = '@MapbenderCoreBundle/Resources/public/mapbender.application.js';
177: $this->addAsset($assets, $type, $file);
178: // Translation API
179: $file = '@MapbenderCoreBundle/Resources/public/mapbender.trans.js';
180: $this->addAsset($assets, $type, $file);
181: // WDT fixup
182: if($this->container->has('web_profiler.debug_toolbar'))
183: {
184: $file = '@MapbenderCoreBundle/Resources/public/mapbender.application.wdt.js';
185: $this->addAsset($assets, $type, $file);
186: }
187: }
188: if($type === 'css')
189: {
190: $file = '@MapbenderCoreBundle/Resources/public/mapbender.application.css';
191: $this->addAsset($assets, $type, $file);
192: }
193:
194: // Load all elements assets
195: foreach($this->getElements() as $region => $elements)
196: {
197: foreach($elements as $element)
198: {
199: $element_assets = $element->getAssets();
200: foreach($element_assets[$type] as $asset)
201: {
202: $this->addAsset($assets, $type, $this->getReference($element, $asset));
203: }
204: }
205: }
206:
207: // Load all layer assets
208: foreach($this->getLayersets() as $layerset)
209: {
210: foreach($layerset->layerObjects as $layer)
211: {
212: $layer_assets = $layer->getAssets();
213: foreach($layer_assets[$type] as $asset)
214: {
215: $this->addAsset($assets, $type, $this->getReference($layer, $asset));
216: }
217: }
218: }
219:
220: // Load the template assets last, so it can easily overwrite element
221: // and layer assets for application specific styling for example
222: foreach($this->getTemplate()->getAssets($type) as $asset)
223: {
224: $file = $this->getReference($this->template, $asset);
225: $this->addAsset($assets, $type, $file);
226: }
227:
228: // Load extra assets given by application
229: $extra_assets = $this->getEntity()->getExtraAssets();
230: if(is_array($extra_assets) && array_key_exists($type, $extra_assets))
231: {
232: foreach($extra_assets[$type] as $asset)
233: {
234: $asset = trim($asset);
235: $this->addAsset($assets, $type, $asset);
236: }
237: }
238:
239: // Get all assets out of the manager and into an collection
240: $collection = new AssetCollection();
241: foreach($assets->getNames() as $name)
242: {
243: $collection->add($assets->get($name));
244: }
245:
246: return $collection;
247: }
248:
249: private function addAsset($manager, $type, $reference)
250: {
251: $locator = $this->container->get('file_locator');
252: $file = $locator->locate($reference);
253:
254: // This stuff is needed for CSS rewrite. This will use the file contents
255: // from the bundle directory, but the path inside the public folder
256: // for URL rewrite
257: $sourceBase = null;
258: $sourcePath = null;
259: if($reference[0] == '@')
260: {
261: // Bundle name
262: $bundle = substr($reference, 1, strpos($reference, '/') - 1);
263: // Installation root directory
264: $root = dirname($this->container->getParameter('kernel.root_dir'));
265: // Path inside the Resources/public folder
266: $assetPath = substr($reference, strlen('@' . $bundle . '/Resources/public'));
267:
268: // Path for the public version
269: $public = $root . '/web/bundles/' .
270: preg_replace('/bundle$/', '', strtolower($bundle)) .
271: $assetPath;
272:
273: $sourceBase = '';
274: $sourcePath = $public;
275: }
276:
277: $asset = new FileAsset($file,
278: array(),
279: $sourceBase,
280: $sourcePath);
281: $name = str_replace(array('@', '/', '.', '-'), '__', $reference);
282: $manager->set($name, $asset);
283: }
284:
285: /**
286: * Get the configuration (application, elements, layers) as an StringAsset.
287: * Filters can be applied later on with the ensureFilter method.
288: *
289: * @return StringAsset The configuration asset object
290: */
291: public function getConfiguration()
292: {
293: $configuration = array();
294:
295: $configuration['application'] = array(
296: 'title' => $this->entity->getTitle(),
297: 'urls' => $this->urls,
298: 'slug' => $this->getSlug());
299:
300: // Get all element configurations
301: $configuration['elements'] = array();
302: foreach($this->getElements() as $region => $elements)
303: {
304: foreach($elements as $element)
305: {
306: $configuration['elements'][$element->getId()] = array(
307: 'init' => $element->getWidgetName(),
308: 'configuration' => $element->getConfiguration());
309: }
310: }
311:
312: // Get all layer configurations
313: $configuration['layersets'] = array();
314: foreach($this->getLayersets() as $layerset)
315: {
316: $configuration['layersets'][$layerset->getId()] = array();
317: $num = 0;
318: foreach($layerset->layerObjects as $layer)
319: {
320: $layerconf = array(
321: $layer->getId() => array(
322: 'type' => $layer->getType(),
323: 'title' => $layer->getTitle(),
324: 'configuration' => $layer->getConfiguration()));
325: $configuration['layersets'][$layerset->getId()][$num] = $layerconf;
326: $num++;
327: }
328: }
329:
330: // Convert to asset
331: $asset = new StringAsset(json_encode((object) $configuration));
332: return $asset->dump();
333: }
334:
335: /**
336: * Return the element with the given id
337: *
338: * @param string $id The element id
339: * @return Element
340: */
341: public function getElement($id)
342: {
343: $elements = $this->getElements();
344: foreach($elements as $region => $element_list)
345: {
346: foreach($element_list as $element)
347: {
348: if($id == $element->getId())
349: {
350: return $element;
351: }
352: }
353: }
354: throw new NotFoundHttpException();
355: }
356:
357: /**
358: * Build an Assetic reference path from a given objects bundle name(space)
359: * and the filename/path within that bundles Resources/public folder
360: *
361: * @param object $object
362: * @param string $file
363: * @return string
364: */
365: private function getReference($object, $file)
366: {
367: // If it starts with an @ we assume it's already an assetic reference
368: if($file[0] !== '@')
369: {
370: $namespaces = explode('\\', get_class($object));
371: $bundle = sprintf('%s%s', $namespaces[0], $namespaces[1]);
372: return sprintf('@%s/Resources/public/%s', $bundle, $file);
373: } else
374: {
375: return $file;
376: }
377: }
378:
379: /**
380: * Get template object
381: *
382: * @return Template
383: */
384: public function getTemplate()
385: {
386: if($this->template === null)
387: {
388: $template = $this->entity->getTemplate();
389: $this->template = new $template($this->container, $this);
390: }
391: return $this->template;
392: }
393:
394: /**
395: * Get elements, optionally by region
396: *
397: * @param string $region Region to get elements for. If null, all elements
398: * are returned.
399: * @return array
400: */
401: public function getElements($region = null)
402: {
403: if($this->elements === null)
404: {
405: // Set up all elements (by region)
406: $this->elements = array();
407: foreach($this->entity->getElements() as $entity)
408: {
409: $class = $entity->getClass();
410: if(!$entity->getEnabled()) {
411: continue;
412: }
413: $element = new $class($this, $this->container, $entity);
414: $r = $entity->getRegion();
415:
416: if(!array_key_exists($r, $this->elements))
417: {
418: $this->elements[$r] = array();
419: }
420: $this->elements[$r][] = $element;
421: }
422:
423: // Sort each region element's by weight
424: foreach($this->elements as $r => $elements)
425: {
426: usort($elements, function($a, $b)
427: {
428: $wa = $a->getEntity()->getWeight();
429: $wb = $b->getEntity()->getWeight();
430: if($wa == $wb)
431: {
432: return 0;
433: }
434: return ($wa < $wb) ? -1 : 1;
435: });
436: }
437: }
438:
439: if($region)
440: {
441: return array_key_exists($region, $this->elements) ?
442: $this->elements[$region] : array();
443: } else
444: {
445: return $this->elements;
446: }
447: }
448:
449: // /**
450: // * @TODO: Needs documentation
451: // */
452: // public function reloadLayers($layersetId, $layeridToRemove, $layersToLoad)
453: // {
454: // // remove old layer configuration
455: // if(isset($this->configuration['layersets'][$layersetId][$layeridToRemove]))
456: // {
457: // unset($this->configuration['layersets'][$layersetId][$layeridToRemove]);
458: // }
459: // // create a new layer configuration
460: // $newLayers = array();
461: // foreach($this->layersets[$layersetId] as $layersAtLs)
462: // {
463: // if($layersAtLs->getLayerId() == $layeridToRemove)
464: // {
465: // foreach($layersToLoad as $layer_ToLoad)
466: // {
467: // $class = $layer_ToLoad['loaderClass'];
468: // $layerL = new $class(
469: // $layersetId,
470: // $layer_ToLoad['layerId'],
471: // array("class" => $layer_ToLoad['loaderClass']),
472: // $this);
473: // $layerL->loadLayer();
474: // $this->configuration['layersets'][$layersetId][$layer_ToLoad['layerId']] = $layerL->getConfiguration();
475: // $newLayers[] = $layerL;
476: // }
477: // } else
478: // {
479: // $newLayers[] = $layersAtLs;
480: // }
481: // }
482: // if(isset($this->layersets[$layersetId]))
483: // {
484: // unset($this->layersets[$layersetId]);
485: // }
486: // $this->layersets[$layersetId] = $newLayers;
487: // }
488:
489: /**
490: * Returns all layersets
491: *
492: * @return array the layersets
493: */
494: public function getLayersets()
495: {
496: if($this->layers === null)
497: {
498:
499: // Set up all elements (by region)
500: $this->layers = array();
501: foreach($this->entity->getLayersets() as $layerset)
502: {
503: $layerset->layerObjects = array();
504: foreach($layerset->getInstances() as $instance)
505: {
506: if($this->getEntity()->getSource() === Entity::SOURCE_YAML)
507: {
508: // $class = $entity->getClass();
509: // $layer = new $class($this->container, $entity);
510: // $layerset->layerObjects[] = $layer;
511:
512: $layerset->layerObjects[] = $instance;
513: } else
514: {
515: if($instance->getEnabled()){
516: // $layerset->layerObjects[] = $instance->getSourceInstance();
517: $layerset->layerObjects[] = $instance;
518: }
519: }
520: }
521: $this->layers[$layerset->getId()] = $layerset;
522: }
523:
524: }
525: return $this->layers;
526: }
527:
528: }
529:
530: