/** * Test that the ModelId class can not be instantiated with invalid values. * * @param ModelInterface $model The model to instantiate from. * * @param string|null $exception The name of the expected exception class. * * @dataProvider modelProvider * * @return void */ public function testInstantiationFromModel($model, $exception = null) { if (null !== $exception) { $this->setExpectedException($exception); } $modelId = ModelId::fromModel($model); $this->assertEquals($model->getId(), $modelId->getId()); $this->assertEquals($model->getProviderName(), $modelId->getDataProviderName()); }
/** * Handle the event. * * @param HandleSubmitEvent $event The event. * * @return void */ public function handleEvent(HandleSubmitEvent $event) { $dispatcher = func_get_arg(2); $currentUrl = $this->environment->get('uri'); switch ($event->getButtonName()) { case 'save': // Could be a create action, always redirect to current page with edit action and id properly set. $url = UrlBuilder::fromUrl($currentUrl)->setQueryParameter('act', 'edit')->setQueryParameter('id', ModelId::fromModel($event->getModel())->getSerialized()); $dispatcher->dispatch(ContaoEvents::CONTROLLER_REDIRECT, new RedirectEvent($url->getUrl())); break; case 'saveNcreate': // We want to create a new model, set create action and pass the current id as "after" to keep sorting. $after = ModelId::fromModel($event->getModel()); $url = UrlBuilder::fromUrl($currentUrl)->unsetQueryParameter('id')->setQueryParameter('act', 'create')->setQueryParameter('after', $after->getSerialized()); $dispatcher->dispatch(ContaoEvents::CONTROLLER_REDIRECT, new RedirectEvent($url->getUrl())); break; default: // Do nothing. } }
/** * Do deep copy. * * @param array $deepCopyList The deep copy list. * * @return void */ protected function doDeepCopy(array $deepCopyList) { if (empty($deepCopyList)) { return; } $factory = DcGeneralFactory::deriveFromEnvironment($this->getEnvironment()); $dataDefinition = $this->getEnvironment()->getDataDefinition(); $modelRelationshipDefinition = $dataDefinition->getModelRelationshipDefinition(); $childConditions = $modelRelationshipDefinition->getChildConditions($dataDefinition->getName()); foreach ($deepCopyList as $deepCopy) { /** @var ModelInterface $origin */ $origin = $deepCopy['origin']; /** @var ModelInterface $model */ $model = $deepCopy['model']; $parentId = ModelId::fromModel($model); foreach ($childConditions as $childCondition) { // create new destination environment $destinationName = $childCondition->getDestinationName(); $factory->setContainerName($destinationName); $destinationEnvironment = $factory->createEnvironment(); $destinationDataDefinition = $destinationEnvironment->getDataDefinition(); $destinationViewDefinition = $destinationDataDefinition->getDefinition(Contao2BackendViewDefinitionInterface::NAME); $destinationDataProvider = $destinationEnvironment->getDataProvider(); $destinationController = $destinationEnvironment->getController(); /** @var Contao2BackendViewDefinitionInterface $destinationViewDefinition */ /** @var DefaultController $destinationController */ $listingConfig = $destinationViewDefinition->getListingConfig(); $groupAndSortingCollection = $listingConfig->getGroupAndSortingDefinition(); $groupAndSorting = $groupAndSortingCollection->getDefault(); // ***** fetch the children $filter = $childCondition->getFilter($origin); // apply parent-child condition $config = $destinationDataProvider->getEmptyConfig(); $config->setFilter($filter); // apply sorting $sorting = array(); foreach ($groupAndSorting as $information) { /** @var GroupAndSortingInformationInterface $information */ $sorting[$information->getProperty()] = $information->getSortingMode(); } $config->setSorting($sorting); // receive children $children = $destinationDataProvider->fetchAll($config); // ***** do the deep copy $actions = array(); // build the copy actions foreach ($children as $childModel) { $childModelId = ModelId::fromModel($childModel); $actions[] = array('model' => $childModel, 'item' => new Item(ItemInterface::DEEP_COPY, $parentId, $childModelId)); } // do the deep copy $childrenModels = $destinationController->doActions($actions, null, null, $parentId); // ensure parent-child condition foreach ($childrenModels as $childrenModel) { $childCondition->applyTo($model, $childrenModel); } $destinationDataProvider->saveEach($childrenModels); } } }
/** * {@inheritdoc} */ public function process() { $event = $this->getEvent(); if ($event->getAction()->getName() !== 'copy') { return; } $environment = $this->getEnvironment(); $modelId = ModelId::fromSerialized($environment->getInputProvider()->getParameter('source')); $this->guardValidEnvironment($modelId); // We want a redirect here if not creatable. $this->guardIsCreatable($modelId, true); if ($this->isEditOnlyResponse()) { return; } // Manual sorting mode. The ClipboardController should pick it up. $manualSortingProperty = ViewHelpers::getManualSortingProperty($environment); if ($manualSortingProperty && $this->environment->getDataProvider()->fieldExists($manualSortingProperty)) { return; } $copiedModel = $this->copy($modelId); $this->redirect($environment, ModelId::fromModel($copiedModel)); }
/** * Calculate the href for a command. * * @param CommandInterface $command The command. * * @param ModelInterface $model The current model. * * @return string */ private function calculateHref(CommandInterface $command, $model) { $modelId = ModelId::fromModel($model)->getSerialized(); $parameters = $this->calculateParameters($command, $modelId); $href = ''; foreach ($parameters as $key => $value) { $href .= sprintf('&%s=%s', $key, $value); } return $this->addToUrl($href); }
/** * Handle the submit and determine which button has been triggered. * * This method will redirect the client. * * @param ModelInterface $model The model that has been submitted. * * @return void * * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) */ protected function handleSubmit(ModelInterface $model) { $environment = $this->getEnvironment(); $dispatcher = $environment->getEventDispatcher(); $inputProvider = $environment->getInputProvider(); if ($inputProvider->hasValue('save')) { $newUrlEvent = new AddToUrlEvent('act=edit&id=' . ModelId::fromModel($model)->getSerialized()); $dispatcher->dispatch(ContaoEvents::BACKEND_ADD_TO_URL, $newUrlEvent); $dispatcher->dispatch(ContaoEvents::CONTROLLER_REDIRECT, new RedirectEvent($newUrlEvent->getUrl())); } elseif ($inputProvider->hasValue('saveNclose')) { $this->clearBackendStates(); $newUrlEvent = new GetReferrerEvent(); $dispatcher->dispatch(ContaoEvents::SYSTEM_GET_REFERRER, $newUrlEvent); $dispatcher->dispatch(ContaoEvents::CONTROLLER_REDIRECT, new RedirectEvent($newUrlEvent->getReferrerUrl())); } elseif ($inputProvider->hasValue('saveNcreate')) { $this->clearBackendStates(); $after = ModelId::fromModel($model); $newUrlEvent = new AddToUrlEvent('act=create&id=&after=' . $after->getSerialized()); $dispatcher->dispatch(ContaoEvents::BACKEND_ADD_TO_URL, $newUrlEvent); $dispatcher->dispatch(ContaoEvents::CONTROLLER_REDIRECT, new RedirectEvent($newUrlEvent->getUrl())); } elseif ($inputProvider->hasValue('saveNback')) { $this->clearBackendStates(); $parentProviderName = $environment->getDataDefinition()->getBasicDefinition()->getParentDataProvider(); $newUrlEvent = new GetReferrerEvent(false, $parentProviderName); $dispatcher->dispatch(ContaoEvents::SYSTEM_GET_REFERRER, $newUrlEvent); $dispatcher->dispatch(ContaoEvents::CONTROLLER_REDIRECT, new RedirectEvent($newUrlEvent->getReferrerUrl())); } }
/** * Generate the paste button. * * @param GetPasteButtonEvent $event The event. * * @return void */ public function generatePasteButton(GetPasteButtonEvent $event) { if ($event->getEnvironment()->getDataDefinition()->getName() !== 'tl_metamodel_filtersetting') { return; } $environment = $event->getEnvironment(); $model = $event->getModel(); $clipboard = $environment->getClipboard(); $filter = new Filter(); $filter->andModelIs(ModelId::fromModel($model))->andActionIs($clipboard::MODE_CUT); // Disable all buttons if there is a circular reference. if ($event->isCircularReference() || !$clipboard->isEmpty($filter)) { $event->setPasteAfterDisabled(true)->setPasteIntoDisabled(true); return; } $factory = $this->getServiceContainer()->getFilterFactory()->getTypeFactory($model->getProperty('type')); // If setting does not support children, omit them. if ($model->getId() && !($factory && $factory->isNestedType())) { $event->setPasteIntoDisabled(true); } }
/** * Generate the paste button. * * @param GetPasteButtonEvent $event The event. * * @return void * * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) */ public function generatePasteButton(GetPasteButtonEvent $event) { if ($event->getEnvironment()->getDataDefinition()->getName() !== 'tl_metamodel_dcasetting_condition') { return; } $environment = $event->getEnvironment(); $model = $event->getModel(); $clipboard = $environment->getClipboard(); // Disable all buttons if there is a circular reference. if ($clipboard->fetch(Filter::create()->andActionIs(ItemInterface::CUT)->andModelIs(ModelId::fromModel($model)))) { $event->setPasteAfterDisabled(true)->setPasteIntoDisabled(true); return; } $flags = $GLOBALS['METAMODELS']['inputscreen_conditions'][$model->getProperty('type')]; // If setting does not support children, omit them. if ($model->getId() && !$flags['nestingAllowed']) { $event->setPasteIntoDisabled(true); return; } if (isset($flags['maxChildren']) && count($event->getEnvironment()->getController()->assembleAllChildrenFrom($model)) > $flags['maxChildren']) { $event->setPasteIntoDisabled(true); } }
/** * Retrieve a list of html buttons to use in the top panel (submit area). * * @param ModelInterface $parentModel The parent model. * * @return null|string */ protected function getHeaderEditButtons($parentModel) { $environment = $this->getEnvironment(); $parentDefinition = $environment->getParentDataDefinition(); if ($parentDefinition && $parentDefinition->getBasicDefinition()->isEditable()) { $definition = $environment->getDataDefinition(); $basicDefinition = $definition->getBasicDefinition(); $parentName = $basicDefinition->getParentDataProvider(); $dispatcher = $environment->getEventDispatcher(); $query = array('do' => $environment->getInputProvider()->getParameter('do'), 'act' => 'edit', 'table' => $parentName, 'id' => ModelId::fromModel($parentModel)->getSerialized()); $factory = DcGeneralFactory::deriveFromEnvironment($this->environment); $factory->setContainerName($parentDefinition->getName()); $parentContainer = $factory->createContainer(); if ($parentContainer->getBasicDefinition()->getParentDataProvider()) { $container = $this->environment->getDataDefinition(); $relationship = $container->getModelRelationshipDefinition()->getChildCondition($parentContainer->getBasicDefinition()->getParentDataProvider(), $parentContainer->getName()); if ($relationship) { $filter = $relationship->getInverseFilterFor($parentModel); $grandParentProvider = $this->environment->getDataProvider($parentContainer->getBasicDefinition()->getParentDataProvider()); $config = $grandParentProvider->getEmptyConfig(); $config->setFilter($filter); $parents = $grandParentProvider->fetchAll($config); if ($parents->length() == 1) { $query['pid'] = ModelId::fromModel($parents->get(0))->getSerialized(); } elseif ($parents->length() > 1) { return null; } } } /** @var GenerateHtmlEvent $imageEvent */ $imageEvent = $dispatcher->dispatch(ContaoEvents::IMAGE_GET_HTML, new GenerateHtmlEvent('edit.gif', $this->translate('editheader.0', $definition->getName()))); return sprintf('<a href="%s" title="%s" onclick="Backend.getScrollOffset()">%s</a>', 'contao/main.php?' . str_replace('%3A', ':', http_build_query($query)), specialchars($this->translate('editheader.1', $definition->getName())), $imageEvent->getHtml()); } return null; }
/** * Compile buttons from the table configuration array and return them as HTML. * * @param ModelInterface $model The model for which the buttons shall be generated for. * @param ModelInterface $previous The previous model in the collection. * @param ModelInterface $next The next model in the collection. * * @return string */ protected function generateButtons(ModelInterface $model, ModelInterface $previous = null, ModelInterface $next = null) { $environment = $this->getEnvironment(); $dataDefinition = $environment->getDataDefinition(); $basicDefinition = $dataDefinition->getBasicDefinition(); $commands = $this->getViewSection()->getModelCommands(); $clipboard = $environment->getClipboard(); $dispatcher = $environment->getEventDispatcher(); $filter = new Filter(); $filter->andModelIsFromProvider($basicDefinition->getDataProvider()); if ($parentDataProviderName = $basicDefinition->getParentDataProvider()) { $filter->andParentIsFromProvider($parentDataProviderName); } else { $filter->andHasNoParent(); } if ($clipboard->isNotEmpty($filter)) { $circularIds = $clipboard->getCircularIds(); $isCircular = in_array(ModelId::fromModel($model)->getSerialized(), $circularIds); } else { $circularIds = array(); $isCircular = false; } $arrButtons = array(); foreach ($commands->getCommands() as $command) { $arrButtons[$command->getName()] = $this->buildCommand($command, $model, $isCircular, $circularIds, $previous, $next); } if (ViewHelpers::getManualSortingProperty($this->environment)) { $clipboardIsEmpty = $clipboard->isEmpty($filter); if ($clipboardIsEmpty && BasicDefinitionInterface::MODE_HIERARCHICAL !== $basicDefinition->getMode()) { /** @var AddToUrlEvent $urlEvent */ $urlEvent = $dispatcher->dispatch(ContaoEvents::BACKEND_ADD_TO_URL, new AddToUrlEvent('act=create&after=' . ModelId::fromModel($model)->getSerialized())); /** @var GenerateHtmlEvent $imageEvent */ $imageEvent = $dispatcher->dispatch(ContaoEvents::IMAGE_GET_HTML, new GenerateHtmlEvent('new.gif', $this->translate('pastenew.0', $this->getDataDefinition()->getName()))); $arrButtons['pasteNew'] = sprintf('<a href="%s" title="%s" onclick="Backend.getScrollOffset()">%s</a>', $urlEvent->getUrl(), specialchars($this->translate('pastenew.1', $this->getDataDefinition()->getName())), $imageEvent->getHtml()); } // Add paste into/after icons. if (!$clipboardIsEmpty) { if ($clipboard->isCreate()) { // Add ext. information. $add2UrlAfter = sprintf('act=create&after=%s&', ModelId::fromModel($model)->getSerialized()); $add2UrlInto = sprintf('act=create&into=%s&', ModelId::fromModel($model)->getSerialized()); } else { // Add ext. information. $add2UrlAfter = sprintf('act=paste&after=%s&', ModelId::fromModel($model)->getSerialized()); $add2UrlInto = sprintf('act=paste&into=%s&', ModelId::fromModel($model)->getSerialized()); } /** @var AddToUrlEvent $urlAfter */ $urlAfter = $dispatcher->dispatch(ContaoEvents::BACKEND_ADD_TO_URL, new AddToUrlEvent($add2UrlAfter)); /** @var AddToUrlEvent $urlInto */ $urlInto = $dispatcher->dispatch(ContaoEvents::BACKEND_ADD_TO_URL, new AddToUrlEvent($add2UrlInto)); $models = $this->environment->getController()->getModelsFromClipboard($parentDataProviderName ? IdSerializer::fromValues($parentDataProviderName, null) : null); $buttonEvent = new GetPasteButtonEvent($this->getEnvironment()); $buttonEvent->setModel($model)->setCircularReference($isCircular)->setPrevious($previous)->setNext($next)->setHrefAfter($urlAfter->getUrl())->setHrefInto($urlInto->getUrl())->setPasteAfterDisabled($clipboard->isCut() && $isCircular)->setPasteIntoDisabled($clipboard->isCut() && $isCircular)->setContainedModels($models); $this->getEnvironment()->getEventDispatcher()->dispatch(GetPasteButtonEvent::NAME, $buttonEvent); $arrButtons['pasteafter'] = $this->renderPasteAfterButton($buttonEvent); if ($this->getDataDefinition()->getBasicDefinition()->getMode() == BasicDefinitionInterface::MODE_HIERARCHICAL) { $arrButtons['pasteinto'] = $this->renderPasteIntoButton($buttonEvent); } } } return implode(' ', $arrButtons); }
/** * Serialize a model and return its ID. * * @param ModelInterface $model The model. * * @return string */ public function serializeModelId(ModelInterface $model) { return ModelId::fromModel($model)->getSerialized(); }
/** * Delete all deep models. * * @param ModelIdInterface $modelId The Model Id. * * @return void * * @SuppressWarnings(PHPMD.LongVariableName) */ protected function deepDelete(ModelIdInterface $modelId) { $environment = $this->getEnvironment(); $dataDefinition = $environment->getDataDefinition(); /** @var DefaultModelRelationshipDefinition $relationships */ $relationships = $dataDefinition->getDefinition('model-relationships'); $childConditions = $relationships->getChildConditions($modelId->getDataProviderName()); // delete child element before delete parent element /** @var ParentChildConditionInterface $childCondition */ foreach ($childConditions as $childCondition) { $destinationChildConditions = $relationships->getChildConditions($childCondition->getDestinationName()); if (empty($destinationChildConditions)) { continue; } $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); $destinationChildDataProvider = $environment->getDataProvider($childCondition->getDestinationName()); $filters = $childCondition->getFilter($model); /** @var DefaultCollection $destinationChildModels */ $destinationChildModels = $destinationChildDataProvider->fetchAll($dataProvider->getEmptyConfig()->setFilter($filters)); if ($destinationChildModels->count() < 1) { continue; } foreach ($destinationChildModels as $destinationChildModel) { $this->deepDelete(ModelId::fromModel($destinationChildModel)); } } foreach ($childConditions as $childCondition) { $dataProvider = $environment->getDataProvider($modelId->getDataProviderName()); $model = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($modelId->getId())); $childDataProvider = $environment->getDataProvider($childCondition->getDestinationName()); $filters = $childCondition->getFilter($model); /** @var DefaultCollection $childModels */ $childModels = $childDataProvider->fetchAll($dataProvider->getEmptyConfig()->setFilter($filters)); if ($childModels->count() < 1) { continue; } foreach ($childModels as $childModel) { // Trigger event before the model will be deleted. $event = new PreDeleteModelEvent($this->getEnvironment(), $childModel); $environment->getEventDispatcher()->dispatch($event::NAME, $event); $childDataProvider->delete($childModel); // Trigger event after the model is deleted. $event = new PostDeleteModelEvent($environment, $childModel); $environment->getEventDispatcher()->dispatch($event::NAME, $event); } } }
/** * 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); } }