Overview

Namespaces

  • DcGeneral
    • Clipboard
    • Contao
      • Callback
      • Compatibility
      • DataDefinition
        • Definition
      • Dca
        • Builder
          • Legacy
        • Definition
        • Palette
        • Populator
      • Event
      • View
        • Contao2BackendView
          • Event
    • Controller
    • Data
    • DataDefinition
      • Builder
      • Definition
        • Properties
        • View
          • Panel
      • ModelRelationship
      • Palette
        • Builder
          • Event
        • Condition
          • Palette
          • Property
    • EnvironmentPopulator
    • Event
    • Exception
    • Factory
      • Event
    • Panel
    • View
      • Event

Classes

  • BaseView
  • ContaoBackendViewTemplate
  • ContaoWidgetManager
  • ListView
  • ParentView
  • TreeView

Interfaces

  • BackendViewInterface
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Todo
   1: <?php
   2: /**
   3:  * PHP version 5
   4:  * @package    generalDriver
   5:  * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
   6:  * @author     Stefan Heimes <stefan_heimes@hotmail.com>
   7:  * @author     Tristan Lins <tristan.lins@bit3.de>
   8:  * @copyright  The MetaModels team.
   9:  * @license    LGPL.
  10:  * @filesource
  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\RedirectEvent;
  18: use ContaoCommunityAlliance\Contao\Bindings\Events\Controller\ReloadEvent;
  19: use ContaoCommunityAlliance\Contao\Bindings\Events\Image\GenerateHtmlEvent;
  20: use ContaoCommunityAlliance\Contao\Bindings\Events\Image\ResizeImageEvent;
  21: use ContaoCommunityAlliance\Contao\Bindings\Events\System\GetReferrerEvent;
  22: use ContaoCommunityAlliance\Contao\Bindings\Events\System\LogEvent;
  23: use DcGeneral\Contao\View\Contao2BackendView\Event\EditModelBeforeSaveEvent;
  24: use DcGeneral\Contao\View\Contao2BackendView\Event\ModelToLabelEvent;
  25: use DcGeneral\Data\ModelInterface;
  26: use DcGeneral\Data\MultiLanguageDataProviderInterface;
  27: use DcGeneral\Data\DCGE;
  28: use DcGeneral\Data\PropertyValueBag;
  29: use DcGeneral\Contao\DataDefinition\Definition\Contao2BackendViewDefinitionInterface;
  30: use DcGeneral\DataDefinition\Definition\BasicDefinitionInterface;
  31: use DcGeneral\DataDefinition\Definition\Properties\PropertyInterface;
  32: use DcGeneral\DataDefinition\Definition\View\Command;
  33: use DcGeneral\DataDefinition\Definition\View\CommandInterface;
  34: use DcGeneral\DataDefinition\Definition\View\CutCommandInterface;
  35: use DcGeneral\DataDefinition\Definition\View\ListingConfigInterface;
  36: use DcGeneral\EnvironmentInterface;
  37: use DcGeneral\Event\PostCreateModelEvent;
  38: use DcGeneral\Event\PostDeleteModelEvent;
  39: use DcGeneral\Event\PostPersistModelEvent;
  40: use DcGeneral\Event\PreCreateModelEvent;
  41: use DcGeneral\Event\PreDeleteModelEvent;
  42: use DcGeneral\Event\PrePersistModelEvent;
  43: use DcGeneral\Exception\DcGeneralInvalidArgumentException;
  44: use DcGeneral\Exception\DcGeneralRuntimeException;
  45: use DcGeneral\Panel\FilterElementInterface;
  46: use DcGeneral\Panel\LimitElementInterface;
  47: use DcGeneral\Panel\PanelContainerInterface;
  48: use DcGeneral\Panel\SearchElementInterface;
  49: use DcGeneral\Panel\SortElementInterface;
  50: use DcGeneral\Panel\SubmitElementInterface;
  51: use DcGeneral\View\Event\RenderReadablePropertyValueEvent;
  52: use DcGeneral\Contao\View\Contao2BackendView\Event\GetBreadcrumbEvent;
  53: use DcGeneral\Contao\View\Contao2BackendView\Event\GetEditModeButtonsEvent;
  54: use DcGeneral\Contao\View\Contao2BackendView\Event\GetGlobalButtonEvent;
  55: use DcGeneral\Contao\View\Contao2BackendView\Event\GetGlobalButtonsEvent;
  56: use DcGeneral\Contao\View\Contao2BackendView\Event\GetGroupHeaderEvent;
  57: use DcGeneral\Contao\View\Contao2BackendView\Event\GetOperationButtonEvent;
  58: use DcGeneral\Contao\View\Contao2BackendView\Event\GetPasteButtonEvent;
  59: use DcGeneral\Contao\View\Contao2BackendView\Event\GetSelectModeButtonsEvent;
  60: use DcGeneral\Contao\BackendBindings;
  61: 
  62: /**
  63:  * Class BaseView.
  64:  *
  65:  * This class is the base class for the different backend view mode sub classes.
  66:  *
  67:  * @package DcGeneral\Contao\View\Contao2BackendView
  68:  */
  69: class BaseView implements BackendViewInterface
  70: {
  71:     /**
  72:      * The error message format string to use when a method is not implemented.
  73:      *
  74:      * @var string
  75:      */
  76:     protected $notImplMsg =
  77:         '<div style="text-align:center; font-weight:bold; padding:40px;">
  78:         The function/view &quot;%s&quot; is not implemented.
  79:         </div>';
  80: 
  81:     /**
  82:      * The attached environment.
  83:      *
  84:      * @var EnvironmentInterface
  85:      */
  86:     protected $environment;
  87: 
  88:     /**
  89:      * The panel container in use.
  90:      *
  91:      * @var PanelContainerInterface
  92:      */
  93:     protected $panel;
  94: 
  95:     /**
  96:      * Dispatch an event to the dispatcher.
  97:      *
  98:      * The event will first get triggered with the name of the active data provider within square brackets appended
  99:      * and plain afterwards.
 100:      *
 101:      * Example:
 102:      *   Event name: "some-event"
 103:      *   DP name:    "tl_table"
 104:      *
 105:      *   1. dispatch: "some-event[tl_table]"
 106:      *   2. dispatch: "some-event"
 107:      *
 108:      * @param string                                   $eventName The name of the event to dispatch.
 109:      *
 110:      * @param \Symfony\Component\EventDispatcher\Event $event     The event to dispatch.
 111:      *
 112:      * @return void
 113:      *
 114:      * @deprecated Use $this->getEnvironment()->getEventPropagator()->propagate() instead.
 115:      */
 116:     protected function dispatchEvent($eventName, $event)
 117:     {
 118:         $this->getEnvironment()->getEventPropagator()->propagate(
 119:             $event::NAME,
 120:             $event,
 121:             array($this->getEnvironment()->getDataDefinition()->getName())
 122:         );
 123:     }
 124: 
 125:     /**
 126:      * {@inheritDoc}
 127:      */
 128:     public function setEnvironment(EnvironmentInterface $environment)
 129:     {
 130:         $this->environment = $environment;
 131:     }
 132: 
 133:     /**
 134:      * {@inheritDoc}
 135:      */
 136:     public function getEnvironment()
 137:     {
 138:         return $this->environment;
 139:     }
 140: 
 141:     /**
 142:      * Retrieve the data definition from the environment.
 143:      *
 144:      * @return \DcGeneral\DataDefinition\ContainerInterface
 145:      */
 146:     protected function getDataDefinition()
 147:     {
 148:         return $this->getEnvironment()->getDataDefinition();
 149:     }
 150: 
 151:     /**
 152:      * Translate a string via the translator.
 153:      *
 154:      * @param string      $path    The path within the translation where the string can be found.
 155:      *
 156:      * @param string|null $section The section from which the translation shall be retrieved.
 157:      *
 158:      * @return string
 159:      */
 160:     protected function translate($path, $section = null)
 161:     {
 162:         return $this->getEnvironment()->getTranslator()->translate($path, $section);
 163:     }
 164: 
 165:     /**
 166:      * Add the value to the template.
 167:      *
 168:      * @param string    $name     Name of the value.
 169:      *
 170:      * @param mixed     $value    The value to add to the template.
 171:      *
 172:      * @param \Template $template The template to add the value to.
 173:      *
 174:      * @return BaseView
 175:      */
 176:     protected function addToTemplate($name, $value, $template)
 177:     {
 178:         $template->$name = $value;
 179: 
 180:         return $this;
 181:     }
 182: 
 183:     /**
 184:      * {@inheritDoc}
 185:      */
 186:     public function setPanel($panelContainer)
 187:     {
 188:         $this->panel = $panelContainer;
 189: 
 190:         return $this;
 191:     }
 192: 
 193:     /**
 194:      * {@inheritDoc}
 195:      */
 196:     public function getPanel()
 197:     {
 198:         return $this->panel;
 199:     }
 200: 
 201:     /**
 202:      * Retrieve the view section for this view.
 203:      *
 204:      * @return Contao2BackendViewDefinitionInterface
 205:      */
 206:     protected function getViewSection()
 207:     {
 208:         return $this->getDataDefinition()->getDefinition(Contao2BackendViewDefinitionInterface::NAME);
 209:     }
 210: 
 211:     /**
 212:      * Redirects to the real back end module.
 213:      *
 214:      * @return void
 215:      */
 216:     protected function redirectHome()
 217:     {
 218:         $environment = $this->getEnvironment();
 219:         $input       = $environment->getInputProvider();
 220: 
 221:         if ($input->hasParameter('table') && $input->hasParameter('pid'))
 222:         {
 223:             if ($input->hasParameter('pid'))
 224:             {
 225:                 $event = new RedirectEvent(sprintf(
 226:                         'contao/main.php?do=%s&table=%s&pid=%s',
 227:                         $input->getParameter('do'),
 228:                         $input->getParameter('table'),
 229:                         $input->getParameter('pid')
 230:                 ));
 231:             }
 232:             else
 233:             {
 234:                 $event = new RedirectEvent(sprintf(
 235:                     'contao/main.php?do=%s&table=%s',
 236:                     $input->getParameter('do'),
 237:                     $input->getParameter('table')
 238:                 ));
 239:             }
 240:         }
 241:         else
 242:         {
 243:             $event = new RedirectEvent(sprintf(
 244:                 'contao/main.php?do=%s',
 245:                 $input->getParameter('do')
 246:             ));
 247:         }
 248: 
 249:         $environment->getEventPropagator()->propagate(ContaoEvents::CONTROLLER_REDIRECT, $event);
 250:     }
 251: 
 252:     /**
 253:      * Determines if this view is opened in a popup frame.
 254:      *
 255:      * @return bool
 256:      */
 257:     protected function isPopup()
 258:     {
 259:         return \Input::getInstance()->get('popup');
 260:     }
 261: 
 262:     /**
 263:      * Determine if the select mode is currently active or not.
 264:      *
 265:      * @return bool
 266:      */
 267:     protected function isSelectModeActive()
 268:     {
 269:         return \Input::getInstance()->get('act') == 'select';
 270:     }
 271: 
 272:     /**
 273:      * Retrieve the currently active grouping mode.
 274:      *
 275:      * @return array|null
 276:      *
 277:      * @see    ListingConfigInterface
 278:      */
 279:     protected function getGroupingMode()
 280:     {
 281:         /** @var Contao2BackendViewDefinitionInterface $viewDefinition */
 282:         $viewDefinition = $this->getViewSection();
 283:         $listingConfig  = $viewDefinition->getListingConfig();
 284: 
 285:         if ($listingConfig->getSortingMode() === ListingConfigInterface::SORT_RANDOM)
 286:         {
 287:             return null;
 288:         }
 289: 
 290:         $definition    = $this->getEnvironment()->getDataDefinition();
 291:         $properties    = $definition->getPropertiesDefinition();
 292:         $sortingFields = array_keys((array)$listingConfig->getDefaultSortingFields());
 293:         $firstSorting  = reset($sortingFields);
 294: 
 295:         $panel = $this->getPanel()->getPanel('sorting');
 296:         if ($panel)
 297:         {
 298:             /** @var \DcGeneral\Panel\SortElementInterface $panel */
 299:             $firstSorting = $panel->getSelected();
 300:         }
 301: 
 302:         // Get the current value of first sorting.
 303:         if (!$firstSorting)
 304:         {
 305:             return null;
 306:         }
 307: 
 308:         $property = $properties->getProperty($firstSorting);
 309: 
 310:         if (count($sortingFields) == 0)
 311:         {
 312:             $groupMode   = ListingConfigInterface::GROUP_NONE;
 313:             $groupLength = 0;
 314:         }
 315:         // Use the information from the property, if given.
 316:         elseif ($property->getGroupingMode() != '')
 317:         {
 318:             $groupMode   = $property->getGroupingMode();
 319:             $groupLength = $property->getGroupingLength();
 320:         }
 321:         // Use the global as fallback.
 322:         else
 323:         {
 324:             $groupMode   = $listingConfig->getGroupingMode();
 325:             $groupLength = $listingConfig->getGroupingLength();
 326:         }
 327: 
 328:         return array
 329:         (
 330:             'mode'     => $groupMode,
 331:             'length'   => $groupLength,
 332:             'property' => $firstSorting
 333:         );
 334:     }
 335: 
 336:     /**
 337:      * Return the formatted value for use in group headers as string.
 338:      *
 339:      * @param string         $field       The name of the property to format.
 340:      *
 341:      * @param ModelInterface $model       The model from which the value shall be taken from.
 342:      *
 343:      * @param string         $groupMode   The grouping mode in use.
 344:      *
 345:      * @param int            $groupLength The length of the value to use for grouping (only used when grouping mode is
 346:      *                                    ListingConfigInterface::GROUP_CHAR).
 347:      *
 348:      * @return string
 349:      */
 350:     public function formatCurrentValue($field, $model, $groupMode, $groupLength)
 351:     {
 352:         $value    = $model->getProperty($field);
 353:         $property = $this->getDataDefinition()->getPropertiesDefinition()->getProperty($field);
 354: 
 355:         // No property? Get out!
 356:         if (!$property)
 357:         {
 358:             return '-';
 359:         }
 360: 
 361:         $evaluation = $property->getExtra();
 362:         $remoteNew  = '';
 363: 
 364:         if ($property->getWidgetType() == 'checkbox' && !$evaluation['multiple'])
 365:         {
 366:             $remoteNew = ($value != '') ? ucfirst($this->translate('MSC.yes')) : ucfirst($this->translate('MSC.no'));
 367:         }
 368:         // TODO: refactor foreignKey is yet undefined.
 369:         elseif (false && $property->getForeignKey())
 370:         {
 371:             // TODO: case handling.
 372:             if ($objParentModel->hasProperties())
 373:             {
 374:                 $remoteNew = $objParentModel->getProperty('value');
 375:             }
 376: 
 377:         }
 378:         elseif ($groupMode != ListingConfigInterface::GROUP_NONE)
 379:         {
 380:             switch ($groupMode)
 381:             {
 382:                 case ListingConfigInterface::GROUP_CHAR:
 383:                     $remoteNew = ($value != '') ? ucfirst(utf8_substr($value, 0, $groupLength)) : '-';
 384:                     break;
 385: 
 386:                 case ListingConfigInterface::GROUP_DAY:
 387:                     $remoteNew = ($value != '') ? BackendBindings::parseDate($GLOBALS['TL_CONFIG']['dateFormat'], $value) : '-';
 388:                     break;
 389: 
 390:                 case ListingConfigInterface::GROUP_MONTH:
 391:                     $remoteNew = ($value != '') ? date('Y-m', $value) : '-';
 392:                     $intMonth  = ($value != '') ? (date('m', $value) - 1) : '-';
 393: 
 394:                     if ($month = $this->translate('MONTHS' . $intMonth))
 395:                     {
 396:                         $remoteNew = ($value != '') ? $month . ' ' . date('Y', $value) : '-';
 397:                     }
 398:                     break;
 399: 
 400:                 case ListingConfigInterface::GROUP_YEAR:
 401:                     $remoteNew = ($value != '') ? date('Y', $value) : '-';
 402:                     break;
 403: 
 404:                 default:
 405:             }
 406:         }
 407:         else
 408:         {
 409:             if ($property->getWidgetType() == 'checkbox' && !$evaluation['multiple'])
 410:             {
 411:                 $remoteNew = ($value != '') ? $field : '';
 412:             }
 413:             // TODO: refactor reference is yet undefined.
 414:             elseif (false && is_array($property->get('reference')))
 415:             {
 416:                 $reference = $property->get('reference');
 417:                 $remoteNew = $reference[$value];
 418:             }
 419:             elseif (array_is_assoc($property->getOptions()))
 420:             {
 421:                 $options   = $property->getOptions();
 422:                 $remoteNew = $options[$value];
 423:             }
 424:             else
 425:             {
 426:                 $remoteNew = $value;
 427:             }
 428: 
 429:             if (is_array($remoteNew))
 430:             {
 431:                 $remoteNew = $remoteNew[0];
 432:             }
 433: 
 434:             if (empty($remoteNew))
 435:             {
 436:                 $remoteNew = '-';
 437:             }
 438:         }
 439: 
 440:         $event = new GetGroupHeaderEvent($this->getEnvironment(), $model, $field, $remoteNew, $groupMode);
 441: 
 442:         $this->getEnvironment()->getEventPropagator()->propagate(
 443:             $event::NAME,
 444:             $event,
 445:             array($this->getEnvironment()->getDataDefinition()->getName())
 446:         );
 447: 
 448:         $remoteNew = $event->getValue();
 449: 
 450:         return $remoteNew;
 451:     }
 452: 
 453:     /**
 454:      * Get the label for a button from the translator.
 455:      *
 456:      * The fallback is as follows:
 457:      * 1. Try to translate the button via the data definition name as translation section.
 458:      * 2. Try to translate the button name with the prefix 'MSC.'.
 459:      * 3. Return the input value as nothing worked out.
 460:      *
 461:      * @param string $strButton The non translated label for the button.
 462:      *
 463:      * @return string
 464:      */
 465:     protected function getButtonLabel($strButton)
 466:     {
 467:         $definition = $this->getEnvironment()->getDataDefinition();
 468:         if (($label = $this->translate($strButton, $definition->getName())) !== $strButton)
 469:         {
 470:             return $label;
 471:         }
 472:         elseif (($label = $this->translate('MSC.' . $strButton)) !== $strButton)
 473:         {
 474:             return $label;
 475:         }
 476: 
 477:         // Fallback, just return the key as is it.
 478:         return $strButton;
 479:     }
 480: 
 481:     /**
 482:      * Retrieve a list of html buttons to use in the bottom panel (submit area).
 483:      *
 484:      * @return array
 485:      */
 486:     protected function getEditButtons()
 487:     {
 488:         $buttons         = array();
 489:         $definition      = $this->getEnvironment()->getDataDefinition();
 490:         $basicDefinition = $definition->getBasicDefinition();
 491: 
 492:         $buttons['save'] = sprintf(
 493:             '<input type="submit" name="save" id="save" class="tl_submit" accesskey="s" value="%s" />',
 494:             $this->getButtonLabel('save')
 495:         );
 496: 
 497:         $buttons['saveNclose'] = sprintf(
 498:             '<input type="submit" name="saveNclose" id="saveNclose" class="tl_submit" accesskey="c" value="%s" />',
 499:             $this->getButtonLabel('saveNclose')
 500:         );
 501: 
 502:         if (!($this->isPopup() || $basicDefinition->isClosed()) && $basicDefinition->isCreatable())
 503:         {
 504:             $buttons['saveNcreate'] = sprintf(
 505:                 '<input type="submit" name="saveNcreate" id="saveNcreate" class="tl_submit" accesskey="n" value="%s" />',
 506:                 $this->getButtonLabel('saveNcreate')
 507:             );
 508:         }
 509: 
 510:         // TODO: unknown input param s2e - I guess it means "switch to edit" but from which view used?
 511:         if (\Input::get('s2e'))
 512:         {
 513:             $buttons['saveNedit'] = sprintf(
 514:                 '<input type="submit" name="saveNedit" id="saveNedit" class="tl_submit" accesskey="e" value="%s" />',
 515:                 $this->getButtonLabel('saveNedit')
 516:             );
 517:         }
 518:         elseif(!$this->isPopup()
 519:             && (($basicDefinition->getMode() == BasicDefinitionInterface::MODE_PARENTEDLIST)
 520:                 || strlen($basicDefinition->getParentDataProvider())
 521:                 || $basicDefinition->isSwitchToEditEnabled()
 522:             )
 523:         )
 524:         {
 525:             $buttons['saveNback'] = sprintf(
 526:                 '<input type="submit" name="saveNback" id="saveNback" class="tl_submit" accesskey="g" value="%s" />',
 527:                 $this->getButtonLabel('saveNback')
 528:             );
 529:         }
 530: 
 531:         $event = new GetEditModeButtonsEvent($this->getEnvironment());
 532:         $event->setButtons($buttons);
 533: 
 534:         $this->getEnvironment()->getEventPropagator()->propagate(
 535:             $event::NAME,
 536:             $event,
 537:             array($definition->getName())
 538:         );
 539: 
 540:         return $event->getButtons();
 541:     }
 542: 
 543:     /**
 544:      * Retrieve a list of html buttons to use in the bottom panel (submit area).
 545:      *
 546:      * @return array
 547:      */
 548:     protected function getSelectButtons()
 549:     {
 550:         $definition      = $this->getDataDefinition();
 551:         $basicDefinition = $definition->getBasicDefinition();
 552:         $buttons         = array();
 553: 
 554:         if ($basicDefinition->isDeletable())
 555:         {
 556:             $buttons['delete'] = sprintf(
 557:                 '<input
 558:                 type="submit"
 559:                 name="delete"
 560:                 id="delete"
 561:                 class="tl_submit"
 562:                 accesskey="d"
 563:                 onclick="return confirm(\'%s\')"
 564:                 value="%s" />',
 565:                 $GLOBALS['TL_LANG']['MSC']['delAllConfirm'],
 566:                 specialchars($this->translate('MSC.deleteSelected'))
 567:             );
 568:         }
 569: 
 570:         if ($basicDefinition->isEditable())
 571:         {
 572:             $buttons['cut'] = sprintf(
 573:                 '<input type="submit" name="cut" id="cut" class="tl_submit" accesskey="x" value="%s">',
 574:                 specialchars($this->translate('MSC.moveSelected'))
 575:             );
 576:         }
 577: 
 578:         if ($basicDefinition->isCreatable())
 579:         {
 580:             $buttons['copy'] = sprintf(
 581:                 '<input type="submit" name="copy" id="copy" class="tl_submit" accesskey="c" value="%s">',
 582:                 specialchars($this->translate('MSC.copySelected'))
 583:             );
 584:         }
 585: 
 586:         if ($basicDefinition->isEditable())
 587:         {
 588:             $buttons['override'] = sprintf(
 589:                 '<input type="submit" name="override" id="override" class="tl_submit" accesskey="v" value="%s">',
 590:                 specialchars($this->translate('MSC.overrideSelected'))
 591:             );
 592: 
 593:             $buttons['edit'] = sprintf(
 594:                 '<input type="submit" name="edit" id="edit" class="tl_submit" accesskey="s" value="%s">',
 595:                 specialchars($this->translate('MSC.editSelected'))
 596:             );
 597:         }
 598: 
 599:         $event = new GetSelectModeButtonsEvent($this->getEnvironment());
 600:         $event->setButtons($buttons);
 601: 
 602:         $this->getEnvironment()->getEventPropagator()->propagate(
 603:             $event::NAME,
 604:             $event,
 605:             array($this->getEnvironment()->getDataDefinition()->getName())
 606:         );
 607: 
 608:         return $event->getButtons();
 609:     }
 610: 
 611:     /**
 612:      * Update the clipboard in the Environment with data from the InputProvider.
 613:      *
 614:      * The following parameters have to be provided by the input provider:
 615:      *
 616:      * Name      Type   Description
 617:      * clipboard bool   Flag determining if the clipboard shall get cleared.
 618:      * act       string Action to perform, either paste, cut or create.
 619:      * id        mixed  The Id of the item to copy. In mode cut this is the id of the item to be moved.
 620:      *
 621:      * @return BaseView
 622:      */
 623:     public function checkClipboard()
 624:     {
 625:         $objInput     = $this->getEnvironment()->getInputProvider();
 626:         $objClipboard = $this->getEnvironment()->getClipboard();
 627: 
 628:         // Reset Clipboard.
 629:         if ($objInput->getParameter('clipboard') == '1')
 630:         {
 631:             $objClipboard->clear();
 632:         }
 633:         // Push some entry into clipboard.
 634:         elseif ($objInput->getParameter('act') == 'paste')
 635:         {
 636:             $objDataProv = $this->getEnvironment()->getDataProvider();
 637:             $id          = $objInput->getParameter('id');
 638: 
 639:             if ($objInput->getParameter('mode') == 'cut')
 640:             {
 641:                 $arrIgnored = array($id);
 642: 
 643:                 $objModel = $objDataProv->fetch($objDataProv->getEmptyConfig()->setId($id));
 644: 
 645:                 // We have to ignore all children of this element in mode 5 (to prevent circular references).
 646:                 if ($this->getDataDefinition()->getBasicDefinition()->getMode() == BasicDefinitionInterface::MODE_HIERARCHICAL)
 647:                 {
 648:                     $arrIgnored = $this->getEnvironment()->getController()->assembleAllChildrenFrom($objModel);
 649:                 }
 650: 
 651:                 $objClipboard
 652:                     ->clear()
 653:                     ->cut($id)
 654:                     ->setCircularIds($arrIgnored);
 655:             }
 656:             elseif ($objInput->getParameter('mode') == 'create')
 657:             {
 658:                 $arrIgnored     = array($id);
 659:                 $objContainedId = trimsplit(',', $objInput->getParameter('childs'));
 660: 
 661:                 $objClipboard
 662:                     ->clear()
 663:                     ->create($id)
 664:                     ->setCircularIds($arrIgnored);
 665: 
 666:                 if (is_array($objContainedId) && !empty($objContainedId))
 667:                 {
 668:                     $objClipboard->setContainedIds($objContainedId);
 669:                 }
 670:             }
 671:         }
 672:         // Check clipboard from session.
 673:         else
 674:         {
 675:             $objClipboard->loadFrom($this->getEnvironment());
 676:         }
 677: 
 678:         // Let the clipboard save it's values persistent.
 679:         $objClipboard->saveTo($this->getEnvironment());
 680: 
 681:         return $this;
 682:     }
 683: 
 684:     /**
 685:      * Determine if we are currently working in multi language mode.
 686:      *
 687:      * @param mixed $mixId The id of the current model.
 688:      *
 689:      * @return bool
 690:      */
 691:     protected function isMultiLanguage($mixId)
 692:     {
 693:         return count($this->getEnvironment()->getController()->getSupportedLanguages($mixId)) > 0;
 694:     }
 695: 
 696:     /**
 697:      * Check if the data provider is multi language and prepare the data provider with the selected language.
 698:      *
 699:      * @return void
 700:      */
 701:     protected function checkLanguage()
 702:     {
 703:         $environment     = $this->getEnvironment();
 704:         $inputProvider   = $environment->getInputProvider();
 705:         $objDataProvider = $environment->getDataProvider();
 706:         $strProviderName = $environment->getDataDefinition()->getName();
 707:         $mixID           = $environment->getInputProvider()->getParameter('id');
 708:         $arrLanguage     = $environment->getController()->getSupportedLanguages($mixID);
 709: 
 710:         if (!$arrLanguage)
 711:         {
 712:             return;
 713:         }
 714: 
 715:         // Load language from Session.
 716:         $arrSession = $inputProvider->getPersistentValue('dc_general');
 717:         if (!is_array($arrSession))
 718:         {
 719:             $arrSession = array();
 720:         }
 721:         /** @var \DcGeneral\Data\MultiLanguageDataProviderInterface $objDataProvider */
 722: 
 723:         // Try to get the language from session.
 724:         if (isset($arrSession['ml_support'][$strProviderName][$mixID]))
 725:         {
 726:             $strCurrentLanguage = $arrSession['ml_support'][$strProviderName][$mixID];
 727:         }
 728:         else
 729:         {
 730:             $strCurrentLanguage = $GLOBALS['TL_LANGUAGE'];
 731:         }
 732: 
 733:         // Get/Check the new language.
 734:         if ((strlen($inputProvider->getValue('language')) != 0)
 735:             && ($inputProvider->getValue('FORM_SUBMIT') == 'language_switch'))
 736:         {
 737:             if (array_key_exists($inputProvider->getValue('language'), $arrLanguage))
 738:             {
 739:                 $strCurrentLanguage = $inputProvider->getValue('language');
 740:             }
 741:         }
 742: 
 743:         if (!array_key_exists($strCurrentLanguage, $arrLanguage))
 744:         {
 745:             $strCurrentLanguage = $objDataProvider->getFallbackLanguage($mixID)->getLanguageCode();
 746:         }
 747: 
 748:         $arrSession['ml_support'][$strProviderName][$mixID] = $strCurrentLanguage;
 749:         $inputProvider->setPersistentValue('dc_general', $arrSession);
 750: 
 751:         $objDataProvider->setCurrentLanguage($strCurrentLanguage);
 752:     }
 753: 
 754:     /**
 755:      * Create a new instance of ContaoBackendViewTemplate with the template file of the given name.
 756:      *
 757:      * @param string $strTemplate Name of the template to create.
 758:      *
 759:      * @return ContaoBackendViewTemplate
 760:      */
 761:     protected function getTemplate($strTemplate)
 762:     {
 763:         return new ContaoBackendViewTemplate($strTemplate);
 764:     }
 765: 
 766:     /**
 767:      * TODO: Handle an ajax call, this method is currently not implemented.
 768:      *
 769:      * @return string
 770:      */
 771:     public function handleAjaxCall()
 772:     {
 773:         $action = $this->getEnvironment()->getInputProvider()->getValue('action');
 774:         return vsprintf($this->notImplMsg, 'handleAjaxCall()');
 775:     }
 776: 
 777:     /**
 778:      * @todo All
 779:      * @return string
 780:      */
 781:     public function copy()
 782:     {
 783:         return vsprintf($this->notImplMsg, 'copy - Mode');
 784:     }
 785: 
 786:     /**
 787:      * @todo All
 788:      * @return string
 789:      */
 790:     public function copyAll()
 791:     {
 792:         return vsprintf($this->notImplMsg, 'copyAll - Mode');
 793:     }
 794: 
 795:     /**
 796:      * Create a new item.
 797:      *
 798:      * @see    edit()
 799:      *
 800:      * @return string
 801:      */
 802:     public function create()
 803:     {
 804:         $preFunction = function($environment, $model, $originalModel)
 805:         {
 806:             $copyEvent = new PreCreateModelEvent($environment, $model);
 807:             $environment->getEventPropagator()->propagate(
 808:                 $copyEvent::NAME,
 809:                 $copyEvent,
 810:                 array(
 811:                     $environment->getDataDefinition()->getName(),
 812:                 )
 813:             );
 814:         };
 815: 
 816:         $postFunction = function($environment, $model, $originalModel)
 817:         {
 818:             $copyEvent = new PostCreateModelEvent($environment, $model);
 819:             $environment->getEventPropagator()->propagate(
 820:                 $copyEvent::NAME,
 821:                 $copyEvent,
 822:                 array(
 823:                     $environment->getDataDefinition()->getName(),
 824:                 )
 825:             );
 826:         };
 827: 
 828:         return $this->createEditMask($this->createEmptyModelWithDefaults(), null, $preFunction, $postFunction);
 829:     }
 830: 
 831:     /**
 832:      * @todo All
 833:      * @return string
 834:      */
 835:     public function cut()
 836:     {
 837:         return vsprintf($this->notImplMsg, 'cut - Mode');
 838:     }
 839: 
 840:     public function paste()
 841:     {
 842: 
 843:     }
 844: 
 845:     /**
 846:      * @todo All
 847:      * @return string
 848:      */
 849:     public function cutAll()
 850:     {
 851:         return vsprintf($this->notImplMsg, 'cutAll - Mode');
 852:     }
 853: 
 854:     /**
 855:      * Delete a model and redirect the user to the listing.
 856:      *
 857:      * NOTE: This method redirects the user to the listing and therefore the script will be ended.
 858:      *
 859:      * @return void
 860:      *
 861:      * @throws DcGeneralRuntimeException If the model to delete could not be loaded.
 862:      */
 863:     public function delete()
 864:     {
 865:         // Check if is it allowed to delete a record.
 866:         if (!$this->getEnvironment()->getDataDefinition()->getBasicDefinition()->isDeletable())
 867:         {
 868:             $this->getEnvironment()->getEventPropagator()->propagate(
 869:                 ContaoEvents::SYSTEM_LOG,
 870:                 new LogEvent(
 871:                     sprintf(
 872:                         'Table "%s" is not deletable', 'DC_General - DefaultController - delete()',
 873:                         $this->getEnvironment()->getDataDefinition()->getName()
 874:                     ),
 875:                     __CLASS__ . '::delete()',
 876:                     TL_ERROR
 877:                 )
 878:             );
 879: 
 880:             $this->getEnvironment()->getEventPropagator()->propagate(
 881:                 ContaoEvents::CONTROLLER_REDIRECT,
 882:                 new RedirectEvent('contao/main.php?act=error')
 883:             );
 884:         }
 885: 
 886:         $environment  = $this->getEnvironment();
 887:         $dataProvider = $environment->getDataProvider();
 888:         $modelId      = $environment->getInputProvider()->getParameter('id');
 889:         $model        = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId));
 890: 
 891:         if (!$model->getId())
 892:         {
 893:             throw new DcGeneralRuntimeException('Could not load model with id ' . $modelId);
 894:         }
 895: 
 896:         // Trigger event before the model will be deleted.
 897:         $event = new PreDeleteModelEvent($environment, $model);
 898:         $environment->getEventPropagator()->propagate(
 899:             $event::NAME,
 900:             $event,
 901:             array(
 902:                 $this->getEnvironment()->getDataDefinition()->getName(),
 903:             )
 904:         );
 905: 
 906:         // FIXME: See DefaultController::delete() - we need to delete the children of this item as well over all data providers.
 907:         /*
 908:         $arrDelIDs = array();
 909: 
 910:         // Delete record
 911:         switch ($definition->getSortingMode())
 912:         {
 913:             case 0:
 914:             case 1:
 915:             case 2:
 916:             case 3:
 917:             case 4:
 918:                 $arrDelIDs = array();
 919:                 $arrDelIDs[] = $intRecordID;
 920:                 break;
 921: 
 922:             case 5:
 923:                 $arrDelIDs = $environment->getController()->fetchMode5ChildrenOf($environment->getCurrentModel(), $blnRecurse = true);
 924:                 $arrDelIDs[] = $intRecordID;
 925:                 break;
 926:         }
 927: 
 928:         // Delete all entries
 929:         foreach ($arrDelIDs as $intId)
 930:         {
 931:             $this->getEnvironment()->getDataProvider()->delete($intId);
 932: 
 933:             // Add a log entry unless we are deleting from tl_log itself
 934:             if ($environment->getDataDefinition()->getName() != 'tl_log')
 935:             {
 936:                 BackendBindings::log('DELETE FROM ' . $environment->getDataDefinition()->getName() . ' WHERE id=' . $intId, 'DC_General - DefaultController - delete()', TL_GENERAL);
 937:             }
 938:         }
 939:          */
 940: 
 941:         $dataProvider->delete($model);
 942: 
 943: 
 944:         // Trigger event after the model is deleted.
 945:         $event = new PostDeleteModelEvent($environment, $model);
 946:         $environment->getEventPropagator()->propagate(
 947:             $event::NAME,
 948:             $event,
 949:             array(
 950:                 $this->getEnvironment()->getDataDefinition()->getName(),
 951:             )
 952:         );
 953: 
 954:         $this->redirectHome();
 955:     }
 956: 
 957:     /**
 958:      * @todo All
 959:      * @return string
 960:      */
 961:     public function move()
 962:     {
 963:         return vsprintf($this->notImplMsg, 'move - Mode');
 964:     }
 965: 
 966:     /**
 967:      * @todo All
 968:      * @return string
 969:      */
 970:     public function undo()
 971:     {
 972:         return vsprintf($this->notImplMsg, 'undo - Mode');
 973:     }
 974: 
 975:     /**
 976:      * Handle the submit and determine which button has been triggered.
 977:      *
 978:      * This method will redirect the client.
 979:      *
 980:      * @param ModelInterface $model The model that has been submitted.
 981:      *
 982:      * @return void
 983:      */
 984:     protected function handleSubmit(ModelInterface $model)
 985:     {
 986:         $environment   = $this->getEnvironment();
 987:         $inputProvider = $environment->getInputProvider();
 988: 
 989:         if ($inputProvider->hasValue('save'))
 990:         {
 991:             if ($inputProvider->getParameter('id'))
 992:             {
 993:                 $environment->getEventPropagator()->propagate(ContaoEvents::CONTROLLER_RELOAD, new ReloadEvent());
 994:             }
 995:             else
 996:             {
 997:                 $newUrlEvent = new AddToUrlEvent('act=edit&id=' . $model->getId());
 998:                 $environment->getEventPropagator()->propagate(ContaoEvents::BACKEND_ADD_TO_URL, $newUrlEvent);
 999:                 $environment->getEventPropagator()->propagate(
1000:                     ContaoEvents::CONTROLLER_REDIRECT,
1001:                     new RedirectEvent($newUrlEvent->getUrl())
1002:                 );
1003:             }
1004:         }
1005:         elseif ($inputProvider->hasValue('saveNclose'))
1006:         {
1007:             setcookie('BE_PAGE_OFFSET', 0, 0, '/');
1008: 
1009:             $_SESSION['TL_INFO']    = '';
1010:             $_SESSION['TL_ERROR']   = '';
1011:             $_SESSION['TL_CONFIRM'] = '';
1012: 
1013:             $newUrlEvent = new GetReferrerEvent();
1014:             $environment->getEventPropagator()->propagate(ContaoEvents::SYSTEM_GET_REFERRER, $newUrlEvent);
1015:             $environment->getEventPropagator()->propagate(
1016:                 ContaoEvents::CONTROLLER_REDIRECT,
1017:                 new RedirectEvent($newUrlEvent->getReferrerUrl())
1018:             );
1019:         }
1020:         elseif ($inputProvider->hasValue('saveNcreate'))
1021:         {
1022:             setcookie('BE_PAGE_OFFSET', 0, 0, '/');
1023: 
1024:             $_SESSION['TL_INFO']    = '';
1025:             $_SESSION['TL_ERROR']   = '';
1026:             $_SESSION['TL_CONFIRM'] = '';
1027: 
1028:             $newUrlEvent = new AddToUrlEvent('act=create&id=');
1029:             $environment->getEventPropagator()->propagate(ContaoEvents::BACKEND_ADD_TO_URL, $newUrlEvent);
1030:             $environment->getEventPropagator()->propagate(
1031:                 ContaoEvents::CONTROLLER_REDIRECT,
1032:                 new RedirectEvent($newUrlEvent->getUrl())
1033:             );
1034: 
1035:         }
1036:         elseif ($inputProvider->hasValue('saveNback'))
1037:         {
1038:             echo vsprintf($this->notImplMsg, 'Save and go back');
1039:             exit;
1040:         }
1041:         else
1042:         {
1043:             // Custom button logic.
1044: 
1045:         }
1046:     }
1047: 
1048:     /**
1049:      * Check the submitted data if we want to restore a previous version of a model.
1050:      *
1051:      * If so, the model will get loaded and marked as active version in the data provider and the client will perform a
1052:      * reload of the page.
1053:      *
1054:      * @return void
1055:      *
1056:      * @throws DcGeneralRuntimeException When the requested version could not be located in the database.
1057:      */
1058:     protected function checkRestoreVersion()
1059:     {
1060:         $environment             = $this->getEnvironment();
1061:         $definition              = $environment->getDataDefinition();
1062:         $basicDefinition         = $definition->getBasicDefinition();
1063:         $dataProviderDefinition  = $definition->getDataProviderDefinition();
1064:         $dataProvider            = $environment->getDataProvider();
1065:         $dataProviderInformation = $dataProviderDefinition->getInformation($basicDefinition->getDataProvider());
1066:         $inputProvider           = $environment->getInputProvider();
1067:         $modelId                 = $inputProvider->getParameter('id');
1068: 
1069:         if ($dataProviderInformation->isVersioningEnabled()
1070:             && ($inputProvider->getValue('FORM_SUBMIT') === 'tl_version')
1071:             && ($modelVersion = $inputProvider->getValue('version')) !== null)
1072:         {
1073:             $model = $dataProvider->getVersion($modelId, $modelVersion);
1074: 
1075:             if ($model === null)
1076:             {
1077:                 $message = sprintf(
1078:                     'Could not load version %s of record ID %s from %s',
1079:                     $modelVersion,
1080:                     $modelId,
1081:                     $basicDefinition->getDataProvider()
1082:                 );
1083: 
1084:                 $environment->getEventPropagator()->propagate(
1085:                     ContaoEvents::SYSTEM_LOG,
1086:                     new LogEvent($message, TL_ERROR, 'DC_General - checkRestoreVersion()')
1087:                 );
1088: 
1089:                 throw new DcGeneralRuntimeException($message);
1090:             }
1091: 
1092:             $dataProvider->save($model);
1093:             $dataProvider->setVersionActive($modelId, $modelVersion);
1094:             $environment->getEventPropagator()->propagate(ContaoEvents::CONTROLLER_RELOAD, new ReloadEvent());
1095:         }
1096:     }
1097: 
1098:     /**
1099:      * Abstract method to be overridden in the certain child classes.
1100:      *
1101:      * This method will update the parent relationship between a model and the parent item.
1102:      *
1103:      * @param ModelInterface $model The model to be updated.
1104:      *
1105:      * @return void
1106:      */
1107:     public function enforceModelRelationship($model)
1108:     {
1109:         // No op in this base class but implemented in subclasses to enforce parent<->child relationship.
1110:     }
1111: 
1112:     /**
1113:      * Create an empty model using the default values from the definition.
1114:      *
1115:      * @return ModelInterface
1116:      */
1117:     protected function createEmptyModelWithDefaults()
1118:     {
1119:         $environment        = $this->getEnvironment();
1120:         $definition         = $environment->getDataDefinition();
1121:         $environment        = $this->getEnvironment();
1122:         $dataProvider       = $environment->getDataProvider();
1123:         $propertyDefinition = $definition->getPropertiesDefinition();
1124:         $properties         = $propertyDefinition->getProperties();
1125:         $model              = $dataProvider->getEmptyModel();
1126: 
1127:         foreach ($properties as $property)
1128:         {
1129:             $propName = $property->getName();
1130:             $model->setProperty($propName, $property->getDefaultValue());
1131:         }
1132: 
1133:         return $model;
1134:     }
1135: 
1136:     /**
1137:      * Update the versioning information in the data provider for a given model (if necessary).
1138:      *
1139:      * @param ModelInterface $model The model to update.
1140:      *
1141:      * @return void
1142:      */
1143:     protected function storeVersion(ModelInterface $model)
1144:     {
1145:         $modelId                 = $model->getId();
1146:         $environment             = $this->getEnvironment();
1147:         $definition              = $environment->getDataDefinition();
1148:         $basicDefinition         = $definition->getBasicDefinition();
1149:         $dataProvider            = $environment->getDataProvider();
1150:         $dataProviderDefinition  = $definition->getDataProviderDefinition();
1151:         $dataProviderInformation = $dataProviderDefinition->getInformation($basicDefinition->getDataProvider());
1152: 
1153:         if (!$dataProviderInformation->isVersioningEnabled())
1154:         {
1155:             return;
1156:         }
1157: 
1158:         // Compare version and current record.
1159:         $currentVersion = $dataProvider->getActiveVersion($modelId);
1160:         if (!$currentVersion
1161:             || !$dataProvider->sameModels($model, $dataProvider->getVersion($modelId, $currentVersion))
1162:         )
1163:         {
1164:             $user = \BackendUser::getInstance();
1165: 
1166:             $dataProvider->saveVersion($model, $user->username);
1167:         }
1168:     }
1169: 
1170:     /**
1171:      * Generate the view for edit.
1172:      *
1173:      * @return string
1174:      *
1175:      * @throws DcGeneralRuntimeException         When the current data definition is not editable or is closed.
1176:      *
1177:      * @throws DcGeneralInvalidArgumentException When an unknown property is mentioned in the palette.
1178:      */
1179:     public function edit()
1180:     {
1181:         $this->checkLanguage();
1182: 
1183:         $environment   = $this->getEnvironment();
1184:         $dataProvider  = $environment->getDataProvider();
1185:         $inputProvider = $environment->getInputProvider();
1186:         $modelId       = $inputProvider->getParameter('id');
1187: 
1188:         $this->checkRestoreVersion();
1189: 
1190:         if (strlen($modelId))
1191:         {
1192:             $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId));
1193:         }
1194:         else
1195:         {
1196:             $model = $this->createEmptyModelWithDefaults();
1197:         }
1198: 
1199:         // We need to keep the original data here.
1200:         $originalModel = clone $model;
1201:         $originalModel->setId($model->getId());
1202: 
1203:         return $this->createEditMask($model, $originalModel, null, null);
1204:     }
1205: 
1206:     /**
1207:      * Create the edit mask.
1208:      *
1209:      * @param ModelInterface $model         The model with the current data.
1210:      *
1211:      * @param ModelInterface $originalModel The data from the original data.
1212:      *
1213:      * @param callable       $preFunction   The function to call before saving an item.
1214:      *
1215:      * @param callable       $postFunction  The function to call after saving an item.
1216:      *
1217:      * @return string
1218:      *
1219:      * @throws DcGeneralRuntimeException         If the data container is not editable, closed.
1220:      *
1221:      * @throws DcGeneralInvalidArgumentException If an unknown property is encountered in the palette.
1222:      */
1223:     protected function createEditMask($model, $originalModel, $preFunction, $postFunction)
1224:     {
1225:         $this->checkLanguage();
1226: 
1227:         $environment             = $this->getEnvironment();
1228:         $definition              = $environment->getDataDefinition();
1229:         $basicDefinition         = $definition->getBasicDefinition();
1230:         $dataProvider            = $environment->getDataProvider();
1231:         $dataProviderDefinition  = $definition->getDataProviderDefinition();
1232:         $dataProviderInformation = $dataProviderDefinition->getInformation($basicDefinition->getDataProvider());
1233:         $inputProvider           = $environment->getInputProvider();
1234:         $palettesDefinition      = $definition->getPalettesDefinition();
1235:         $modelId                 = $inputProvider->getParameter('id');
1236:         $propertyDefinitions     = $definition->getPropertiesDefinition();
1237:         $blnSubmitted            = ($inputProvider->getValue('FORM_SUBMIT') === $definition->getName());
1238:         $blnIsAutoSubmit         = ($inputProvider->getValue('SUBMIT_TYPE') === 'auto');
1239: 
1240:         $widgetManager = new ContaoWidgetManager($environment, $model);
1241: 
1242:         // Check if table is editable.
1243:         if (!$basicDefinition->isEditable())
1244:         {
1245:             $message = 'DataContainer ' . $definition->getName() . ' is not editable';
1246:             $environment->getEventPropagator()->propagate(
1247:                 ContaoEvents::SYSTEM_LOG,
1248:                 new LogEvent($message, TL_ERROR, 'DC_General - edit()')
1249:             );
1250:             throw new DcGeneralRuntimeException($message);
1251:         }
1252: 
1253:         // Check if table is closed but we are adding a new item.
1254:         if ((!$modelId) && $basicDefinition->isClosed())
1255:         {
1256:             $message = 'DataContainer ' . $definition->getName() . ' is closed';
1257:             $environment->getEventPropagator()->propagate(
1258:                 ContaoEvents::SYSTEM_LOG,
1259:                 new LogEvent($message, TL_ERROR, 'DC_General - edit()')
1260:             );
1261:             throw new DcGeneralRuntimeException($message);
1262:         }
1263: 
1264:         $this->enforceModelRelationship($model);
1265: 
1266:         // Pass 1: Get the palette for the values stored in the model.
1267:         $palette = $palettesDefinition->findPalette($model);
1268: 
1269:         $propertyValues = $this->processInput($widgetManager);
1270:         $errors         = array();
1271:         if ($blnSubmitted && $propertyValues)
1272:         {
1273:             // Pass 2: Determine the real palette we want to work on if we have some data submitted.
1274:             $palette = $palettesDefinition->findPalette($model, $propertyValues);
1275: 
1276:             // Update the model - the model might add some more errors to the propertyValueBag via exceptions.
1277:             $this->getEnvironment()->getController()->updateModelFromPropertyBag($model, $propertyValues);
1278:         }
1279: 
1280:         $arrFieldSets = array();
1281:         foreach ($palette->getLegends() as $legend)
1282:         {
1283:             $legendName = $environment->getTranslator()->translate($legend->getName() . '_legend', $definition->getName());
1284:             $fields     = array();
1285:             $properties = $legend->getProperties($model, $propertyValues);
1286: 
1287:             if (!$properties)
1288:             {
1289:                 continue;
1290:             }
1291: 
1292:             foreach ($properties as $property)
1293:             {
1294:                 if (!$propertyDefinitions->hasProperty($property->getName()))
1295:                 {
1296:                     throw new DcGeneralInvalidArgumentException(
1297:                         sprintf(
1298:                             'Property %s is mentioned in palette but not defined in propertyDefinition.',
1299:                             $property->getName()
1300:                         )
1301:                     );
1302:                 }
1303: 
1304:                 // If this property is invalid, fetch the error.
1305:                 if ((!$blnIsAutoSubmit)
1306:                     && $propertyValues
1307:                     && $propertyValues->hasPropertyValue($property->getName())
1308:                     && $propertyValues->isPropertyValueInvalid($property->getName())
1309:                 )
1310:                 {
1311:                     $errors = array_merge($errors, $propertyValues->getPropertyValueErrors($property->getName()));
1312:                 }
1313: 
1314:                 $fields[] = $widgetManager->renderWidget($property->getName(), $blnIsAutoSubmit);
1315:             }
1316: 
1317:             $arrFieldSet['label']   = $legendName;
1318:             $arrFieldSet['class']   = 'tl_box';
1319:             $arrFieldSet['palette'] = implode('', $fields);
1320:             $arrFieldSet['legend']  = $legend->getName();
1321:             $arrFieldSets[]         = $arrFieldSet;
1322:         }
1323: 
1324:         if ((!$blnIsAutoSubmit) && $blnSubmitted && empty($errors))
1325:         {
1326:             if ($model->getMeta(DCGE::MODEL_IS_CHANGED))
1327:             {
1328:                 // Trigger the event for post persists or create.
1329:                 if ($preFunction != null)
1330:                 {
1331:                     $preFunction($environment, $model, $originalModel);
1332:                 }
1333: 
1334:                 $event = new PrePersistModelEvent($environment, $model, $originalModel);
1335:                 $environment->getEventPropagator()->propagate(
1336:                     $event::NAME,
1337:                     $event,
1338:                     array(
1339:                         $this->getEnvironment()->getDataDefinition()->getName(),
1340:                     )
1341:                 );
1342: 
1343:                 // Save the model.
1344:                 $dataProvider->save($model);
1345: 
1346:                 // Trigger the event for post persists or create.
1347:                 if ($postFunction != null)
1348:                 {
1349:                     $postFunction($environment, $model, $originalModel);
1350:                 }
1351: 
1352:                 $event = new PostPersistModelEvent($environment, $model, $originalModel);
1353:                 $environment->getEventPropagator()->propagate(
1354:                     $event::NAME,
1355:                     $event,
1356:                     array(
1357:                         $this->getEnvironment()->getDataDefinition()->getName(),
1358:                     )
1359:                 );
1360: 
1361:                 $this->storeVersion($model);
1362:             }
1363: 
1364:             $this->handleSubmit($model);
1365:         }
1366: 
1367:         if ($model->getId())
1368:         {
1369:             $strHeadline = sprintf($this->translate('editRecord', $definition->getName()), 'ID ' . $model->getId());
1370:             if ($strHeadline === 'editRecord')
1371:             {
1372:                 $strHeadline = sprintf($this->translate('MSC.editRecord'), 'ID ' . $model->getId());
1373:             }
1374:         }
1375:         else
1376:         {
1377:             $strHeadline = sprintf($this->translate('newRecord', $definition->getName()), 'ID ' . $model->getId());
1378:             if ($strHeadline === 'newRecord')
1379:             {
1380:                 $strHeadline = sprintf($this->translate('MSC.editRecord'), '');
1381:             }
1382:         }
1383: 
1384:         $objTemplate = $this->getTemplate('dcbe_general_edit');
1385:         $objTemplate->setData(array(
1386:                 'fieldsets' => $arrFieldSets,
1387:                 'versions' => $dataProviderInformation->isVersioningEnabled() ? $dataProvider->getVersions($model->getId()) : null,
1388:                 'subHeadline' => $strHeadline,
1389:                 'table' => $definition->getName(),
1390:                 'enctype' => 'multipart/form-data',
1391:                 'error' => $errors,
1392:                 'editButtons' => $this->getEditButtons(),
1393:                 'noReload' => (bool)$errors
1394:             ));
1395: 
1396:         if ($this->isMultiLanguage($model->getId()))
1397:         {
1398:             /** @var MultiLanguageDataProviderInterface $dataProvider */
1399:             $langsNative = array();
1400:             require TL_ROOT . '/system/config/languages.php';
1401: 
1402:             $this
1403:                 ->addToTemplate('languages', $environment->getController()->getSupportedLanguages($model->getId()), $objTemplate)
1404:                 ->addToTemplate('language', $dataProvider->getCurrentLanguage(), $objTemplate)
1405:                 ->addToTemplate('languageHeadline', $langsNative[$dataProvider->getCurrentLanguage()], $objTemplate);
1406:         }
1407:         else
1408:         {
1409:             $this
1410:                 ->addToTemplate('languages', null, $objTemplate)
1411:                 ->addToTemplate('languageHeadline', '', $objTemplate);
1412:         }
1413: 
1414:         return $objTemplate->parse();
1415:     }
1416: 
1417:     /**
1418:      * Calculate the label of a property to se in "show" view.
1419:      *
1420:      * @param PropertyInterface $property The property for which the label shall be calculated.
1421:      *
1422:      * @return string
1423:      */
1424:     protected function getLabelForShow(PropertyInterface $property)
1425:     {
1426:         $environment = $this->getEnvironment();
1427:         $definition  = $environment->getDataDefinition();
1428: 
1429:         $label = $environment->getTranslator()->translate($property->getLabel(), $definition->getName());
1430: 
1431:         if (!$label)
1432:         {
1433:             $label = $environment->getTranslator()->translate('MSC.' . $property->getName());
1434:         }
1435: 
1436:         if (is_array($label))
1437:         {
1438:             $label = $label[0];
1439:         }
1440: 
1441:         if (!$label)
1442:         {
1443:             $label = $property->getName();
1444:         }
1445: 
1446:         return $label;
1447:     }
1448: 
1449:     /**
1450:      * Show Information about a model.
1451:      *
1452:      * @return string
1453:      *
1454:      * @throws DcGeneralRuntimeException When an unknown property is mentioned in the palette.
1455:      */
1456:     public function show()
1457:     {
1458:         // Load check multi language.
1459:         $environment  = $this->getEnvironment();
1460:         $definition   = $environment->getDataDefinition();
1461:         $properties   = $definition->getPropertiesDefinition();
1462:         $translator   = $environment->getTranslator();
1463:         $dataProvider = $environment->getDataProvider();
1464:         $modelId      = $environment->getInputProvider()->getParameter('id');
1465: 
1466:         // Select language in data provider.
1467:         $this->checkLanguage();
1468: 
1469:         $objDBModel = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId));
1470: 
1471:         if ($objDBModel == null)
1472:         {
1473:             $environment->getEventPropagator()->propagate(
1474:                 ContaoEvents::SYSTEM_LOG,
1475:                 new LogEvent(
1476:                     sprintf(
1477:                         'Could not find ID %s in %s.', 'DC_General show()',
1478:                         $modelId,
1479:                         $definition->getName()
1480:                     ),
1481:                     __CLASS__ . '::' . __FUNCTION__,
1482:                     TL_ERROR
1483:                 )
1484:             );
1485: 
1486:             $environment->getEventPropagator()->propagate(
1487:                 ContaoEvents::CONTROLLER_REDIRECT,
1488:                 new RedirectEvent('contao/main.php?act=error')
1489:             );
1490:         }
1491: 
1492:         $values = array();
1493:         $labels = array();
1494: 
1495:         $palette = $definition->getPalettesDefinition()->findPalette($objDBModel);
1496: 
1497:         // Show only allowed fields.
1498:         foreach ($palette->getVisibleProperties($objDBModel) as $paletteProperty)
1499:         {
1500:             $property = $properties->getProperty($paletteProperty->getName());
1501: 
1502:             if (!$property)
1503:             {
1504:                 throw new DcGeneralRuntimeException('Unable to retrieve property ' . $paletteProperty->getName());
1505:             }
1506: 
1507:             // Make it human readable.
1508:             $values[$paletteProperty->getName()] = $this->getReadableFieldValue(
1509:                 $property,
1510:                 $objDBModel,
1511:                 $objDBModel->getProperty($paletteProperty->getName())
1512:             );
1513:             $labels[$paletteProperty->getName()] = $this->getLabelForShow($property);
1514:         }
1515: 
1516:         $headline = $translator->translate(
1517:             'MSC.showRecord',
1518:             $definition->getName(),
1519:             array($objDBModel->getId() ? 'ID ' . $objDBModel->getId() : '')
1520:         );
1521: 
1522:         if ($headline == 'MSC.showRecord')
1523:         {
1524:             $headline = $translator->translate(
1525:                 'MSC.showRecord',
1526:                 null,
1527:                 array($objDBModel->getId() ? 'ID ' . $objDBModel->getId() : '')
1528:             );
1529:         }
1530: 
1531:         $template = $this->getTemplate('dcbe_general_show');
1532:         $this
1533:             ->addToTemplate('headline', $headline, $template)
1534:             ->addToTemplate('arrFields', $values, $template)
1535:             ->addToTemplate('arrLabels', $labels, $template);
1536: 
1537:         if ($this->isMultiLanguage($objDBModel->getId()))
1538:         {
1539:             /** @var MultiLanguageDataProviderInterface $dataProvider */
1540:             $this
1541:                 ->addToTemplate('languages', $environment->getController()->getSupportedLanguages($objDBModel->getId()), $template)
1542:                 ->addToTemplate('currentLanguage', $dataProvider->getCurrentLanguage(), $template)
1543:                 ->addToTemplate('languageSubmit', specialchars($translator->translate('MSC.showSelected')), $template)
1544:                 ->addToTemplate('backBT', specialchars($translator->translate('MSC.backBT')), $template);
1545:         }
1546:         else
1547:         {
1548:             $this->addToTemplate('language', null, $template);
1549:         }
1550: 
1551:         return $template->parse();
1552:     }
1553: 
1554:     /**
1555:      * Show all entries from one table.
1556:      *
1557:      * @return string
1558:      */
1559:     public function showAll()
1560:     {
1561:         return sprintf(
1562:             $this->notImplMsg,
1563:             'showAll - Mode ' . $this->getViewSection()->getListingConfig()->getGroupingMode()
1564:         );
1565:     }
1566: 
1567:     /**
1568:      * Generates a sub palette for the given selector (field name).
1569:      *
1570:      * @param string $strSelector The name of the selector field.
1571:      *
1572:      * @return string
1573:      */
1574:     public function generateAjaxPalette($strSelector)
1575:     {
1576:         return vsprintf($this->notImplMsg, 'generateAjaxPalette');
1577:     }
1578: 
1579:     /**
1580:      * Create the "new" button.
1581:      *
1582:      * @return CommandInterface|null
1583:      */
1584:     protected function getCreateModelCommand()
1585:     {
1586:         $environment        = $this->getEnvironment();
1587:         $definition         = $environment->getDataDefinition();
1588:         $basicDefinition    = $definition->getBasicDefinition();
1589:         $parentProviderName = $environment->getDataDefinition()->getBasicDefinition()->getParentDataProvider();
1590:         $providerName       = $environment->getDataDefinition()->getName();
1591: 
1592:         if ($basicDefinition->isClosed())
1593:         {
1594:             return null;
1595:         }
1596: 
1597:         $pid             = $environment->getInputProvider()->getParameter('pid');
1598:         $mode            = $basicDefinition->getMode();
1599:         $config          = $this->getEnvironment()->getController()->getBaseConfig();
1600:         $sorting         = $config->getSorting();
1601: 
1602:         $command    = new Command();
1603:         $parameters = $command->getParameters();
1604:         $extra      = $command->getExtra();
1605: 
1606:         $extra['class']      = 'header_new';
1607:         $extra['accesskey']  = 'n';
1608:         $extra['attributes'] = 'onclick="Backend.getScrollOffset();"';
1609: 
1610:         $command
1611:             ->setName('button_new')
1612:             ->setLabel($this->translate('new.0', $providerName))
1613:             ->setDescription($this->translate('new.1', $providerName));
1614: 
1615:         $this->getPanel()->initialize($config);
1616: 
1617:         // Add new button.
1618:         if (($mode == BasicDefinitionInterface::MODE_FLAT)
1619:             || (($mode == BasicDefinitionInterface::MODE_PARENTEDLIST) && !$sorting))
1620:         {
1621:             $parameters['act'] = 'create';
1622:             // Add new button.
1623:             if (strlen($parentProviderName) && $pid)
1624:             {
1625:                 $parameters['pid'] = $pid;
1626:             }
1627:         }
1628:         elseif(($mode == BasicDefinitionInterface::MODE_PARENTEDLIST)
1629:             || ($mode == BasicDefinitionInterface::MODE_HIERARCHICAL))
1630:         {
1631:             if ($environment->getClipboard()->isNotEmpty())
1632:             {
1633:                 return null;
1634:             }
1635: 
1636:             $parameters['act']  = 'paste';
1637:             $parameters['mode'] = 'create';
1638:             // Add new button.
1639:             if (strlen($parentProviderName) && $pid)
1640:             {
1641:                 $parameters['pid'] = $pid;
1642:             }
1643:         }
1644: 
1645:         return $command;
1646:     }
1647:     /**
1648:      * Create the "clear clipboard" button.
1649:      *
1650:      * @return CommandInterface|null
1651:      */
1652:     protected function getClearClipboardCommand()
1653:     {
1654:         if ($this->getEnvironment()->getClipboard()->isEmpty())
1655:         {
1656:             return null;
1657:         }
1658:         $command             = new Command();
1659:         $parameters          = $command->getParameters();
1660:         $extra               = $command->getExtra();
1661:         $extra['class']      = 'header_clipboard';
1662:         $extra['accesskey']  = 'x';
1663:         $extra['attributes'] = 'onclick="Backend.getScrollOffset();"';
1664: 
1665:         $parameters['clipboard'] = '1';
1666: 
1667:         $command
1668:             ->setName('button_clipboard')
1669:             ->setLabel($this->translate('MSC.clearClipboard'))
1670:             ->setDescription($this->translate('MSC.clearClipboard'));
1671: 
1672:         return $command;
1673:     }
1674: 
1675:     /**
1676:      * Create the "back" button.
1677:      *
1678:      * @return CommandInterface|null
1679:      */
1680:     protected function getBackCommand()
1681:     {
1682:         $environment = $this->getEnvironment();
1683:         if (!($this->isSelectModeActive() || $environment->getDataDefinition()->getBasicDefinition()->getParentDataProvider()))
1684:         {
1685:             return null;
1686:         }
1687: 
1688:         /** @var GetReferrerEvent $event */
1689:         $event = $environment->getEventPropagator()->propagate(
1690:             ContaoEvents::SYSTEM_GET_REFERRER,
1691:             new GetReferrerEvent(true, $environment->getParentDataDefinition()->getName())
1692:         );
1693: 
1694:         $command             = new Command();
1695:         $extra               = $command->getExtra();
1696:         $extra['class']      = 'header_back';
1697:         $extra['accesskey']  = 'b';
1698:         $extra['attributes'] = 'onclick="Backend.getScrollOffset();"';
1699:         $extra['href']       = $event->getReferrerUrl();
1700: 
1701:         $command
1702:             ->setName('back_button')
1703:             ->setLabel($this->translate('MSC.backBT'))
1704:             ->setDescription($this->translate('MSC.backBT'));
1705: 
1706:         return $command;
1707:     }
1708: 
1709:     /**
1710:      * Render a single header button.
1711:      *
1712:      * @param CommandInterface $command
1713:      *
1714:      * @return string
1715:      */
1716:     protected function generateHeaderButton(CommandInterface $command)
1717:     {
1718:         $environment = $this->getEnvironment();
1719:         $extra       = $command->getExtra();
1720:         $label       = $command->getLabel();
1721: 
1722:         if (isset($extra['href']))
1723:         {
1724:             $href = $extra['href'];
1725:         }
1726:         else
1727:         {
1728:             $href = '';
1729:             foreach ($command->getParameters() as $key => $value)
1730:             {
1731:                 $href .= '&' . $key . '=' . $value;
1732:             }
1733: 
1734:             /** @var AddToUrlEvent $event */
1735:             $event = $environment->getEventPropagator()->propagate(
1736:                 ContaoEvents::BACKEND_ADD_TO_URL,
1737:                 new AddToUrlEvent(
1738:                     $href
1739:                 )
1740:             );
1741: 
1742:             $href = $event->getUrl();
1743:         }
1744: 
1745:         if (!strlen($label))
1746:         {
1747:             $label = $command->getName();
1748:         }
1749: 
1750:         $buttonEvent = new GetGlobalButtonEvent($this->getEnvironment());
1751:         $buttonEvent
1752:             ->setAccessKey(trim($extra['accesskey']))
1753:             ->setAttributes(' ' . ltrim($extra['attributes']))
1754:             ->setClass($extra['class'])
1755:             ->setKey($command->getName())
1756:             ->setHref($href)
1757:             ->setLabel($label)
1758:             ->setTitle($command->getDescription());
1759: 
1760:         $this->getEnvironment()->getEventPropagator()->propagate(
1761:             $buttonEvent::NAME,
1762:             $buttonEvent,
1763:             array(
1764:                 $this->getEnvironment()->getDataDefinition()->getName(),
1765:                 $command->getName()
1766:             )
1767:         );
1768: 
1769:         // Allow to override the button entirely.
1770:         $html = $buttonEvent->getHtml();
1771:         if (!is_null($html))
1772:         {
1773:             return $html;
1774:         }
1775: 
1776:         // Use the view native button building.
1777:         return sprintf(
1778:             '<a href="%s" class="%s" title="%s"%s>%s</a> ',
1779:             $buttonEvent->getHref(),
1780:             $buttonEvent->getClass(),
1781:             specialchars($buttonEvent->getTitle()),
1782:             $buttonEvent->getAttributes(),
1783:             $buttonEvent->getLabel()
1784:         );
1785:     }
1786: 
1787: 
1788:     /**
1789:      * Generate all buttons for the header of a view.
1790:      *
1791:      * @param string $strButtonId The id for the surrounding html div element.
1792:      *
1793:      * @return string
1794:      */
1795:     protected function generateHeaderButtons($strButtonId)
1796:     {
1797:         /** @var CommandInterface[] $globalOperations */
1798:         $globalOperations   = $this->getViewSection()->getGlobalCommands()->getCommands();
1799:         $buttons            = array();
1800: 
1801:         if (!is_array($globalOperations))
1802:         {
1803:             $globalOperations = array();
1804:         }
1805: 
1806:         // Special case - if select mode active, we must not display the "edit all" button.
1807:         if ($this->isSelectModeActive())
1808:         {
1809:             unset($globalOperations['all']);
1810:         }
1811:         // We do not have the select mode.
1812:         else
1813:         {
1814:             $command = $this->getCreateModelCommand();
1815:             if ($command !== null)
1816:             {
1817:                 // New button always first.
1818:                 array_unshift($globalOperations, $command);
1819:             }
1820: 
1821:             $command = $this->getClearClipboardCommand();
1822:             if ($command !== null)
1823:             {
1824:                 // Clear clipboard to the end.
1825:                 $globalOperations[] = $command;
1826:             }
1827:         }
1828: 
1829:         $command = $this->getBackCommand();
1830:         if ($command !== null)
1831:         {
1832:             // Back button always to the end.
1833:             $globalOperations[] = $command;
1834:         }
1835: 
1836: 
1837:         foreach ($globalOperations as $command)
1838:         {
1839:             $buttons[$command->getName()] = $this->generateHeaderButton($command);
1840:         }
1841: 
1842:         $buttonsEvent = new GetGlobalButtonsEvent($this->getEnvironment());
1843:         $buttonsEvent->setButtons($buttons);
1844: 
1845:         $this->getEnvironment()->getEventPropagator()->propagate(
1846:             $buttonsEvent::NAME,
1847:             $buttonsEvent,
1848:             array($this->getEnvironment()->getDataDefinition()->getName())
1849:         );
1850: 
1851:         return '<div id="' . $strButtonId . '">' . implode(' &nbsp; :: &nbsp; ', $buttonsEvent->getButtons()) . '</div>';
1852:     }
1853: 
1854:     /**
1855:      * Render a command button.
1856:      *
1857:      * @param CommandInterface $objCommand           The command to render the button for.
1858:      *
1859:      * @param ModelInterface   $objModel             The model to which the command shall get applied.
1860:      *
1861:      * @param bool             $blnCircularReference Determinator if there exists a circular reference between the model
1862:      *                                               and the model(s) contained in the clipboard.
1863:      *
1864:      * @param array            $arrChildRecordIds    List of the ids of all child models of the current model.
1865:      *
1866:      * @param ModelInterface   $previous             The previous model in the collection.
1867:      *
1868:      * @param ModelInterface   $next                 The next model in the collection.
1869:      *
1870:      * @return string
1871:      */
1872:     protected function buildCommand($objCommand, $objModel, $blnCircularReference, $arrChildRecordIds, $previous, $next)
1873:     {
1874:         $propagator = $this->getEnvironment()->getEventPropagator();
1875: 
1876:         // Set basic information.
1877:         $opLabel = $objCommand->getLabel();
1878:         if (strlen($opLabel))
1879:         {
1880:             $label = $opLabel;
1881:         }
1882:         else
1883:         {
1884:             $label = $objCommand->getName();
1885:         }
1886: 
1887:         $opDesc = $objCommand->getDescription();
1888:         if (strlen($opDesc))
1889:         {
1890:             $title = sprintf($opDesc, $objModel->getID());
1891:         }
1892:         else
1893:         {
1894:             $title = sprintf('%s id %s', $label, $objModel->getID());
1895:         }
1896: 
1897:         $strAttributes = $objCommand->getExtra()['attributes'];
1898:         $attributes    = '';
1899:         if (strlen($strAttributes))
1900:         {
1901:             $attributes = ltrim(sprintf($strAttributes, $objModel->getID()));
1902:         }
1903: 
1904:         // Cut needs some special information.
1905:         if ($objCommand instanceof CutCommandInterface)
1906:         {
1907:             $arrParameters = array();
1908:             $arrParameters['act'] = 'cut';
1909: 
1910:                 // Get data provider from current and parent.
1911:             $strParentDataProvider = $objModel->getMeta(DCGE::MODEL_PTABLE);
1912:             $arrParameters['cdp']  = $objModel->getProviderName();
1913: 
1914:             // Add parent provider if exists.
1915:             if ($strParentDataProvider != null)
1916:             {
1917:                 $arrParameters['pdp'] = $strParentDataProvider;
1918:             }
1919: 
1920:             // If we have a pid add it, used for mode 4 and all parent -> current views.
1921:             if ($this->getEnvironment()->getInputProvider()->hasParameter('pid'))
1922:             {
1923:                 $arrParameters['pid'] = $this->getEnvironment()->getInputProvider()->getParameter('pid');
1924:             }
1925: 
1926:             // Source is the id of the element which should move.
1927:             $arrParameters['source'] = $objModel->getID();
1928:         }
1929:         else
1930:         {
1931:             $arrParameters = (array)$objCommand->getParameters();
1932: 
1933:             // TODO: Shall we interface this option?
1934:             $idParam = $objCommand->getExtra()['idparam'];
1935:             if ($idParam)
1936:             {
1937:                 $arrParameters[$idParam] = $objModel->getID();
1938:             }
1939:             else
1940:             {
1941:                 $arrParameters['id'] = $objModel->getID();
1942:             }
1943:         }
1944: 
1945:         $strHref = '';
1946:         foreach ($arrParameters as $key => $value)
1947:         {
1948:             $strHref .= sprintf('&%s=%s', $key, $value);
1949:         }
1950: 
1951:         /** @var AddToUrlEvent $event */
1952:         $event = $propagator->propagate(
1953:             ContaoEvents::BACKEND_ADD_TO_URL,
1954:             new AddToUrlEvent($strHref)
1955:         );
1956: 
1957:         $strHref = $event->getUrl();
1958: 
1959:         $buttonEvent = new GetOperationButtonEvent($this->getEnvironment());
1960:         $buttonEvent
1961:             ->setCommand($objCommand)
1962:             ->setObjModel($objModel)
1963:             ->setAttributes($attributes)
1964:             ->setLabel($label)
1965:             ->setTitle($title)
1966:             ->setHref($strHref)
1967:             ->setChildRecordIds($arrChildRecordIds)
1968:             ->setCircularReference($blnCircularReference)
1969:             ->setPrevious($previous)
1970:             ->setNext($next);
1971: 
1972:         $propagator->propagate(
1973:             $buttonEvent::NAME,
1974:             $buttonEvent,
1975:             array(
1976:                 $this->getEnvironment()->getDataDefinition()->getName(),
1977:                 $objCommand->getName()
1978:             )
1979:         );
1980: 
1981:         // If the event created a button, use it.
1982:         if (!is_null($buttonEvent->getHtml()))
1983:         {
1984:             return trim($buttonEvent->getHtml());
1985:         }
1986: 
1987:         $extra = $objCommand->getExtra();
1988:         $icon  = $extra['icon'];
1989: 
1990:         if ($objCommand->isDisabled())
1991:         {
1992:             /** @var GenerateHtmlEvent $event */
1993:             $event = $propagator->propagate(
1994:                 ContaoEvents::IMAGE_GET_HTML,
1995:                 new GenerateHtmlEvent(
1996:                     substr_replace($icon, '_1', strrpos($icon, '.'), 0),
1997:                     $buttonEvent->getLabel()
1998:                 )
1999:             );
2000: 
2001:             return $event->getHtml();
2002:         }
2003: 
2004:         /** @var GenerateHtmlEvent $event */
2005:         $event = $propagator->propagate(
2006:             ContaoEvents::IMAGE_GET_HTML,
2007:             new GenerateHtmlEvent(
2008:                 $icon,
2009:                 $buttonEvent->getLabel()
2010:             )
2011:         );
2012: 
2013:         return sprintf(' <a href="%s" title="%s" %s>%s</a>',
2014:             $buttonEvent->getHref(),
2015:             specialchars($buttonEvent->getTitle()),
2016:             $buttonEvent->getAttributes(),
2017:             $event->getHtml()
2018:         );
2019:     }
2020: 
2021:     /**
2022:      * Render the paste into button.
2023:      *
2024:      * @param GetPasteButtonEvent $event The event that has been triggered.
2025:      *
2026:      * @return string
2027:      */
2028:     public function renderPasteIntoButton(GetPasteButtonEvent $event)
2029:     {
2030:         if (!is_null($event->getHtmlPasteInto()))
2031:         {
2032:             return $event->getHtmlPasteInto();
2033:         }
2034: 
2035:         $strLabel = $this->translate('pasteinto.0', $event->getModel()->getProviderName());
2036:         if ($event->isPasteIntoDisabled())
2037:         {
2038:             /** @var GenerateHtmlEvent $imageEvent */
2039:             $imageEvent = $this->getEnvironment()->getEventPropagator()->propagate(
2040:                 ContaoEvents::IMAGE_GET_HTML,
2041:                 new GenerateHtmlEvent(
2042:                     'pasteinto_.gif',
2043:                     $strLabel,
2044:                     'class="blink"'
2045:                 )
2046:             );
2047: 
2048:             return $imageEvent->getHtml();
2049:         }
2050: 
2051:         /** @var GenerateHtmlEvent $imageEvent */
2052:         $imageEvent = $this->getEnvironment()->getEventPropagator()->propagate(
2053:             ContaoEvents::IMAGE_GET_HTML,
2054:             new GenerateHtmlEvent(
2055:                 'pasteinto.gif',
2056:                 $strLabel,
2057:                 'class="blink"'
2058:             )
2059:         );
2060: 
2061:         return sprintf(' <a href="%s" title="%s" %s>%s</a>',
2062:                 $event->getHrefInto(),
2063:                 specialchars($strLabel),
2064:                 'onclick="Backend.getScrollOffset()"',
2065:                 $imageEvent->getHtml()
2066:             );
2067:     }
2068: 
2069:     /**
2070:      * Render the paste after button.
2071:      *
2072:      * @param GetPasteButtonEvent $event The event that has been triggered.
2073:      *
2074:      * @return string
2075:      */
2076:     public function renderPasteAfterButton(GetPasteButtonEvent $event)
2077:     {
2078:         if (!is_null($event->getHtmlPasteAfter()))
2079:         {
2080:             return $event->getHtmlPasteAfter();
2081:         }
2082: 
2083:         $strLabel = $this->translate('pasteafter.0', $event->getModel()->getProviderName());
2084:         if ($event->isPasteAfterDisabled())
2085:         {
2086:             /** @var GenerateHtmlEvent $imageEvent */
2087:             $imageEvent = $this->getEnvironment()->getEventPropagator()->propagate(
2088:                 ContaoEvents::IMAGE_GET_HTML,
2089:                 new GenerateHtmlEvent(
2090:                     'pasteafter_.gif',
2091:                     $strLabel,
2092:                     'class="blink"'
2093:                 )
2094:             );
2095: 
2096:             return $imageEvent->getHtml();
2097:         }
2098: 
2099:         /** @var GenerateHtmlEvent $imageEvent */
2100:         $imageEvent = $this->getEnvironment()->getEventPropagator()->propagate(
2101:             ContaoEvents::IMAGE_GET_HTML,
2102:             new GenerateHtmlEvent(
2103:                 'pasteafter.gif',
2104:                 $strLabel,
2105:                 'class="blink"'
2106:             )
2107:         );
2108: 
2109:         return sprintf(' <a href="%s" title="%s" %s>%s</a>',
2110:             $event->getHrefAfter(),
2111:             specialchars($strLabel),
2112:             'onclick="Backend.getScrollOffset()"',
2113:             $imageEvent->getHtml()
2114:         );
2115:     }
2116: 
2117:     /**
2118:      * Compile buttons from the table configuration array and return them as HTML.
2119:      *
2120:      * @param ModelInterface $model    The model for which the buttons shall be generated for.
2121:      * @param ModelInterface $previous The previous model in the collection.
2122:      * @param ModelInterface $next     The next model in the collection.
2123:      * @return string
2124:      */
2125:     protected function generateButtons(
2126:         ModelInterface $model,
2127:         ModelInterface $previous = null,
2128:         ModelInterface $next = null
2129:     )
2130:     {
2131:         $commands     = $this->getViewSection()->getModelCommands();
2132:         $objClipboard = $this->getEnvironment()->getClipboard();
2133:         $propagator   = $this->getEnvironment()->getEventPropagator();
2134: 
2135:         if ($this->getEnvironment()->getClipboard()->isNotEmpty())
2136:         {
2137:             $circularIds = $objClipboard->getCircularIds();
2138:             $isCircular  = in_array($model->getID(), $circularIds);
2139:         }
2140:         else
2141:         {
2142:             $circularIds = array();
2143:             $isCircular  = false;
2144:         }
2145: 
2146:         $arrButtons = array();
2147:         foreach ($commands->getCommands() as $command)
2148:         {
2149:             $arrButtons[$command->getName()] = $this->buildCommand(
2150:                 $command,
2151:                 $model,
2152:                 $isCircular,
2153:                 $circularIds,
2154:                 $previous,
2155:                 $next
2156:             );
2157:         }
2158: 
2159:         // Add paste into/after icons.
2160:         if ($this->getEnvironment()->getClipboard()->isNotEmpty())
2161:         {
2162: 
2163:             $strMode = $objClipboard->getMode();
2164: 
2165:             // Add ext. information.
2166:             $add2UrlAfter = sprintf('act=%s&after=%s&',
2167:                 $strMode,
2168:                 $model->getID()
2169:             );
2170: 
2171:             $add2UrlInto = sprintf('act=%s&into=%s&',
2172:                 $strMode,
2173:                 $model->getID()
2174:             );
2175: 
2176:             /** @var AddToUrlEvent $urlAfter */
2177:             $urlAfter = $propagator->propagate(
2178:                 ContaoEvents::BACKEND_ADD_TO_URL,
2179:                 new AddToUrlEvent($add2UrlAfter)
2180:             );
2181: 
2182:             /** @var AddToUrlEvent $urlInto */
2183:             $urlInto = $propagator->propagate(
2184:                 ContaoEvents::BACKEND_ADD_TO_URL,
2185:                 new AddToUrlEvent($add2UrlInto)
2186:             );
2187: 
2188:             $buttonEvent = new GetPasteButtonEvent($this->getEnvironment());
2189:             $buttonEvent
2190:                 ->setModel($model)
2191:                 ->setCircularReference($isCircular)
2192:                 ->setPrevious($previous)
2193:                 ->setNext($next)
2194:                 ->setHrefAfter($urlAfter->getUrl())
2195:                 ->setHrefInto($urlInto->getUrl())
2196:                 // Check if the id is in the ignore list.
2197:                 ->setPasteAfterDisabled($objClipboard->isCut() && $isCircular)
2198:                 ->setPasteIntoDisabled($objClipboard->isCut() && $isCircular);
2199: 
2200:             $this->getEnvironment()->getEventPropagator()->propagate(
2201:                 $buttonEvent::NAME,
2202:                 $buttonEvent,
2203:                 array($this->getEnvironment()->getDataDefinition()->getName())
2204:             );
2205: 
2206:             $arrButtons['pasteafter'] = $this->renderPasteAfterButton($buttonEvent);
2207:             if ($this->getDataDefinition()->getBasicDefinition()->getMode() == BasicDefinitionInterface::MODE_HIERARCHICAL)
2208:             {
2209:                 $arrButtons['pasteinto'] = $this->renderPasteIntoButton($buttonEvent);
2210:             }
2211: 
2212:         }
2213: 
2214:         return implode(' ', $arrButtons);
2215:     }
2216: 
2217:     /**
2218:      * Render the panel.
2219:      *
2220:      * @return string
2221:      *
2222:      * @throws DcGeneralRuntimeException When no panel has been defined.
2223:      */
2224:     protected function panel()
2225:     {
2226:         if ($this->getPanel() === null)
2227:         {
2228:             throw new DcGeneralRuntimeException('No panel information stored in data container.');
2229:         }
2230: 
2231:         $arrPanels = array();
2232:         foreach ($this->getPanel() as $objPanel)
2233:         {
2234:             $arrPanel = array();
2235:             foreach ($objPanel as $objElement)
2236:             {
2237:                 $objElementTemplate = null;
2238:                 if ($objElement instanceof FilterElementInterface)
2239:                 {
2240:                     $objElementTemplate = $this->getTemplate('dcbe_general_panel_filter');
2241:                 }
2242:                 elseif ($objElement instanceof LimitElementInterface)
2243:                 {
2244:                     $objElementTemplate = $this->getTemplate('dcbe_general_panel_limit');
2245:                 }
2246:                 elseif ($objElement instanceof SearchElementInterface)
2247:                 {
2248:                     $objElementTemplate = $this->getTemplate('dcbe_general_panel_search');
2249:                 }
2250:                 elseif ($objElement instanceof SortElementInterface)
2251:                 {
2252:                     $objElementTemplate = $this->getTemplate('dcbe_general_panel_sort');
2253:                 }
2254:                 elseif ($objElement instanceof SubmitElementInterface)
2255:                 {
2256:                     $objElementTemplate = $this->getTemplate('dcbe_general_panel_submit');
2257:                 }
2258:                 $objElement->render($objElementTemplate);
2259: 
2260:                 $arrPanel[] = $objElementTemplate->parse();
2261:             }
2262:             $arrPanels[] = $arrPanel;
2263:         }
2264: 
2265:         if (count($arrPanels))
2266:         {
2267:             $objTemplate = $this->getTemplate('dcbe_general_panel');
2268:             $this
2269:                 ->addToTemplate('action', ampersand($this->getEnvironment()->getInputProvider()->getRequestUrl(), true), $objTemplate)
2270:                 // ->addToTemplate('theme', $this->getTheme(), $objTemplate) // FIXME: dependency injection
2271:                 ->addToTemplate('panel', $arrPanels, $objTemplate);
2272: 
2273:             return $objTemplate->parse();
2274:         }
2275: 
2276:         return '';
2277:     }
2278: 
2279:     /**
2280:      * Get the breadcrumb navigation via event.
2281:      *
2282:      * @return string
2283:      */
2284:     protected function breadcrumb()
2285:     {
2286:         $event = new GetBreadcrumbEvent($this->getEnvironment());
2287: 
2288:         $this->getEnvironment()->getEventPropagator()->propagate(
2289:             $event::NAME,
2290:             $event,
2291:             array($this->getEnvironment()->getDataDefinition()->getName())
2292:         );
2293: 
2294:         $arrReturn = $event->getElements();
2295: 
2296:         if (!is_array($arrReturn) || count($arrReturn) == 0)
2297:         {
2298:             return null;
2299:         }
2300: 
2301:         $GLOBALS['TL_CSS'][] = 'system/modules/dc-general/html/css/generalBreadcrumb.css';
2302: 
2303:         $objTemplate = $this->getTemplate('dcbe_general_breadcrumb');
2304:         $this->addToTemplate('elements', $arrReturn, $objTemplate);
2305: 
2306:         return $objTemplate->parse();
2307:     }
2308: 
2309:     /**
2310:      * Process input and return all modified properties or null if there is no input.
2311:      *
2312:      * @param ContaoWidgetManager $widgetManager The widget manager in use.
2313:      *
2314:      * @return null|PropertyValueBag
2315:      */
2316:     public function processInput($widgetManager)
2317:     {
2318:         $input = $this->getEnvironment()->getInputProvider();
2319: 
2320:         if ($input->getValue('FORM_SUBMIT') == $this->getEnvironment()->getDataDefinition()->getName())
2321:         {
2322:             $propertyValues = new PropertyValueBag();
2323:             $propertyNames  = $this->getEnvironment()->getDataDefinition()->getPropertiesDefinition()->getPropertyNames();
2324: 
2325:             // Process input and update changed properties.
2326:             foreach ($propertyNames as $propertyName)
2327:             {
2328:                 if ($input->hasValue($propertyName))
2329:                 {
2330:                     $propertyValue = $input->getValue($propertyName);
2331:                     $propertyValues->setPropertyValue($propertyName, $propertyValue);
2332:                 }
2333:             }
2334:             $widgetManager->processInput($propertyValues);
2335: 
2336:             return $propertyValues;
2337:         }
2338: 
2339:         return null;
2340:     }
2341: 
2342:     /**
2343:      * Format a model accordingly to the current configuration.
2344:      *
2345:      * Returns either an array when in tree mode or a string in (parented) list mode.
2346:      *
2347:      * @param ModelInterface $model The model that shall be formatted.
2348:      *
2349:      * @return array
2350:      */
2351:     public function formatModel(ModelInterface $model)
2352:     {
2353:         $listing      = $this->getViewSection()->getListingConfig();
2354:         $properties   = $this->getDataDefinition()->getPropertiesDefinition();
2355:         $formatter    = $listing->getLabelFormatter($model->getProviderName());
2356:         $sorting      = array_keys((array)$listing->getDefaultSortingFields());
2357:         $firstSorting = reset($sorting);
2358: 
2359:         $args = array();
2360:         foreach ($formatter->getPropertyNames() as $propertyName)
2361:         {
2362:             if ($properties->hasProperty($propertyName))
2363:             {
2364:                 $property = $properties->getProperty($propertyName);
2365: 
2366:                 $args[$propertyName] = (string)$this->getReadableFieldValue($property, $model, $model->getProperty($propertyName));
2367:             }
2368:             else
2369:             {
2370:                 $args[$propertyName] = '-';
2371:             }
2372: 
2373:         }
2374: 
2375:         $event = new ModelToLabelEvent($this->getEnvironment(), $model);
2376:         $event
2377:             ->setArgs($args)
2378:             ->setLabel($formatter->getFormat())
2379:             ->setFormatter($formatter);
2380: 
2381:         $this->getEnvironment()->getEventPropagator()->propagate(
2382:             $event::NAME,
2383:             $event,
2384:             array($this->getEnvironment()->getDataDefinition()->getName())
2385:         );
2386: 
2387:         $arrLabel = array();
2388: 
2389:         // Add columns.
2390:         if ($listing->getShowColumns())
2391:         {
2392:             $fields = $formatter->getPropertyNames();
2393:             $args   = $event->getArgs();
2394: 
2395:             if (!is_array($args))
2396:             {
2397:                 $arrLabel[] = array(
2398:                     'colspan' => count($fields),
2399:                     'class' => 'tl_file_list col_1',
2400:                     'content' => $args
2401:                 );
2402:             }
2403:             else
2404:             {
2405:                 foreach ($fields as $j => $propertyName)
2406:                 {
2407:                     $arrLabel[] = array(
2408:                         'colspan' => 1,
2409:                         'class' => 'tl_file_list col_' . $j . (($propertyName == $firstSorting) ? ' ordered_by' : ''),
2410:                         'content' => (($args[$propertyName] != '') ? $args[$propertyName] : '-')
2411:                     );
2412:                 }
2413:             }
2414:         }
2415:         else
2416:         {
2417:             if (!is_array($event->getArgs()))
2418:             {
2419:                 $string = $event->getArgs();
2420:             }
2421:             else
2422:             {
2423:                 $string = vsprintf($event->getLabel(), $event->getArgs());
2424:             }
2425: 
2426:             if ($formatter->getMaxLength() !== null && strlen($string) > $formatter->getMaxLength())
2427:             {
2428:                 $string = substr($string, 0, $formatter->getMaxLength());
2429:             }
2430: 
2431:             $arrLabel[] = array(
2432:                 'colspan' => null,
2433:                 'class'   => 'tl_file_list',
2434:                 'content' => $string
2435:             );
2436:         }
2437: 
2438:         return $arrLabel;
2439:     }
2440: 
2441:     /**
2442:      * Get for a field the readable value.
2443:      *
2444:      * @param PropertyInterface $property The property to be rendered.
2445:      *
2446:      * @param ModelInterface    $model    The model from which the property value shall be retrieved from.
2447:      *
2448:      * @param mixed             $value    The value for the property.
2449:      *
2450:      * @return mixed
2451:      */
2452:     public function getReadableFieldValue(PropertyInterface $property, ModelInterface $model, $value)
2453:     {
2454:         $event = new RenderReadablePropertyValueEvent($this->getEnvironment(), $model, $property, $value);
2455:         $this->getEnvironment()->getEventPropagator()->propagate(
2456:             $event::NAME,
2457:             $event,
2458:             array(
2459:                 $this->getEnvironment()->getDataDefinition()->getName(),
2460:                 $property->getName()
2461:             )
2462:         );
2463: 
2464:         if ($event->getRendered() !== null)
2465:         {
2466:             return $event->getRendered();
2467:         }
2468: 
2469:         return $value;
2470:     }
2471: }
2472: 
contao-community-alliance/dc-general API documentation generated by ApiGen 2.8.0