gt: 					if (thumbnailBinaryChannel == null){
context: /**
   * Copyright (C) 2005-2012 BetaCONCEPT Limited
   *
   * This file is part of Astroboa.
   *
   * Astroboa is free software: you can redistribute it and/or modify
   * it under the terms of the GNU Lesser General Public License as published by
   * the Free Software Foundation, either version 3 of the License, or
   * (at your option) any later version.
   *
   * Astroboa is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public License
   * along with Astroboa.  If not, see <http://www.gnu.org/licenses/>.
   */
  package org.betaconceptframework.astroboa.console.jsf.edit;
  
  
  import java.util.ArrayList;
  import java.util.Calendar;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  import javax.faces.application.FacesMessage;
  import javax.faces.context.FacesContext;
  import javax.faces.event.ValueChangeEvent;
  import javax.faces.model.SelectItem;
  import javax.servlet.ServletContext;
  
  import org.apache.commons.collections.CollectionUtils;
  import org.apache.commons.lang.StringUtils;
  import org.betaconceptframework.astroboa.api.model.BinaryChannel;
  import org.betaconceptframework.astroboa.api.model.BinaryProperty;
  import org.betaconceptframework.astroboa.api.model.CalendarProperty;
  import org.betaconceptframework.astroboa.api.model.ComplexCmsProperty;
  import org.betaconceptframework.astroboa.api.model.ComplexCmsRootProperty;
  import org.betaconceptframework.astroboa.api.model.ContentObject;
  import org.betaconceptframework.astroboa.api.model.Space;
  import org.betaconceptframework.astroboa.api.model.StringProperty;
  import org.betaconceptframework.astroboa.api.model.Topic;
  import org.betaconceptframework.astroboa.api.model.TopicReferenceProperty;
  import org.betaconceptframework.astroboa.api.model.definition.ComplexCmsPropertyDefinition;
  import org.betaconceptframework.astroboa.api.model.definition.ContentObjectTypeDefinition;
  import org.betaconceptframework.astroboa.api.model.exception.CmsConcurrentModificationException;
  import org.betaconceptframework.astroboa.api.model.exception.CmsNonUniqueContentObjectSystemNameException;
  import org.betaconceptframework.astroboa.api.model.io.FetchLevel;
  import org.betaconceptframework.astroboa.api.model.io.ResourceRepresentationType;
  import org.betaconceptframework.astroboa.api.model.query.CmsOutcome;
  import org.betaconceptframework.astroboa.api.model.query.ContentAccessMode;
  import org.betaconceptframework.astroboa.api.model.query.criteria.ContentObjectCriteria;
  import org.betaconceptframework.astroboa.api.security.CmsRole;
  import org.betaconceptframework.astroboa.api.security.management.Person;
  import org.betaconceptframework.astroboa.api.service.ContentService;
  import org.betaconceptframework.astroboa.api.service.DefinitionService;
  import org.betaconceptframework.astroboa.api.service.SpaceService;
  import org.betaconceptframework.astroboa.api.service.TopicService;
  import org.betaconceptframework.astroboa.console.commons.CMSUtilities;
  import org.betaconceptframework.astroboa.console.commons.ContentObjectStatefulSearchService;
  import org.betaconceptframework.astroboa.console.commons.ContentObjectUIWrapper;
  import org.betaconceptframework.astroboa.console.commons.ContentObjectUIWrapperFactory;
  import org.betaconceptframework.astroboa.console.commons.Utils;
  import org.betaconceptframework.astroboa.console.jsf.ContentObjectList;
  import org.betaconceptframework.astroboa.console.jsf.LoggedInRepositoryUserSettings;
  import org.betaconceptframework.astroboa.console.jsf.PageController;
  import org.betaconceptframework.astroboa.console.jsf.UIComponentBinding;
  import org.betaconceptframework.astroboa.console.jsf.edit.ComplexCmsPropertyEdit.EditorTab;
  import org.betaconceptframework.astroboa.console.jsf.edit.draft.DraftItem;
  import org.betaconceptframework.astroboa.console.jsf.space.UserSpaceNavigation;
  import org.betaconceptframework.astroboa.console.jsf.taxonomy.LazyLoadingTopicTreeNode;
  import org.betaconceptframework.astroboa.console.jsf.visitor.CmsPropertyValidatorVisitor;
  import org.betaconceptframework.astroboa.console.seam.SeamEventNames;
  import org.betaconceptframework.astroboa.console.security.IdentityStoreRunAsSystem;
  import org.betaconceptframework.astroboa.console.security.LoggedInRepositoryUser;
  import org.betaconceptframework.astroboa.context.AstroboaClientContextHolder;
  import org.betaconceptframework.astroboa.context.SecurityContext;
  import org.betaconceptframework.astroboa.model.factory.CmsCriteriaFactory;
  import org.betaconceptframework.astroboa.model.factory.CmsRepositoryEntityFactory;
  import org.betaconceptframework.astroboa.security.CmsRoleAffiliationFactory;
  import org.betaconceptframework.astroboa.util.CmsConstants;
  import org.betaconceptframework.astroboa.util.CmsConstants.ContentObjectStatus;
  import org.betaconceptframework.astroboa.util.DateUtils;
  import org.betaconceptframework.astroboa.util.PropertyPath;
  import org.betaconceptframework.ui.jsf.AbstractUIBean;
  import org.betaconceptframework.ui.jsf.utility.JSFUtilities;
  import org.betaconceptframework.utility.ImageUtils;
  import org.jboss.seam.ScopeType;
  import org.jboss.seam.annotations.In;
  import org.jboss.seam.annotations.Name;
  import org.jboss.seam.annotations.Observer;
  import org.jboss.seam.annotations.Scope;
  import org.jboss.seam.contexts.Contexts;
  import org.jboss.seam.core.Events;
  import org.jboss.seam.international.LocaleSelector;
  import org.jboss.seam.international.TimeZoneSelector;
  import org.richfaces.event.DropEvent;
  import org.springframework.mail.javamail.ConfigurableMimeFileTypeMap;
  import org.springframework.web.context.support.ServletContextResource;
  
  /**
   * @author Gregory Chomatas (gchomatas@betaconcept.com)
   * @author Savvas Triantafyllou (striantafyllou@betaconcept.com)
   *
   */
  @Name("contentObjectEdit")
  @Scope(ScopeType.CONVERSATION)
  public class ContentObjectEdit extends AbstractUIBean {
  
  	private static final long serialVersionUID = 1L;
  
  	private static final String CLEAR_CONTENT_OBJECT_EDIT_AND_LOAD_AGAIN_XHTML = "/clearContentObjectEditAndLoadAgain.xhtml"; 
  
  	// injected beans
  	private ContentObjectList contentObjectList;
  	private DefinitionService definitionService;
  	private ContentService contentService;
  	private SpaceService spaceService;
  	private UserSpaceNavigation userSpaceNavigation;
  	private CMSUtilities cmsUtilities;
  	private ContentObjectUIWrapperFactory contentObjectUIWrapperFactory;
  	private LoggedInRepositoryUser loggedInRepositoryUser;
  
  	private PageController pageController;
  	private CmsPropertyValidatorVisitor cmsPropertyValidatorVisitor;
  	private TopicService topicService;
  
  	@In(create=true)
  	private IdentityStoreRunAsSystem identityStoreRunAsSystem;
  
  	@In(create=true)
  	private LoggedInRepositoryUserSettings loggedInRepositoryUserSettings;
  
  	@In(required=false)
  	UIComponentBinding uiComponentBinding;
  
  	@In
  	private LocaleSelector  localeSelector;
  	
  	@In(create=true)
  	private LanguageSelector  languageSelector;
  	
  	private String contentObjectTitle;
  	private String contentObjectTypeLocalizedName;
  
  	// if an error occurs when saving a new content object there is a possibility that it has been assigned an id despite the error during saving.
  	//therefore the existence of an id is not a safe criterion to find out if a content object is new and we explicitly keep a boolean variable for this.
  	private boolean newContentObject;
  
  	private String contentObjectTypeForNewObject;
  
  	//private TreeNode contentObjectAsTreeData;
  
  	private String selectedContentObjectIdentifier;
  	private ContentObjectUIWrapper selectedContentObjectForEdit;
  
  	private final Integer DO_NOT_ATTACH_TO_SPACE = 1;
  	private final Integer ATTACH_TO_MY_ROOT_SPACE = 2;
  	private final Integer ATTACH_TO_SELECTED_SPACE = 3;
  
  	public enum AccessRight{
  		canBeReadBy, 
  		canBeUpdatedBy, 
  		canBeDeletedBy, 
  		canBeTaggedBy,
  	}
  
  	private Space spaceToCopyNewObject;
  
  	private String selectedTopicIdForRemovalFromContentObjectSubject;
  	private String selectedLoggedInUserTagId;
  	
  	private List<SelectItem> selectedLanguages;
  
  	//Four instances of the same Spring Prototype Bean injected
  	private ComplexCmsPropertyEdit complexCmsPropertyEdit;
  	private ComplexCmsPropertyEdit profilePropertyEdit;
  	private ComplexCmsPropertyEdit extraPropertyEdit;
  
  	private String newTagLabel;
  
  	/* The following four properties hold the security setting that the user has selected
  	 * (through radio buttons) for each of the four types of access rights defined for content objects (read, update, delete, tag).
  	 * The are 3 possible security settings for each type of access right: ALL(1), NONE(0), ONLY_THE_SPECIFIED_GROUPS_AND_USERS(3)
  	 * 
  	 */
  	//private Integer radioButtonSelectionForCanBeReadBy;
  	//private Integer radioButtonSelectionForCanBeUpdatedBy;
  	//private Integer radioButtonSelectionForCanBeDeletedBy;
  	//private Integer radioButtonSelectionForCanBeTaggedBy;
  	
  	
  	//Used by accessibility-tab.xhtml and is a replacement of the above individual 
  	// Integer values. This map contains four entries
  	//one for each access list. Since JSF and EL parser does not handle map paremters whose key is an
  	//enum, map's key is an enum but take values from the AccessRight enum
  	private Map<String, String> radioButtonSelectionMap = new HashMap<String, String>();
  
  	/* If the user selects the security setting: ONLY_THE_SPECIFIED_GROUPS_AND_USERS(3), then
  	 * for the 4 types of access rights we use the following 4 lists of HashMaps to hold for each type of access rights 
  	 * the corresponding set of user groups and individual users that are granted with the specific access right.
  	 * The HashMaps have 2 keys: "userOrGroupId" and  "userOrGroupName" and their respective values hold the group or user id (the external id in a crm or DB which holds info about users and groups) and the
  	 * group or user name which is used by the UI to present meaningful names instead of ids
  	 */
  	//private List<Map<String,String>> canBeReadByUserList = new ArrayList<Map<String,String>>();
  	//private List<Map<String,String>> canBeUpdatedByUserList = new ArrayList<Map<String,String>>();
  	//private List<Map<String,String>> canBeDeletedByUserList = new ArrayList<Map<String,String>>();
  	//private List<Map<String,String>> canBeTaggedByUserList = new ArrayList<Map<String,String>>();
  	
  	//Group above variables to a Map by AccessRight
  	//oSince JSF and EL parser does not handle map paremters whose key is an
  	//enum, map's key is an enum but take values from the AccessRight enum
  	private Map<String, List<Map<String,String>>> userListByAccessRight = new HashMap<String, List<Map<String,String>>>();
  	
  	
  	private ConfigurableMimeFileTypeMap mimeTypesMap;
  
  	private List<String> listOfTopicIdsWhoseNumberOfContentObjectReferrersShouldBeUpdated = new ArrayList<String>();
  
  	//Corresponds to input text for adding new creator to a content object
  	private String newCreatorName;
  	
  	private List<ComplexCmsPropertyBreadCrumb> fixedPropertyBreadCrumbs = new ArrayList<ComplexCmsPropertyBreadCrumb>();
  	private List<ComplexCmsPropertyBreadCrumb> extraPropertyBreadCrumbs = new ArrayList<ComplexCmsPropertyBreadCrumb>();
  
  	private final static String defaultFixedPropertyEditorPagePath = "/WEB-INF/pageComponents/edit/fixedPropertiesEditor.xhtml"; 
  	private final static String defaultExtraPropertyEditorPagePath = "/WEB-INF/pageComponents/edit/extraPropertiesEditor.xhtml"; 
  	private String complexCmsPropertyEditorPagePath = defaultFixedPropertyEditorPagePath;
  
  	private ContentObject explicitlySetContentObjectForEdit;
  	
  	private String selectedPersonOrRoleTextFilter;
  	
  	
  	
  	private CmsRepositoryEntityFactory cmsRepositoryEntityFactory;
  
  	protected String editContentObjectFromDraft_UIAction(ContentObject contentObject)
  	{
  		if (contentObject == null)
  		{
  			getLogger().error("Error while loading content object from draft. Content Object is null"); 
  			JSFUtilities.addMessage(null, "object.edit.load.error", null, FacesMessage.SEVERITY_ERROR); 
  			return null;
  		}
  		
  		if (contentObject.getId() != null)
  		{
  			setSelectedContentObjectIdentifier(contentObject.getId());
  			explicitlySetContentObjectForEdit(contentObject);
  
  			return editContentObjectAndDisplayDefaultPage_UIAction();
  		}
  		else
  		{
  			//Content object without id
  			setSelectedContentObjectIdentifier(null);
  			explicitlySetContentObjectForEdit(contentObject);
  
  			//We do not want to create new content
  			newContentObject = true;
  			contentObjectTypeForNewObject = null;
  			selectedContentObjectForEdit = contentObjectUIWrapperFactory.getInstance(contentObject);
  			contentObjectTypeLocalizedName = selectedContentObjectForEdit.getContentObject().getTypeDefinition().getDisplayName().getLocalizedLabelForLocale(JSFUtilities.getLocaleAsString());
  			contentObjectTitle = ((StringProperty) selectedContentObjectForEdit.getContentObject().getCmsProperty("profile.title")).getSimpleTypeValue(); 
  			
  			/* initialize the security input components (i.e. radio buttons which allow the user to select if ALL, NONE or SELECTED GROUPS/USERS have a specific access right) 
  			 * Also initialize the lists which are used by the UI to present the groups/users granted with each type of access right (i.e. read / update / delete / tag).
  			 * The access right lists in the content object hold only user / group ids which are not very meaningful to the end user.
  			 */
  			initializeInputAndOutputUIComponentsRelatedtoSecurityProperties();
  
  			return initializeContentObjectEdit();
  		}
  	}
  	
  	
  	protected String editContentObject_UIAction() {
  		explicitlySetContentObjectForEdit = null;
  		return editContentObjectAndDisplayDefaultPage_UIAction();
  	}
  	
  	protected String editContentObjectAndDisplayDefaultPage_UIAction() {
  
  		if (getSelectedContentObjectIdentifier() != null) {
  			try {
  				ContentObject selectedContentObject = getSelectedContentObject();
  
  				selectedContentObjectForEdit = contentObjectUIWrapperFactory.getInstance(selectedContentObject);
  				contentObjectTypeLocalizedName = selectedContentObjectForEdit.getContentObject().getTypeDefinition().getDisplayName().getLocalizedLabelForLocale(JSFUtilities.getLocaleAsString());
  				contentObjectTitle = ((StringProperty) selectedContentObjectForEdit.getContentObject().getCmsProperty("profile.title")).getSimpleTypeValue(); 
  				
  				/* initialize the security input components (i.e. radio buttons which allow the user to select if ALL, NONE or SELECTED GROUPS/USERS have a specific access right) 
  				 * Also initialize the lists which are used by the UI to present the groups/users granted with each type of access right (i.e. read / update / delete / tag).
  				 * The access right lists in the content object hold only user / group ids which are not very meaningful to the end user. 
  				 */
  				initializeInputAndOutputUIComponentsRelatedtoSecurityProperties();
  				
  			}
  			catch (Exception e) {
  				getLogger().error("Error while loading content objects ",  e); 
  				JSFUtilities.addMessage(null, "object.edit.load.error", null, FacesMessage.SEVERITY_ERROR); 
  				//pageController.loadPageComponentInDynamicUIArea(DynamicUIAreaPageComponent.ContentObjectList.getDynamicUIAreaPageComponent());
  				return null;
  			}
  		}
  		else { // it is a new content object. Lets initialize it
  			try {
  				initializeNewContentObject();
  			}
  			catch (Exception e) {
  				getLogger().error("Error while generating template for new content object ", e); 
  				JSFUtilities.addMessage(null, "object.edit.error.creating.new.content.object", null, FacesMessage.SEVERITY_ERROR); 
  				//pageController.loadPageComponentInDynamicUIArea(DynamicUIAreaPageComponent.ContentObjectList.getDynamicUIAreaPageComponent());
  				return null;
  			}
  		}
  
  		//generateCmsPropertiesTree();
  
  		return initializeContentObjectEdit();
  
  	}
  
  
  	private String initializeContentObjectEdit() {
  		complexCmsPropertyEdit.setEditedContentObject(selectedContentObjectForEdit.getContentObject());
  		complexCmsPropertyEdit.setEditorTab(EditorTab.FIXED_PROPERTIES);
  		profilePropertyEdit.setEditedContentObject(selectedContentObjectForEdit.getContentObject());
  		profilePropertyEdit.setEditorTab(EditorTab.ADMIN_PROPERTY);
  		extraPropertyEdit.setEditedContentObject(selectedContentObjectForEdit.getContentObject());
  		extraPropertyEdit.setEditorTab(EditorTab.EXTRA_PROPERTIES);
  
  		loadComplexCmsPropertyToComplexCmsPropertyEditor(selectedContentObjectForEdit.getContentObject().getComplexCmsRootProperty(), complexCmsPropertyEdit);
  		
  		profilePropertyEdit.editComplexCmsProperty((ComplexCmsProperty<ComplexCmsPropertyDefinition, ComplexCmsProperty<?,?>>)selectedContentObjectForEdit.getContentObject().getCmsProperty("profile"));
  
  		listOfTopicIdsWhoseNumberOfContentObjectReferrersShouldBeUpdated.clear();
  
  		if (cmsPropertyValidatorVisitor == null){
  			cmsPropertyValidatorVisitor = new CmsPropertyValidatorVisitor(definitionService);
  		}
  		else{
  			cmsPropertyValidatorVisitor.reset(true);
  		}
  
  		complexCmsPropertyEdit.setCmsPropertyValidatorVisitor(cmsPropertyValidatorVisitor);
  		
  		profilePropertyEdit.setCmsPropertyValidatorVisitor(cmsPropertyValidatorVisitor);
  		
  		extraPropertyEdit.setCmsPropertyValidatorVisitor(cmsPropertyValidatorVisitor);
  		
  		return null;
  	}
  
  
  	private ContentObject getSelectedContentObject() {
  		if (explicitlySetContentObjectForEdit != null){
  			return explicitlySetContentObjectForEdit;
  		}
  		else{
  			return (ContentObject) contentService.getContentObject(selectedContentObjectIdentifier, ResourceRepresentationType.CONTENT_OBJECT_INSTANCE, FetchLevel.ENTITY, null, null, true);
  		}
  	}
  
  
  	private void createFixedPropertyBreadCrumbs(
  			ComplexCmsProperty<?,?> complexCmsProperty) {
  
  		fixedPropertyBreadCrumbs.clear();
  		
  		ComplexCmsProperty<?,?> currentProperty = complexCmsProperty;
  
  		do{
  			String localizedLabel = (currentProperty instanceof ComplexCmsRootProperty ? 
  					selectedContentObjectForEdit.getContentObject().getTypeDefinition().getDisplayName().getLocalizedLabelForLocale(localeSelector.getLocaleString()) :
  						currentProperty.getAvailableLocalizedLabel(localeSelector.getLocaleString()));
  
  			if (StringUtils.isBlank(localizedLabel))
  				localizedLabel = currentProperty.getName(); 
  
  			//If property has multiple values add the value index to label
  			if(currentProperty.getPropertyDefinition().isMultiple()){
  				int index = PropertyPath.extractIndexFromPath(currentProperty.getPath());
  				if (index == PropertyPath.NO_INDEX)
  					index = 0;
  				localizedLabel += CmsConstants.LEFT_BRACKET+index+
  				CmsConstants.RIGHT_BRACKET;
  			}
  
  			// check if the schema defines child properties that their values need to be appended in property localized name as extra identifiers 
  			String propertyLabel =  ((ComplexCmsProperty<?,?>)currentProperty).getPropertyLabel(localeSelector.getLocaleString());	
  			if (StringUtils.isNotBlank(propertyLabel)){
  				localizedLabel += " - "+propertyLabel;
  			}
  				
  			
  			if (currentProperty instanceof ComplexCmsRootProperty){
  				//Root property does not have a path
  				fixedPropertyBreadCrumbs.add(new ComplexCmsPropertyBreadCrumb(localizedLabel,selectedContentObjectForEdit.getContentObject().getTypeDefinition().getName()));
  			}
  			else{
  				fixedPropertyBreadCrumbs.add(new ComplexCmsPropertyBreadCrumb(localizedLabel,currentProperty.getPath()));
  			}
  
  			currentProperty = currentProperty.getParentProperty();
  
  		}
  		while( currentProperty != null);
  
  		if (fixedPropertyBreadCrumbs.size() > 1)
  			Collections.reverse(fixedPropertyBreadCrumbs);
  
  	}
  
  	private void createExtraPropertyBreadCrumbs(
  			ComplexCmsProperty<?,?> complexCmsProperty) {
  
  		extraPropertyBreadCrumbs.clear();
  		
  		ComplexCmsProperty<?,?> currentProperty = complexCmsProperty;
  
  		do{
  			String localizedLabel;
  			// if we are the level of the root property then the bread crumb label will be "Extra Properties" instead of the object type label in order to allow the user go back
  			// to the extra properties form
  			if (currentProperty instanceof ComplexCmsRootProperty) {
  				localizedLabel =  JSFUtilities.getStringI18n("object.edit.tab.extraProperties");
  				
  				// instead of a real property path we will create this bread crumb with the string 'EXTRA_PROPERIES' as path. 
  				// This will force a return to the extra properties form
  				extraPropertyBreadCrumbs.add(new ComplexCmsPropertyBreadCrumb(localizedLabel, "EXTRA_PROPERTIES"));
  			}
  			else {
  				localizedLabel =  
  						currentProperty.getAvailableLocalizedLabel(localeSelector.getLocaleString());
  				
  				if (StringUtils.isBlank(localizedLabel))
  					localizedLabel = currentProperty.getName(); 
  
  				//If property has multiple values add the value index to label
  				if(currentProperty.getPropertyDefinition().isMultiple()){
  					int index = PropertyPath.extractIndexFromPath(currentProperty.getPath());
  					if (index == PropertyPath.NO_INDEX)
  						index = 0;
  					localizedLabel += CmsConstants.LEFT_BRACKET+index+
  					CmsConstants.RIGHT_BRACKET;
  				}
  				
  				// check if the schema defines child properties that their values need to be appended in property localized name as extra identifiers 
  				String propertyLabel =  ((ComplexCmsProperty<?,?>)currentProperty).getPropertyLabel(localeSelector.getLocaleString());	
  				if (StringUtils.isNotBlank(propertyLabel)){
  					localizedLabel += " - "+propertyLabel;
  				}
  				
  				extraPropertyBreadCrumbs.add(new ComplexCmsPropertyBreadCrumb(localizedLabel,currentProperty.getPath()));
  			}
  
  			currentProperty = currentProperty.getParentProperty();
  
  		}
  		while( currentProperty != null);
  
  		if (extraPropertyBreadCrumbs.size() > 1)
  			Collections.reverse(extraPropertyBreadCrumbs);
  
  	}
  
  
  	//private void generateCmsPropertiesTree() {
  
  	/*List<String> aspects = selectedContentObjectForEdit.getContentObject().getAspects();
  
  		Events.instance().raiseEvent(SeamEventNames.NEW_CMS_DEFINITION_TREE, 
  				selectedContentObjectForEdit.getContentObject().getContentObjectType(), 
  				aspects);
  
  		if (CollectionUtils.isNotEmpty(aspects)){
  			selectedAspects = new String[aspects.size()];
  			selectedContentObjectForEdit.getContentObject().getAspects().toArray(selectedAspects);
  		}*/
  
  	/*Events.instance().raiseEvent(SeamEventNames.NEW_CMS_PROPERTIES_TREE, 
  				selectedContentObjectForEdit.getContentObject());
  
  		List<String> aspects = selectedContentObjectForEdit.getContentObject().getAspects();
  		if (CollectionUtils.isNotEmpty(aspects)){
  			selectedAspects = new String[aspects.size()];
  			selectedContentObjectForEdit.getContentObject().getAspects().toArray(selectedAspects);
  		}
  
  	}	*/
  
  
  	private void initializeNewContentObject() throws Exception{
  		newContentObject = true;
  		ContentObject contentObject;
  
  		// get a content object template for the user selected content type. Profile and Accessibility properties are also initialized
  		if (contentObjectTypeForNewObject == null) {
  			//Check in pageController
  			contentObjectTypeForNewObject = pageController.getContentObjectTypeToUseWhenLoadingClearedContentObjectEditForm();
  
  			//Found no content object type found throw new Exception
  			if (contentObjectTypeForNewObject == null){
  				newContentObject = false;
  				JSFUtilities.addMessage(null, "object.edit.error.no.content.type", null, FacesMessage.SEVERITY_ERROR); 
  				throw new Exception("ContentObject Type for the generation of a new content object has not been provided"); 
  			}
  			else{
  				//Clear value
  				pageController.setContentObjectTypeToUseWhenLoadingClearedContentObjectEditForm(null);
  			}
  		}
  
  		contentObject = cmsRepositoryEntityFactory.newObjectForType(contentObjectTypeForNewObject);
  
  
  		selectedContentObjectForEdit = contentObjectUIWrapperFactory.getInstance(contentObject);
  		
  		if (contentObject.getComplexCmsRootProperty().isChildPropertyDefined("profile.language")){ 
  			((StringProperty)contentObject.getCmsProperty("profile.language")).addSimpleTypeValue(JSFUtilities.getLocaleAsString()); 
  		}
  
  		// get the current user and set the owner of this new content object accordingly
  		contentObject.setOwner(loggedInRepositoryUser.getRepositoryUser());
  
  		// initialize the creator with the name of the owner
  		if (contentObject.getComplexCmsRootProperty().isChildPropertyDefined("profile.creator")){ 
  			((StringProperty)contentObject.getCmsProperty("profile.creator")).addSimpleTypeValue(loggedInRepositoryUser.getRepositoryUser().getLabel()); 
  		}
  
  		// initialize content type localized name for presentation at the form
  		contentObjectTypeLocalizedName = contentObject.getTypeDefinition().getDisplayName().getLocalizedLabelForLocale(JSFUtilities.getLocaleAsString());
  
  		// initialize the default accessibility values setting appropriately the corresponding input components in the form (i.e. the radio button default values)
  
  		clearUIAccessRightLists();
  		
  		radioButtonSelectionMap.put(AccessRight.canBeReadBy.toString(), "1");// READ BY ALL
  		radioButtonSelectionMap.put(AccessRight.canBeUpdatedBy.toString(), "2"); // UPDATED BY NONE (only owner is allowed)
  		radioButtonSelectionMap.put(AccessRight.canBeDeletedBy.toString(), "2"); // DELETED BY NONE (only owner is allowed)
  		radioButtonSelectionMap.put(AccessRight.canBeTaggedBy.toString(), "1"); // TAGGED BY ALL
  		 
  
  		contentObjectTitle = null;
  	}
  
  
  	private void loadComplexCmsPropertyToComplexCmsPropertyEditor(ComplexCmsProperty<?,?> complexCmsProperty, ComplexCmsPropertyEdit complexCmsPropertyEdit) {
  
  		//Find appropriate template to load
  		String cmsPropertyTemplatePath = "/WEB-INF/pageComponents/";    
  
  		String definitionFullPath = complexCmsProperty.getPropertyDefinition().getFullPath();
  
  		complexCmsPropertyEditorPagePath = cmsPropertyTemplatePath+"edit/propertyTemplates/"+generateCmsPropertyTemplateName(definitionFullPath)+"-editor.xhtml"; 
  
  		try{
  
  			ServletContextResource templateResource = 
  				new ServletContextResource((ServletContext)FacesContext.getCurrentInstance().getExternalContext().getContext(), complexCmsPropertyEditorPagePath);
  
  			if (!templateResource.exists()){
  				
  				//Template for property was not found. 
  				//Check if there is a template for this complex property as a global one
  				//and not in terms of its type
  				String definitionPath = complexCmsProperty.getPropertyDefinition().getPath();
  				
  				complexCmsPropertyEditorPagePath = cmsPropertyTemplatePath+"edit/propertyTemplates/"+generateCmsPropertyTemplateName(definitionPath)+"-editor.xhtml"; 
  
  				templateResource = 
  					new ServletContextResource((ServletContext)FacesContext.getCurrentInstance().getExternalContext().getContext(), complexCmsPropertyEditorPagePath);
  				
  				if (!templateResource.exists()){
  					throw new Exception();
  				}
  			}
  
  
  		}
  		catch(Exception e){
  			logger.debug("Template '"+complexCmsPropertyEditorPagePath+"' for cms property '"+definitionFullPath + "' not found. Loading default "+defaultFixedPropertyEditorPagePath); 
  
  			//Return default template
  			if (complexCmsPropertyEdit.getEditorTab().equals(EditorTab.FIXED_PROPERTIES)) {
  				complexCmsPropertyEditorPagePath = defaultFixedPropertyEditorPagePath;
  			}
  			else if (complexCmsPropertyEdit.getEditorTab().equals(EditorTab.EXTRA_PROPERTIES)) {
  				complexCmsPropertyEditorPagePath = defaultExtraPropertyEditorPagePath;
  			}
  		}
  		finally{
  			//Render with default editor
  			complexCmsPropertyEdit.editComplexCmsProperty(complexCmsProperty);
  		}
  
  
  		//Create bread crumbs
  		if (complexCmsPropertyEdit.getEditorTab().equals(EditorTab.FIXED_PROPERTIES)) {
  			createFixedPropertyBreadCrumbs(complexCmsProperty);
  		}
  		else {
  			createExtraPropertyBreadCrumbs(complexCmsProperty);
  		}
  
  	}
  
  
  	private String generateCmsPropertyTemplateName(String definitionFullPath) {
  		return definitionFullPath.replaceAll("\\.", "-"); 
  	}
  
  	public void removeExtraProperty_UIAction(String propertyName) {
  
  		if (selectedContentObjectForEdit != null && selectedContentObjectForEdit.getContentObject() != null){
  			try{
  				//	Remove aspect from list in ContentObject
  				if (!selectedContentObjectForEdit.getContentObject().getComplexCmsRootProperty().hasAspect(propertyName)){
  					logger.warn("Unable to remove extra property '"+ propertyName+ "' from content object "+ selectedContentObjectForEdit.getContentObject().getId()  
  							+ " because extra property does not exist in content object"); 
  				}
  				else{
  					ComplexCmsProperty<?,?> extraProperty = (ComplexCmsProperty<?,?>)selectedContentObjectForEdit.getContentObject().getCmsProperty(propertyName);
  					selectedContentObjectForEdit.getContentObject().removeCmsProperty(propertyName);
  					extraPropertyEdit.reloadEditedCmsProperties();
  					JSFUtilities.addMessage(null, "object.edit.removeExtraProperty.success", new String[] {extraProperty.getAvailableLocalizedLabel(localeSelector.getLocaleString())}, FacesMessage.SEVERITY_INFO);
  				}
  			}
  			catch (Exception e){
  				logger.error("",e); 
  				JSFUtilities.addMessage(null, "object.edit.removeExtraProperty.error", null, FacesMessage.SEVERITY_ERROR); 
  			}
  		}
  
  	}
  	
  	/*
  	public void addAspectToComplexProperty_UIAction(String newlyAddedAspect){
  		if (selectedContentObjectForEdit != null){
  			try{
  				//Aspect is new. Add to content object
  				//selectedContentObjectForEdit.getContentObject().getComplexCmsRootProperty().addAspect(newlyAddedAspect);
  				selectedContentObjectForEdit.getContentObject().getCmsProperty(newlyAddedAspect);
  
  				//Aspect path now contains content object type
  				showSelectedComplexPropertyForPath_UIAction(newlyAddedAspect);
  
  			}
  			catch (Exception e){
  				logger.error("",e); 
  				JSFUtilities.addMessage(null, "object.edit.add.aspect.error", null, FacesMessage.SEVERITY_ERROR); 
  			}
  		}
  	}
  	*/
  	
  	/* Extra properties are properties that are not statically defined in object type schemas
  	 * They can be dynamically added in any object instance.
  	 * Any complex property type defined outside of an object type definition can be used as a prototype for adding extra properties to object instances.
  	 * When a user selects to add an extra property from the available prototypes, this method displays the property form in the console editor 
  	 * The property name is the name of the prototype.
  	 */
  	public void showNewExtraProperty_UIAction(String propertyName) {
  		ComplexCmsProperty<?,?> extraProperty = (ComplexCmsProperty<?,?>)selectedContentObjectForEdit.getContentObject().getCmsProperty(propertyName);
  		if (extraProperty != null) {
  			extraPropertyEdit.reloadEditedCmsProperties();
  			JSFUtilities.addMessage(null, "object.edit.addExtraProperty.success", new String[] {extraProperty.getAvailableLocalizedLabel(localeSelector.getLocaleString())}, FacesMessage.SEVERITY_INFO);
  		}
  		else {
  			JSFUtilities.addMessage(null, "object.edit.addExtraProperty.error", null, FacesMessage.SEVERITY_WARN);
  		}
  	}
  	
  	// This method displays the selected complex property either in the properties or in the extra properties tab according to which complexCmsPropertyEdit instance has been passed to the method
  	@Observer({SeamEventNames.NEW_COMPLEX_CMS_PROPERTY_ADDED})
  	public void showSelectedComplexPropertyForPath_UIAction(String complexCmsPropertyPath, ComplexCmsPropertyEdit complexCmsPropertyEdit){
  		
  		if (StringUtils.isNotBlank(complexCmsPropertyPath)) { 
  			
  			// when path equals 'EXTRA_PROPERTIES' we set the edited property in the complexPropertiesEdit 
  			// to be null in order to force loading the extra properties form. 
  			// We also se the template to be used and we reset the extra properties bread crumbs array
  			if (complexCmsPropertyPath.equals("EXTRA_PROPERTIES")){ 
  				complexCmsPropertyEdit.editComplexCmsProperty(null);
  				complexCmsPropertyEditorPagePath = defaultExtraPropertyEditorPagePath;
  				extraPropertyBreadCrumbs = new ArrayList<ComplexCmsPropertyBreadCrumb>();
  			}
  			else {
  
  				try{
  	
  					ComplexCmsProperty<?,?> complexCmsProperty = null;
  					
  					if (selectedContentObjectForEdit.getContentObject().getTypeDefinition().getName().equals(complexCmsPropertyPath)){
  						//Load root property
  						complexCmsProperty = selectedContentObjectForEdit.getContentObject().getComplexCmsRootProperty();
  					}
  					else{
  						complexCmsProperty = (ComplexCmsProperty<?,?>)selectedContentObjectForEdit.getContentObject().getCmsProperty(complexCmsPropertyPath);
  					}
  	
  					if (complexCmsProperty == null){
  						//Complex Cms Property is either not defined or
  						//is a multiple complex cms property
  						if (selectedContentObjectForEdit.getContentObject().getComplexCmsRootProperty().isChildPropertyDefined(complexCmsPropertyPath)){
  	
  							//Create a template for complex cms property
  							complexCmsProperty = (ComplexCmsProperty<?,?>)complexCmsPropertyEdit.getEditedComplexCmsProperty().createNewValueForMulitpleComplexCmsProperty(complexCmsPropertyPath);
  							
  						}
  					}
  					
  					if (complexCmsProperty == null){
  							logger.warn("Attempt to create and load complex cms Property "+ complexCmsPropertyPath + " failed"); 
  					}
  					else{
  						//Load complex cms property editor only if complex cms property was found
  						loadComplexCmsPropertyToComplexCmsPropertyEditor(complexCmsProperty, complexCmsPropertyEdit);
  					}
  				}
  				catch(Exception e){
  					logger.error("",e); 
  					JSFUtilities.addMessage(null, "application.unknown.error.message", null, FacesMessage.SEVERITY_WARN); 
  				}
  			}
  		}
  	}
  
  	//Save a content object without version
  	//Temporarily sets loggedInRepositoryUserSetting for creatingVersion on save to false
  	//performs save and then restores previous value
  	private String disableCreateVersionAndSaveContentObject() {
  
  		boolean previousValue = Boolean.valueOf(loggedInRepositoryUserSettings.isCreateVersionUponSuccessfulSave());
  
  		loggedInRepositoryUserSettings.setCreateVersionUponSuccessfulSave(false);
  
  		String saveOutcome = saveContentObject();
  
  		loggedInRepositoryUserSettings.setCreateVersionUponSuccessfulSave(previousValue);
  
  		return saveOutcome;
  	}
  
  
  	public String saveContentObject_UIAction() {
  		//We care about the outcome only if it is about
  		//clear
  		String saveOutcome = saveContentObject();
  
  		if (StringUtils.isNotBlank(saveOutcome) &&
  				CLEAR_CONTENT_OBJECT_EDIT_AND_LOAD_AGAIN_XHTML.equals(saveOutcome)){
  			return saveOutcome;
  		}
  		else{
  			//Return null to stay in the same page
  			return null;
  		}
  		
  	}
  
  	private String saveContentObject() {
  
  
  		String contentObjectPreparationOutcome = "error"; 
  
  		boolean contentObjectWasSavedInRepository = false;
  		
  		String providedSystemName = selectedContentObjectForEdit.getContentObject().getSystemName() != null ?
  				new String(selectedContentObjectForEdit.getContentObject().getSystemName()) : null;
  				
  		// we will need this to know if we have been required to use admin privileges  in order to successfully save the object 
  		boolean adminRoleHasBeenAdded = false;
  		// and these in order to add admin privileges if we are required to do so
  		SecurityContext securityContext = null;
  		String roleAdminForActiveRepository = null;
  				
  		try {
  			// first do some checks and prepare content object for save
  			contentObjectPreparationOutcome = prepareContentObjectForSave();
  
  			// if checks and preparation were successful we now we may proceed to save the content object
  			if (contentObjectPreparationOutcome.equals("contentObjectReadyForSave")) { 
  				
  				//Generate draftItemId before save as during save an id is created
  				String draftItemId = DraftItem.generateDraftItemIdForCmsRepositoryEntity(selectedContentObjectForEdit.getContentObject());
  				
  				// Sent an event just before saving to allow custom observers to perform actions or change the object
  				// We are providing a default observer that looks for a specially named script objects in the repository and
  				// runs the script. This allows users to dynamically add code for this event directly through astroboa console
  				// The script object should have a system name equal to the event name, i.e. "org.betaconceptframework.astroboa.console.contentObjectReadyForSave".
  				// Additionally the script object should be owned by SYSTEM to prevent unauthorized users from changing the behaviour of content object editing.
  				// Finally the script itself should be a GROOVY class implementing the "PreSaveGroovyActionInterface"
  				// Example:
  				//	import org.betaconceptframework.astroboa.api.model.*;
  				//	import org.betaconceptframework.astroboa.console.jsf.edit.PreSaveGroovyAction;
  				//	import org.slf4j.Logger;
  				//
  				//	public class PreSaveAction implements PreSaveGroovyAction {
  				//
  				//		public void run(ContentObject contentObject, Logger logger) {
  				//			// do some actions
  				//		}
  				//	}
  				//
  				// A common case for adding custom script code is to automatically alter 
  				// object accessibility settings according to some common rules set by each organization
  				if (!selectedContentObjectForEdit
  						.getContentObject()
  						.getSystemName()
  						.equals(SeamEventNames.CONTENT_OBJECT_READY_FOR_SAVE)) { // We do not want to sent the event if the edited object is the script object itself that handles the event
  					Events.instance().raiseEvent(SeamEventNames.CONTENT_OBJECT_READY_FOR_SAVE, new Object[]{
  						selectedContentObjectForEdit.getContentObject()});
  				}
  				
  				// we save the content object
  				// but before we should check if the object type is a subclass of personType and the logged in username is the same as the username stored in the person object 
  				// "personAuthentication.username" property. In other words we should detect the case that a user tries to save her user profile. 
  				// If such a case exists then we should check if the user is the owner of the object (usually she is not because an admin will create the accounts). 
  				// If not then we should perform the save as an admin on behalf of the user.
  				if (selectedContentObjectForEdit.isLoggedInUsersPersonProfile() &&
  					!selectedContentObjectForEdit.getContentObject().getOwner().getExternalId().equals(loggedInRepositoryUser.getIdentity())) {
  					
  					// we will emulate a run as functionality adding the admin role into user roles and after the save we will remove it
  					securityContext = AstroboaClientContextHolder.getActiveSecurityContext();
  					roleAdminForActiveRepository = CmsRoleAffiliationFactory.INSTANCE.getCmsRoleAffiliationForActiveRepository(CmsRole.ROLE_ADMIN);
  					
  					if (securityContext != null && ! securityContext.hasRole(roleAdminForActiveRepository)) {
  						adminRoleHasBeenAdded = securityContext.addRole(roleAdminForActiveRepository);
  					}
  				}
  				
  				contentService.save(selectedContentObjectForEdit.getContentObject(), loggedInRepositoryUserSettings.isCreateVersionUponSuccessfulSave(), true, null);
  
  				//Flag indicating that object was saved in repository
  				contentObjectWasSavedInRepository = true;
  
  				// make necessary resets in views and objects before leaving edit mode
  				//	resetViewAndState();
  
  				// change the header message of the content object list panel
  				contentObjectList.setContentObjectListHeaderMessage(JSFUtilities.getStringI18n("object.list.message.contentObjectListHeaderMessageAfterContentObjectSave")); 
  				
  				// Update the accessibility UI components in the case the pre-save actions 
  				// (any code triggered by the CONTENT_OBJECT_READY_FOR_SAVE event) have altered accessibility property
  				initializeInputAndOutputUIComponentsRelatedtoSecurityProperties();
  				
  				// generate a success message
  				JSFUtilities.addMessage(null, "object.edit.successful.save.info.message", null, FacesMessage.SEVERITY_INFO); 
  
  				if (listOfTopicIdsWhoseNumberOfContentObjectReferrersShouldBeUpdated.size() > 0)
  					Events.instance().raiseEvent(SeamEventNames.UPDATE_NO_OF_CONTENT_OBJECT_REFERRERS, listOfTopicIdsWhoseNumberOfContentObjectReferrersShouldBeUpdated);
  
  				listOfTopicIdsWhoseNumberOfContentObjectReferrersShouldBeUpdated.clear();
  
  				//Reset loggedinRepositoryUser's root topics
  				//in order to force aspect to reload them from database
  				resetLoggedInRepositoryUserFolksonomyRootTopics();
  
  
  				//Notify content type tree if content object is new
  				if (newContentObject){
  					Events.instance().raiseEvent(SeamEventNames.CONTENT_OBJECT_ADDED, new Object[]{
  							selectedContentObjectForEdit.getContentObject().getContentObjectType(), selectedContentObjectForEdit.getContentObject().getId(), 
  							(selectedContentObjectForEdit.getContentObject().getComplexCmsRootProperty().isChildPropertyDefined("profile.created") ? 
  							((CalendarProperty)selectedContentObjectForEdit.getContentObject().getCmsProperty("profile.created")).getSimpleTypeValue() : Calendar.getInstance())}); 
  				}
  				else{
  					Events.instance().raiseEvent(SeamEventNames.CONTENT_OBJECT_UPDATED, 
  								new Object[]{selectedContentObjectForEdit.getContentObject().getContentObjectType(),
  												selectedContentObjectForEdit.getContentObject().getId()});
  				}
  				
  				//Raise a special event for draft. LoggedInRepositoryUser is needed
  				Events.instance().raiseEvent(SeamEventNames.CONTENT_OBJECT_MODIFIED, new Object[]{loggedInRepositoryUser.getRepositoryUser().getId(), 
  						draftItemId});
  				
  				// Reset the extra properties editor in order to reload the extra properties list
  				// We need this in the case that the user has added an extra property, added no values in it 
  				// and after the successful save the empty property has been automatically removed from the object 
  				extraPropertyEdit.reloadEditedCmsProperties();
  			} 
  			else{
  				return contentObjectPreparationOutcome;
  			}
  
  		}
  		catch(CmsNonUniqueContentObjectSystemNameException e)
  		{
  			if (! contentObjectWasSavedInRepository){
  				//Produce error message only if content object was not saved at all
  				JSFUtilities.addMessage(null, "object.edit.non.unique.system.name", new String[]{selectedContentObjectForEdit.getContentObject().getSystemName()}, FacesMessage.SEVERITY_WARN); 
  
  				if (newContentObject) {
  					selectedContentObjectForEdit.getContentObject().setId(null);
  				}
  			}
  			
  			return "error"; 
  			
  		}
  		catch(CmsConcurrentModificationException e)
  		{
  			JSFUtilities.addMessage(null, "object.edit.save.concurrentModificationError", null, FacesMessage.SEVERITY_WARN); 
  
  			if (newContentObject) {
  				selectedContentObjectForEdit.getContentObject().setId(null);
  			}
  			
  			return "error"; 
  			
  		}
  		catch (Exception e) {
  			getLogger().error("Content Object could not be saved",e); 
  
  			if (! contentObjectWasSavedInRepository){
  				//Produce error message only if content object was not saved at all
  				JSFUtilities.addMessage(null, "object.edit.save.error", null, FacesMessage.SEVERITY_ERROR); 
  				if (newContentObject) {
  					selectedContentObjectForEdit.getContentObject().setId(null);
  				}
  			}
  			
  			return "error"; 
  
  		}
  		finally {
  			if (adminRoleHasBeenAdded) {
  				securityContext.removeRole(roleAdminForActiveRepository);
  			}
  		}
  		
  		
  		//check if content object's system name was not unique 
  		if (newContentObject && selectedContentObjectForEdit.getContentObject().getSystemName() != null && selectedContentObjectForEdit.getContentObject().getId() != null && 
  				selectedContentObjectForEdit.getContentObject().getSystemName().endsWith(selectedContentObjectForEdit.getContentObject().getId()))
  		{
  			JSFUtilities.addMessage(null, "object.edit.save.system.name.provided",
  					providedSystemName != null ? new String[]{providedSystemName}:
  						new String[]{""}, FacesMessage.SEVERITY_WARN);
  		}
  
  		// check if user wishes to copy the content object to a SPACE_INSTANCE
  		
  		try {
  			
  			if (spaceToCopyNewObject != null) {
  				
  				String contentObjectId = selectedContentObjectForEdit.getContentObject().getId();
  				
  				spaceToCopyNewObject.addContentObjectReference(contentObjectId);
  				spaceService.save(spaceToCopyNewObject);
  				// recreate space items
  				userSpaceNavigation.getUserSpaceItems();
  				
  				// reset selected space to prevent coping it in successive saves of the same object
  				spaceToCopyNewObject = null;
  				
  				// generate a success message
  				JSFUtilities.addMessage(null, "object.edit.sucessfully.attach.content.object.to.space", null, FacesMessage.SEVERITY_INFO); 
  			}
  				
  				
  
  		}
  		catch (Exception e) {
  			JSFUtilities.addMessage(null, "object.edit.space.attach.error", null, FacesMessage.SEVERITY_ERROR); 
  			getLogger().error("Content Object could not be attached to requested SPACE_INSTANCE",e); 
  			return "error"; 
  
  		}
  		
  
  		//Decide if editor should be closed
  		/*
  		if (contentObjectPreparationOutcome.equals("contentObjectReadyForSave")) {
  			
  			//Copy to clipboard
  			if (loggedInRepositoryUserSettings.isCopyContentObjectToClipboardUponSuccessfulSave()){
  				clipboard.copyContentObjectToClipboard_UIAction(
  						selectedContentObjectForEdit.getContentObject().getId(), 
  						selectedContentObjectForEdit.getContentObject().getSystemName(), 
  						((StringProperty) selectedContentObjectForEdit.getContentObject().getCmsProperty("profile.title")).getSimpleTypeValue(),
  						selectedContentObjectForEdit.getContentObjectTypeForCurrentLocale(),
  						selectedContentObjectForEdit.getContentObject().getContentObjectType());
  			}
  			
  			if (newContentObject && loggedInRepositoryUserSettings.isOpenNewContentObjectEditorUponSuccessfulSave()){
  				pageController.setContentObjectTypeToUseWhenLoadingClearedContentObjectEditForm(selectedContentObjectForEdit.getContentObject().getContentObjectType());
  				return CLEAR_CONTENT_OBJECT_EDIT_AND_LOAD_AGAIN_XHTML;
  			}
  		}
  		*/
  		
  		//Remained in the same page. Set newContentObject flag to false
  		if (contentObjectWasSavedInRepository){
  			newContentObject = false;
  		}
  		return contentObjectPreparationOutcome;
  
  	}
  
  
  	private void resetLoggedInRepositoryUserFolksonomyRootTopics() {
  		loggedInRepositoryUser.getRepositoryUser().getFolksonomy().setRootTopics(null);
  	}
  
  
  	private String prepareContentObjectForSave() throws Exception {
  
  		// generate the accessibility properties from the user provided input values (through the accessibility tab in the content object UI form)
  		String methodOutcome = generateContentObjectAccessibilityPropertiesFromUIFormInputValues();
  		if (methodOutcome.equals("error")) 
  			return "error"; 
  		
  		// the following is a fix for profile.language. If it is an empty list we should nullify it because the pick list UI component 
  		// generates an immutable ArrayList that prevents the repository engine to add the default value (as is set in XSD)
  		//StringProperty language = (StringProperty)selectedContentObjectForEdit.getContentObjectProperty().get("profile.language");
  		//if (language != null && CollectionUtils.isEmpty(language.getSimpleTypeValues())) {
  		//	language.setSimpleTypeValues(null);
  		//}
  		
  		//Validate ContentObject properties
  		validateProperties();
  
  		// check if all mandatory properties have been provided. The checking method produces the appropriate UI messages when a property is missing 
  		//if (mandatoryPropertiesMissing()) {
  		if (cmsPropertyValidatorVisitor.mandatoryPropertiesAreMissing()) {
  			JSFUtilities.addMessage(null, "object.edit.error.required", null, FacesMessage.SEVERITY_WARN); 
  			return "error"; 
  		}
  
  		if (cmsPropertyValidatorVisitor.hasErrors())
  			return "error"; 
  
  
  		// Perform a specific validation in case content object is of 'portal' type
  		if (CmsConstants.PORTAL_CONTENT_OBJECT_TYPE.equals(selectedContentObjectForEdit.getContentObject().getContentObjectType())){
  			
  			String portalSystemName = selectedContentObjectForEdit.getContentObject().getSystemName(); 
  
  			//Look for duplicate portalSystem name
  			ContentObjectCriteria portalCriteria = CmsCriteriaFactory.newContentObjectCriteria();
  			
  			portalCriteria.addContentObjectTypeEqualsCriterion(CmsConstants.PORTAL_CONTENT_OBJECT_TYPE);
  			portalCriteria.addSystemNameEqualsCriterion(portalSystemName);
  			
  			if (selectedContentObjectForEdit.getContentObject().getId() != null){
  				portalCriteria.addIdNotEqualsCriterion(selectedContentObjectForEdit.getContentObject().getId());
  			}
  			portalCriteria.doNotCacheResults();
  			portalCriteria.setOffsetAndLimit(0, 1);
  			
  			CmsOutcome<ContentObject> portalOutcome = contentService.searchContentObjects(portalCriteria, ResourceRepresentationType.CONTENT_OBJECT_LIST);
  			
  			if (portalOutcome !=null && portalOutcome.getCount() > 0){
  				JSFUtilities.addMessage(null, "object.edit.already.existing.portal.system.name", new String[]{portalSystemName}, FacesMessage.SEVERITY_WARN); 
  				return "error"; 
  			}
  		}
  		
  		//Perform one final check. In case content object status is scheduledForPublication but no
  		//webPublication.webPublicationStartDate is provided throw a mandatory error message
  		StringProperty contentObjectStatusProperty = ((StringProperty) selectedContentObjectForEdit.getContentObject().getCmsProperty("profile.contentObjectStatus")); 
  		if (contentObjectStatusProperty != null && contentObjectStatusProperty.hasValues())
  		{
  			String contentObjectStatus = contentObjectStatusProperty.getSimpleTypeValue();
  			
  			if (StringUtils.equals(ContentObjectStatus.scheduledForPublication.toString(), contentObjectStatus))
  			{
  				//Check webPublicationStartDate property
  				CalendarProperty webPublicationStartDateProperty = ((CalendarProperty) selectedContentObjectForEdit.getContentObject().getCmsProperty("webPublication.webPublicationStartDate"));
  				
  				if (webPublicationStartDateProperty == null || webPublicationStartDateProperty.hasNoValues())
  				{
  					String propertyLocalizedLabel = (webPublicationStartDateProperty == null? 
  							JSFUtilities.getLocalizedMessage("object.edit.web.publication.start.date", null) :
  							webPublicationStartDateProperty.getLocalizedLabelOfFullPathforLocaleWithDelimiter(JSFUtilities.getLocaleAsString(), " > ")
  							);
  					
  					JSFUtilities.addMessage("webPublication.webPublicationStartDate", "errors.required", 
  							new String[]{propertyLocalizedLabel}, FacesMessage.SEVERITY_WARN);
  					
  					return "error";
  					
  				}
  			}
  		}
  		
  		
  		return "contentObjectReadyForSave"; 
  
  	}
  
  
  
  	private void validateProperties() {
  
  		ContentObjectTypeDefinition contentObjectTypeDefinition = (ContentObjectTypeDefinition) definitionService.getCmsDefinition(selectedContentObjectForEdit.getContentObject().getContentObjectType(), ResourceRepresentationType.DEFINITION_INSTANCE, false);
  
  		cmsPropertyValidatorVisitor.reset(false);
  
  		cmsPropertyValidatorVisitor.validateContentObjectProperties(selectedContentObjectForEdit.getContentObject(), contentObjectTypeDefinition);
  	}
  
  
  	/*private boolean mandatoryPropertiesMissing() throws Exception {
  
  		boolean mandatoryPropertiesMissing = false;
  
  		// check for mandatory fields in Administrative Bundles
  	//	if (mandatoryPropertiesOfAdministrativeBundlesMissing())
  		//	mandatoryPropertiesMissing = true;
  
  		// check if all mandatory content type properties have been provided
  		if (mandatoryPropertiesOfContentTypeMissing(selectedContentObjectForEdit.getContentObject().getContentObjectType()))
  			mandatoryPropertiesMissing = true;
  
  
  		// if content type is textBased, image, audio or video
  		//if (selectedContentObjectForEdit.isFile()) {
  
  			//Check if  binary channels exist or have been uploaded
  			//if (selectedContentObjectForEdit.getContentObject().getBinaryChannels() == null) {
  				//JSFUtilities.addMessage(null, "contentObjectEdit.noFileHasBeenUploadedError", null, FacesMessage.SEVERITY_ERROR);
  			//}
  		//}
  
  		return mandatoryPropertiesMissing;
  	}
  
  
  	private boolean mandatoryPropertiesOfContentTypeMissing(String contentType) throws Exception {
  
  		boolean mandatoryPropertiesMissing = false;
  
  			MandatoryPropertyDefinitionVisitor mandatoryPropertyDefinitionVisitor = new MandatoryPropertyDefinitionVisitor(selectedContentObjectForEdit.getContentObject());
  			ContentObjectTypeDefinition contentObjectTypeDefinition = definitionService.getContentObjectTypeDefinition(contentType);
  
  			contentObjectTypeDefinition.accept(mandatoryPropertyDefinitionVisitor);
  			mandatoryPropertiesMissing = mandatoryPropertyDefinitionVisitor.mandatoryPropertiesAreMissing();
  
  		return mandatoryPropertiesMissing;
  	}*/
  
  
  	public void grantAccessRightToDraggedAndDroppedUserOrRole_Listener(DropEvent dropEvent) {
  		String typeOfAccessRight = (String) dropEvent.getDropValue();
  		String typeOfDraggedObject = dropEvent.getDragType();
  		
  		Map<String,String> userOrGroupMap = new HashMap<String,String>();
  		if ("user".equals(typeOfDraggedObject)) { 
  			Person user = (Person) dropEvent.getDragValue();
  			userOrGroupMap.put("userOrGroupId", user.getUsername()); 
  			userOrGroupMap.put("userOrGroupName", StringUtils.isBlank(user.getDisplayName())? user.getUsername() : user.getDisplayName()); 
  		}
  		else if ("role".equals(typeOfDraggedObject)) { 
  			String	personGroup = (String) dropEvent.getDragValue();
  			userOrGroupMap.put("userOrGroupId", personGroup); 
  			userOrGroupMap.put("userOrGroupName", Utils.retrieveDisplayNameForRoleOrPerson(identityStoreRunAsSystem, personGroup)); 
  		}
  
  		grantAccessRightToPersonOrRole(AccessRight.valueOf(typeOfAccessRight), userOrGroupMap);
  	}
  	
  	public void grantAccessRightToPerson(Person person, AccessRight typeOfAccessRight){
  		
  		Map<String,String> userOrGroupMap = new HashMap<String,String>();
  		userOrGroupMap.put("userOrGroupId", person.getUsername()); 
  		userOrGroupMap.put("userOrGroupName", StringUtils.isBlank(person.getDisplayName())? person.getUsername() : person.getDisplayName()); 
  		
  		grantAccessRightToPersonOrRole(typeOfAccessRight, userOrGroupMap);
  		
  	}
  
  
  	public void grantAccessRightToPersonOrRole(AccessRight typeOfAccessRight,
  			Map<String, String> userOrRoleMap) {
  		
  		
  		if (typeOfAccessRight != null){
  			
  			if (!userListByAccessRight.containsKey(typeOfAccessRight.toString())){
  				userListByAccessRight.put(typeOfAccessRight.toString(), new ArrayList<Map<String,String>>());
  			}
  			
  			List<Map<String, String>> userList = userListByAccessRight.get(typeOfAccessRight.toString());
  			
  			if (userList != null){
  				if (userOrGroupIsNotInAccessRightList(userList, userOrRoleMap.get("userOrGroupName"))) 
  					userList.add(userOrRoleMap);
  				else {
  					logger.warn("User already in access right list. selection is ignored"); 
  					JSFUtilities.addMessage(null, "object.edit.accessibility.value.already.exists.warning", null, FacesMessage.SEVERITY_WARN); 
  				} 
  			}
  		}
  	}
  
  
  	public void removeUserOrGroupFromAccessRightList_UIAction(List<HashMap<String,String>> accessRightList, HashMap<String,String> userOrGroup) {
  		accessRightList.remove(userOrGroup);
  	}
  
  	private boolean userOrGroupIsNotInAccessRightList(List<Map<String,String>> accessRightList, String userOrGroupName) {
  		if (CollectionUtils.isEmpty(accessRightList))
  			return true;
  		for (Map<String, String> userHashMap : accessRightList) {
  			if (userHashMap.get("userOrGroupName").equals(userOrGroupName)) 
  				return false;
  		}
  		return true;
  	}
  
  	private String generateContentObjectAccessibilityPropertiesFromUIFormForAccessRight(AccessRight accessRight, StringProperty accessibilityProperty){
  		if (accessRight ==null){
  			return "error";
  		}
  		
  		if (radioButtonSelectionMap.containsKey(accessRight.toString())) {
  			
  			switch (Integer.parseInt(radioButtonSelectionMap.get(accessRight.toString()))) {
  			case 1:
  				
  				if (accessibilityProperty.hasValues()){
  					accessibilityProperty.getSimpleTypeValues().clear();
  				}
  				
  				accessibilityProperty.addSimpleTypeValue(ContentAccessMode.ALL.toString());
  				
  				break;
  			case 2:
  				
  				if (accessibilityProperty.hasValues()){
  					accessibilityProperty.getSimpleTypeValues().clear();
  				}
  				
  				accessibilityProperty.addSimpleTypeValue(ContentAccessMode.NONE.toString());
  				
  				break;
  			case 3:
  				
  				List<Map<String, String>> userList = userListByAccessRight.get(accessRight.toString());
  				if (userList == null || userList.size() == 0) {
  					JSFUtilities.addMessage(null, "object.edit.accessibility.value.user.group."+accessRight.toString()+".empty", null, FacesMessage.SEVERITY_ERROR); 
  					return "error"; 
  				}
  				copyUIAccessRightListToContentObjectAccessRightList(userList, accessibilityProperty.getSimpleTypeValues());
  				break;
  			
  			default:
  				// we do not support any other value. reset radio button to default, generate an error message and return
  				JSFUtilities.addMessage(null, "object.edit.invalid.accessibility.value", 
  						new String[] {radioButtonSelectionMap.get(accessRight.toString())}, FacesMessage.SEVERITY_WARN); 
  			
  				radioButtonSelectionMap.put(accessRight.toString(), "1");
  			
  				return "error";
  				
  			}
  			
  			return "success";
  		}
  		
  		return "success";
  		
  	}
  	
  	private String generateContentObjectAccessibilityPropertiesFromUIFormInputValues() {
  		// do nothing if no accessibility properties exist
  		if (!selectedContentObjectForEdit.getContentObject().getComplexCmsRootProperty().isChildPropertyDefined("accessibility")) 
  			return "success"; 
  		// Load Accessibility properties
  		StringProperty canBeReadBy = (StringProperty)selectedContentObjectForEdit.getContentObject().getCmsProperty("accessibility.canBeReadBy"); 
  		StringProperty canBeUpdatedBy = (StringProperty)selectedContentObjectForEdit.getContentObject().getCmsProperty("accessibility.canBeUpdatedBy"); 
  		StringProperty canBeDeletedBy = (StringProperty)selectedContentObjectForEdit.getContentObject().getCmsProperty("accessibility.canBeDeletedBy"); 
  		StringProperty canBeTaggedBy = (StringProperty) selectedContentObjectForEdit.getContentObject().getCmsProperty("accessibility.canBeTaggedBy"); 
  
  		String outcome = generateContentObjectAccessibilityPropertiesFromUIFormForAccessRight(AccessRight.canBeReadBy, canBeReadBy);
  		
  		if (!"error".equals(outcome)){
  		
  			outcome = generateContentObjectAccessibilityPropertiesFromUIFormForAccessRight(AccessRight.canBeUpdatedBy, canBeUpdatedBy);
  			
  			if (!"error".equals(outcome)){
  				outcome = generateContentObjectAccessibilityPropertiesFromUIFormForAccessRight(AccessRight.canBeDeletedBy, canBeDeletedBy);
  				
  				if (!"error".equals(outcome)){
  					return generateContentObjectAccessibilityPropertiesFromUIFormForAccessRight(AccessRight.canBeTaggedBy, canBeTaggedBy);
  				}
  				else{
  					return "error";
  				}
  			}
  			else{
  				return "error";
  			}
  		}
  		else{
  			return "error";
  		}
  		
  	}
  
  
  
  	/* Copy an access right list from content object into a list which is more appropriate for UI presentation. Actually turn user and group ids to meaningful names.
  	 * The access right lists in the content object holds only user / group ids which are not very meaningful to the end user. So we connect to the crm database, we read the
  	 * user / group names and generate the access rights lists which are used by the UI to present meaningful info about access rights 
  	 */
  	private void copyContentObjectAccessRightListToUIAccessRightList(List<String> contentObjectAccessRightList, List<Map<String,String>> uiAccessRightList) {
  		for (String prefixedUserOrGroupId :  contentObjectAccessRightList) {		
  				Map<String,String> userOrGroup = new HashMap<String,String>();
  				userOrGroup.put("userOrGroupId", prefixedUserOrGroupId);
  				
  				userOrGroup.put("userOrGroupName", Utils.retrieveDisplayNameForRoleOrPerson(identityStoreRunAsSystem, prefixedUserOrGroupId));
  				
  				uiAccessRightList.add(userOrGroup);
  		}
  	}
  
  
  
  
  	/* This method does the opposite function of "copyContentObjectAccessRightListToUIAccessRightList" method
  	 * It gets an access right list genetated by the UI after user interaction and produces the appropriate access right list for insertion into the content object
  	 */
  	private void copyUIAccessRightListToContentObjectAccessRightList(List<Map<String,String>> uiAccessRightList, List<String> contentObjectAccessRightList) {
  		
  		//Remove old values and add new ones
  		contentObjectAccessRightList.clear();
  		for (Map<String,String> userOrGroup : uiAccessRightList) {
  			contentObjectAccessRightList.add(userOrGroup.get("userOrGroupId")); 
  		}
  	}
  
  
  	private void initializeInputAndOutputUIComponentsRelatedtoSecurityProperty(AccessRight accessRight, List<String> accessValuesList, String defaultAccessRightValue){
  
  		if (CollectionUtils.isNotEmpty(accessValuesList)) {
  
  			if(accessValuesList.get(0).equals(ContentAccessMode.ALL.toString())) {
  				radioButtonSelectionMap.put(accessRight.toString(), "1");
  			}
  			else if (accessValuesList.get(0).equals(ContentAccessMode.NONE.toString())) {
  				radioButtonSelectionMap.put(accessRight.toString(), "2");
  			}
  			else {
  				radioButtonSelectionMap.put(accessRight.toString(), "3");
  				copyContentObjectAccessRightListToUIAccessRightList(accessValuesList, userListByAccessRight.get(accessRight.toString()));
  			}
  		}
  		else{
  			if (StringUtils.isBlank(defaultAccessRightValue))
  			{
  				//Default value for access right
  				radioButtonSelectionMap.put(accessRight.toString(), "1");
  			}
  			else
  			{
  				if(StringUtils.equals(ContentAccessMode.ALL.toString(), defaultAccessRightValue)) {
  					radioButtonSelectionMap.put(accessRight.toString(), "1");
  				}
  				else if (StringUtils.equals(ContentAccessMode.NONE.toString(), defaultAccessRightValue)) {
  					radioButtonSelectionMap.put(accessRight.toString(), "2");
  				}
  			}
  		}
  	
  	}
  	
  	private void initializeInputAndOutputUIComponentsRelatedtoSecurityProperties() {
  		// do nothing if no accessibility properties exist
  		if (!selectedContentObjectForEdit.getContentObject().getComplexCmsRootProperty().isChildPropertyDefined("accessibility")) 
  			return;
  
  		// Create Accessibility Lists
  		List<String> canBeReadByList = ((StringProperty)selectedContentObjectForEdit.getContentObject().getCmsProperty("accessibility.canBeReadBy")).getSimpleTypeValues(); 
  		List<String> canBeUpdatedByList = ((StringProperty)selectedContentObjectForEdit.getContentObject().getCmsProperty("accessibility.canBeUpdatedBy")).getSimpleTypeValues(); 
  		List<String> canBeDeletedByList = ((StringProperty)selectedContentObjectForEdit.getContentObject().getCmsProperty("accessibility.canBeDeletedBy")).getSimpleTypeValues(); 
  		List<String> canBeTaggedByList = ((StringProperty)selectedContentObjectForEdit.getContentObject().getCmsProperty("accessibility.canBeTaggedBy")).getSimpleTypeValues(); 
  
  		clearUIAccessRightLists();
  		
  		initializeInputAndOutputUIComponentsRelatedtoSecurityProperty(AccessRight.canBeReadBy, canBeReadByList, ContentAccessMode.ALL.toString());
  		initializeInputAndOutputUIComponentsRelatedtoSecurityProperty(AccessRight.canBeUpdatedBy, canBeUpdatedByList, ContentAccessMode.NONE.toString());
  		initializeInputAndOutputUIComponentsRelatedtoSecurityProperty(AccessRight.canBeDeletedBy, canBeDeletedByList, ContentAccessMode.NONE.toString());
  		initializeInputAndOutputUIComponentsRelatedtoSecurityProperty(AccessRight.canBeTaggedBy, canBeTaggedByList,ContentAccessMode.ALL.toString());
  	}
  
  
  	private void clearUIAccessRightLists() {
  		
  		if (radioButtonSelectionMap == null){
  			radioButtonSelectionMap = new HashMap<String, String>();
  		}
  		else{
  			radioButtonSelectionMap.clear();
  		}
  		
  		if (userListByAccessRight == null){
  			userListByAccessRight = new HashMap<String, List<Map<String,String>>>();
  		}
  		else{
  			userListByAccessRight.clear();
  		}
  		
  		for (AccessRight accessRight : AccessRight.values()){
  			userListByAccessRight.put(accessRight.toString(), new ArrayList<Map<String,String>>());
  		}
  		
  	}
  
  
  	public void removeTopicFromContentObjectSubject_UIAction() {
  		try {
  			getSelectedContentObjectForEdit().removeTopicFromContentObjectSubject(selectedTopicIdForRemovalFromContentObjectSubject);
  			updateListOfTopicIdsWhoseNumberOfContentObjectReferrersShouldBeUpdated(selectedTopicIdForRemovalFromContentObjectSubject);
  		}
  		catch (Exception e) {
  			logger.error("",e); 
  			JSFUtilities.addMessage(null, "application.unknown.error.message", null, FacesMessage.SEVERITY_WARN);
  			return;
  		}
  	}
  
  	public void addNewTagToContentObjectSubject_UIAction() {
  		if (StringUtils.isNotBlank(newTagLabel)) {
  			//	check if the new tag exists in user tags
  			Topic newUserTag = cmsUtilities.createNewUserTag(newTagLabel, JSFUtilities.getLocaleAsString(), loggedInRepositoryUser.getRepositoryUser());
  			if (cmsUtilities.findTopicInTopicListByLocalizedTopicLabel(loggedInRepositoryUser.getRepositoryUser().getFolksonomy().getRootTopics(), 
  					newTagLabel) != null) {
  				logger.warn("The user tag is not a new tag since it is already in user tags"); 
  				JSFUtilities.addMessage(null, "object.edit.tag.already.exists", null, FacesMessage.SEVERITY_WARN); 
  				return;
  			}
  
  			// check if the new tag is already in contentObject subject. Remember that the new tags are saved with the object and are not yet in the list of user tags, so the above check is not enough 
  			if (cmsUtilities.findTopicInTopicListByLocalizedTopicLabel(((TopicReferenceProperty)selectedContentObjectForEdit.getContentObject().getCmsProperty("profile.subject")).getSimpleTypeValues(),  
  					newTagLabel) != null) {
  				logger.warn("The user tag is already in content object subject."); 
  				JSFUtilities.addMessage(null, "object.edit.tag.already.selected", null, FacesMessage.SEVERITY_WARN); 
  				return;
  			}
  
  			// if we have reached this point everything is ok. Let proceed to add the new  tag
  			//Save Tag
  			topicService.save(newUserTag);
  
  			//Attach tag to content object
  			((TopicReferenceProperty)selectedContentObjectForEdit.getContentObject().getCmsProperty("profile.subject")).addSimpleTypeValue(newUserTag); 
  
  			// reset tag value
  			newTagLabel = null;
  
  			//Reset loggedinRepositoryUser's root topics
  			//in order to force aspect to reload them from database
  			resetLoggedInRepositoryUserFolksonomyRootTopics();
  
  		}
  		else {
  			logger.error("An empty label has been provided. User Tag cannot be created with an empty label"); 
  			JSFUtilities.addMessage(null, "object.edit.empty.tag.error", null, FacesMessage.SEVERITY_WARN); 
  
  		}
  	}
  
  	public void addExistingUserTagToContentObjectSubject_UIAction() {
  		Topic selectedLoggedInUserTag = cmsUtilities.findTopicInTopicListByTopicId(loggedInRepositoryUser.getRepositoryUser().getFolksonomy().getRootTopics(), selectedLoggedInUserTagId);
  		if (selectedLoggedInUserTag != null) {
  			//	check if the selected tag is already in contentObject subject 
  			if (cmsUtilities.findTopicInTopicListByLocalizedTopicLabel(((TopicReferenceProperty)selectedContentObjectForEdit.getContentObject().getCmsProperty("profile.subject")).getSimpleTypeValues(),  
  					selectedLoggedInUserTag.getAvailableLocalizedLabel(JSFUtilities.getLocaleAsString())) != null) {
  				logger.warn("The selected tag is already in content object subject."); 
  				JSFUtilities.addMessage(null, "object.edit.tag.already.selected", null, FacesMessage.SEVERITY_WARN); 
  				return;
  			}
  
  			// if we reached this point everything is ok. Let proceed to add the selected tag
  			((TopicReferenceProperty)selectedContentObjectForEdit.getContentObject().getCmsProperty("profile.subject")).addSimpleTypeValue(selectedLoggedInUserTag); 
  		}
  		else {
  			logger.error("Some strange problem occured. Selected tag is not present in logged in user Tags List (loggedInUserTags)"); 
  			JSFUtilities.addMessage(null, "application.unknown.error.message", null, FacesMessage.SEVERITY_WARN);
  			return;
  
  		}
  		setSelectedLoggedInUserTagId(null);
  
  	}
  
  
  
  	public void addDraggedAndDroppedSpace_Listener(DropEvent dropEvent) {
  		spaceToCopyNewObject = (Space) dropEvent.getDragValue();
  	}
  
  	@Observer({SeamEventNames.UPDATE_LIST_OF_TOPICS_WHOSE_NUMBER_OF_CO_REFERENCES_SHOULD_CHANGE})
  	public void updateListOfTopicIdsWhoseNumberOfContentObjectReferrersShouldBeUpdated(String topicId) {
  		listOfTopicIdsWhoseNumberOfContentObjectReferrersShouldBeUpdated.add(topicId);
  	}
  
  	public void addDraggedAndDroppedTopicToContentObjectProfileSubject_Listener(DropEvent dropEvent) {
  		LazyLoadingTopicTreeNode topicTreeNode = (LazyLoadingTopicTreeNode) dropEvent.getDragValue();
  
  		//Check that dropped Topic belongs to Subject Taxonomy
  		Topic droppedTopic = topicTreeNode.getTopic();
  
  		//Check that Dropped Topic belongs to an accepted taxonomy
  		List<String> acceptedTaxonomies = selectedContentObjectForEdit.getAcceptedTaxonomiesForProfileSubject();
  		if (CollectionUtils.isNotEmpty(acceptedTaxonomies)){
  			String droppedTopicTaxonomyName = null;
  			if (droppedTopic != null && droppedTopic.getTaxonomy() != null){ 
  				droppedTopicTaxonomyName = droppedTopic.getTaxonomy().getName();
  			}
  
  			if (StringUtils.isEmpty(droppedTopicTaxonomyName) || !acceptedTaxonomies.contains(droppedTopicTaxonomyName)){
  				JSFUtilities.addMessage(null, "object.edit.invalid.topic.does.not.belong.to.accepted.taxonomies", null , FacesMessage.SEVERITY_WARN); 
  				return;
  			}
  		}
  
  		//Load existing subject topics
  		List<Topic> subjectTopics= ((TopicReferenceProperty)selectedContentObjectForEdit.getContentObject().getCmsProperty("profile.subject")).getSimpleTypeValues(); 
  
  		String selectedTopicId = droppedTopic.getId();
  
  		// check if selected topic is already in subject
  		boolean topicExists = false;
  		for (Topic subjectTopic : subjectTopics) {
  			// we check first if topic Id exists because there may be new user tags in the list and new tags do not have an id yet 
  			if (subjectTopic.getId() != null && subjectTopic.getId().equals(selectedTopicId)) {
  				topicExists = true;
  				break;
  			}
  		}
  
  		if (!topicExists) {
  			((TopicReferenceProperty)selectedContentObjectForEdit.getContentObject().getCmsProperty("profile.subject")).addSimpleTypeValue(topicTreeNode.getTopic()); 
  
  			JSFUtilities.addMessage(null, "object.edit.topic.added.to.content.object", null , FacesMessage.SEVERITY_INFO); 
  
  			updateListOfTopicIdsWhoseNumberOfContentObjectReferrersShouldBeUpdated(topicTreeNode.getTopic().getId());
  
  		}
  		else
  			JSFUtilities.addMessage(null, "object.edit.topic.already.exists", null , FacesMessage.SEVERITY_WARN); 
  
  
  	}
  
  
  	public void removeCreator_UIAction(Integer index){
  		if (index != null){
  			StringProperty creator = (StringProperty)selectedContentObjectForEdit.getContentObjectProperty().get("profile.creator"); 
  			if (creator != null)
  				creator.removeSimpleTypeValue(index);
  		}
  	}
  	
  	public void removeLanguage_UIAction(Integer index){
  		if (index != null){
  			StringProperty language = (StringProperty)selectedContentObjectForEdit.getContentObjectProperty().get("profile.language"); 
  			if (language != null)
  				language.removeSimpleTypeValue(index);
  		}
  	}
  
  
  	public void addNewCreator_UIAction(){
  		if (StringUtils.isNotBlank(newCreatorName)){
  			StringProperty creator = (StringProperty)selectedContentObjectForEdit.getContentObjectProperty().get("profile.creator"); 
  			if (creator != null)
  				creator.addSimpleTypeValue(newCreatorName);
  		}
  
  		newCreatorName = null;
  	}
  
  
  	public boolean isThumbnailPropertyDefinedForEditedContentObject(){
  		return selectedContentObjectForEdit.getContentObject() != null &&
  		selectedContentObjectForEdit.getContentObject().getTypeDefinition() != null &&
  		selectedContentObjectForEdit.getContentObject().getTypeDefinition().hasCmsPropertyDefinition("thumbnail"); 
  	}
  	/*
  	 * This method generates a thumbnail from a binary channel of mime type jpeg and
  	 * saves outcome to property 'thumbnail', if there is any property defined for this type
  	 */
  	public void generateThumbnailFromSelectedBinaryChannel_UIAction(Integer indexOfSimpleCmsPropertyWrapper,
  			Integer indexOfSimpleCmsPropertyValueWrapper){
  
  		//Check if content object type definition has a 'thumbnail' property
  		if (selectedContentObjectForEdit.getContentObject().getTypeDefinition().hasCmsPropertyDefinition("thumbnail")){ 
  
  			//Retrieve simple cms property wrapper which contains binary channel
  			SimpleCmsPropertyValueWrapper binaryPropertyValueWrapper = complexCmsPropertyEdit.getSimpleCmsPropertyValueWrapper(indexOfSimpleCmsPropertyWrapper, indexOfSimpleCmsPropertyValueWrapper);
  			BinaryChannel binaryChannelWhoseThumbnailWillBeCreated = binaryPropertyValueWrapper.getBinaryChannelValue();
  
  			if (binaryChannelWhoseThumbnailWillBeCreated == null || binaryChannelWhoseThumbnailWillBeCreated.getContent() == null){
  				logger.error("Could not find selected binary channel in order to create thumbnail"); 
  				JSFUtilities.addMessage(null, "application.unknown.error.message", null, FacesMessage.SEVERITY_WARN); 
  				return;
  			}
  
  			String mimeType = binaryChannelWhoseThumbnailWillBeCreated.getMimeType();
  
  			//Generate Thumbnail only for specific mimetTypes
  			if (mimeType != null && 
  					( mimeType.equals("image/jpeg")	|| mimeType.equals("image/gif")	|| mimeType.equals("image/png") || 
  							mimeType.equals("image/x-png"))){ 
  
  				BinaryProperty thumbnailProperty = (BinaryProperty)selectedContentObjectForEdit.getContentObject().getCmsProperty("thumbnail"); 
  				if (thumbnailProperty == null){
  					logger.error("Could not find 'thumbnail' property for content object "+selectedContentObjectForEdit.getContentObject().getId()); 
  					JSFUtilities.addMessage(null, "application.unknown.error.message",  null, FacesMessage.SEVERITY_WARN); 
  					return;
  				}
  
  				try {
  					// the method name for generating the thumbnail is misleading. It gets a JPEG, PNG or GIF image and generated a thumbnail in PNG format  
  					byte[] thumbnailContent = ImageUtils.changeAspectRatioAndResize(binaryChannelWhoseThumbnailWillBeCreated.getContent(),
  							"image/png", 128, 0, 1.0, "TOPLEFT");
  
  					BinaryChannel thumbnailBinaryChannel = thumbnailProperty.getSimpleTypeValue();
  