/* * Copyright 2000-2013 Enonic AS * http://www.enonic.com/license */ package com.enonic.cms.core.structure.menuitem; import java.util.HashSet; import java.util.List; import java.util.Set; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import com.google.common.base.Preconditions; import com.enonic.cms.core.content.ContentEntity; import com.enonic.cms.core.content.ContentKey; import com.enonic.cms.core.content.category.CategoryAccessException; import com.enonic.cms.core.content.category.CategoryAccessResolver; import com.enonic.cms.core.content.category.CategoryAccessType; import com.enonic.cms.core.search.IndexTransactionService; import com.enonic.cms.core.security.user.UserEntity; import com.enonic.cms.core.security.user.UserKey; import com.enonic.cms.core.structure.menuitem.section.SectionContentEntity; import com.enonic.cms.core.structure.page.template.PageTemplateEntity; import com.enonic.cms.core.time.TimeService; import com.enonic.cms.store.dao.ContentDao; import com.enonic.cms.store.dao.ContentHomeDao; import com.enonic.cms.store.dao.GroupDao; import com.enonic.cms.store.dao.MenuItemDao; import com.enonic.cms.store.dao.PageTemplateDao; import com.enonic.cms.store.dao.SectionContentDao; import com.enonic.cms.store.dao.UserDao; @Service("menuItemService") public class MenuItemServiceImpl implements MenuItemService { private final static int ORDER_SPACE = 1000; @Autowired private GroupDao groupDao; @Autowired private MenuItemDao menuItemDao; @Autowired private SectionContentDao sectionContentDao; @Autowired private PageTemplateDao pageTemplateDao; @Autowired private ContentHomeDao contentHomeDao; @Autowired private ContentDao contentDao; @Autowired private UserDao userDao; private TimeService timeService; @Autowired private IndexTransactionService indexTransactionService; @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) public void execute( MenuItemServiceCommand... commands ) { indexTransactionService.startTransaction(); for ( final MenuItemServiceCommand command : commands ) { if ( command instanceof RemoveContentsFromSectionCommand ) { doExecuteRemoveContentsFromSectionCommand( (RemoveContentsFromSectionCommand) command ); } else if ( command instanceof SetContentHomeCommand ) { doExecuteSetContentHomeCommand( (SetContentHomeCommand) command ); } else if ( command instanceof AddContentToSectionCommand ) { doExecuteAddContentToSectionCommand( (AddContentToSectionCommand) command ); } else if ( command instanceof ApproveContentInSectionCommand ) { doExecuteApproveContentInSectionCommand( (ApproveContentInSectionCommand) command ); } else if ( command instanceof ApproveContentsInSectionCommand ) { doExecuteApproveContentsInSectionCommand( (ApproveContentsInSectionCommand) command ); } else if ( command instanceof UnapproveContentsInSectionCommand ) { doExecuteUnapproveContentsInSectionCommand( (UnapproveContentsInSectionCommand) command ); } else if ( command instanceof OrderContentsInSectionCommand ) { doExecuteOrderContentsInSectionCommand( (OrderContentsInSectionCommand) command ); } else { throw new UnsupportedOperationException( "Unsupported menu-item service command: " + command.getClass().getName() ); } sectionContentDao.getHibernateTemplate().flush(); } } private void doExecuteOrderContentsInSectionCommand( final OrderContentsInSectionCommand command ) { Preconditions.checkNotNull( command.getSectionKey(), "section key cannot be null" ); Preconditions.checkNotNull( command.getWantedOrder(), "wanted order cannot be null" ); final MenuItemEntity section = menuItemDao.findByKey( command.getSectionKey() ); final ContentsInSectionOrderer orderer = new ContentsInSectionOrderer( command.getWantedOrder(), section, ORDER_SPACE ); final Set<SectionContentEntity> orderedSectionContents = orderer.order(); doRegisterSectionContentForIndexUpdate( orderedSectionContents ); } private void doExecuteSetContentHomeCommand( final SetContentHomeCommand command ) { Preconditions.checkNotNull( command.getSetter(), "setter cannot be null" ); Preconditions.checkNotNull( command.getContent(), "content to set home for cannot be null" ); Preconditions.checkNotNull( command.getSection(), "section to set home to cannot be null" ); final UserEntity setter = doResolveUser( command.getSetter(), "setter" ); final ContentEntity content = contentDao.findByKey( command.getContent() ); final MenuItemEntity section = doResolveSection( command.getSection() ); final CategoryAccessResolver categoryAccessResolver = new CategoryAccessResolver( groupDao ); if ( !categoryAccessResolver.hasApproveContentAccess( setter, content.getCategory() ) ) { throw new CategoryAccessException( "Cannot set content home", setter.getQualifiedName(), CategoryAccessType.APPROVE, content.getCategory().getKey() ); } PageTemplateEntity pageTemplate = null; if ( command.getPageTemplate() != null ) { pageTemplate = pageTemplateDao.findByKey( command.getPageTemplate().toInt() ); } doSetContentHome( content, section, pageTemplate ); } private void doSetContentHome( final ContentEntity content, final MenuItemEntity section, final PageTemplateEntity pageTemplate ) { final ContentHomeEntity existingHome = content.getContentHome( section.getSite().getKey() ); if ( existingHome != null ) { existingHome.setMenuItem( section ); existingHome.setPageTemplate( pageTemplate ); } else { final ContentHomeEntity newContentHome = new ContentHomeEntity(); newContentHome.setKey( new ContentHomeKey( section.getSite().getKey(), content.getKey() ) ); newContentHome.setSite( section.getSite() ); newContentHome.setContent( content ); newContentHome.setMenuItem( section ); newContentHome.setPageTemplate( pageTemplate ); contentHomeDao.storeNew( newContentHome ); content.addContentHome( newContentHome ); } indexTransactionService.registerUpdate( content.getKey(), true ); } private void doExecuteAddContentToSectionCommand( final AddContentToSectionCommand command ) { Preconditions.checkNotNull( command, "a command is required" ); Preconditions.checkNotNull( command.getContributor(), "the command's contributor argument is required" ); Preconditions.checkNotNull( command.getContent(), "the command's content argument is required" ); Preconditions.checkNotNull( command.getSection(), "the command's section argument is required" ); if ( !command.isApproveInSection() ) { Preconditions.checkArgument( !command.isAddOnTop(), "no point in adding content to top when not approving" ); } final ContentEntity content = contentDao.findByKey( command.getContent() ); Preconditions.checkNotNull( content, "content does not exist: " + command.getContent() ); Preconditions.checkArgument( !content.isDeleted(), "content is deleted: " + command.getContent() ); final MenuItemEntity section = doResolveSection( command.getSection() ); if ( !section.isOrderedSection() ) { Preconditions.checkArgument( command.getOrderContentsInSectionCommand() == null, "section is not ordered, did not expect to get order specified in command" ); } final UserEntity contributor = doResolveUser( command.getContributor(), "contributor" ); final MenuItemAccessResolver menuItemAccessResolver = new MenuItemAccessResolver( groupDao ); menuItemAccessResolver.checkAccessToAddContentToSection( contributor, section, "Cannot add content in section." ); if ( command.isApproveInSection() ) { menuItemAccessResolver.checkAccessToApproveContentInSection( contributor, section, "Cannot approve content in section." ); } if ( section.isSection() && section.hasSectionContentTypeFilter() ) { if ( !section.supportsSectionContentType( content.getCategory().getContentType() ) ) { throw new ContentTypeNotSupportedException( content.getCategory().getContentType(), section ); } } else if ( section.getType() == MenuItemType.PAGE && section.getPage().getTemplate().getContentTypes().size() > 0 ) { if ( !section.getPage().getTemplate().supportsContentType( content.getCategory().getContentType() ) ) { throw new ContentTypeNotSupportedException( content.getCategory().getContentType(), section ); } } final SectionContentEntity sectionContent = new SectionContentEntity(); sectionContent.setOrder( 0 ); sectionContent.setContent( content ); sectionContent.setMenuItem( section ); sectionContent.setApproved( command.isApproveInSection() ); sectionContent.setTimestamp( timeService.getNowAsDateTime().toDate() ); if ( command.isAddOnTop() && section.isOrderedSection() ) { sectionContent.setOrder( resolveOrderValueForInsertOnTopOfApprovedContentInSection( section ) ); } content.addSectionContent( sectionContent ); sectionContentDao.getHibernateTemplate().flush(); sectionContentDao.getHibernateTemplate().getSessionFactory().evictCollection( MenuItemEntity.class.getName() + ".sectionContents", section.getKey() ); if ( !content.hasHome( section.getSite().getKey() ) ) { doSetContentHome( content, section, null ); } if ( section.isOrderedSection() ) { if ( command.getOrderContentsInSectionCommand() != null ) { // ensure section will have it's newly added content sectionContentDao.getHibernateTemplate().refresh( section ); final List<ContentKey> wantedOrder = command.getOrderContentsInSectionCommand().getWantedOrder(); final ContentsInSectionOrderer orderer = new ContentsInSectionOrderer( wantedOrder, section, ORDER_SPACE ); final Set<SectionContentEntity> sectionContentsWithUpdatedOrder = orderer.order(); doRegisterSectionContentForIndexUpdate( sectionContentsWithUpdatedOrder ); } } indexTransactionService.registerUpdate( command.getContent(), true ); } private void doExecuteRemoveContentsFromSectionCommand( final RemoveContentsFromSectionCommand command ) { Preconditions.checkNotNull( command.getSection(), "section cannot be null" ); Preconditions.checkNotNull( command.getRemover(), "remover cannot be null" ); Preconditions.checkNotNull( command.getContentsToRemove(), "content to remove cannot be null" ); final UserEntity remover = userDao.findByKey( command.getRemover() ); final MenuItemEntity section = doResolveSection( command.getSection() ); final MenuItemAccessResolver menuItemAccessResolver = new MenuItemAccessResolver( groupDao ); for ( ContentKey contentKey : command.getContentsToRemove() ) { final SectionContentEntity sectionContentToRemove = section.getSectionContent( contentKey ); Preconditions.checkNotNull( sectionContentToRemove, "content in section (" + section.getKey() + ") not found: " + contentKey ); final boolean contentIsApprovedInSection = sectionContentToRemove.isApproved(); if ( contentIsApprovedInSection ) { menuItemAccessResolver.checkAccessToUnapproveContentInSection( remover, section, "Cannot remove approved content from section." ); } else { menuItemAccessResolver.checkAccessToRemoveUnapprovedContentFromSection( remover, section, "Cannot remove unapproved content from section." ); } final ContentEntity content = contentDao.findByKey( contentKey ); content.removeSectionContent( command.getSection() ); sectionContentDao.getHibernateTemplate().flush(); sectionContentDao.getHibernateTemplate().getSessionFactory().evictCollection( MenuItemEntity.class.getName() + ".sectionContents", section.getKey() ); removeContentHomeIfThisSectionIs( content, section ); indexTransactionService.registerUpdate( content.getKey(), true ); } } private void doExecuteApproveContentInSectionCommand( final ApproveContentInSectionCommand command ) { Preconditions.checkNotNull( command.getSection(), "section cannot be null" ); Preconditions.checkNotNull( command.getApprover(), "approver cannot be null" ); Preconditions.checkNotNull( command.getContentToApprove(), "content to approve cannot be null" ); final MenuItemEntity section = doResolveSection( command.getSection() ); final UserEntity approver = doResolveUser( command.getApprover(), "approver" ); new MenuItemAccessResolver( groupDao ).checkAccessToApproveContentInSection( approver, section, "Cannot approve content in section." ); final SectionContentEntity sectionContent = section.getSectionContent( command.getContentToApprove() ); boolean changed = false; if ( !sectionContent.isApproved() ) { sectionContent.setApproved( true ); changed = true; } final int newOrder = resolveOrderValueForInsertOnTopOfApprovedContentInSection( section ); if ( sectionContent.getOrder() != newOrder ) { sectionContent.setOrder( newOrder ); changed = true; } if ( changed ) { sectionContent.setTimestamp( timeService.getNowAsDateTime().toDate() ); indexTransactionService.registerUpdate( command.getContentToApprove(), true ); } } private void doExecuteApproveContentsInSectionCommand( final ApproveContentsInSectionCommand command ) { Preconditions.checkNotNull( command.getSection(), "section cannot be null" ); Preconditions.checkNotNull( command.getApprover(), "approver cannot be null" ); Preconditions.checkNotNull( command.getContentsToApprove().size(), "no given content to approve in section" ); final MenuItemEntity section = doResolveSection( command.getSection() ); final UserEntity approver = doResolveUser( command.getApprover(), "approver" ); new MenuItemAccessResolver( groupDao ).checkAccessToApproveContentInSection( approver, section, "Cannot approve content in section." ); final Set<SectionContentEntity> changedSectionContents = new HashSet<SectionContentEntity>(); for ( ContentKey contentKey : command.getContentsToApprove() ) { final SectionContentEntity sectionContent = section.getSectionContent( contentKey ); if ( !sectionContent.isApproved() ) { sectionContent.setApproved( true ); changedSectionContents.add( sectionContent ); } } // handle re-order command if ( command.getOrderContentsInSectionCommand() != null ) { if ( section.isOrderedSection() ) { changedSectionContents.addAll( new ContentsInSectionOrderer( command.getOrderContentsInSectionCommand().getWantedOrder(), section, ORDER_SPACE ).order() ); } } // update timestamp of only those who have changed for ( SectionContentEntity changedSectionContent : changedSectionContents ) { changedSectionContent.setTimestamp( timeService.getNowAsDateTime().toDate() ); indexTransactionService.registerUpdate( changedSectionContent.getContent().getKey(), true ); } for ( ContentKey contentKey : command.getContentsToApprove() ) { indexTransactionService.registerUpdate( contentKey, true ); } } private void doExecuteUnapproveContentsInSectionCommand( final UnapproveContentsInSectionCommand command ) { Preconditions.checkNotNull( command.getSection(), "section cannot be null" ); Preconditions.checkNotNull( command.getUnapprover(), "unapprover cannot be null" ); Preconditions.checkNotNull( command.getContentToUnapprove().size(), "no given content to unapprove in section" ); final UserEntity updater = doResolveUser( command.getUnapprover(), "unapprover" ); final MenuItemEntity section = doResolveSection( command.getSection() ); new MenuItemAccessResolver( groupDao ).checkAccessToUnapproveContentInSection( updater, section, "Cannot unapprove section content." ); for ( ContentKey contentKey : command.getContentToUnapprove() ) { final SectionContentEntity sectionContent = section.getSectionContent( contentKey ); if ( sectionContent == null ) { continue; } doUnapproveContentInSection( sectionContent ); indexTransactionService.registerUpdate( contentKey, true ); } } private int resolveOrderValueForInsertOnTopOfApprovedContentInSection( final MenuItemEntity section ) { if ( section.getSectionContents().size() == 0 ) { return 0; } int lowestOrderValue = Integer.MAX_VALUE; for ( SectionContentEntity sectionContent : section.getSectionContents() ) { if ( sectionContent.isApproved() && sectionContent.getOrder() < lowestOrderValue ) { lowestOrderValue = sectionContent.getOrder(); } } return lowestOrderValue - ORDER_SPACE; } private MenuItemEntity doResolveSection( final MenuItemKey sectionKey ) { final MenuItemEntity section = menuItemDao.findByKey( sectionKey ); Preconditions.checkNotNull( section, "section does not exist: " + sectionKey ); Preconditions.checkArgument( section.isSection(), "menu item is not a section:" + sectionKey ); return section; } private UserEntity doResolveUser( final UserKey userKey, final String subject ) { final UserEntity user = userDao.findByKey( userKey ); Preconditions.checkNotNull( user, subject + " does not exist: " + userKey ); return user; } private void doUnapproveContentInSection( final SectionContentEntity sectionContent ) { boolean changed = false; if ( sectionContent.isApproved() ) { sectionContent.setApproved( false ); changed = true; } if ( sectionContent.getOrder() != 0 ) { sectionContent.setOrder( 0 ); changed = true; } if ( changed ) { sectionContent.setTimestamp( timeService.getNowAsDateTime().toDate() ); } } private void removeContentHomeIfThisSectionIs( final ContentEntity content, final MenuItemEntity section ) { final ContentHomeEntity contentHome = content.getContentHome( section.getSite().getKey() ); if ( contentHome != null && contentHome.getMenuItem() == null ) { content.removeContentHome( section.getSite().getKey() ); contentHomeDao.delete( contentHome ); } else if ( contentHome != null && section.getKey() == contentHome.getMenuItem().getKey() ) { content.removeContentHome( section.getSite().getKey() ); contentHomeDao.delete( contentHome ); } contentHomeDao.getHibernateTemplate().getSessionFactory().evictCollection( ContentEntity.class.getName() + ".contentHomes", content.getKey() ); } private void doRegisterSectionContentForIndexUpdate( Iterable<SectionContentEntity> sectionContents ) { for ( SectionContentEntity sectionContentWithUpdatedOrder : sectionContents ) { indexTransactionService.registerUpdate( sectionContentWithUpdatedOrder.getContent().getKey(), true ); } } @Autowired public void setTimeService( TimeService timeService ) { this.timeService = timeService; } }