/** * Handle the persisting of the currently loaded model. * * @return void */ private function doPersist() { if (!$this->model->getMeta(ModelInterface::IS_CHANGED)) { return; } $this->handlePrePersist(); // FIXME: manual sorting property handling is not enabled here as it originates from the backend defininiton. // Save the model. $dataProvider = $this->environment->getDataProvider($this->model->getProviderName()); $dataProvider->save($this->model); $this->handlePostPersist(); $this->storeVersion($this->model); }
/** * Create the edit mask. * * @return string * * @throws DcGeneralRuntimeException If the data container is not editable, closed. * * @throws DcGeneralInvalidArgumentException If an unknown property is encountered in the palette. * * @SuppressWarnings(PHPMD.LongVariable) */ public function execute() { $environment = $this->getEnvironment(); $definition = $this->getDataDefinition(); $dataProvider = $environment->getDataProvider($this->model->getProviderName()); $dataProviderDefinition = $definition->getDataProviderDefinition(); $dataProviderInformation = $dataProviderDefinition->getInformation($this->model->getProviderName()); $inputProvider = $environment->getInputProvider(); $palettesDefinition = $definition->getPalettesDefinition(); $blnSubmitted = $inputProvider->getValue('FORM_SUBMIT') === $definition->getName(); $blnIsAutoSubmit = $inputProvider->getValue('SUBMIT_TYPE') === 'auto'; $widgetManager = new ContaoWidgetManager($environment, $this->model); $this->checkEditable($this->model); $this->checkCreatable($this->model); $environment->getEventDispatcher()->dispatch(PreEditModelEvent::NAME, new PreEditModelEvent($environment, $this->model)); $environment->getEventDispatcher()->dispatch(DcGeneralEvents::ENFORCE_MODEL_RELATIONSHIP, new EnforceModelRelationshipEvent($this->getEnvironment(), $this->model)); // Pass 1: Get the palette for the values stored in the model. $palette = $palettesDefinition->findPalette($this->model); $propertyValues = $this->processInput($widgetManager); if ($blnSubmitted && $propertyValues) { // Pass 2: Determine the real palette we want to work on if we have some data submitted. $palette = $palettesDefinition->findPalette($this->model, $propertyValues); // Update the model - the model might add some more errors to the propertyValueBag via exceptions. $this->getEnvironment()->getController()->updateModelFromPropertyBag($this->model, $propertyValues); } $fieldSets = $this->buildFieldSet($widgetManager, $palette, $propertyValues); if (!$blnIsAutoSubmit && $blnSubmitted && empty($this->errors)) { if ($this->doPersist()) { $this->handleSubmit($this->model); } } $objTemplate = new ContaoBackendViewTemplate('dcbe_general_edit'); $objTemplate->setData(array('fieldsets' => $fieldSets, 'versions' => $dataProviderInformation->isVersioningEnabled() ? $dataProvider->getVersions($this->model->getId()) : null, 'subHeadline' => $this->getHeadline(), 'table' => $definition->getName(), 'enctype' => 'multipart/form-data', 'error' => $this->errors, 'editButtons' => $this->getEditButtons(), 'noReload' => (bool) $this->errors, 'breadcrumb' => $this->breadcrumb)); if (in_array('ContaoCommunityAlliance\\DcGeneral\\Data\\MultiLanguageDataProviderInterface', class_implements($environment->getDataProvider($this->model->getProviderName())))) { /** @var MultiLanguageDataProviderInterface $dataProvider */ $langsNative = array(); require TL_ROOT . '/system/config/languages.php'; $objTemplate->set('languages', $environment->getController()->getSupportedLanguages($this->model->getId()))->set('language', $dataProvider->getCurrentLanguage())->set('languageHeadline', $langsNative[$dataProvider->getCurrentLanguage()]); } else { $objTemplate->set('languages', null)->set('languageHeadline', ''); } return $objTemplate->parse(); }
/** * Retrieve the instance of a widget for the given property. * * @param string $property Name of the property for which the widget shall be retrieved. * * @param PropertyValueBag $valueBag The input values to use (optional). * * @return \Widget * * @throws DcGeneralRuntimeException When No widget could be build. * @throws DcGeneralInvalidArgumentException When property is not defined in the property definitions. */ public function getWidget($property, PropertyValueBag $valueBag = null) { $environment = $this->getEnvironment(); $dispatcher = $environment->getEventDispatcher(); $propertyDefinitions = $environment->getDataDefinition()->getPropertiesDefinition(); if (!$propertyDefinitions->hasProperty($property)) { throw new DcGeneralInvalidArgumentException('Property ' . $property . ' is not defined in propertyDefinitions.'); } $model = clone $this->model; $model->setId($this->model->getId()); if ($valueBag) { $values = new PropertyValueBag($valueBag->getArrayCopy()); $this->environment->getController()->updateModelFromPropertyBag($model, $values); } $propertyDefinition = $propertyDefinitions->getProperty($property); $event = new BuildWidgetEvent($environment, $model, $propertyDefinition); $dispatcher->dispatch(DcGeneralFrontendEvents::BUILD_WIDGET, $event); if (!$event->getWidget()) { throw new DcGeneralRuntimeException(sprintf('Widget was not build for property %s::%s.', $this->model->getProviderName(), $property)); } return $event->getWidget(); }
/** * Retrieve children of a given model. * * @param ModelInterface $model The model for which the children shall be retrieved. * * @param string|null $sortingProperty The property name to use for sorting. * * @return CollectionInterface * * @throws DcGeneralRuntimeException When not in hierarchical mode. */ protected function assembleChildrenFor(ModelInterface $model, $sortingProperty = null) { $environment = $this->getEnvironment(); $definition = $environment->getDataDefinition(); $provider = $environment->getDataProvider($model->getProviderName()); $config = $environment->getBaseConfigRegistry()->getBaseConfig(); $relationships = $definition->getModelRelationshipDefinition(); if ($definition->getBasicDefinition()->getMode() !== BasicDefinitionInterface::MODE_HIERARCHICAL) { throw new DcGeneralRuntimeException('Unable to retrieve children in non hierarchical mode.'); } $condition = $relationships->getChildCondition($model->getProviderName(), $model->getProviderName()); $config->setFilter($condition->getFilter($model)); if ($sortingProperty) { $config->setSorting(array((string) $sortingProperty => 'ASC')); } $siblings = $provider->fetchAll($config); return $siblings; }
/** * Determine if the passed model is expanded. * * @param ModelInterface $model The model to check. * * @return bool */ protected function isModelOpen($model) { $openModels = $this->getOpenElements(); if (isset($openModels['all']) && $openModels['all'] == 1) { return true; } if (isset($openModels[$model->getProviderName()][$model->getID()]) && $openModels[$model->getProviderName()][$model->getID()]) { return true; } return false; }
/** * Add the parent filtering to the given data config if any defined. * * @param ConfigInterface $config The data config. * * @param ModelInterface $parentModel The parent model. * * @return void * * @throws \RuntimeException When the parent provider does not match. */ private function addParentFilter(ConfigInterface $config, ModelInterface $parentModel) { $environment = $this->getEnvironment(); $definition = $environment->getDataDefinition(); $basicDefinition = $definition->getBasicDefinition(); $relationships = $definition->getModelRelationshipDefinition(); if ($basicDefinition->getParentDataProvider() !== $parentModel->getProviderName()) { throw new \RuntimeException(sprintf('Parent provider mismatch: %s vs. %s', $basicDefinition->getParentDataProvider(), $parentModel->getProviderName())); } if (!$basicDefinition->getParentDataProvider()) { return; } // Apply parent filtering, do this only for root elements. if ($parentCondition = $relationships->getChildCondition($basicDefinition->getParentDataProvider(), $basicDefinition->getRootDataProvider())) { $baseFilter = $config->getFilter(); $filter = $parentCondition->getFilter($parentModel); if ($baseFilter) { $filter = array_merge($baseFilter, $filter); } $config->setFilter($filter); } }
/** * Determine the toggle state of a toggle command. * * @param ToggleCommandInterface $command The toggle command. * * @param ModelInterface $model The model in scope. * * @return bool */ private function isTogglerInActiveState($command, $model) { $dataProvider = $this->environment->getDataProvider($model->getProviderName()); if ($command instanceof TranslatedToggleCommandInterface && $dataProvider instanceof MultiLanguageDataProviderInterface) { $language = $dataProvider->getCurrentLanguage(); $dataProvider->setCurrentLanguage($command->getLanguage()); $propModel = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($model->getId())->setFields($command->getToggleProperty())); $dataProvider->setCurrentLanguage($language); } else { $propModel = $model; } if ($command->isInverse()) { return !$propModel->getProperty($command->getToggleProperty()); } return (bool) $propModel->getProperty($command->getToggleProperty()); }
/** * Retrieve the MetaModel the given model is attached to. * * @param ModelInterface $model The input screen model for which to retrieve the MetaModel. * * @return IMetaModel * * @throws DcGeneralInvalidArgumentException When an invalid model has been passed or the model does not have an id. */ protected function getMetaModelFromModel(ModelInterface $model) { if (!($model->getProviderName() == 'tl_metamodel_dcasetting' && $model->getProperty('pid'))) { throw new DcGeneralInvalidArgumentException(sprintf('Model must originate from tl_metamodel_dcasetting and be saved, this one originates from %s and ' . 'has pid %s', $model->getProviderName(), $model->getProperty('pid'))); } $metaModelId = $this->getDatabase()->prepare('SELECT pid FROM tl_metamodel_dca WHERE id=?')->execute($model->getProperty('pid')); return $this->getMetaModelById($metaModelId->pid); }
/** * Get the headline for the template. * * @param ModelInterface $model The model. * * @return string */ protected function getHeadline($model) { $translator = $this->getEnvironment()->getTranslator(); $headline = $translator->translate('MSC.showRecord', $model->getProviderName(), array('ID ' . $model->getId())); if ($headline !== 'MSC.showRecord') { return $headline; } return $translator->translate('MSC.showRecord', null, array('ID ' . $model->getId())); }
/** * Create an instance from a model. * * @param ModelInterface $model The model. * * @return IdSerializer */ public static function fromModel(ModelInterface $model) { return self::fromValues($model->getProviderName(), $model->getId()); }
/** * Retrieve the formatter for the given model. * * @param ModelInterface $model The model for which the formatter shall be retrieved. * @param bool $treeMode Flag if we are running in tree mode or not. * * @return ModelFormatterConfigInterface */ protected function getFormatter(ModelInterface $model, $treeMode) { /** @var ListingConfigInterface $listing */ $definition = $this->getEnvironment()->getDataDefinition(); $listing = $definition->getDefinition(Contao2BackendViewDefinitionInterface::NAME)->getListingConfig(); if ($listing->hasLabelFormatter($model->getProviderName())) { return $listing->getLabelFormatter($model->getProviderName()); } // If not in tree mode and custom label has been defined, use it. if (!$treeMode && $this->itemLabel) { $label = $this->itemLabel; $formatter = new DefaultModelFormatterConfig(); $formatter->setPropertyNames($label['fields']); $formatter->setFormat($label['format']); $formatter->setMaxLength($label['maxCharacters']); return $formatter; } // If no label has been defined, use some default. $properties = array(); foreach ($definition->getPropertiesDefinition()->getProperties() as $property) { if ($property->getWidgetType() == 'text') { $properties[] = $property->getName(); } } $formatter = new DefaultModelFormatterConfig(); $formatter->setPropertyNames($properties); $formatter->setFormat(str_repeat('%s ', count($properties))); return $formatter; }
/** * Render a command button. * * @param CommandInterface $objCommand The command to render the button for. * * @param ModelInterface $objModel The model to which the command shall get applied. * * @param bool $blnCircularReference Determinator if there exists a circular reference between the model * and the model(s) contained in the clipboard. * * @param array $arrChildRecordIds List of the ids of all child models of the current model. * * @param ModelInterface $previous The previous model in the collection. * * @param ModelInterface $next The next model in the collection. * * @return string */ protected function buildCommand($objCommand, $objModel, $blnCircularReference, $arrChildRecordIds, $previous, $next) { $environment = $this->getEnvironment(); $inputProvider = $environment->getInputProvider(); $dispatcher = $environment->getEventDispatcher(); $dataDefinitionName = $environment->getDataDefinition()->getName(); $commandName = $objCommand->getName(); $parameters = (array) $objCommand->getParameters(); $extra = (array) $objCommand->getExtra(); $extraAttributes = !empty($extra['attributes']) ? $extra['attributes'] : null; $attributes = ''; // Set basic information. $opLabel = $objCommand->getLabel(); if (strlen($opLabel)) { $label = $opLabel; } else { $label = $commandName; } $label = $this->translate($label, $dataDefinitionName); if (is_array($label)) { $label = $label[0]; } $opDesc = $this->translate($objCommand->getDescription(), $dataDefinitionName); if (strlen($opDesc)) { $title = sprintf($opDesc, $objModel->getID()); } else { $title = sprintf('%s id %s', $label, $objModel->getID()); } // Toggle has to trigger the javascript. if ($objCommand instanceof ToggleCommandInterface) { $parameters['act'] = $commandName; $icon = $extra['icon']; $iconDisabled = isset($extra['icon_disabled']) ? $extra['icon_disabled'] : 'invisible.gif'; $attributes = sprintf('onclick="Backend.getScrollOffset(); return BackendGeneral.toggleVisibility(this, \'%s\', \'%s\');"', $icon, $iconDisabled); $dataProvider = $this->getEnvironment()->getDataProvider($objModel->getProviderName()); if ($objCommand instanceof TranslatedToggleCommandInterface && $dataProvider instanceof MultiLanguageDataProviderInterface) { $language = $dataProvider->getCurrentLanguage(); $dataProvider->setCurrentLanguage($objCommand->getLanguage()); $propModel = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($objModel->getId())->setFields($objCommand->getToggleProperty())); $dataProvider->setCurrentLanguage($language); } else { $propModel = $objModel; } $state = $objCommand->isInverse() ? !$propModel->getProperty($objCommand->getToggleProperty()) : $propModel->getProperty($objCommand->getToggleProperty()); if (!$state) { $extra['icon'] = $iconDisabled ?: 'invisible.gif'; } } if ($extraAttributes) { $attributes .= ltrim(sprintf($extraAttributes, $objModel->getID())); } $serializedModelId = ModelId::fromModel($objModel)->getSerialized(); // Cut needs some special information. if ($objCommand instanceof CutCommandInterface) { $parameters = array(); $parameters['act'] = $commandName; // If we have a pid add it, used for mode 4 and all parent -> current views. if ($inputProvider->hasParameter('pid')) { $parameters['pid'] = $inputProvider->getParameter('pid'); } // Source is the id of the element which should move. $parameters['source'] = $serializedModelId; } elseif ($objCommand instanceof CopyCommandInterface) { // The copy operation. $parameters = array(); $parameters['act'] = $commandName; // If we have a pid add it, used for mode 4 and all parent -> current views. if ($inputProvider->hasParameter('pid')) { $parameters['pid'] = $inputProvider->getParameter('pid'); } // Source is the id of the element which should move. $parameters['source'] = $serializedModelId; } else { // TODO: Shall we interface this option? $idParam = isset($extra['idparam']) ? $extra['idparam'] : null; if ($idParam) { $parameters[$idParam] = $serializedModelId; } else { $parameters['id'] = $serializedModelId; } } $strHref = ''; foreach ($parameters as $key => $value) { $strHref .= sprintf('&%s=%s', $key, $value); } /** @var AddToUrlEvent $event */ $event = $dispatcher->dispatch(ContaoEvents::BACKEND_ADD_TO_URL, new AddToUrlEvent($strHref)); $strHref = $event->getUrl(); $buttonEvent = new GetOperationButtonEvent($this->getEnvironment()); $buttonEvent->setCommand($objCommand)->setObjModel($objModel)->setAttributes($attributes)->setLabel($label)->setTitle($title)->setHref($strHref)->setChildRecordIds($arrChildRecordIds)->setCircularReference($blnCircularReference)->setPrevious($previous)->setNext($next)->setDisabled($objCommand->isDisabled()); $dispatcher->dispatch(GetOperationButtonEvent::NAME, $buttonEvent); // If the event created a button, use it. if ($buttonEvent->getHtml() !== null) { return trim($buttonEvent->getHtml()); } $icon = $extra['icon']; if ($buttonEvent->isDisabled()) { /** @var GenerateHtmlEvent $event */ $event = $dispatcher->dispatch(ContaoEvents::IMAGE_GET_HTML, new GenerateHtmlEvent(substr_replace($icon, '_1', strrpos($icon, '.'), 0), $buttonEvent->getLabel())); return $event->getHtml(); } /** @var GenerateHtmlEvent $event */ $event = $dispatcher->dispatch(ContaoEvents::IMAGE_GET_HTML, new GenerateHtmlEvent($icon, $buttonEvent->getLabel())); return sprintf(' <a href="%s" title="%s" %s>%s</a>', $buttonEvent->getHref(), specialchars($buttonEvent->getTitle()), $buttonEvent->getAttributes(), $event->getHtml()); }
/** * Format a model accordingly to the current configuration. * * Returns either an array when in tree mode or a string in (parented) list mode. * * @param ModelInterface $model The model that shall be formatted. * * @return array */ public function formatModel(ModelInterface $model) { $listing = $this->getViewSection()->getListingConfig(); $properties = $this->getDataDefinition()->getPropertiesDefinition(); $formatter = $listing->getLabelFormatter($model->getProviderName()); $sorting = array_keys((array) $listing->getDefaultSortingFields()); $firstSorting = reset($sorting); $args = array(); foreach ($formatter->getPropertyNames() as $propertyName) { if ($properties->hasProperty($propertyName)) { $property = $properties->getProperty($propertyName); $args[$propertyName] = (string) $this->getReadableFieldValue($property, $model, $model->getProperty($propertyName)); } else { $args[$propertyName] = '-'; } } $event = new ModelToLabelEvent($this->getEnvironment(), $model); $event->setArgs($args)->setLabel($formatter->getFormat())->setFormatter($formatter); $this->getEnvironment()->getEventPropagator()->propagate($event::NAME, $event, array($this->getEnvironment()->getDataDefinition()->getName())); $arrLabel = array(); // Add columns. if ($listing->getShowColumns()) { $fields = $formatter->getPropertyNames(); $args = $event->getArgs(); if (!is_array($args)) { $arrLabel[] = array('colspan' => count($fields), 'class' => 'tl_file_list col_1', 'content' => $args); } else { foreach ($fields as $j => $propertyName) { $arrLabel[] = array('colspan' => 1, 'class' => 'tl_file_list col_' . $j . ($propertyName == $firstSorting ? ' ordered_by' : ''), 'content' => $args[$propertyName] != '' ? $args[$propertyName] : '-'); } } } else { if (!is_array($event->getArgs())) { $string = $event->getArgs(); } else { $string = vsprintf($event->getLabel(), $event->getArgs()); } if ($formatter->getMaxLength() !== null && strlen($string) > $formatter->getMaxLength()) { $string = substr($string, 0, $formatter->getMaxLength()); } $arrLabel[] = array('colspan' => null, 'class' => 'tl_file_list', 'content' => $string); } return $arrLabel; }
/** * Render a given model. * * @param ModelInterface $objModel The model to render. * * @param string $strToggleID The id of the toggler. * * @return string */ protected function parseModel($objModel, $strToggleID) { $event = new FormatModelLabelEvent($this->environment, $objModel); $this->environment->getEventDispatcher()->dispatch(DcGeneralEvents::FORMAT_MODEL_LABEL, $event); $objModel->setMeta($objModel::LABEL_VALUE, $event->getLabel()); $objTemplate = $this->getTemplate('dcbe_general_treeview_entry'); if ($objModel->getMeta($objModel::SHOW_CHILDREN)) { $toggleTitle = $this->getEnvironment()->getTranslator()->translate('collapseNode', 'MSC'); } else { $toggleTitle = $this->getEnvironment()->getTranslator()->translate('expandNode', 'MSC'); } $toggleScript = sprintf('Backend.getScrollOffset(); return BackendGeneral.loadSubTree(this, ' . '{\'toggler\':\'%s\', \'id\':\'%s\', \'providerName\':\'%s\', \'level\':\'%s\', \'mode\':\'%s\'});', $strToggleID, $objModel->getId(), $objModel->getProviderName(), $objModel->getMeta('dc_gen_tv_level'), 6); $toggleUrlEvent = new AddToUrlEvent('ptg=' . $objModel->getId() . '&provider=' . $objModel->getProviderName()); $this->getEnvironment()->getEventDispatcher()->dispatch(ContaoEvents::BACKEND_ADD_TO_URL, $toggleUrlEvent); $this->addToTemplate('environment', $this->getEnvironment(), $objTemplate)->addToTemplate('objModel', $objModel, $objTemplate)->addToTemplate('select', $this->isSelectModeActive(), $objTemplate)->addToTemplate('intMode', 6, $objTemplate)->addToTemplate('strToggleID', $strToggleID, $objTemplate)->addToTemplate('toggleUrl', $toggleUrlEvent->getUrl(), $objTemplate)->addToTemplate('toggleTitle', $toggleTitle, $objTemplate)->addToTemplate('toggleScript', $toggleScript, $objTemplate); return $objTemplate->parse(); }
/** * Determine if the passed model is expanded. * * @param ModelInterface $model The model to check. * * @return bool */ protected function isModelOpen($model) { return $this->getTreeNodeStates()->isModelOpen($model->getProviderName(), $model->getID()); }
/** * Copy each children. * * @param ModelIdInterface $modelId The model id. * @param ModelInterface $copiedModel The copied model. * @param EnvironmentInterface $environment The environment. * * @return void * * @SuppressWarnings(PHPMD.LongVariableName) */ protected function copyEachChilds(ModelIdInterface $modelId, ModelInterface $copiedModel, EnvironmentInterface $environment) { $childDataProviderName = $environment->getInputProvider()->getParameter('ctable'); $dataProvider = $environment->getDataProvider(); $childDataProvider = $environment->getDataProvider($childDataProviderName); $modelRelationship = $environment->getParentDataDefinition()->getModelRelationshipDefinition(); $childCondition = $modelRelationship->getChildCondition($copiedModel->getProviderName(), $childDataProviderName); if (!$childCondition) { return; } $parentModel = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); $parentFilters = $childCondition->getFilter($parentModel); $copiedFilters = $childCondition->getFilter($copiedModel); $filter = array(); // Todo can many filter has operation equal foreach ($parentFilters as $index => $parentFilter) { if ($parentFilter['operation'] !== '=') { continue; } $filter = array('parent' => $parentFilter, 'copied' => $copiedFilters[$index]); } $childModels = $childDataProvider->fetchAll($childDataProvider->getEmptyConfig()->setFilter(array($filter['parent']))); if ($childModels->count() < 1) { return; } foreach ($childModels->getModelIds() as $index => $modelId) { $childModel = $childModels->get($index); $copyModelId = ModelId::fromModel($childModel); $copiedChildModel = null; if ($environment->getDataDefinition()->getName() !== $copyModelId->getDataProviderName()) { $copiedChildModel = $this->copyParent($copyModelId, $environment); } if ($environment->getDataDefinition()->getName() === $copyModelId->getDataProviderName() && !$copiedChildModel) { $copiedChildModel = $this->copyHandler($copyModelId, $environment); } $copiedChildModel->setProperty($filter['copied']['property'], $filter['copied']['value']); $childDataProvider->save($copiedChildModel); } }