1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
12:
13: namespace DcGeneral\Contao\View\Contao2BackendView;
14:
15: use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents;
16: use ContaoCommunityAlliance\Contao\Bindings\Events\Backend\AddToUrlEvent;
17: use ContaoCommunityAlliance\Contao\Bindings\Events\Controller\ReloadEvent;
18: use ContaoCommunityAlliance\Contao\Bindings\Events\Image\GenerateHtmlEvent;
19: use DcGeneral\Contao\BackendBindings;
20: use DcGeneral\Data\CollectionInterface;
21: use DcGeneral\Data\DCGE;
22: use DcGeneral\Data\ModelInterface;
23: use DcGeneral\Contao\View\Contao2BackendView\Event\GetPasteRootButtonEvent;
24: use DcGeneral\Exception\DcGeneralRuntimeException;
25:
26: 27: 28: 29: 30: 31: 32:
33: class TreeView extends BaseView
34: {
35: 36: 37: 38: 39:
40: protected function getToggleId()
41: {
42: return $this->getEnvironment()->getDataDefinition()->getName() . '_tree';
43: }
44:
45: 46: 47: 48: 49:
50: protected function getOpenElements()
51: {
52: $inputProvider = $this->getEnvironment()->getInputProvider();
53:
54: $openElements = $inputProvider->getPersistentValue($this->getToggleId());
55:
56: if (!is_array($openElements))
57: {
58: $openElements = array();
59: $inputProvider->setPersistentValue($this->getToggleId(), $openElements);
60: }
61:
62:
63: if ($inputProvider->getParameter('ptg') == 'all')
64: {
65: $openElements = array();
66: if (!array_key_exists('all', $openElements))
67: {
68: $openElements = array();
69: $openElements['all'] = 1;
70: }
71:
72:
73: $inputProvider->setPersistentValue($this->getToggleId(), $openElements);
74:
75: $this->getEnvironment()->getEventPropagator()->propagate(ContaoEvents::CONTROLLER_RELOAD, new ReloadEvent());
76: }
77:
78: return $openElements;
79: }
80:
81: 82: 83: 84: 85: 86: 87: 88: 89:
90: protected function toggleModel($providerName, $id)
91: {
92: $inputProvider = $this->getEnvironment()->getInputProvider();
93: $openElements = $this->getOpenElements();
94:
95: if (!isset($openElements[$providerName]))
96: {
97: $openElements[$providerName] = array();
98: }
99:
100: if (!isset($openElements[$providerName][$id]))
101: {
102: $openElements[$providerName][$id] = 1;
103: }
104: else
105: {
106: $openElements[$providerName][$id] = !$openElements[$providerName][$id];
107: }
108:
109: $inputProvider->setPersistentValue($this->getToggleId(), $openElements);
110: }
111:
112: 113: 114: 115: 116: 117: 118:
119: protected function isModelOpen($model)
120: {
121: $openModels = $this->getOpenElements();
122:
123: if (isset($openModels['all']) && ($openModels['all'] == 1))
124: {
125: return true;
126: }
127:
128: if (isset($openModels[$model->getProviderName()][$model->getID()])
129: && ($openModels[$model->getProviderName()][$model->getID()])
130: )
131: {
132: return true;
133: }
134:
135: return false;
136: }
137:
138:
139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149:
150: public function loadCollection($rootId = null, $intLevel = 0, $providerName = null)
151: {
152: $environment = $this->getEnvironment();
153: $dataDriver = $environment->getDataProvider($providerName);
154:
155: $objCollection = $this->getTreeCollectionRecursive($rootId, $intLevel, $providerName);
156:
157: if ($rootId)
158: {
159: $objTreeData = $dataDriver->getEmptyCollection();
160: $objModel = $objCollection->get(0);
161: foreach ($objModel->getMeta(DCGE::TREE_VIEW_CHILD_COLLECTION) as $objCollection)
162: {
163: foreach ($objCollection as $objSubModel)
164: {
165: $objTreeData->push($objSubModel);
166: }
167: }
168: return $objTreeData;
169: }
170:
171: return $objCollection;
172: }
173:
174: 175: 176: 177: 178: 179: 180: 181:
182: protected function loadParentModel()
183: {
184: $environment = $this->getEnvironment();
185:
186: if (!($parentId = $environment->getInputProvider()->getParameter('pid')))
187: {
188: throw new DcGeneralRuntimeException(
189: 'TreeView needs a proper parent id defined, somehow none is defined?',
190: 1
191: );
192: }
193:
194: if (!($objParentProvider =
195: $environment->getDataProvider(
196: $environment->getDataDefinition()->getBasicDefinition()->getParentDataProvider()
197: )
198: ))
199: {
200: throw new DcGeneralRuntimeException(
201: 'TreeView needs a proper parent data provider defined, somehow none is defined?',
202: 1
203: );
204: }
205:
206: $objParentItem = $objParentProvider->fetch($objParentProvider->getEmptyConfig()->setId($parentId));
207:
208: if (!$objParentItem)
209: {
210:
211:
212:
213: $objParentItem = $objParentProvider->getEmptyModel();
214: $objParentItem->setID($parentId);
215: }
216:
217: return $objParentItem;
218: }
219:
220: 221: 222: 223: 224: 225: 226:
227: protected function calcLabelFields($strTable)
228: {
229: return $config = $this->getViewSection()->getListingConfig()->getLabelFormatter($strTable)->getPropertyNames();
230: }
231:
232: 233: 234: 235: 236: 237: 238: 239: 240:
241: protected function determineModelState(ModelInterface $model, $level)
242: {
243: $model->setMeta(DCGE::TREE_VIEW_LEVEL, $level);
244: $model->setMeta(DCGE::TREE_VIEW_IS_OPEN, $this->isModelOpen($model));
245: }
246:
247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257:
258: protected function treeWalkModel(ModelInterface $objModel, $intLevel, $arrSubTables = array())
259: {
260: $relationships = $this->getEnvironment()->getDataDefinition()->getModelRelationshipDefinition();
261: $blnHasChild = false;
262:
263: $this->determineModelState($objModel, $intLevel);
264:
265: $arrChildCollections = array();
266: foreach ($arrSubTables as $strSubTable)
267: {
268:
269: $arrChildFilter = $relationships->getChildCondition($objModel->getProviderName(), $strSubTable);
270:
271:
272: if (!$arrChildFilter)
273: {
274: continue;
275: }
276:
277:
278: $objChildConfig = $this->getEnvironment()->getDataProvider($strSubTable)->getEmptyConfig();
279: $objChildConfig->setFilter($arrChildFilter->getFilter($objModel));
280:
281:
282:
283:
284: $objChildConfig->setSorting(array('sorting' => 'ASC'));
285:
286: $objChildCollection = $this->getEnvironment()->getDataProvider($strSubTable)->fetchAll($objChildConfig);
287:
288: $blnHasChild = ($objChildCollection->length() > 0);
289:
290:
291: if ($blnHasChild && !$objModel->getMeta(DCGE::TREE_VIEW_IS_OPEN))
292: {
293: break;
294: }
295: elseif ($blnHasChild)
296: {
297: foreach ($objChildCollection as $objChildModel)
298: {
299:
300: $objModel->setMeta(DCGE::MODEL_PID, $objModel->getID());
301: $objModel->setMeta(DCGE::MODEL_PTABLE, $objModel->getProviderName());
302:
303: $mySubTables = array();
304: foreach ($relationships->getChildConditions($objModel->getProviderName()) as $condition)
305: {
306: $mySubTables[] = $condition->getDestinationName();
307: }
308:
309: $this->treeWalkModel($objChildModel, ($intLevel + 1), $mySubTables);
310: }
311: $arrChildCollections[] = $objChildCollection;
312:
313:
314: if (!$objModel->getMeta(DCGE::TREE_VIEW_IS_OPEN))
315: {
316: break;
317: }
318: }
319: }
320:
321:
322: if ($objModel->getMeta(DCGE::TREE_VIEW_IS_OPEN) && count($arrChildCollections) != 0)
323: {
324: $objModel->setMeta(DCGE::TREE_VIEW_CHILD_COLLECTION, $arrChildCollections);
325: }
326:
327: $objModel->setMeta(DCGE::TREE_VIEW_HAS_CHILDS, $blnHasChild);
328: }
329:
330: 331: 332: 333: 334: 335: 336: 337: 338: 339: 340:
341: public function getTreeCollectionRecursive($rootId, $intLevel = 0, $providerName = null)
342: {
343: $environment = $this->getEnvironment();
344: $definition = $environment->getDataDefinition();
345: $dataDriver = $environment->getDataProvider($providerName);
346: $objTableTreeData = $dataDriver->getEmptyCollection();
347: $objRootConfig = $environment->getController()->getBaseConfig();
348: $relationships = $definition->getModelRelationshipDefinition();
349:
350: $this->getPanel()->initialize($objRootConfig);
351:
352:
353: if (!$rootId)
354: {
355: $objRootCondition = $definition->getModelRelationshipDefinition()->getRootCondition();
356:
357: if ($objRootCondition)
358: {
359: $arrBaseFilter = $objRootConfig->getFilter();
360: $arrFilter = $objRootCondition->getFilterArray();
361:
362: if ($arrBaseFilter)
363: {
364: $arrFilter = array_merge($arrBaseFilter, $arrFilter);
365: }
366:
367: $objRootConfig->setFilter($arrFilter);
368: }
369:
370: $objRootCollection = $dataDriver->fetchAll($objRootConfig);
371:
372: if ($objRootCollection->length() > 0)
373: {
374: $mySubTables = array();
375: foreach ($relationships->getChildConditions($objRootCollection->get(0)->getProviderName()) as $condition)
376: {
377: $mySubTables[] = $condition->getDestinationName();
378: }
379:
380: foreach ($objRootCollection as $objRootModel)
381: {
382:
383: $objTableTreeData->push($objRootModel);
384: $this->treeWalkModel($objRootModel, $intLevel, $mySubTables);
385: }
386: }
387:
388: return $objTableTreeData;
389: }
390:
391: $objRootConfig->setId($rootId);
392:
393: $objRootModel = $dataDriver->fetch($objRootConfig);
394:
395: $mySubTables = array();
396: foreach ($relationships->getChildConditions($objRootModel->getProviderName()) as $condition)
397: {
398: $mySubTables[] = $condition->getDestinationName();
399: }
400:
401: $this->treeWalkModel($objRootModel, $intLevel, $mySubTables);
402: $objRootCollection = $dataDriver->getEmptyCollection();
403: $objRootCollection->push($objRootModel);
404:
405: return $objRootCollection;
406: }
407:
408: 409: 410: 411: 412: 413: 414: 415: 416:
417: protected function parseModel($objModel, $strToggleID)
418: {
419: $objModel->setMeta(DCGE::MODEL_BUTTONS, $this->generateButtons($objModel));
420: $objModel->setMeta(DCGE::MODEL_LABEL_VALUE, $this->formatModel($objModel));
421:
422: $objTemplate = $this->getTemplate('dcbe_general_treeview_entry');
423:
424: if ($objModel->getMeta(DCGE::TREE_VIEW_IS_OPEN))
425: {
426: $toggleTitle = $this->getEnvironment()->getTranslator()->translate('collapseNode', 'MSC');
427: }
428: else
429: {
430: $toggleTitle = $this->getEnvironment()->getTranslator()->translate('expandNode', 'MSC');
431: }
432:
433: $toggleScript = sprintf(
434: 'Backend.getScrollOffset(); return BackendGeneral.loadSubTree(this, {\'toggler\':\'%s\', \'id\':\'%s\', \'providerName\':\'%s\', \'level\':\'%s\', \'mode\':\'%s\'});',
435: $strToggleID,
436: $objModel->getId(),
437: $objModel->getProviderName(),
438: $objModel->getMeta('dc_gen_tv_level'),
439:
440: 6
441: );
442:
443: $toggleUrlEvent = new AddToUrlEvent('ptg=' . $objModel->getId() . '&provider=' . $objModel->getProviderName());
444: $this->getEnvironment()->getEventPropagator()->propagate(ContaoEvents::BACKEND_ADD_TO_URL, $toggleUrlEvent);
445:
446: $this
447: ->addToTemplate('environment', $this->getEnvironment(), $objTemplate)
448: ->addToTemplate('objModel', $objModel, $objTemplate)
449:
450: ->addToTemplate('intMode', 6, $objTemplate)
451: ->addToTemplate('strToggleID', $strToggleID, $objTemplate)
452: ->addToTemplate(
453: 'toggleUrl',
454: $toggleUrlEvent->getUrl(),
455: $objTemplate
456: )
457: ->addToTemplate('toggleTitle', $toggleTitle, $objTemplate)
458: ->addToTemplate('toggleScript', $toggleScript, $objTemplate);
459:
460: return $objTemplate->parse();
461: }
462:
463: 464: 465: 466: 467: 468: 469:
470: protected function generateTreeView($objCollection, $treeClass)
471: {
472: $arrHtml = array();
473:
474: foreach ($objCollection as $objModel)
475: {
476:
477:
478: $strToggleID = $objModel->getProviderName() . '_' . $treeClass . '_' . $objModel->getID();
479:
480: $arrHtml[] = $this->parseModel($objModel, $strToggleID);
481:
482: if ($objModel->getMeta(DCGE::TREE_VIEW_HAS_CHILDS) && $objModel->getMeta(DCGE::TREE_VIEW_IS_OPEN))
483: {
484: $objTemplate = $this->getTemplate('dcbe_general_treeview_child');
485: $strSubHtml = '';
486:
487: foreach ($objModel->getMeta(DCGE::TREE_VIEW_CHILD_COLLECTION) as $objCollection)
488: {
489: $strSubHtml .= $this->generateTreeView($objCollection, $treeClass);
490: }
491:
492: $this
493: ->addToTemplate('objParentModel', $objModel, $objTemplate)
494: ->addToTemplate('strToggleID', $strToggleID, $objTemplate)
495: ->addToTemplate('strHTML', $strSubHtml, $objTemplate)
496: ->addToTemplate('strTable', $objModel->getProviderName(), $objTemplate);
497:
498: $arrHtml[] = $objTemplate->parse();
499: }
500: }
501:
502: return implode("\n", $arrHtml);
503: }
504:
505: 506: 507: 508: 509: 510: 511:
512: public static function renderPasteRootButton(GetPasteRootButtonEvent $event)
513: {
514: if (!is_null($event->getHtml()))
515: {
516: return $event->getHtml();
517: }
518:
519: $strLabel = $GLOBALS['TL_LANG'][$event->getEnvironment()->getDataDefinition()->getName()]['pasteinto'][0];
520: if ($event->isPasteDisabled())
521: {
522:
523: $imageEvent = $event->getEnvironment()->getEventPropagator()->propagate(
524: ContaoEvents::IMAGE_GET_HTML,
525: new GenerateHtmlEvent(
526: 'pasteinto_.gif',
527: $strLabel,
528: 'class="blink"'
529: )
530: );
531:
532: return $imageEvent->getHtml();
533: }
534:
535:
536: $imageEvent = $event->getEnvironment()->getEventPropagator()->propagate(
537: ContaoEvents::IMAGE_GET_HTML,
538: new GenerateHtmlEvent(
539: 'pasteinto.gif',
540: $strLabel,
541: 'class="blink"'
542: )
543: );
544:
545: return sprintf(' <a href="%s" title="%s" %s>%s</a>',
546: $event->getHref(),
547: specialchars($strLabel),
548: 'onclick="Backend.getScrollOffset()"',
549: $imageEvent->getHtml()
550: );
551: }
552:
553: 554: 555: 556: 557: 558: 559:
560: protected function viewTree($collection)
561: {
562: $definition = $this->getDataDefinition();
563:
564:
565: switch (6 )
566: {
567: case 6:
568: $treeClass = 'tree_xtnd';
569: break;
570:
571: default:
572: $treeClass = 'tree';
573: }
574:
575:
576:
577: if (true || strlen($this->getViewSection()->getListingConfig()->getLabel()) == 0)
578: {
579: $strLabelText = 'DC General Tree BackendView Ultimate';
580: }
581: else
582: {
583: $strLabelText = $definition->getLabel();
584: }
585:
586:
587: if (true || strlen($definition->getIcon()) == 0)
588: {
589: $strLabelIcon = 'pagemounts.gif';
590: }
591: else
592: {
593: $strLabelIcon = $definition->getIcon();
594: }
595:
596:
597: if ($this->getEnvironment()->getClipboard()->isNotEmpty())
598: {
599: $objClipboard = $this->getEnvironment()->getClipboard();
600:
601: $urlEvent = $this->getEnvironment()->getEventPropagator()->propagate(
602: ContaoEvents::BACKEND_ADD_TO_URL,
603: new AddToUrlEvent(sprintf('act=%s&into=0&children=%s',
604: $objClipboard->getMode(),
605: $objClipboard->getContainedIds(),
606: implode(',', $objClipboard->getCircularIds())
607: ))
608: );
609:
610: $buttonEvent = new GetPasteRootButtonEvent($this->getEnvironment());
611: $buttonEvent
612: ->setHref($urlEvent->getUrl())
613: ->setPasteDisabled(false);
614:
615: $this->getEnvironment()->getEventPropagator()->propagate(
616: $buttonEvent::NAME,
617: $buttonEvent,
618: $this->getEnvironment()->getDataDefinition()->getName()
619: );
620:
621: $strRootPasteInto = $this->renderPasteRootButton($buttonEvent);
622: }
623: else
624: {
625: $strRootPasteInto = '';
626: }
627:
628:
629: $imageEvent = $this->getEnvironment()->getEventPropagator()->propagate(
630: ContaoEvents::IMAGE_GET_HTML,
631: new GenerateHtmlEvent($strLabelIcon)
632: );
633:
634:
635:
636: $objTemplate = new \BackendTemplate('dcbe_general_treeview');
637: $objTemplate->treeClass = 'tl_' . $treeClass;
638: $objTemplate->tableName = $definition->getName();
639: $objTemplate->strLabelIcon = $imageEvent->getHtml();
640: $objTemplate->strLabelText = $strLabelText;
641: $objTemplate->strHTML = $this->generateTreeView($collection, $treeClass);
642: $objTemplate->strRootPasteinto = $strRootPasteInto;
643:
644: $objTemplate->intMode = 6;
645:
646:
647: $strBreadcrumb = $this->breadcrumb();
648: if ($strBreadcrumb != null)
649: {
650: $objTemplate->breadcrumb = $strBreadcrumb;
651: }
652:
653: return $objTemplate->parse();
654: }
655:
656: 657: 658:
659: public function enforceModelRelationship($model)
660: {
661: $definition = $this->getEnvironment()->getDataDefinition();
662: $basic = $definition->getBasicDefinition();
663:
664: $parent = $this->loadParentModel();
665:
666: $condition = $definition
667: ->getModelRelationshipDefinition()
668: ->getChildCondition(
669: $basic->getParentDataProvider(),
670: $basic->getDataProvider()
671: );
672:
673: if ($condition)
674: {
675: $condition->applyTo($parent, $model);
676: }
677: }
678:
679: 680: 681: 682: 683:
684: public function showAll()
685: {
686: $input = $this->getEnvironment()->getInputProvider();
687: if (($id = $input->hasParameter('ptg')) && ($providerName = $input->hasParameter('provider')))
688: {
689: $this->toggleModel($providerName, $id);
690: $this->redirectHome();
691: }
692:
693: $this->checkClipboard();
694:
695: $collection = $this->loadCollection();
696: $arrReturn = array();
697:
698: 699: 700: 701: 702: 703:
704: $arrReturn['buttons'] = $this->generateHeaderButtons('tl_buttons_a');
705: $arrReturn['body'] = $this->viewTree($collection);
706:
707: return implode("\n", $arrReturn);
708: }
709:
710: 711: 712: 713: 714:
715: public function handleAjaxCall()
716: {
717: $input = $this->getEnvironment()->getInputProvider();
718:
719: switch ($input->getValue('action'))
720: {
721: case 'DcGeneralLoadSubTree':
722: header('Content-Type: text/html; charset=' . $GLOBALS['TL_CONFIG']['characterSet']);
723: echo $this->ajaxTreeView(
724: $input->getValue('id'),
725: $input->getValue('providerName'),
726: $input->getValue('level'),
727: $input->getValue('mode')
728: );
729: exit;
730:
731: default:
732: }
733:
734: parent::handleAjaxCall();
735: }
736:
737: 738: 739: 740: 741: 742: 743: 744: 745: 746: 747:
748: public function ajaxTreeView($id, $providerName, $level)
749: {
750: $this->toggleModel($providerName, $id);
751:
752: $collection = $this->loadCollection($id, $level, $providerName);
753:
754: $treeClass = '';
755: switch (6 )
756: {
757: case 5:
758: $treeClass = 'tree';
759: break;
760:
761: case 6:
762: $treeClass = 'tree_xtnd';
763: break;
764:
765: default:
766: }
767:
768: $strHtml = $this->generateTreeView($collection, $treeClass);
769:
770: return $strHtml;
771: }
772: }
773: