/** * Adds object $contentObject to the search database. * * @param \eZContentObject $contentObject Object to add to search engine * @param bool $commit Whether to commit after adding the object * * @return bool True if the operation succeeded. */ public function addObject($contentObject, $commit = true) { // Indexing is not implemented in eZ Publish 5 legacy search engine if ($this->searchHandler instanceof LegacyHandler) { $searchEngine = new eZSearchEngine(); $searchEngine->addObject($contentObject, $commit); return true; } try { // If the method is called for restoring from trash we'll be inside a transaction, // meaning created Location(s) will not be visible outside of it. // We check that Content's Locations are visible from the new stack, if not Content // will be registered for indexing. foreach ($contentObject->assignedNodes() as $node) { $this->persistenceHandler->locationHandler()->load($node->attribute('node_id')); } $content = $this->persistenceHandler->contentHandler()->load((int) $contentObject->attribute('id'), (int) $contentObject->attribute('current_version')); } catch (NotFoundException $e) { $pendingAction = new eZPendingActions(array('action' => 'index_object', 'created' => time(), 'param' => (int) $contentObject->attribute('id'))); $pendingAction->store(); return true; } $this->searchHandler->indexContent($content); if ($commit) { $this->commit(); } return true; }
/** * Adds object $contentObject to the search database. * * @param \eZContentObject $contentObject Object to add to search engine * @param bool $commit Whether to commit after adding the object * * @return bool True if the operation succeeded. */ public function addObject($contentObject, $commit = true) { // Indexing is not implemented in eZ Publish 5 legacy search engine if ($this->searchEngine == 'legacy') { $searchEngine = new eZSearchEngine(); $searchEngine->addObject($contentObject, $commit); } else { $content = $this->persistenceHandler->contentHandler()->load((int) $contentObject->attribute('id'), (int) $contentObject->attribute('current_version')); $this->searchHandler->indexContent($content); } if ($commit) { $this->commit(); } return true; }
/** * Removes a relation of type COMMON from a draft. * * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed edit this version * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if there is no relation of type COMMON for the given destination * * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $sourceVersion * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $destinationContent */ public function deleteRelation(APIVersionInfo $sourceVersion, ContentInfo $destinationContent) { $sourceVersion = $this->loadVersionInfoById($sourceVersion->contentInfo->id, $sourceVersion->versionNo); if ($sourceVersion->status !== APIVersionInfo::STATUS_DRAFT) { throw new BadStateException('$sourceVersion', 'Relations of type common can only be removed from versions of status draft'); } if (!$this->repository->canUser('content', 'edit', $sourceVersion)) { throw new UnauthorizedException('content', 'edit', array('contentId' => $sourceVersion->contentInfo->id)); } $spiRelations = $this->persistenceHandler->contentHandler()->loadRelations($sourceVersion->getContentInfo()->id, $sourceVersion->versionNo, APIRelation::COMMON); if (empty($spiRelations)) { throw new InvalidArgumentException('$sourceVersion', 'There are no relations of type COMMON for the given destination'); } // there should be only one relation of type COMMON for each destination, // but in case there were ever more then one, we will remove them all // @todo: alternatively, throw BadStateException? $this->repository->beginTransaction(); try { foreach ($spiRelations as $spiRelation) { if ($spiRelation->destinationContentId == $destinationContent->id) { $this->persistenceHandler->contentHandler()->removeRelation($spiRelation->id, APIRelation::COMMON); } } $this->repository->commit(); } catch (Exception $e) { $this->repository->rollback(); throw $e; } }
/** * Deletes $location and all its descendants. * * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user is not allowed to delete this location or a descendant * * @param \eZ\Publish\API\Repository\Values\Content\Location $location */ public function deleteLocation(APILocation $location) { $location = $this->loadLocation($location->id); if (!$this->repository->canUser('content', 'manage_locations', $location->getContentInfo())) { throw new UnauthorizedException('content', 'manage_locations'); } if (!$this->repository->canUser('content', 'remove', $location->getContentInfo(), $location)) { throw new UnauthorizedException('content', 'remove'); } /** Check remove access to descendants * @var boolean|\eZ\Publish\API\Repository\Values\Content\Query\Criterion $contentReadCriterion */ $contentReadCriterion = $this->permissionsCriterionHandler->getPermissionsCriterion('content', 'remove'); if ($contentReadCriterion === false) { throw new UnauthorizedException('content', 'remove'); } else { if ($contentReadCriterion !== true) { // Query if there are any content in subtree current user don't have access to $query = new Query(array('limit' => 0, 'filter' => new CriterionLogicalAnd(array(new CriterionSubtree($location->pathString), new CriterionLogicalNot($contentReadCriterion))))); $result = $this->repository->getSearchService()->findContent($query, array(), false); if ($result->totalCount > 0) { throw new UnauthorizedException('content', 'remove'); } } } $this->repository->beginTransaction(); try { $this->persistenceHandler->locationHandler()->removeSubtree($location->id); $this->persistenceHandler->urlAliasHandler()->locationDeleted($location->id); $this->repository->commit(); } catch (Exception $e) { $this->repository->rollback(); throw $e; } }
/** * Returns a collection of Trashed locations contained in the trash. * * $query allows to filter/sort the elements to be contained in the collection. * * @param \eZ\Publish\API\Repository\Values\Content\Query $query * * @return \eZ\Publish\API\Repository\Values\Content\SearchResult */ public function findTrashItems(Query $query) { if ($query->filter !== null && !$query->filter instanceof Criterion) { throw new InvalidArgumentValue("query->filter", $query->filter, "Query"); } if ($query->sortClauses !== null) { if (!is_array($query->sortClauses)) { throw new InvalidArgumentValue("query->sortClauses", $query->sortClauses, "Query"); } foreach ($query->sortClauses as $sortClause) { if (!$sortClause instanceof SortClause) { throw new InvalidArgumentValue("query->sortClauses", "only instances of SortClause class are allowed"); } } } if ($query->offset !== null && !is_numeric($query->offset)) { throw new InvalidArgumentValue("query->offset", $query->offset, "Query"); } if ($query->limit !== null && !is_numeric($query->limit)) { throw new InvalidArgumentValue("query->limit", $query->limit, "Query"); } $spiTrashItems = $this->persistenceHandler->trashHandler()->findTrashItems($query->filter !== null ? $query->filter : null, $query->offset !== null && $query->offset > 0 ? (int) $query->offset : 0, $query->limit !== null && $query->limit >= 1 ? (int) $query->limit : null, $query->sortClauses !== null ? $query->sortClauses : null); $trashItems = array(); foreach ($spiTrashItems as $spiTrashItem) { $trashItems[] = $this->buildDomainTrashItemObject($spiTrashItem); } $searchResult = new SearchResult(); $searchResult->count = count($trashItems); $searchResult->items = $trashItems; $searchResult->query = $query; return $searchResult; }
/** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { @trigger_error(sprintf('%s is deprecated since 6.7. Use ezplatform:reindex command instead', $this->getName()), E_USER_DEPRECATED); $bulkCount = $input->getArgument('bulk_count'); // Indexing Content $totalCount = $this->getContentObjectsTotalCount($this->databaseHandler, ContentInfo::STATUS_PUBLISHED); $query = $this->databaseHandler->createSelectQuery(); $query->select('id', 'current_version')->from('ezcontentobject')->where($query->expr->eq('status', ContentInfo::STATUS_PUBLISHED)); $stmt = $query->prepare(); $stmt->execute(); $this->searchHandler->purgeIndex(); $output->writeln('Indexing Content...'); /* @var \Symfony\Component\Console\Helper\ProgressHelper $progress */ $progress = $this->getHelperSet()->get('progress'); $progress->start($output, $totalCount); $i = 0; do { $contentObjects = []; for ($k = 0; $k <= $bulkCount; ++$k) { if (!($row = $stmt->fetch(PDO::FETCH_ASSOC))) { break; } try { $contentObjects[] = $this->persistenceHandler->contentHandler()->load($row['id'], $row['current_version']); } catch (NotFoundException $e) { $this->logWarning($output, $progress, "Could not load current version of Content with id {$row['id']}, so skipped for indexing. Full exception: " . $e->getMessage()); } } $this->searchHandler->bulkIndex($contentObjects, function (Content $content, NotFoundException $e) use($output, $progress) { $this->logWarning($output, $progress, 'Content with id ' . $content->versionInfo->id . ' has missing data, so skipped for indexing. Full exception: ' . $e->getMessage()); }); $progress->advance($k); } while (($i += $bulkCount) < $totalCount); $progress->finish(); }
/** * Returns a persistence Handler mock * * @return \eZ\Publish\SPI\Persistence\Handler|\PHPUnit_Framework_MockObject_MockObject */ protected function getPersistenceMock() { if ( !isset( $this->persistenceMock ) ) { $this->persistenceMock = $this->getMock( "eZ\\Publish\\SPI\\Persistence\\Handler", array(), array(), '', false ); $this->persistenceMock->expects( $this->any() ) ->method( 'contentHandler' ) ->will( $this->returnValue( $this->getPersistenceMockHandler( 'Content\\Handler' ) ) ); $this->persistenceMock->expects( $this->any() ) ->method( 'searchHandler' ) ->will( $this->returnValue( $this->getSPIMockHandler( 'Search\\Handler' ) ) ); $this->persistenceMock->expects( $this->any() ) ->method( 'contentTypeHandler' ) ->will( $this->returnValue( $this->getPersistenceMockHandler( 'Content\\Type\\Handler' ) ) ); $this->persistenceMock->expects( $this->any() ) ->method( 'contentLanguageHandler' ) ->will( $this->returnValue( $this->getPersistenceMockHandler( 'Content\\Language\\Handler' ) ) ); $this->persistenceMock->expects( $this->any() ) ->method( 'locationHandler' ) ->will( $this->returnValue( $this->getPersistenceMockHandler( 'Content\\Location\\Handler' ) ) ); $this->persistenceMock->expects( $this->any() ) ->method( 'objectStateHandler' ) ->will( $this->returnValue( $this->getPersistenceMockHandler( 'Content\\ObjectState\\Handler' ) ) ); $this->persistenceMock->expects( $this->any() ) ->method( 'trashHandler' ) ->will( $this->returnValue( $this->getPersistenceMockHandler( 'Content\\Location\\Trash\\Handler' ) ) ); $this->persistenceMock->expects( $this->any() ) ->method( 'userHandler' ) ->will( $this->returnValue( $this->getPersistenceMockHandler( 'User\\Handler' ) ) ); $this->persistenceMock->expects( $this->any() ) ->method( 'sectionHandler' ) ->will( $this->returnValue( $this->getPersistenceMockHandler( 'Content\\Section\\Handler' ) ) ); $this->persistenceMock->expects( $this->any() ) ->method( 'urlAliasHandler' ) ->will( $this->returnValue( $this->getPersistenceMockHandler( 'Content\\UrlAlias\\Handler' ) ) ); $this->persistenceMock->expects( $this->any() ) ->method( 'urlWildcardHandler' ) ->will( $this->returnValue( $this->getPersistenceMockHandler( 'Content\\UrlWildcard\\Handler' ) ) ); } return $this->persistenceMock; }
/** * Persists relation data for a content version. * * This method creates new relations and deletes removed relations. * * @param array $inputRelations * @param mixed $sourceContentId * @param mixed $sourceContentVersionNo * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType * @param \eZ\Publish\API\Repository\Values\Content\Relation[] $existingRelations An array of existing relations for Content version (empty when creating new content) * * @return void */ public function processFieldRelations(array $inputRelations, $sourceContentId, $sourceContentVersionNo, ContentType $contentType, array $existingRelations = array()) { // Map existing relations for easier handling $mappedRelations = array(); foreach ($existingRelations as $relation) { if ($relation->type === Relation::FIELD) { $fieldDefinitionId = $contentType->getFieldDefinition($relation->sourceFieldDefinitionIdentifier)->id; $mappedRelations[$relation->type][$fieldDefinitionId][$relation->destinationContentInfo->id] = $relation; } // Using bitwise AND as Legacy Stack stores COMMON, LINK and EMBED relation types // in the same entry using bitmask if ($relation->type & Relation::LINK) { $mappedRelations[Relation::LINK][$relation->destinationContentInfo->id] = $relation; } if ($relation->type & Relation::EMBED) { $mappedRelations[Relation::EMBED][$relation->destinationContentInfo->id] = $relation; } } // Add new relations foreach ($inputRelations as $relationType => $relationData) { if ($relationType === Relation::FIELD) { foreach ($relationData as $fieldDefinitionId => $contentIds) { foreach (array_keys($contentIds) as $destinationContentId) { if (isset($mappedRelations[$relationType][$fieldDefinitionId][$destinationContentId])) { unset($mappedRelations[$relationType][$fieldDefinitionId][$destinationContentId]); } else { $this->persistenceHandler->contentHandler()->addRelation(new SPIRelationCreateStruct(array("sourceContentId" => $sourceContentId, "sourceContentVersionNo" => $sourceContentVersionNo, "sourceFieldDefinitionId" => $fieldDefinitionId, "destinationContentId" => $destinationContentId, "type" => $relationType))); } } } } else { if ($relationType === Relation::LINK || $relationType === Relation::EMBED) { foreach (array_keys($relationData) as $destinationContentId) { if (isset($mappedRelations[$relationType][$destinationContentId])) { unset($mappedRelations[$relationType][$destinationContentId]); } else { $this->persistenceHandler->contentHandler()->addRelation(new SPIRelationCreateStruct(array("sourceContentId" => $sourceContentId, "sourceContentVersionNo" => $sourceContentVersionNo, "sourceFieldDefinitionId" => null, "destinationContentId" => $destinationContentId, "type" => $relationType))); } } } } } // Remove relations not present in input set foreach ($mappedRelations as $relationType => $relationData) { foreach ($relationData as $relationEntry) { switch ($relationType) { case Relation::FIELD: foreach ($relationEntry as $relation) { $this->persistenceHandler->contentHandler()->removeRelation($relation->id, $relationType); } break; case Relation::LINK: case Relation::EMBED: $this->persistenceHandler->contentHandler()->removeRelation($relationEntry->id, $relationType); } } } }
/** * Rollback transaction * * Rollback transaction, or throw exceptions if no transactions has been started. * * @throws RuntimeException If no transaction has been started */ public function rollback() { try { $this->persistenceHandler->rollback(); --$this->transactionDepth; unset($this->commitEventsQueue[$this->transactionCount]); } catch (Exception $e) { throw new RuntimeException($e->getMessage(), 0, $e); } }
/** * @return \eZ\Publish\SPI\Persistence\Content\UrlWildcard\Handler * @todo Create cache implementation so we can avoid injecting persistenceHandler and logger */ public function urlWildcardHandler() { $this->logger->logUnCachedHandler(__METHOD__); return $this->persistenceHandler->urlWildcardHandler(); }
/** * Test that instance is of correct type * * @covers eZ\Publish\Core\Persistence\Factory::getUrlWildcardHandler * @depends testGetPersistenceHandler */ public function testGetUrlWildcardHandler() { $this->persistenceMock->expects($this->once())->method('urlWildcardHandler'); $this->persistenceFactory->getUrlWildcardHandler(); }