/*
* Copyright 2000-2013 Enonic AS
* http://www.enonic.com/license
*/
package com.enonic.cms.server.service.admin.ajax;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import javax.servlet.http.HttpSession;
import javax.xml.transform.URIResolver;
import javax.xml.transform.dom.DOMSource;
import org.apache.commons.lang.StringUtils;
import org.directwebremoting.annotations.RemoteMethod;
import org.directwebremoting.annotations.RemoteProxy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import com.enonic.esl.xml.XMLTool;
import com.enonic.vertical.adminweb.AdminStore;
import com.enonic.vertical.adminweb.VerticalAdminLogger;
import com.enonic.cms.framework.xml.XMLDocument;
import com.enonic.cms.core.content.ContentEntity;
import com.enonic.cms.core.content.ContentKey;
import com.enonic.cms.core.content.ContentService;
import com.enonic.cms.core.content.ContentVersionEntity;
import com.enonic.cms.core.content.ContentVersionKey;
import com.enonic.cms.core.content.ContentXMLCreator;
import com.enonic.cms.core.content.access.ContentAccessResolver;
import com.enonic.cms.core.content.query.ContentByContentQuery;
import com.enonic.cms.core.content.query.RelatedContentQuery;
import com.enonic.cms.core.content.resultset.ContentResultSet;
import com.enonic.cms.core.content.resultset.RelatedContentResultSet;
import com.enonic.cms.core.country.Country;
import com.enonic.cms.core.country.CountryCode;
import com.enonic.cms.core.country.CountryService;
import com.enonic.cms.core.country.Region;
import com.enonic.cms.core.preference.PreferenceEntity;
import com.enonic.cms.core.preference.PreferenceSpecification;
import com.enonic.cms.core.security.SecurityService;
import com.enonic.cms.core.security.group.GroupKey;
import com.enonic.cms.core.security.user.UserEntity;
import com.enonic.cms.core.security.user.UserKey;
import com.enonic.cms.core.security.userstore.MemberOfResolver;
import com.enonic.cms.core.security.userstore.UserStoreKey;
import com.enonic.cms.core.security.userstore.connector.synchronize.SynchronizeUserStoreJobFactory;
import com.enonic.cms.core.service.AdminService;
import com.enonic.cms.core.servlet.ServletRequestAccessor;
import com.enonic.cms.core.structure.SiteKey;
import com.enonic.cms.core.structure.menuitem.MenuItemEntity;
import com.enonic.cms.core.structure.menuitem.MenuItemKey;
import com.enonic.cms.core.structure.menuitem.MenuItemSpecification;
import com.enonic.cms.core.xslt.XsltProcessor;
import com.enonic.cms.core.xslt.XsltProcessorException;
import com.enonic.cms.core.xslt.XsltResource;
import com.enonic.cms.core.xslt.admin.AdminXsltProcessor;
import com.enonic.cms.core.xslt.admin.AdminXsltProcessorFactory;
import com.enonic.cms.server.service.admin.ajax.dto.PreferenceDto;
import com.enonic.cms.server.service.admin.ajax.dto.RegionDto;
import com.enonic.cms.server.service.admin.ajax.dto.SynchronizeStatusDto;
import com.enonic.cms.server.service.admin.ajax.dto.UserDto;
import com.enonic.cms.store.dao.ContentDao;
import com.enonic.cms.store.dao.GroupDao;
import com.enonic.cms.store.dao.MenuItemDao;
import com.enonic.cms.store.dao.PortletDao;
import com.enonic.cms.store.dao.PreferenceDao;
import com.enonic.cms.store.dao.SiteDao;
import com.enonic.cms.store.dao.UserDao;
@RemoteProxy(name = "AjaxService", creator = AdminAjaxServiceCreator.class)
public class AdminAjaxServiceImpl
implements AdminAjaxService, InitializingBean
{
private static final Logger LOG = LoggerFactory.getLogger( AdminAjaxServiceImpl.class );
private static final String STRING_EMPTY_RESULT_RETURN_VALUE = null;
@Autowired
private AdminService adminService;
@Autowired
private ContentService contentService;
@Autowired
private SecurityService securityService;
@Autowired
private MemberOfResolver memberOfResolver;
@Autowired
private CountryService countryService;
@Autowired
private SynchronizeUserStoreJobFactory syncUserStoreJobFactory;
@Autowired
private MenuItemDao menuItemDao;
@Autowired
private ContentDao contentDao;
@Autowired
private UserDao userDao;
@Autowired
private GroupDao groupDao;
@Autowired
private PortletDao portletDao;
@Autowired
private PreferenceDao preferenceDao;
@Autowired
private SiteDao siteDao;
@Autowired
private AdminXsltProcessorFactory xsltProcessorFactory;
private SyncUserStoreExecutorManager syncUserStoreExecutorManager;
public void afterPropertiesSet()
throws Exception
{
this.syncUserStoreExecutorManager = new SyncUserStoreExecutorManager( this.syncUserStoreJobFactory );
}
@RemoteMethod
public String deleteContentVersion( int versionKey )
{
ensureUserIsLoggedIn();
UserEntity deleter = getLoggedInAdminConsoleUser();
try
{
contentService.deleteVersion( deleter, new ContentVersionKey( versionKey ) );
}
catch ( Exception e )
{
LOG.warn( "ERROR: " + e.getMessage(), e );
return "An error occured while deleting content version: " + e.getLocalizedMessage();
}
return null;
}
@RemoteMethod
public String getArchiveSizeByCategory( int categoryKey )
{
ensureUserHasEnterpriseAdministratorPowers();
try
{
return String.valueOf( adminService.getArchiveSizeByCategory( categoryKey ) );
}
catch ( Exception e )
{
LOG.warn( "ERROR: " + e.getMessage(), e );
return "ERROR: " + e.getMessage();
}
}
@RemoteMethod
public String getArchiveSizeByUnit( int unitKey )
{
ensureUserHasEnterpriseAdministratorPowers();
try
{
return String.valueOf( adminService.getArchiveSizeByUnit( unitKey ) );
}
catch ( Exception e )
{
LOG.warn( "ERROR: " + e.getMessage(), e );
return "ERROR: " + e.getMessage();
}
}
@RemoteMethod
public boolean isContentInUse( String[] contentkeys )
{
ensureUserIsLoggedIn();
return contentService.isContentInUse( ContentKey.convertToList( contentkeys ) );
}
@RemoteMethod
public String getContentUsedByAsHtml( int contentKey )
{
ensureUserIsLoggedIn();
UserEntity user = getLoggedInAdminConsoleUser();
try
{
final Date now = new Date();
List<ContentKey> contentKeyList = new ArrayList<ContentKey>();
contentKeyList.add( new ContentKey( contentKey ) );
ContentByContentQuery contentByContentQuery = new ContentByContentQuery();
contentByContentQuery.setContentKeyFilter( contentKeyList );
contentByContentQuery.setCount( contentKeyList.size() );
contentByContentQuery.setUser( user );
ContentResultSet contentResultSet = contentService.queryContent( contentByContentQuery );
if ( contentResultSet.getLength() == 1 )
{
RelatedContentQuery relatedContentQuery = new RelatedContentQuery( now );
relatedContentQuery.setUser( user );
relatedContentQuery.setContentResultSet( contentResultSet );
relatedContentQuery.setParentLevel( 1 );
relatedContentQuery.setChildrenLevel( 0 );
relatedContentQuery.setParentChildrenLevel( 0 );
relatedContentQuery.setIncludeOnlyMainVersions( true );
relatedContentQuery.setFilterIncludeOfflineContent();
RelatedContentResultSet relatedContents = contentService.queryRelatedContent( relatedContentQuery );
ContentXMLCreator contentXMLCreator = new ContentXMLCreator();
contentXMLCreator.setIncludeRelatedContentsInfo( true );
contentXMLCreator.setIncludeRepositoryPathInfo( true );
ContentVersionEntity versionEntity = contentResultSet.getContent( 0 ).getMainVersion();
XMLDocument xmlDoc = contentXMLCreator.createContentsDocument( user, versionEntity, relatedContents );
return transformXML( xmlDoc.getAsDOMDocument(), "ajax_get_used_by_for_content.xsl" );
}
//return something if content is not found?
return "";
}
catch ( Exception e )
{
LOG.warn( "ERROR: " + e.getMessage(), e );
return "ERROR: " + e.getMessage();
}
}
@RemoteMethod
public String getPortletUsedByAsHtml( int portletKey )
{
ensureUserIsLoggedIn();
UserEntity user = getLoggedInAdminConsoleUser();
try
{
final XMLDocument menuItemsByContentObject = adminService.getMenuItemsByContentObject( user, portletKey );
final Document menuItemsDoc = menuItemsByContentObject.getAsDOMDocument();
final Element[] menuitems = XMLTool.getElements( menuItemsDoc, "menuitem" );
for ( Element menuitem : menuitems )
{
final String key = menuitem.getAttribute( "key" );
MenuItemEntity selectedMenuItem = menuItemDao.findByKey( Integer.parseInt( key ) );
menuitem.setAttribute( "path-to-menu", selectedMenuItem.getPathAsString() );
}
return transformXML( menuItemsDoc, "ajax_get_used_by_for_portlet.xsl" );
}
catch ( Exception e )
{
LOG.error( "ERROR: " + e.getMessage(), e );
return "ERROR: " + e.getMessage();
}
}
@RemoteMethod
public String getPageTemplateUsedByAsHtml( int pageTemplateKey )
{
ensureUserIsLoggedIn();
UserEntity user = getLoggedInAdminConsoleUser();
try
{
final XMLDocument menuItemsByPageTemplates = adminService.getMenuItemsByPageTemplates( user, new int[]{pageTemplateKey} );
final Document menuItemsDoc = menuItemsByPageTemplates.getAsDOMDocument();
final Element[] menuitems = XMLTool.getElements( menuItemsDoc, "menuitem" );
for ( Element menuitem : menuitems )
{
final String key = menuitem.getAttribute( "key" );
MenuItemEntity selectedMenuItem = menuItemDao.findByKey( Integer.parseInt( key ) );
menuitem.setAttribute( "path-to-menu", selectedMenuItem.getPathAsString() );
}
return transformXML( menuItemsDoc, "ajax_get_used_by_for_pagetemplate.xsl" );
}
catch ( Exception e )
{
LOG.error( "ERROR: " + e.getMessage(), e );
return "ERROR: " + e.getMessage();
}
}
private String transformXML( Document doc, String xslPath )
{
try
{
HttpSession session = ServletRequestAccessor.getSession();
if ( session == null )
{
VerticalAdminLogger.errorAdmin( "Http session is null" );
return "ERROR: Http session is null";
}
String languageCode = (String) session.getAttribute( "languageCode" );
XMLDocument xslDoc = AdminStore.getStylesheetAsDocument( languageCode, xslPath );
URIResolver uriResolver = AdminStore.getURIResolver( languageCode );
XsltProcessor processor = createProcessor( xslDoc.getSystemId(), xslDoc, uriResolver );
return processor.process( new DOMSource( doc ) );
}
catch ( XsltProcessorException xpe )
{
String msg = "Failed to transform xml: %t";
VerticalAdminLogger.errorAdmin( msg, xpe );
return msg;
}
}
private XsltProcessor createProcessor( String name, XMLDocument xslt, URIResolver uriResolver )
throws XsltProcessorException
{
XsltResource resource = new XsltResource( name, xslt.getAsString() );
AdminXsltProcessor processor = xsltProcessorFactory.createProcessor( resource, uriResolver );
processor.setOmitXmlDecl( true );
return processor;
}
private UserEntity getLoggedInAdminConsoleUser()
{
return securityService.getLoggedInAdminConsoleUserAsEntity();
}
private void ensureUserIsLoggedIn()
{
UserEntity user = getLoggedInAdminConsoleUser();
if ( user == null || user.isAnonymous() )
{
throw new IllegalStateException( "User not logged in" );
}
}
private void ensureUserHasEnterpriseAdministratorPowers()
{
UserEntity user = getLoggedInAdminConsoleUser();
if ( user == null )
{
throw new IllegalStateException( "User is not logged in" );
}
else if ( !memberOfResolver.hasEnterpriseAdminPowers( user ) )
{
throw new IllegalStateException( "User is not Administrator" );
}
}
private void ensureUserStoreAdministratorPowers( UserStoreKey userStoreKey )
{
UserEntity user = getLoggedInAdminConsoleUser();
if ( user == null )
{
throw new IllegalStateException( "User is not logged in" );
}
else if ( !memberOfResolver.hasUserStoreAdministratorPowers( getLoggedInAdminConsoleUser(), userStoreKey ) )
{
throw new IllegalStateException( "User is not User Store Administrator" );
}
}
@RemoteMethod
public Collection<RegionDto> getCountryRegions( final String countryCode )
{
ensureUserIsLoggedIn();
final CountryCode code = new CountryCode( countryCode );
final Country country = countryService.getCountry( code );
final ArrayList<RegionDto> list = new ArrayList<RegionDto>();
for ( final Region region : country.getRegions() )
{
final RegionDto dto = new RegionDto();
dto.setCode( region.getCode() );
dto.setEnglishName( region.getEnglishName() );
dto.setLocalName( region.getLocalName() );
list.add( dto );
}
return list;
}
@RemoteMethod
public boolean startSyncUserStore( String userStoreKey, boolean users, boolean groups, int batchSize )
{
ensureUserStoreAdministratorPowers( new UserStoreKey( userStoreKey ) );
return this.syncUserStoreExecutorManager.start( userStoreKey, users, groups, batchSize );
}
@RemoteMethod
public SynchronizeStatusDto getSynchUserStoreStatus( String userStoreKey )
{
if ( !StringUtils.isEmpty( userStoreKey ) )
{
ensureUserStoreAdministratorPowers( new UserStoreKey( userStoreKey ) );
String languageCode = (String) ServletRequestAccessor.getSession().getAttribute( "languageCode" );
return this.syncUserStoreExecutorManager.getStatus( userStoreKey, languageCode );
}
else
{
// to prevent client-side java script to possibly fail
return new SynchronizeStatusDto( "" );
}
}
@RemoteMethod
public boolean menuItemNameExistsUnderParent( int siteKeyInt, String menuItemName, int existingMenuItemKeyInt, int parentKey )
{
ensureUserIsLoggedIn();
final SiteKey siteKey = new SiteKey( siteKeyInt );
final MenuItemSpecification menuItemSpec = new MenuItemSpecification();
menuItemSpec.setSiteKey( siteKey );
menuItemSpec.setMenuItemName( menuItemName );
if ( parentKey >= 0 )
{
menuItemSpec.setParentKey( new MenuItemKey( parentKey ) );
}
else
{
menuItemSpec.setRootLevelOnly( true );
}
final List<MenuItemEntity> foundMenuItems = menuItemDao.findBySpecification( menuItemSpec );
if ( foundMenuItems.size() > 1 )
{
return true;
}
if ( foundMenuItems.size() == 1 )
{
final MenuItemKey existingMenuItemKey = new MenuItemKey( existingMenuItemKeyInt );
if ( menuItemNameExistsButItsMeSoItsOk( existingMenuItemKey, foundMenuItems ) )
{
return false;
}
else
{
return true;
}
}
return false;
}
private boolean menuItemNameExistsButItsMeSoItsOk( MenuItemKey existingMenuItemKey, List<MenuItemEntity> foundMenuItems )
{
if ( foundMenuItems.get( 0 ) != null && foundMenuItems.get( 0 ).getKey().equals( existingMenuItemKey ) )
{
return true;
}
return false;
}
@RemoteMethod
public String getContentPath( int contentKey )
{
ensureUserIsLoggedIn();
if ( contentKey == -1 )
{
return STRING_EMPTY_RESULT_RETURN_VALUE;
}
ContentEntity content = contentDao.findByKey( new ContentKey( contentKey ) );
if ( content == null )
{
return STRING_EMPTY_RESULT_RETURN_VALUE;
}
return content.getPathAsString();
}
@RemoteMethod
public String getPagePath( int menuItemKey )
{
ensureUserIsLoggedIn();
if ( menuItemKey == -1 )
{
return STRING_EMPTY_RESULT_RETURN_VALUE;
}
MenuItemEntity menuItem = menuItemDao.findByKey( menuItemKey );
if ( menuItem == null )
{
return STRING_EMPTY_RESULT_RETURN_VALUE;
}
return menuItem.getSite().getName() + menuItem.getPathAsString();
}
@RemoteMethod
public Collection<UserDto> findUsers( String name )
{
ensureUserIsLoggedIn();
List<UserDto> foundUserDtos = new ArrayList<UserDto>();
List<UserEntity> foundUsers = userDao.findByQuery( null, name, null, true );
for ( UserEntity user : foundUsers )
{
if ( verifyUserProperties( user ) )
{
String qualifiedName = user.getQualifiedName().toString();
String userKey = user.getKey().toString();
UserDto userDto = createUserDto( user, qualifiedName, userKey );
foundUserDtos.add( userDto );
}
}
return foundUserDtos;
}
@RemoteMethod
public Collection<UserDto> findUsersAndAccessType( String name, int contentKey )
{
ensureUserIsLoggedIn();
return doFindUsers( name, null, contentKey );
}
private Collection<UserDto> doFindUsers( String name, AccessType accessType, Integer contentKey )
{
List<UserDto> foundUserDtos = new ArrayList<UserDto>();
List<UserEntity> foundUsers = userDao.findByQuery( null, name, null, true );
if ( contentKey == null )
{
throw new IllegalArgumentException( "contentKey should not be null" );
}
ContentEntity content = contentDao.findByKey( new ContentKey( contentKey ) );
boolean doCheckAccessRights = accessType != null && contentKey != null;
if ( content == null )
{
throw new IllegalArgumentException( "Content with key: " + contentKey + " not found" );
}
ContentAccessResolver contentAccessResolver = new ContentAccessResolver( groupDao );
for ( UserEntity user : foundUsers )
{
AccessRightsResolver accessRightsResolver = new AccessRightsResolver( user, content, contentAccessResolver );
if ( doCheckAccessRights && !accessRightsResolver.hasAccess( accessType ) )
{
continue;
}
if ( verifyUserProperties( user ) )
{
String qualifiedName = user.getQualifiedName().toString();
String userKey = user.getKey().toString();
UserDto userDto = createUserDto( user, qualifiedName, userKey );
userDto.setHighestAccessRight( accessRightsResolver.getHighestAccessRight() );
foundUserDtos.add( userDto );
}
}
return foundUserDtos;
}
private UserDto createUserDto( UserEntity user, String qualifiedName, String userKey )
{
UserDto userDto = new UserDto();
GroupKey userGroupKey = user.getUserGroupKey();
String userGroupKeyAsString = userGroupKey != null ? userGroupKey.toString() : "";
userDto.setDisplayName( user.getDisplayName() );
userDto.setQualifiedName( qualifiedName );
userDto.setEmail( user.getEmail() );
userDto.setKey( userKey );
userDto.setUserGroupKey( userGroupKeyAsString );
userDto.setPhotoExists( user.hasPhoto() );
userDto.setLabel( user.getDisplayName() );
userDto.setValue( user.getDisplayName() );
return userDto;
}
private boolean verifyUserProperties( UserEntity user )
{
boolean verified = true;
if ( StringUtils.isBlank( user.getEmail() ) )
{
return false;
}
if ( StringUtils.isBlank( user.getDisplayName() ) )
{
return false;
}
return verified;
}
private enum AccessType
{
APPROVER( "approve", 3 ),
EDITOR( "update", 2 ),
READER( "read", 1 );
String stringValue;
int priority;
AccessType( String stringValue, int priority )
{
this.stringValue = stringValue;
this.priority = priority;
}
}
private class AccessRightsResolver
{
AccessType highestAccessRight;
AccessRightsResolver( UserEntity user, ContentEntity content, ContentAccessResolver contentAccessResolver )
{
if ( contentAccessResolver.hasApproveContentAccess( user, content ) )
{
highestAccessRight = AccessType.APPROVER;
}
else if ( contentAccessResolver.hasUpdateDraftVersionAccess( user, content ) )
{
highestAccessRight = AccessType.EDITOR;
}
else
{
highestAccessRight = AccessType.READER;
}
}
public boolean hasAccess( AccessType accessType )
{
if ( highestAccessRight.priority >= accessType.priority )
{
return true;
}
return false;
}
public String getHighestAccessRight()
{
return highestAccessRight.stringValue;
}
}
@RemoteMethod
public Collection<PreferenceDto> getUserPreferences( String uid )
{
ensureUserIsLoggedIn();
if ( StringUtils.isBlank( uid ) )
{
throw new IllegalArgumentException( "Uid is null or empty" );
}
List<PreferenceDto> preferenceDtos = new ArrayList<PreferenceDto>();
UserKey userKey = uid == null ? null : new UserKey( uid );
UserEntity user = userDao.findByKey( userKey );
if ( user == null )
{
return preferenceDtos;
}
PreferenceSpecification preferenceSpec = new PreferenceSpecification( user );
List<PreferenceEntity> preferences = preferenceDao.findBy( preferenceSpec );
PreferenceDtoCreator preferenceDtoCreator = new PreferenceDtoCreator( portletDao, preferenceDao, siteDao, menuItemDao );
for ( PreferenceEntity preference : preferences )
{
PreferenceDto preferenceDto = preferenceDtoCreator.createPreferenceDto( preference );
if ( preferenceDto != null )
{
preferenceDtos.add( preferenceDto );
}
}
return preferenceDtos;
}
}