diff --git a/config/alfresco/messages/webclient.properties b/config/alfresco/messages/webclient.properties index 93bacf2207..f637613589 100644 --- a/config/alfresco/messages/webclient.properties +++ b/config/alfresco/messages/webclient.properties @@ -38,6 +38,7 @@ deep_copy=Deep Copy new_edition=New Edition new_edition_from=New Edition from this new_edition_tooltip=Create a new edition from this multilingual content. +properties_close=Edit multilingual properties when the dialog close pivot_translation=Pivot translation edition=Edition editions=Editions @@ -50,12 +51,16 @@ step1_choose_translation_desc=Step one, select the starting item from the list o new_edition_choose_translation_title=Step One - Starting item new_edition_choose_translation_desc=Choose the content item you want to create the new edition. step2_edition_details=Edition Details +step1_edition_details_desc=Step one, edit the detail of the new edition step2_edition_details_desc=Step two, edit the detail of the new edition -new_edition_details_title=Step Two - Edition Details +step1_new_edition_details_title=Step One - Edition Details +step2_new_edition_details_title=Step Two - Edition Details new_edition_details_desc=Enter information about the new edition. step3_edition_summary=Summary +step2_edition_summary_desc=Step two, view the summary of the new edition step3_edition_summary_desc=Step three, view the summary of the new edition -new_edition_summary_title=Step Three - Summary +step2_new_edition_summary_title=Step Two - Summary +step3_new_edition_summary_title=Step Three - Summary new_edition_summary_desc=The information you entered is shown below. new_edition_finish_instruction=To close this wizard and apply your changes click Finish. edition_properties=Edition Properties @@ -69,8 +74,19 @@ create_new_edition_using=Create a new edition using translations_checked_out_error=Cannot create a new edition because the following translations are checked out. available_translations=Available translations marker_tooltip=Change your Content Language to view or edit text in different languages. +checked_out_by=Checked out by +doc_name=Document name +title_mlcontainer_details=Multilingual Document Details +# Versioning of the metadatas + +versioned_details_of=Versioned details of +versioned_documentdetails_description=View the details about the versioned content. +title_versioned_doc_details=Versioned Document Details +view_versioned_properties=Properties from a versioned content +view_versioned_properties_description=View the properties of a versioned content. + # Date Pattern date_pattern=d MMMM yyyy date_time_pattern=d MMMM yyyy HH:mm @@ -1658,6 +1674,9 @@ delete_post_info=To remove this post from the topic, click OK. delete_post_confirm=Are you sure you want to delete the post from \"{0}\"? delete_file_info=To remove this file and any previous versions, click OK. delete_file_confirm=Are you sure you want to delete \"{0}\" and all previous versions? +delete_translation_confirm=Are you sure you want to delete \"{0}\" and all previous versions? Its multilingual properties will not be recoverable. +delete_empty_translation_confirm=Are you sure you want to permanently delete \"{0}\"? This document will not be recovered. +delete_ml_container_confirm=Are you sure you want to delete \"{0}\" and all previous editions? Each translation will be deleted too. delete_rule_info=To remove this rule from the space, click Yes. delete_user_info=To delete this user from the system, click Yes. delete_rule_confirm=Are you sure you want to delete \"{0}\"? diff --git a/config/alfresco/web-client-config-actions.xml b/config/alfresco/web-client-config-actions.xml index 06c92f06a0..4bc48a75c9 100644 --- a/config/alfresco/web-client-config-actions.xml +++ b/config/alfresco/web-client-config-actions.xml @@ -590,9 +590,6 @@ - - Write - org.alfresco.web.action.evaluator.AddTranslationEvaluator add_translation add_translation_tooltip @@ -606,9 +603,6 @@ - - Write - org.alfresco.web.action.evaluator.AddTranslationEvaluator add_translation_wc add_translation_wc_tooltip @@ -628,7 +622,7 @@ manage_multilingual_details manage_multilingual_details_tooltip /images/icons/multilingual_details.gif - #{BrowseBean.setupContentAction} + #{BrowseBean.setupMLContainerContentAction} dialog:showMLContainerDetails #{actionContext.id} @@ -640,6 +634,7 @@ Write + org.alfresco.web.action.evaluator.NewEditionEvaluator new_edition new_edition_tooltip /images/icons/new_edition_icon.gif @@ -761,6 +756,10 @@ + + + + diff --git a/config/alfresco/web-client-config-wizards.xml b/config/alfresco/web-client-config-wizards.xml index fa6ce9eea7..5a3cec9dc5 100644 --- a/config/alfresco/web-client-config-wizards.xml +++ b/config/alfresco/web-client-config-wizards.xml @@ -424,10 +424,10 @@ - - @@ -439,7 +439,7 @@ instruction-id="regenerate_renditions_summary_instruction" /> - + - + + title-id="step1_choose_translation" + description-id="step1_choose_translation_desc"> + title-id="new_edition_choose_translation_title" + description-id="new_edition_choose_translation_desc" + instruction-id="default_instruction" /> @@ -515,11 +515,49 @@ title-id="step3_edition_summary" description-id="step3_edition_summary_desc"> + + + + + + + + + + + + + + + + + + diff --git a/source/java/org/alfresco/web/action/evaluator/AddTranslationEvaluator.java b/source/java/org/alfresco/web/action/evaluator/AddTranslationEvaluator.java index 6e7391e0c1..0a8320337f 100644 --- a/source/java/org/alfresco/web/action/evaluator/AddTranslationEvaluator.java +++ b/source/java/org/alfresco/web/action/evaluator/AddTranslationEvaluator.java @@ -15,11 +15,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ package org.alfresco.web.action.evaluator; @@ -31,36 +31,58 @@ import org.alfresco.service.cmr.ml.MultilingualContentService; import org.alfresco.web.action.ActionEvaluator; import org.alfresco.web.app.servlet.FacesHelper; import org.alfresco.web.bean.UserPreferencesBean; +import org.alfresco.web.bean.ml.MultilingualUtils; import org.alfresco.web.bean.repository.Node; /** - * Evaluates whether the Add Translation (with or without content) action should be visible. - * - * If the node is not already Multilingual, locked, or if a translation exists for each available + * Evaluates whether the Add Translation (with or without content) action should be visible. + * + * If the node is not already Multilingual, locked, or if a translation exists for each available * filter language, don't allow the action. - * + * + * The current user can add a translation to a translation set only if he has enough right to add + * a content to the space where the pivot translation is located in. + * * @author Yannick Pignot */ public class AddTranslationEvaluator implements ActionEvaluator { public boolean evaluate(Node node) { - FacesContext fc = FacesContext.getCurrentInstance(); + boolean isNodeMultililingal = node.hasAspect(ContentModel.ASPECT_MULTILINGUAL_DOCUMENT); + boolean isMLContainer = node.getType().equals(ContentModel.TYPE_MULTILINGUAL_CONTAINER); - MultilingualContentService mlservice = - (MultilingualContentService) FacesHelper.getManagedBean(fc, "MultilingualContentService"); + // the node must be multiligual (translation or ml container) + if(isNodeMultililingal || isMLContainer) + { + FacesContext fc = FacesContext.getCurrentInstance(); - UserPreferencesBean userprefs = - (UserPreferencesBean) FacesHelper.getManagedBean(fc, "UserPreferencesBean"); + // the current user must have enough right to add a content to the space + // where the pivot translation is located in + if(MultilingualUtils.canAddChildrenToPivotSpace(node, fc)) + { + MultilingualContentService mlservice = + (MultilingualContentService) FacesHelper.getManagedBean(fc, "MultilingualContentService"); - // the number of translation of this document - int availableTranslationCount = mlservice.getTranslations(node.getNodeRef()).size(); - // the total number of available languages for the translation - int contentFilterLanguagesCount = userprefs.getContentFilterLanguages(false).length; + UserPreferencesBean userprefs = + (UserPreferencesBean) FacesHelper.getManagedBean(fc, "UserPreferencesBean"); - return (node.isLocked() == false && - node.hasAspect(ContentModel.ASPECT_WORKING_COPY) == false && - node.hasAspect(ContentModel.ASPECT_MULTILINGUAL_DOCUMENT) == true && - availableTranslationCount < contentFilterLanguagesCount); + // the number of translation of this document + int availableTranslationCount = mlservice.getTranslations(node.getNodeRef()).size(); + // the total number of available languages for the translation + int contentFilterLanguagesCount = userprefs.getContentFilterLanguages(false).length; + + // the number of translation must be < to the total number of available language for the content filter + return (availableTranslationCount < contentFilterLanguagesCount); + } + else + { + return false; + } + } + else + { + return false; + } } } \ No newline at end of file diff --git a/source/java/org/alfresco/web/action/evaluator/CutNodeEvaluator.java b/source/java/org/alfresco/web/action/evaluator/CutNodeEvaluator.java index 0911b109a2..9e95c2cd36 100644 --- a/source/java/org/alfresco/web/action/evaluator/CutNodeEvaluator.java +++ b/source/java/org/alfresco/web/action/evaluator/CutNodeEvaluator.java @@ -15,35 +15,50 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ package org.alfresco.web.action.evaluator; +import javax.faces.context.FacesContext; + import org.alfresco.model.ContentModel; import org.alfresco.web.action.ActionEvaluator; +import org.alfresco.web.bean.ml.MultilingualUtils; import org.alfresco.web.bean.repository.Node; /** - * Evaluates whether the Cut Node action should be visible. - * - * Among all available operations over non-multilingual documents (i.e. copy, - * delete, start discussion, etc), there is a missing one: Move. - * Translations cannot be moved due to the exiting link it has with the logical - * document. Despite it is technically achievable, it could be functionally - * troublesome. Spreading translations of the same semantic message among several + * Evaluates whether the Cut Node action should be visible. + * + * Among all available operations over non-multilingual documents (i.e. copy, + * delete, start discussion, etc), there is a missing one: Move. + * Translations cannot be moved due to the exiting link it has with the logical + * document. Despite it is technically achievable, it could be functionally + * troublesome. Spreading translations of the same semantic message among several * spaces could lead to confusion and problems. - * + * + * If the node to move is a mlContainer, the user must have enough right to delete each translation + * * @author Yannick Pignot */ public class CutNodeEvaluator implements ActionEvaluator { public boolean evaluate(Node node) { + + FacesContext fc = FacesContext.getCurrentInstance(); + + // the node to delete is a ml container, test if the user has enought right on each translation + if(node.getType().equals(ContentModel.TYPE_MULTILINGUAL_CONTAINER)) + { + return MultilingualUtils.canMoveEachTranslation(node, fc); + } + + boolean eval = true; // impossible to cut/copy a translation without content. @@ -53,9 +68,9 @@ public class CutNodeEvaluator implements ActionEvaluator } else { - eval = !node.getAspects().contains(ContentModel.ASPECT_MULTILINGUAL_DOCUMENT); + eval = !node.getAspects().contains(ContentModel.ASPECT_MULTILINGUAL_DOCUMENT); } - - return eval; + + return eval; } } diff --git a/source/java/org/alfresco/web/action/evaluator/DeleteDocEvaluator.java b/source/java/org/alfresco/web/action/evaluator/DeleteDocEvaluator.java index 8fc5d75f95..4752d41576 100644 --- a/source/java/org/alfresco/web/action/evaluator/DeleteDocEvaluator.java +++ b/source/java/org/alfresco/web/action/evaluator/DeleteDocEvaluator.java @@ -15,11 +15,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ package org.alfresco.web.action.evaluator; @@ -30,11 +30,12 @@ import org.alfresco.model.ContentModel; import org.alfresco.service.cmr.ml.MultilingualContentService; import org.alfresco.web.action.ActionEvaluator; import org.alfresco.web.app.servlet.FacesHelper; +import org.alfresco.web.bean.ml.MultilingualUtils; import org.alfresco.web.bean.repository.Node; /** * UI Action Evaluator - Delete document. - * + * * @author Kevin Roast */ public class DeleteDocEvaluator implements ActionEvaluator @@ -44,14 +45,20 @@ public class DeleteDocEvaluator implements ActionEvaluator */ public boolean evaluate(Node node) { + FacesContext fc = FacesContext.getCurrentInstance(); + + // the node to delete is a ml container, test if the user has enought right on each translation + if(node.getType().equals(ContentModel.TYPE_MULTILINGUAL_CONTAINER)) + { + return MultilingualUtils.canDeleteEachTranslation(node, fc); + } + boolean isPivot = false; // special case for multilingual documents if (node.getAspects().contains(ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)) { - FacesContext fc = FacesContext.getCurrentInstance(); - - MultilingualContentService mlservice = + MultilingualContentService mlservice = (MultilingualContentService) FacesHelper.getManagedBean(fc, "MultilingualContentService"); // if the translation is the last translation, user can delete it @@ -67,7 +74,7 @@ public class DeleteDocEvaluator implements ActionEvaluator } // finally, the node is not the pivot translation, user can delete it } - + return (node.isLocked() == false && node.hasAspect(ContentModel.ASPECT_WORKING_COPY) == false && isPivot == false); diff --git a/source/java/org/alfresco/web/action/evaluator/NewEditionEvaluator.java b/source/java/org/alfresco/web/action/evaluator/NewEditionEvaluator.java new file mode 100644 index 0000000000..c59164ca0f --- /dev/null +++ b/source/java/org/alfresco/web/action/evaluator/NewEditionEvaluator.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.web.action.evaluator; + +import javax.faces.context.FacesContext; + +import org.alfresco.web.action.ActionEvaluator; +import org.alfresco.web.bean.ml.MultilingualUtils; +import org.alfresco.web.bean.repository.Node; + +/** + * Evaluates whether the new edition wizard action should be visible. + * + * The creation of a new edtion implies the deletion of each translation and the creation + * of new content in the space + * + * @author Yanick Pignot + */ +public class NewEditionEvaluator implements ActionEvaluator +{ + public boolean evaluate(Node node) + { + FacesContext fc = FacesContext.getCurrentInstance(); + return MultilingualUtils.canStartNewEditon(node, fc); + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/web/bean/BrowseBean.java b/source/java/org/alfresco/web/bean/BrowseBean.java index a1affbb3ee..46fe89d4f0 100644 --- a/source/java/org/alfresco/web/bean/BrowseBean.java +++ b/source/java/org/alfresco/web/bean/BrowseBean.java @@ -15,11 +15,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ package org.alfresco.web.bean; @@ -46,6 +46,7 @@ import org.alfresco.model.ContentModel; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.TypeDefinition; import org.alfresco.service.cmr.lock.LockService; +import org.alfresco.service.cmr.ml.MultilingualContentService; import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.model.FileInfo; import org.alfresco.service.cmr.repository.ChildAssociationRef; @@ -90,17 +91,17 @@ import org.apache.log4j.Priority; /** * Bean providing properties and behaviour for the main folder/document browse screen and * search results screens. - * + * * @author Kevin Roast */ public class BrowseBean implements IContextListener { /** Public JSF Bean name */ public static final String BEAN_NAME = "BrowseBean"; - - + + // ------------------------------------------------------------------------------ - // Construction + // Construction /** * Default Constructor @@ -108,14 +109,14 @@ public class BrowseBean implements IContextListener public BrowseBean() { UIContextService.getInstance(FacesContext.getCurrentInstance()).registerBean(this); - + initFromClientConfig(); } - - + + // ------------------------------------------------------------------------------ - // Bean property getters and setters - + // Bean property getters and setters + /** * @param nodeService The NodeService to set. */ @@ -131,13 +132,13 @@ public class BrowseBean implements IContextListener { this.searchService = searchService; } - + /** * @param userPreferencesBean The UserPreferencesBean to set. */ public void setUserPreferencesBean(UserPreferencesBean userPreferencesBean) { - this.userPreferencesBean = userPreferencesBean; + this.userPreferencesBean = userPreferencesBean; } /** @@ -147,7 +148,7 @@ public class BrowseBean implements IContextListener { this.lockService = lockService; } - + /** * @param navigator The NavigationBean to set. */ @@ -155,7 +156,7 @@ public class BrowseBean implements IContextListener { this.navigator = navigator; } - + /** * @param dictionaryService The DictionaryService to set. */ @@ -163,7 +164,15 @@ public class BrowseBean implements IContextListener { this.dictionaryService = dictionaryService; } - + + /** + * @param multilingualContentService The Multilingual Content Service to set. + */ + public void setMultilingualContentService(MultilingualContentService multilingualContentService) + { + this.multilingualContentService = multilingualContentService; + } + /** * @param fileFolderService The FileFolderService to set. */ @@ -171,7 +180,7 @@ public class BrowseBean implements IContextListener { this.fileFolderService = fileFolderService; } - + /** * @return Returns the browse View mode. See UIRichList */ @@ -179,7 +188,7 @@ public class BrowseBean implements IContextListener { return this.browseViewMode; } - + /** * @param browseViewMode The browse View mode to set. See UIRichList. */ @@ -187,7 +196,7 @@ public class BrowseBean implements IContextListener { this.browseViewMode = browseViewMode; } - + /** * @return Returns true if dashboard view is available for the current node. */ @@ -230,7 +239,7 @@ public class BrowseBean implements IContextListener this.pageSizeSpaces = pageSizeSpaces; this.pageSizeSpacesStr = Integer.toString(pageSizeSpaces); } - + public String getPageSizeContentStr() { return this.pageSizeContentStr; @@ -259,7 +268,7 @@ public class BrowseBean implements IContextListener return Application.getClientConfig(FacesContext.getCurrentInstance()). getSearchMinimum(); } - + /** * @return Returns the panels expanded state map. */ @@ -275,7 +284,7 @@ public class BrowseBean implements IContextListener { this.panels = panels; } - + /** * @return Returns the Space Node being used for the current browse screen action. */ @@ -283,7 +292,7 @@ public class BrowseBean implements IContextListener { return this.actionSpace; } - + /** * @param actionSpace Set the Space Node to be used for the current browse screen action. */ @@ -298,7 +307,7 @@ public class BrowseBean implements IContextListener } this.actionSpace = actionSpace; } - + /** * @return The document node being used for the current operation */ @@ -344,7 +353,7 @@ public class BrowseBean implements IContextListener externalForceRefresh = false; } } - + /** * @return Returns the contentRichList. */ @@ -352,7 +361,7 @@ public class BrowseBean implements IContextListener { return this.contentRichList; } - + /** * @param spacesRichList The spacesRichList to set. */ @@ -372,7 +381,7 @@ public class BrowseBean implements IContextListener this.spacesRichList.setValue(null); } } - + /** * @return Returns the spacesRichList. */ @@ -380,7 +389,7 @@ public class BrowseBean implements IContextListener { return this.spacesRichList; } - + /** * @return Returns the statusMessage component. */ @@ -396,7 +405,7 @@ public class BrowseBean implements IContextListener { this.statusMessage = statusMessage; } - + /** * @return Returns the deleteMessage. */ @@ -412,10 +421,10 @@ public class BrowseBean implements IContextListener { this.deleteMessage = deleteMessage; } - + /** * Page accessed bean method to get the container nodes currently being browsed - * + * * @return List of container Node objects for the current browse location */ public List getNodes() @@ -436,15 +445,15 @@ public class BrowseBean implements IContextListener } } List result = this.containerNodes; - + // we clear the member variable during invalidateComponents() - + return result; } - + /** * Page accessed bean method to get the content nodes currently being browsed - * + * * @return List of content Node objects for the current browse location */ public List getContent() @@ -462,12 +471,12 @@ public class BrowseBean implements IContextListener } } List result = this.contentNodes; - + // we clear the member variable during invalidateComponents() - + return result; } - + /** * Setup the common properties required at data-binding time. *

@@ -476,8 +485,8 @@ public class BrowseBean implements IContextListener * by name. Information such as download URL, size and filetype are provided etc. *

* We use a set of anonymous inner classes to provide the implemention for the property - * getters. The interfaces are only called when the properties are first requested. - * + * getters. The interfaces are only called when the properties are first requested. + * * @param node Node to add the properties too */ public void setupCommonBindingProperties(Node node) @@ -491,11 +500,11 @@ public class BrowseBean implements IContextListener node.addPropertyResolver("size", this.resolverSize); node.addPropertyResolver("lang", this.resolverLang); } - - + + // ------------------------------------------------------------------------------ - // IContextListener implementation - + // IContextListener implementation + /** * @see org.alfresco.web.app.context.IContextListener#contextUpdated() */ @@ -503,7 +512,7 @@ public class BrowseBean implements IContextListener { invalidateComponents(); } - + /** * @see org.alfresco.web.app.context.IContextListener#areaChanged() */ @@ -519,7 +528,7 @@ public class BrowseBean implements IContextListener { // nothing to do } - + // ------------------------------------------------------------------------------ // NodeEventListener listeners @@ -530,7 +539,7 @@ public class BrowseBean implements IContextListener { getNodeEventListeners().add(listener); } - + /** * Remove a listener from the list of those called by BrowseBean */ @@ -538,41 +547,41 @@ public class BrowseBean implements IContextListener { getNodeEventListeners().remove(listener); } - - + + // ------------------------------------------------------------------------------ - // Navigation action event handlers - + // Navigation action event handlers + /** * Change the current view mode based on user selection - * + * * @param event ActionEvent */ public void viewModeChanged(ActionEvent event) { UIModeList viewList = (UIModeList)event.getComponent(); - + // get the view mode ID String viewMode = viewList.getValue().toString(); - + if (VIEWMODE_DASHBOARD.equals(viewMode) == false) - { + { // set the page size based on the style of display int pageSize = this.viewsConfig.getDefaultPageSize(PAGE_NAME_BROWSE, viewMode); setPageSizeContent(pageSize); setPageSizeSpaces(pageSize); - + if (logger.isDebugEnabled()) logger.debug("Browse view page size set to: " + pageSize); - + setDashboardView(false); - + // push the view mode into the lists setBrowseViewMode(viewMode); - + // setup dispatch context for custom views this.navigator.setupDispatchContext(this.navigator.getCurrentNode()); - + navigateBrowseScreen(); } else @@ -581,14 +590,14 @@ public class BrowseBean implements IContextListener setDashboardView(true); } } - - + + // ------------------------------------------------------------------------------ // Helper methods - + /** * Query a list of nodes for the specified parent node Id - * + * * @param parentNodeId Id of the parent node or null for the root node */ private void queryBrowseNodes(String parentNodeId) @@ -596,14 +605,14 @@ public class BrowseBean implements IContextListener long startTime = 0; if (logger.isDebugEnabled()) startTime = System.currentTimeMillis(); - + UserTransaction tx = null; try { FacesContext context = FacesContext.getCurrentInstance(); tx = Repository.getUserTransaction(context, true); tx.begin(); - + NodeRef parentRef; if (parentNodeId == null) { @@ -623,35 +632,35 @@ public class BrowseBean implements IContextListener { // create our Node representation from the NodeRef NodeRef nodeRef = fileInfo.getNodeRef(); - + // find it's type so we can see if it's a node we are interested in QName type = this.nodeService.getType(nodeRef); - + // make sure the type is defined in the data dictionary TypeDefinition typeDef = this.dictionaryService.getType(type); - + if (typeDef != null) { MapNode node = null; - + // look for File content node if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT)) { // create our Node representation node = new MapNode(nodeRef, this.nodeService, fileInfo.getProperties()); setupCommonBindingProperties(node); - + this.contentNodes.add(node); } // look for Space folder node - else if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_FOLDER) == true && + else if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_FOLDER) == true && this.dictionaryService.isSubClass(type, ContentModel.TYPE_SYSTEM_FOLDER) == false) { // create our Node representation node = new MapNode(nodeRef, this.nodeService, fileInfo.getProperties()); node.addPropertyResolver("icon", this.resolverSpaceIcon); node.addPropertyResolver("smallIcon", this.resolverSmallIcon); - + this.containerNodes.add(node); } // look for File Link object node @@ -666,7 +675,7 @@ public class BrowseBean implements IContextListener node.addPropertyResolver("fileType32", this.resolverFileType32); node.addPropertyResolver("size", this.resolverSize); node.addPropertyResolver("lang", this.resolverLang); - + this.contentNodes.add(node); } else if (ApplicationModel.TYPE_FOLDERLINK.equals(type)) @@ -675,10 +684,10 @@ public class BrowseBean implements IContextListener node = new MapNode(nodeRef, this.nodeService, fileInfo.getProperties()); node.addPropertyResolver("icon", this.resolverSpaceIcon); node.addPropertyResolver("smallIcon", this.resolverSmallIcon); - + this.containerNodes.add(node); } - + // inform any listeners that a Node wrapper has been created if (node != null) { @@ -694,7 +703,7 @@ public class BrowseBean implements IContextListener logger.warn("Found invalid object in database: id = " + nodeRef + ", type = " + type); } } - + // commit the transaction tx.commit(); } @@ -714,17 +723,17 @@ public class BrowseBean implements IContextListener this.contentNodes = Collections.emptyList(); try { if (tx != null) {tx.rollback();} } catch (Exception tex) {} } - + if (logger.isDebugEnabled()) { long endTime = System.currentTimeMillis(); logger.debug("Time to query and build map nodes: " + (endTime - startTime) + "ms"); } } - + /** * Search for a list of nodes using the specific search context - * + * * @param searchContext To use to perform the search */ private void searchBrowseNodes(SearchContext searchContext) @@ -732,7 +741,7 @@ public class BrowseBean implements IContextListener long startTime = 0; if (logger.isDebugEnabled()) startTime = System.currentTimeMillis(); - + // get the searcher object to build the query String query = searchContext.buildQuery(getMinimumSearchLength()); if (query == null) @@ -745,7 +754,7 @@ public class BrowseBean implements IContextListener this.contentNodes = Collections.emptyList(); return; } - + // perform the search against the repo UserTransaction tx = null; ResultSet results = null; @@ -753,24 +762,24 @@ public class BrowseBean implements IContextListener { tx = Repository.getUserTransaction(FacesContext.getCurrentInstance(), true); tx.begin(); - + // Limit search to the first 100 matches SearchParameters sp = new SearchParameters(); sp.setLanguage(SearchService.LANGUAGE_LUCENE); sp.setQuery(query); sp.addStore(Repository.getStoreRef()); - + int searchLimit = Application.getClientConfig(FacesContext.getCurrentInstance()).getSearchMaxResults(); if(searchLimit > 0) { sp.setLimitBy(LimitBy.FINAL_SIZE); sp.setLimit(searchLimit); } - + results = this.searchService.query(sp); if (logger.isDebugEnabled()) logger.debug("Search results returned: " + results.length()); - + // create a list of items from the results this.containerNodes = new ArrayList(results.length()); this.contentNodes = new ArrayList(results.length()); @@ -779,43 +788,43 @@ public class BrowseBean implements IContextListener for (ResultSetRow row: results) { NodeRef nodeRef = row.getNodeRef(); - + if (this.nodeService.exists(nodeRef)) { // find it's type so we can see if it's a node we are interested in QName type = this.nodeService.getType(nodeRef); - + // make sure the type is defined in the data dictionary TypeDefinition typeDef = this.dictionaryService.getType(type); - + if (typeDef != null) { MapNode node = null; - + // look for Space or File nodes - if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_FOLDER) && + if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_FOLDER) && this.dictionaryService.isSubClass(type, ContentModel.TYPE_SYSTEM_FOLDER) == false) { // create our Node representation node = new MapNode(nodeRef, this.nodeService, false); - + node.addPropertyResolver("path", this.resolverPath); node.addPropertyResolver("displayPath", this.resolverDisplayPath); node.addPropertyResolver("icon", this.resolverSpaceIcon); node.addPropertyResolver("smallIcon", this.resolverSmallIcon); - + this.containerNodes.add(node); } else if (this.dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT)) { // create our Node representation node = new MapNode(nodeRef, this.nodeService, false); - + setupCommonBindingProperties(node); - + node.addPropertyResolver("path", this.resolverPath); node.addPropertyResolver("displayPath", this.resolverDisplayPath); - + this.contentNodes.add(node); } // look for File Link object node @@ -832,7 +841,7 @@ public class BrowseBean implements IContextListener node.addPropertyResolver("lang", this.resolverLang); node.addPropertyResolver("path", this.resolverPath); node.addPropertyResolver("displayPath", this.resolverDisplayPath); - + this.contentNodes.add(node); } else if (ApplicationModel.TYPE_FOLDERLINK.equals(type)) @@ -843,10 +852,10 @@ public class BrowseBean implements IContextListener node.addPropertyResolver("smallIcon", this.resolverSmallIcon); node.addPropertyResolver("path", this.resolverPath); node.addPropertyResolver("displayPath", this.resolverDisplayPath); - + this.containerNodes.add(node); } - + // inform any listeners that a Node wrapper has been created if (node != null) { @@ -869,7 +878,7 @@ public class BrowseBean implements IContextListener } } } - + // commit the transaction tx.commit(); } @@ -897,15 +906,15 @@ public class BrowseBean implements IContextListener results.close(); } } - + if (logger.isDebugEnabled()) { long endTime = System.currentTimeMillis(); logger.debug("Time to query and build map nodes: " + (endTime - startTime) + "ms"); } } - - + + // ------------------------------------------------------------------------------ // Property Resolvers @@ -914,25 +923,25 @@ public class BrowseBean implements IContextListener return DownloadContentServlet.generateDownloadURL(node.getNodeRef(), node.getName()); } }; - + public NodePropertyResolver resolverUrl = new NodePropertyResolver() { public Object get(Node node) { return DownloadContentServlet.generateBrowserURL(node.getNodeRef(), node.getName()); } }; - + public NodePropertyResolver resolverWebdavUrl = new NodePropertyResolver() { public Object get(Node node) { - return Utils.generateURL(FacesContext.getCurrentInstance(), node, URLMode.WEBDAV); - } + return Utils.generateURL(FacesContext.getCurrentInstance(), node, URLMode.WEBDAV); + } }; - + public NodePropertyResolver resolverCifsPath = new NodePropertyResolver() { public Object get(Node node) { return Utils.generateURL(FacesContext.getCurrentInstance(), node, URLMode.CIFS); } }; - + public NodePropertyResolver resolverLinkDownload = new NodePropertyResolver() { public Object get(Node node) { NodeRef destRef = (NodeRef)node.getProperties().get(ContentModel.PROP_LINK_DESTINATION); @@ -948,7 +957,7 @@ public class BrowseBean implements IContextListener } } }; - + public NodePropertyResolver resolverLinkUrl = new NodePropertyResolver() { public Object get(Node node) { NodeRef destRef = (NodeRef)node.getProperties().get(ContentModel.PROP_LINK_DESTINATION); @@ -964,7 +973,7 @@ public class BrowseBean implements IContextListener } } }; - + public NodePropertyResolver resolverLinkWebdavUrl = new NodePropertyResolver() { public Object get(Node node) { NodeRef destRef = (NodeRef)node.getProperties().get(ContentModel.PROP_LINK_DESTINATION); @@ -977,9 +986,9 @@ public class BrowseBean implements IContextListener // TODO: link object is missing - navigate to a page with appropriate message return "#"; } - } + } }; - + public NodePropertyResolver resolverLinkCifsPath = new NodePropertyResolver() { public Object get(Node node) { NodeRef destRef = (NodeRef)node.getProperties().get(ContentModel.PROP_LINK_DESTINATION); @@ -994,32 +1003,32 @@ public class BrowseBean implements IContextListener } } }; - + public NodePropertyResolver resolverFileType16 = new NodePropertyResolver() { public Object get(Node node) { return Utils.getFileTypeImage(node.getName(), true); } }; - + public NodePropertyResolver resolverFileType32 = new NodePropertyResolver() { public Object get(Node node) { return Utils.getFileTypeImage(node.getName(), false); } }; - + public NodePropertyResolver resolverPath = new NodePropertyResolver() { public Object get(Node node) { return node.getNodePath(); } }; - + public NodePropertyResolver resolverDisplayPath = new NodePropertyResolver() { public Object get(Node node) { // TODO: replace this with a method that shows the full display name - not QNames? return Repository.getDisplayPath(node.getNodePath()); } }; - + public NodePropertyResolver resolverSpaceIcon = new NodePropertyResolver() { public Object get(Node node) { QNameNodeMap props = (QNameNodeMap)node.getProperties(); @@ -1027,7 +1036,7 @@ public class BrowseBean implements IContextListener return (icon != null ? icon : CreateSpaceWizard.DEFAULT_SPACE_ICON_NAME); } }; - + public NodePropertyResolver resolverSmallIcon = new NodePropertyResolver() { public Object get(Node node) { QNameNodeMap props = (QNameNodeMap)node.getProperties(); @@ -1035,14 +1044,14 @@ public class BrowseBean implements IContextListener return (icon != null ? icon + "-16" : SPACE_SMALL_DEFAULT); } }; - + public NodePropertyResolver resolverMimetype = new NodePropertyResolver() { public Object get(Node node) { ContentData content = (ContentData)node.getProperties().get(ContentModel.PROP_CONTENT); return (content != null ? content.getMimetype() : null); } }; - + public NodePropertyResolver resolverEncoding = new NodePropertyResolver() { public Object get(Node node) { ContentData content = (ContentData)node.getProperties().get(ContentModel.PROP_CONTENT); @@ -1059,41 +1068,51 @@ public class BrowseBean implements IContextListener public NodePropertyResolver resolverLang = new NodePropertyResolver() { public Object get(Node node) { - - String lang = null; - + + String lang = null; + if (node.getAspects().contains(ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)) { - Locale locale = (Locale) node.getProperties().get(ContentModel.PROP_LOCALE); - + Locale locale = null; + + if(node.hasAspect(ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION)) + { + // if the translation is empty, the lang of the content is the lang of it's pivot. + NodeRef pivot = multilingualContentService.getPivotTranslation(node.getNodeRef()); + locale = (Locale) nodeService.getProperty(pivot, ContentModel.PROP_LOCALE); + } + else + { + locale = (Locale) node.getProperties().get(ContentModel.PROP_LOCALE); + } // the content filter lang defined by the user String userLang = userPreferencesBean.getContentFilterLanguage(); - // the node lang + // the node lang String nodeLang = locale.getLanguage(); - + // if filter equals all languages : display the lang for each translation if (nodeLang == null) { lang = nodeLang; } - + // if filter is different : display the lang else if (!nodeLang.equalsIgnoreCase(userLang)) { - lang = nodeLang; + lang = nodeLang; } - + // else if the filter is equal to the lang node : nothing to do [lang = null] } - return lang; + return lang; } }; - - + + // ------------------------------------------------------------------------------ // Navigation action event handlers - + /** * Action called from the Simple Search component. * Sets up the SearchContext object with the values from the simple search menu. @@ -1103,10 +1122,10 @@ public class BrowseBean implements IContextListener // setup the search text string on the top-level navigation handler UISimpleSearch search = (UISimpleSearch)event.getComponent(); this.navigator.setSearchContext(search.getSearchContext()); - + navigateBrowseScreen(); } - + /** * Action called to Close the search dialog by returning to the last view node Id */ @@ -1115,7 +1134,7 @@ public class BrowseBean implements IContextListener // set the current node Id ready for page refresh this.navigator.setCurrentNodeId( this.navigator.getCurrentNodeId() ); } - + /** * Update page size based on user selection */ @@ -1140,7 +1159,7 @@ public class BrowseBean implements IContextListener this.pageSizeSpacesStr = Integer.toString(this.pageSizeSpaces); } } - + /** * Update page size based on user selection */ @@ -1165,7 +1184,7 @@ public class BrowseBean implements IContextListener this.pageSizeContentStr = Integer.toString(this.pageSizeContent); } } - + /** * Action called when a folder space is clicked. * Navigate into the space. @@ -1180,13 +1199,13 @@ public class BrowseBean implements IContextListener try { NodeRef ref = new NodeRef(Repository.getStoreRef(), id); - + // handle special folder link node case if (ApplicationModel.TYPE_FOLDERLINK.equals(this.nodeService.getType(ref))) { ref = (NodeRef)this.nodeService.getProperty(ref, ContentModel.PROP_LINK_DESTINATION); } - + clickSpace(ref); } catch (InvalidNodeRefException refErr) @@ -1196,7 +1215,7 @@ public class BrowseBean implements IContextListener } } } - + /** * Action called when a folder space is clicked. * @@ -1207,7 +1226,7 @@ public class BrowseBean implements IContextListener // refresh UI based on node selection updateUILocation(nodeRef); } - + /** * Handler called when a path element is clicked - navigate to the appropriate Space */ @@ -1226,7 +1245,7 @@ public class BrowseBean implements IContextListener FacesContext.getCurrentInstance(), Repository.ERROR_NODEREF), new Object[] {ref.getId()}) ); } } - + /** * Action called when a folders direct descendant (in the 'list' browse mode) is clicked. * Navigate into the the descendant space. @@ -1239,29 +1258,29 @@ public class BrowseBean implements IContextListener { throw new IllegalStateException("NodeRef returned from UINodeDescendants.NodeSelectedEvent cannot be null!"); } - + if (logger.isDebugEnabled()) logger.debug("Selected noderef Id: " + nodeRef.getId()); - + try { // user can either select a descendant of a node display on the page which means we // must add the it's parent and itself to the breadcrumb ChildAssociationRef parentAssocRef = nodeService.getPrimaryParent(nodeRef); - + if (logger.isDebugEnabled()) { logger.debug("Selected item getPrimaryParent().getChildRef() noderef Id: " + parentAssocRef.getChildRef().getId()); logger.debug("Selected item getPrimaryParent().getParentRef() noderef Id: " + parentAssocRef.getParentRef().getId()); logger.debug("Current value getNavigator().getCurrentNodeId() noderef Id: " + this.navigator.getCurrentNodeId()); } - + if (nodeEvent.IsParent == false) { // a descendant of the displayed node was selected // first refresh based on the parent and add to the breadcrumb updateUILocation(parentAssocRef.getParentRef()); - + // now add our selected node updateUILocation(nodeRef); } @@ -1277,12 +1296,12 @@ public class BrowseBean implements IContextListener FacesContext.getCurrentInstance(), Repository.ERROR_NODEREF), new Object[] {nodeRef.getId()}) ); } } - + /** * Action event called by all Browse actions that need to setup a Space context * before an action page/wizard is called. The context will be a Node in setActionSpace() which * can be retrieved on the action page from BrowseBean.getActionSpace(). - * + * * @param event ActionEvent */ public void setupSpaceAction(ActionEvent event) @@ -1292,10 +1311,10 @@ public class BrowseBean implements IContextListener String id = params.get("id"); setupSpaceAction(id, true); } - + /** * Public helper to setup action pages with Space context - * + * * @param id of the Space node to setup context for */ public void setupSpaceAction(String id, boolean invalidate) @@ -1304,19 +1323,19 @@ public class BrowseBean implements IContextListener { if (logger.isDebugEnabled()) logger.debug("Setup for action, setting current space to: " + id); - + try { // create the node ref, then our node representation NodeRef ref = new NodeRef(Repository.getStoreRef(), id); Node node = new Node(ref); - + // resolve icon in-case one has not been set node.addPropertyResolver("icon", this.resolverSpaceIcon); - + // prepare a node for the action context setActionSpace(node); - + // setup the dispatch context in case it is required this.navigator.setupDispatchContext(node); } @@ -1330,7 +1349,7 @@ public class BrowseBean implements IContextListener { setActionSpace(null); } - + // clear the UI state in preparation for finishing the next action if (invalidate == true) { @@ -1338,17 +1357,17 @@ public class BrowseBean implements IContextListener UIContextService.getInstance(FacesContext.getCurrentInstance()).notifyBeans(); } } - + /** * Acrtion event called by Delete Space actions. We setup the action space as normal, then prepare - * any special case message string to be shown to the user if they are trying to delete specific spaces. + * any special case message string to be shown to the user if they are trying to delete specific spaces. */ public void setupDeleteAction(ActionEvent event) { String message = null; - + setupSpaceAction(event); - + Node node = getActionSpace(); if (node != null) { @@ -1358,12 +1377,12 @@ public class BrowseBean implements IContextListener message = Application.getMessage(FacesContext.getCurrentInstance(), MSG_DELETE_COMPANYROOT); } } - + setDeleteMessage(message); } - + /** - * Action event called by all actions that need to setup a Content Document context on the + * Action event called by all actions that need to setup a Content Document context on the * BrowseBean before an action page/wizard is called. The context will be a Node in * setDocument() which can be retrieved on the action page from BrowseBean.getDocument(). */ @@ -1373,10 +1392,34 @@ public class BrowseBean implements IContextListener Map params = link.getParameterMap(); setupContentAction(params.get("id"), true); } - + + /** + * Action event called by all actions that need to setup a Multilingual Content Document context on the + * BrowseBean before an action page/wizard is called. The context will be a Node in + * setDocument() which can be retrieved on the action page from BrowseBean.getDocument(). + */ + public void setupMLContainerContentAction(ActionEvent event) + { + UIActionLink link = (UIActionLink)event.getComponent(); + Map params = link.getParameterMap(); + + String id = params.get("id"); + + NodeRef translation = new NodeRef(Repository.getStoreRef(), id); + + // remember the bean from wich the action comes + DocumentDetailsBean docDetails = (DocumentDetailsBean)FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("DocumentDetailsBean"); + docDetails.setTranslationDocument(new Node(translation)); + + // set the ml container as the current document + NodeRef mlContainer = multilingualContentService.getTranslationContainer(translation); + + setupContentAction(mlContainer.getId(), true); + } + /** * Public helper to setup action pages with content context - * + * * @param id of the content node to setup context for */ public void setupContentAction(String id, boolean invalidate) @@ -1385,13 +1428,13 @@ public class BrowseBean implements IContextListener { if (logger.isDebugEnabled()) logger.debug("Setup for action, setting current document to: " + id); - + try { // create the node ref, then our node representation NodeRef ref = new NodeRef(Repository.getStoreRef(), id); Node node = new Node(ref); - + // store the URL to for downloading the content if (ApplicationModel.TYPE_FILELINK.equals(node.getType())) { @@ -1406,12 +1449,12 @@ public class BrowseBean implements IContextListener node.addPropertyResolver("encoding", this.resolverEncoding); node.addPropertyResolver("size", this.resolverSize); node.addPropertyResolver("lang", this.resolverLang); - + for (NodeEventListener listener : getNodeEventListeners()) { listener.created(node, node.getType()); } - + // get hold of the DocumentDetailsBean and reset it DocumentDetailsBean docDetails = (DocumentDetailsBean)FacesContext.getCurrentInstance(). getExternalContext().getSessionMap().get("DocumentDetailsBean"); @@ -1419,10 +1462,10 @@ public class BrowseBean implements IContextListener { docDetails.reset(); } - + // remember the document setDocument(node); - + // setup the dispatch context in case it is required this.navigator.setupDispatchContext(node); } @@ -1436,7 +1479,7 @@ public class BrowseBean implements IContextListener { setDocument(null); } - + // clear the UI state in preparation for finishing the next action if (invalidate == true) { @@ -1444,10 +1487,10 @@ public class BrowseBean implements IContextListener UIContextService.getInstance(FacesContext.getCurrentInstance()).notifyBeans(); } } - + /** * Removes the given node from the breadcrumb i.e. following a delete - * + * * @param node The space to remove from the breadcrumb */ public void removeSpaceFromBreadcrumb(Node node) @@ -1456,11 +1499,11 @@ public class BrowseBean implements IContextListener IBreadcrumbHandler handler = location.get(location.size() - 1); if (handler instanceof BrowseBreadcrumbHandler) { - // see if the current breadcrumb location is our node + // see if the current breadcrumb location is our node if ( ((BrowseBreadcrumbHandler)handler).getNodeRef().equals(node.getNodeRef()) == true ) { location.remove(location.size() - 1); - + // now work out which node to set the list to refresh against if (location.size() != 0) { @@ -1479,7 +1522,7 @@ public class BrowseBean implements IContextListener } } } - + /** * Support for refresh of lists via special case for an External Access URL. * these URLs restart the JSF lifecycle but an old UIRichList is restored from @@ -1489,7 +1532,7 @@ public class BrowseBean implements IContextListener { this.externalForceRefresh = true; } - + /** * Save the state of the panel that was expanded/collapsed */ @@ -1501,27 +1544,27 @@ public class BrowseBean implements IContextListener this.panels.put(id, ((ExpandedEvent)event).State); } } - - + + // ------------------------------------------------------------------------------ // Private helpers - + /** * Initialise default values from client configuration */ private void initFromClientConfig() { ConfigService config = Application.getConfigService(FacesContext.getCurrentInstance()); - + this.viewsConfig = (ViewsConfigElement)config.getConfig("Views"). getConfigElement(ViewsConfigElement.CONFIG_ELEMENT_ID); - + this.browseViewMode = this.viewsConfig.getDefaultView(PAGE_NAME_BROWSE); int pageSize = this.viewsConfig.getDefaultPageSize(PAGE_NAME_BROWSE, this.browseViewMode); setPageSizeContent(pageSize); setPageSizeSpaces(pageSize); } - + /** * @return the Set of NodeEventListeners registered against this bean */ @@ -1530,9 +1573,9 @@ public class BrowseBean implements IContextListener if (this.nodeEventListeners == null) { this.nodeEventListeners = new HashSet(); - + FacesContext fc = FacesContext.getCurrentInstance(); - + Config listenerConfig = Application.getConfigService(fc).getConfig("Node Event Listeners"); if (listenerConfig != null) { @@ -1557,11 +1600,11 @@ public class BrowseBean implements IContextListener } return this.nodeEventListeners; } - + /** * Refresh the UI after a Space selection change. Adds the selected space to the breadcrumb * location path and also updates the list components in the UI. - * + * * @param ref NodeRef of the selected space */ public void updateUILocation(NodeRef ref) @@ -1594,13 +1637,13 @@ public class BrowseBean implements IContextListener { location.remove(i+1); } - + foundNode = true; break; } } } - + // add new node to the end of the existing breadcrumb if (foundNode == false) { @@ -1614,19 +1657,19 @@ public class BrowseBean implements IContextListener String name = Repository.getNameForNode(this.nodeService, ref); location.add(new BrowseBreadcrumbHandler(ref, name)); } - + // set the current node Id ready for page refresh this.navigator.setCurrentNodeId(ref.getId()); - + // set up the dispatch context for the navigation handler this.navigator.setupDispatchContext(new Node(ref)); - + // inform any listeners that the current space has changed UIContextService.getInstance(FacesContext.getCurrentInstance()).spaceChanged(); - + navigateBrowseScreen(); } - + /** * Invalidate list component state after an action which changes the UI context */ @@ -1634,7 +1677,7 @@ public class BrowseBean implements IContextListener { if (logger.isDebugEnabled()) logger.debug("Invalidating browse components..."); - + // clear the value for the list components - will cause re-bind to it's data and refresh if (this.contentRichList != null) { @@ -1654,12 +1697,12 @@ public class BrowseBean implements IContextListener this.spacesRichList.clearSort(); } } - + // clear the storage of the last set of nodes this.containerNodes = null; this.contentNodes = null; } - + /** * @return whether the current View ID is the "browse" screen */ @@ -1667,37 +1710,37 @@ public class BrowseBean implements IContextListener { return (FacesContext.getCurrentInstance().getViewRoot().getViewId().equals(BROWSE_VIEW_ID)); } - + /** * Perform navigation to the browse screen if it is not already the current View */ private void navigateBrowseScreen() { String outcome = null; - + if (isViewCurrent() == false) { outcome = "browse"; } - + FacesContext fc = FacesContext.getCurrentInstance(); fc.getApplication().getNavigationHandler().handleNavigation(fc, null, outcome); } - - + + // ------------------------------------------------------------------------------ // Inner classes - + /** * Class to handle breadcrumb interaction for Browse pages */ private class BrowseBreadcrumbHandler implements IRepoBreadcrumbHandler { private static final long serialVersionUID = 3833183653173016630L; - + /** * Constructor - * + * * @param NodeRef The NodeRef for this browse navigation element * @param label Element label */ @@ -1706,7 +1749,7 @@ public class BrowseBean implements IContextListener this.label = label; this.nodeRef = nodeRef; } - + /** * @see java.lang.Object#toString() */ @@ -1725,104 +1768,107 @@ public class BrowseBean implements IContextListener // set the current node id navigator.setCurrentNodeId(this.nodeRef.getId()); navigator.setLocation( (List)breadcrumb.getValue() ); - + // setup the dispatch context navigator.setupDispatchContext(new Node(this.nodeRef)); - + // inform any listeners that the current space has changed UIContextService.getInstance(FacesContext.getCurrentInstance()).spaceChanged(); - + // return to browse page if required - return (isViewCurrent() ? null : "browse"); + return (isViewCurrent() ? null : "browse"); } - + public NodeRef getNodeRef() { return this.nodeRef; } - + private NodeRef nodeRef; private String label; } - + // ------------------------------------------------------------------------------ // Private data - + /** Browse screen view ID */ public static final String BROWSE_VIEW_ID = "/jsp/browse/browse.jsp"; - + /** Small icon default name */ public static final String SPACE_SMALL_DEFAULT = "space_small"; - + private static final String VIEWMODE_DASHBOARD = "dashboard"; private static final String PAGE_NAME_BROWSE = "browse"; - + /** I18N messages */ private static final String MSG_DELETE_COMPANYROOT = "delete_companyroot_confirm"; private static final String MSG_SEARCH_MINIMUM = "search_minimum"; - + private static Logger logger = Logger.getLogger(BrowseBean.class); - + /** The NodeService to be used by the bean */ protected NodeService nodeService; - + /** The SearchService to be used by the bean */ protected SearchService searchService; - + /** The LockService to be used by the bean */ protected LockService lockService; - + /** The NavigationBean bean reference */ protected NavigationBean navigator; - + /** The UserPreferencesBean to be used by the bean */ protected UserPreferencesBean userPreferencesBean; - + /** The DictionaryService bean reference */ protected DictionaryService dictionaryService; - + /** The file folder service */ protected FileFolderService fileFolderService; + /** The Multilingual Content Service */ + protected MultilingualContentService multilingualContentService; + /** Views configuration object */ protected ViewsConfigElement viewsConfig = null; - + /** Listeners for Node events */ protected Set nodeEventListeners = null; - + /** Collapsable Panel state */ private Map panels = new HashMap(4, 1.0f); - + /** Component references */ protected UIRichList spacesRichList; protected UIRichList contentRichList; private UIStatusMessage statusMessage; - + /** Transient lists of container and content nodes for display */ protected List containerNodes = null; protected List contentNodes = null; - + /** The current space and it's properties - if any */ protected Node actionSpace; - + /** The current document */ protected Node document; - + /** Special message to display when user deleting certain folders e.g. Company Home */ private String deleteMessage; - + /** The current browse view mode - set to a well known IRichListRenderer identifier */ private String browseViewMode; - + /** The current browse view page sizes */ private int pageSizeSpaces; private int pageSizeContent; private String pageSizeSpacesStr; private String pageSizeContentStr; - + /** True if current space has a dashboard (template) view available */ private boolean dashboardView; - + private boolean externalForceRefresh = false; } diff --git a/source/java/org/alfresco/web/bean/DocumentDetailsBean.java b/source/java/org/alfresco/web/bean/DocumentDetailsBean.java index 825d55721f..ccbef56163 100644 --- a/source/java/org/alfresco/web/bean/DocumentDetailsBean.java +++ b/source/java/org/alfresco/web/bean/DocumentDetailsBean.java @@ -29,7 +29,6 @@ import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -44,6 +43,7 @@ import org.alfresco.model.ContentModel; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.repo.version.common.VersionLabelComparator; import org.alfresco.service.cmr.coci.CheckOutCheckInService; import org.alfresco.service.cmr.lock.LockService; import org.alfresco.service.cmr.ml.ContentFilterLanguagesService; @@ -59,6 +59,7 @@ import org.alfresco.service.cmr.version.VersionService; import org.alfresco.service.namespace.QName; import org.alfresco.web.app.Application; import org.alfresco.web.app.servlet.DownloadContentServlet; +import org.alfresco.web.bean.ml.MultilingualUtils; import org.alfresco.web.bean.repository.MapNode; import org.alfresco.web.bean.repository.Node; import org.alfresco.web.bean.repository.Repository; @@ -95,6 +96,8 @@ public class DocumentDetailsBean extends BaseDetailsBean private NodeRef addedCategory; private List categories; + private Node translationDocument; + // ------------------------------------------------------------------------------ // Construction @@ -213,7 +216,7 @@ public class DocumentDetailsBean extends BaseDetailsBean { this.navigator.setupDispatchContext(getDocument()); } - + /** * Save the state of the panel that was expanded/collapsed */ @@ -252,8 +255,17 @@ public class DocumentDetailsBean extends BaseDetailsBean clientVersion.put("notes", version.getDescription()); clientVersion.put("author", version.getCreator()); clientVersion.put("versionDate", version.getCreatedDate()); - clientVersion.put("url", DownloadContentServlet.generateBrowserURL(version.getFrozenStateNodeRef(), - clientVersion.getName())); + + if(getDocument().hasAspect(ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION)) + { + clientVersion.put("url", null); + } + else + { + clientVersion.put("url", DownloadContentServlet.generateBrowserURL(version.getFrozenStateNodeRef(), + clientVersion.getName())); + } + // add the client side version to the list versions.add(clientVersion); @@ -264,23 +276,6 @@ public class DocumentDetailsBean extends BaseDetailsBean return versions; } - /** - * The comparator to sort a version list according their version label ascending - * - * TODO add this code in the repository as Version Util class ? - */ - private Comparator versionComparator = new Comparator() - { - public int compare(Object o1, Object o2) - { - String label01 = ((Version) o1).getVersionLabel(); - String label02 = ((Version) o2).getVersionLabel(); - - // sort the list ascending - return label02.compareTo(label01); - } - }; - /** List of client light weight edition histories */ private List editionHistory = null; @@ -366,102 +361,115 @@ public class DocumentDetailsBean extends BaseDetailsBean private List initEditionHistory() { // get the mlContainer - NodeRef mlContainer = getDocumentMlContainer().getNodeRef(); + NodeRef mlContainer = getDocumentMlContainer().getNodeRef(); - // get all editions and sort them ascending according their version label - List orderedEditionList = new ArrayList(editionService.getEditions(mlContainer).getAllVersions()); - Collections.sort(orderedEditionList, versionComparator); + // get all editions and sort them ascending according their version label + List orderedEditionList = new ArrayList(editionService.getEditions(mlContainer).getAllVersions()); + Collections.sort(orderedEditionList, new VersionLabelComparator()); - // the list of Single Edition Bean to return - editionHistory = new ArrayList(orderedEditionList.size()); + // the list of Single Edition Bean to return + editionHistory = new ArrayList(orderedEditionList.size()); - boolean firstEdition = true; + boolean firstEdition = true; - // for each edition, init a SingleEditionBean - for (Version edition : orderedEditionList) - { - SingleEditionBean editionBean = new SingleEditionBean(); + // for each edition, init a SingleEditionBean + for (Version edition : orderedEditionList) + { + SingleEditionBean editionBean = new SingleEditionBean(); - MapNode clientEdition = new MapNode(edition.getFrozenStateNodeRef()); + MapNode clientEdition = new MapNode(edition.getFrozenStateNodeRef()); - String editionLabel = edition.getVersionLabel(); - if (firstEdition) - { - editionLabel += " (" + Application.getMessage(FacesContext.getCurrentInstance(), MSG_CURRENT) + ")"; - } + String editionLabel = edition.getVersionLabel(); + if (firstEdition) + { + editionLabel += " (" + Application.getMessage(FacesContext.getCurrentInstance(), MSG_CURRENT) + ")"; + } - clientEdition.put("editionLabel", editionLabel); - clientEdition.put("editionNotes", edition.getDescription()); - clientEdition.put("editionAuthor", edition.getCreator()); - clientEdition.put("editionDate", edition.getCreatedDate()); + clientEdition.put("editionLabel", editionLabel); + clientEdition.put("editionNotes", edition.getDescription()); + clientEdition.put("editionAuthor", edition.getCreator()); + clientEdition.put("editionDate", edition.getCreatedDate()); - // Set the edition of the edition bean - editionBean.setEdition(clientEdition); + // Set the edition of the edition bean + editionBean.setEdition(clientEdition); - // get translations - List translationHistories = null; + // get translations + List translationHistories = null; - if (firstEdition) - { - // Get the translations because the current edition doesn't content link with its - // translation in the version store. - Map translations = multilingualContentService.getTranslations(mlContainer); - translationHistories = new ArrayList(translations.size()); - for (NodeRef translation : translations.values()) - { - translationHistories.add(versionService.getVersionHistory(translation)); - } - } - else - { + if (firstEdition) + { + // Get the translations because the current edition doesn't content link with its + // translation in the version store. + Map translations = multilingualContentService.getTranslations(mlContainer); + translationHistories = new ArrayList(translations.size()); + for (NodeRef translation : translations.values()) + { + translationHistories.add(versionService.getVersionHistory(translation)); + } + } + else + { translationHistories = editionService.getVersionedTranslations(edition); - } + } - // add each translation in the SingleEditionBean - for (VersionHistory versionHistory : translationHistories) - { - // get the list of versions and sort them ascending according their version label - List orderedVersions = new ArrayList(versionHistory.getAllVersions()); - Collections.sort(orderedVersions, versionComparator); + // add each translation in the SingleEditionBean + for (VersionHistory versionHistory : translationHistories) + { + // get the list of versions and sort them ascending according their version label + List orderedVersions = new ArrayList(versionHistory.getAllVersions()); + Collections.sort(orderedVersions, new VersionLabelComparator()); - // the last version is the first version of the list - Version lastVersion = orderedVersions.get(0); + // the last version is the first version of the list + Version lastVersion = orderedVersions.get(0); - // get the properties of the lastVersion - Map lastVersionProperties = editionService.getVersionedMetadatas(lastVersion); - Locale language = (Locale) lastVersionProperties.get(ContentModel.PROP_LOCALE); + // get the properties of the lastVersion + Map lastVersionProperties = editionService.getVersionedMetadatas(lastVersion); + Locale language = (Locale) lastVersionProperties.get(ContentModel.PROP_LOCALE); - // create a map node representation of the last version - MapNode clientLastVersion = new MapNode(lastVersion.getFrozenStateNodeRef()); + // create a map node representation of the last version + MapNode clientLastVersion = new MapNode(lastVersion.getFrozenStateNodeRef()); - clientLastVersion.put("versionName", lastVersionProperties.get(ContentModel.PROP_NAME)); - clientLastVersion.put("versionDescription", lastVersionProperties.get(ContentModel.PROP_DESCRIPTION)); - clientLastVersion.put("versionAuthor", lastVersionProperties.get(ContentModel.PROP_AUTHOR)); - clientLastVersion.put("versionCreatedDate", lastVersionProperties.get(ContentModel.PROP_CREATED)); - clientLastVersion.put("versionModifiedDate", lastVersionProperties.get(ContentModel.PROP_MODIFIED)); - clientLastVersion.put("versionLanguage", this.contentFilterLanguagesService.convertToNewISOCode(language.getLanguage()).toUpperCase()); - clientLastVersion.put("versionUrl", DownloadContentServlet.generateBrowserURL(lastVersion.getFrozenStateNodeRef(), clientLastVersion.getName())); + clientLastVersion.put("versionName", lastVersionProperties.get(ContentModel.PROP_NAME)); + // use the node service for the description to ensure that the returned value is a text and not a MLText + clientLastVersion.put("versionDescription", nodeService.getProperty(lastVersion.getFrozenStateNodeRef(), ContentModel.PROP_DESCRIPTION)); + clientLastVersion.put("versionAuthor", lastVersionProperties.get(ContentModel.PROP_AUTHOR)); + clientLastVersion.put("versionCreatedDate", lastVersionProperties.get(ContentModel.PROP_CREATED)); + clientLastVersion.put("versionModifiedDate", lastVersionProperties.get(ContentModel.PROP_MODIFIED)); + clientLastVersion.put("versionLanguage", this.contentFilterLanguagesService.convertToNewISOCode(language.getLanguage()).toUpperCase()); - // add a translation of the editionBean - editionBean.addTranslations(clientLastVersion); - } - editionHistory.add(editionBean); - firstEdition = false; - } + if(nodeService.hasAspect(lastVersion.getFrozenStateNodeRef(), ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION)) + { + clientLastVersion.put("versionUrl", null); + } + else + { + clientLastVersion.put("versionUrl", DownloadContentServlet.generateBrowserURL(lastVersion.getFrozenStateNodeRef(), clientLastVersion.getName())); + } - return editionHistory; + // add a translation of the editionBean + editionBean.addTranslations(clientLastVersion); + } + editionHistory.add(editionBean); + firstEdition = false; + } + + return editionHistory; } /** - * Returns a list of objects representing the translations of the current document - * + * Returns a list of objects representing the translations of the current document + * * @return List of translations */ public List getTranslations() { List translations = new ArrayList(); - if (getDocument().hasAspect(ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)) + Node document = getDocument(); + + boolean canNewEdtion = MultilingualUtils.canStartNewEditon(document, FacesContext.getCurrentInstance()); + + if (document.hasAspect(ContentModel.ASPECT_MULTILINGUAL_DOCUMENT) || ContentModel.TYPE_MULTILINGUAL_CONTAINER.equals(document.getType())) { Map translationsMap = this.multilingualContentService.getTranslations(getDocument().getNodeRef()); @@ -485,6 +493,8 @@ public class DocumentDetailsBean extends BaseDetailsBean mapNode.put("language", lgge); mapNode.put("url", DownloadContentServlet.generateBrowserURL(nodeRef, mapNode.getName())); + mapNode.put("notEmpty", new Boolean(!nodeService.hasAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION))); + mapNode.put("userHasRight", new Boolean(canNewEdtion)); // add the client side version to the list translations.add(mapNode); } @@ -593,23 +603,23 @@ public class DocumentDetailsBean extends BaseDetailsBean /** * Sets the category added from the multi value editor - * + * * @param addedCategory The added category */ public void setAddedCategory(NodeRef addedCategory) { this.addedCategory = addedCategory; } - + /** * Updates the categories for the current document - * + * * @return The outcome */ public String saveCategories() { String outcome = "cancel"; - + try { RetryingTransactionHelper txnHelper = Repository.getRetryingTransactionHelper(FacesContext.getCurrentInstance()); @@ -617,22 +627,22 @@ public class DocumentDetailsBean extends BaseDetailsBean { public Object execute() throws Throwable { - // firstly retrieve all the properties for the current node + // firstly retrieve all the properties for the current node Map updateProps = nodeService.getProperties(getDocument().getNodeRef()); - - // create a node ref representation of the selected id and set the new properties + + // create a node ref representation of the selected id and set the new properties updateProps.put(ContentModel.PROP_CATEGORIES, (Serializable) categories); - - // set the properties on the node + + // set the properties on the node nodeService.setProperties(getDocument().getNodeRef(), updateProps); return null; } }; txnHelper.doInTransaction(callback); - + // reset the state of the current document so it reflects the changes just made getDocument().reset(); - + outcome = "finish"; } catch (Throwable e) @@ -640,10 +650,10 @@ public class DocumentDetailsBean extends BaseDetailsBean Utils.addErrorMessage(MessageFormat.format(Application.getMessage( FacesContext.getCurrentInstance(), MSG_ERROR_UPDATE_CATEGORY), e.getMessage()), e); } - + return outcome; } - + /** * Applies the classifiable aspect to the current document */ @@ -656,13 +666,13 @@ public class DocumentDetailsBean extends BaseDetailsBean { public Object execute() throws Throwable { - // add the general classifiable aspect to the node + // add the general classifiable aspect to the node nodeService.addAspect(getDocument().getNodeRef(), ContentModel.ASPECT_GEN_CLASSIFIABLE, null); return null; } }; txnHelper.doInTransaction(callback); - + // reset the state of the current document getDocument().reset(); } @@ -672,7 +682,7 @@ public class DocumentDetailsBean extends BaseDetailsBean FacesContext.getCurrentInstance(), MSG_ERROR_ASPECT_CLASSIFY), e.getMessage()), e); } } - + /** * Applies the versionable aspect to the current document */ @@ -685,13 +695,13 @@ public class DocumentDetailsBean extends BaseDetailsBean { public Object execute() throws Throwable { - // add the versionable aspect to the node + // add the versionable aspect to the node nodeService.addAspect(getDocument().getNodeRef(), ContentModel.ASPECT_VERSIONABLE, null); return null; } }; txnHelper.doInTransaction(callback); - + // reset the state of the current document getDocument().reset(); } @@ -701,14 +711,14 @@ public class DocumentDetailsBean extends BaseDetailsBean FacesContext.getCurrentInstance(), MSG_ERROR_ASPECT_VERSIONING), e.getMessage()), e); } } - + /** * Action Handler to unlock a locked document */ public void unlock(final ActionEvent event) { final FacesContext fc = FacesContext.getCurrentInstance(); - + try { RetryingTransactionHelper txnHelper = Repository.getRetryingTransactionHelper(FacesContext.getCurrentInstance()); @@ -717,13 +727,13 @@ public class DocumentDetailsBean extends BaseDetailsBean public Object execute() throws Throwable { lockService.unlock(getNode().getNodeRef()); - - String msg = Application.getMessage(fc, MSG_SUCCESS_UNLOCK); - FacesMessage facesMsg = new FacesMessage(FacesMessage.SEVERITY_INFO, msg, msg); - String formId = Utils.getParentForm(fc, event.getComponent()).getClientId(fc); - fc.addMessage(formId + ':' + getPropertiesPanelId(), facesMsg); - - getNode().reset(); + + String msg = Application.getMessage(fc, MSG_SUCCESS_UNLOCK); + FacesMessage facesMsg = new FacesMessage(FacesMessage.SEVERITY_INFO, msg, msg); + String formId = Utils.getParentForm(fc, event.getComponent()).getClientId(fc); + fc.addMessage(formId + ':' + getPropertiesPanelId(), facesMsg); + + getNode().reset(); return null; } }; @@ -735,7 +745,7 @@ public class DocumentDetailsBean extends BaseDetailsBean fc, Repository.ERROR_GENERIC), e.getMessage()), e); } } - + /** * Applies the inlineeditable aspect to the current document */ @@ -748,33 +758,33 @@ public class DocumentDetailsBean extends BaseDetailsBean { public Object execute() throws Throwable { - // add the inlineeditable aspect to the node - Map props = new HashMap(1, 1.0f); - String contentType = null; - ContentData contentData = (ContentData)getDocument().getProperties().get(ContentModel.PROP_CONTENT); - if (contentData != null) - { - contentType = contentData.getMimetype(); - } - if (contentType != null) - { - // set the property to true by default if the filetype is a known content type - if (MimetypeMap.MIMETYPE_HTML.equals(contentType) || - MimetypeMap.MIMETYPE_TEXT_PLAIN.equals(contentType) || - MimetypeMap.MIMETYPE_XML.equals(contentType) || - MimetypeMap.MIMETYPE_TEXT_CSS.equals(contentType) || - MimetypeMap.MIMETYPE_JAVASCRIPT.equals(contentType)) - { - props.put(ApplicationModel.PROP_EDITINLINE, true); - } - } + // add the inlineeditable aspect to the node + Map props = new HashMap(1, 1.0f); + String contentType = null; + ContentData contentData = (ContentData)getDocument().getProperties().get(ContentModel.PROP_CONTENT); + if (contentData != null) + { + contentType = contentData.getMimetype(); + } + if (contentType != null) + { + // set the property to true by default if the filetype is a known content type + if (MimetypeMap.MIMETYPE_HTML.equals(contentType) || + MimetypeMap.MIMETYPE_TEXT_PLAIN.equals(contentType) || + MimetypeMap.MIMETYPE_XML.equals(contentType) || + MimetypeMap.MIMETYPE_TEXT_CSS.equals(contentType) || + MimetypeMap.MIMETYPE_JAVASCRIPT.equals(contentType)) + { + props.put(ApplicationModel.PROP_EDITINLINE, true); + } + } nodeService.addAspect(getDocument().getNodeRef(), ApplicationModel.ASPECT_INLINEEDITABLE, props); - + return null; } }; txnHelper.doInTransaction(callback); - + // reset the state of the current document getDocument().reset(); } @@ -970,6 +980,27 @@ public class DocumentDetailsBean extends BaseDetailsBean return this.getNode(); } + /** + * Before opening the ml container details, remeber the translation + * from which the action comes. + * + * @param node + */ + public void setTranslationDocument(Node node) + { + this.translationDocument = node; + } + + /** + * Restore the translationf from which the ml container + * details dialog comes. + */ + public void resetMLDocument(ActionEvent event) + { + this.browseBean.setupCommonBindingProperties(this.translationDocument); + this.browseBean.setDocument(this.translationDocument); + } + /** * Returns the ml container of the document this bean is currently representing * @@ -977,11 +1008,19 @@ public class DocumentDetailsBean extends BaseDetailsBean */ public Node getDocumentMlContainer() { - NodeRef nodeRef = getNode().getNodeRef(); + Node currentNode = getNode(); - return new Node(multilingualContentService.getTranslationContainer(nodeRef)); + if(ContentModel.TYPE_MULTILINGUAL_CONTAINER.equals(currentNode.getType())) + { + return currentNode; + } + else + { + NodeRef nodeRef = getNode().getNodeRef(); + + return new Node(multilingualContentService.getTranslationContainer(nodeRef)); + } } - /** * Sets the lock service instance the bean should use * diff --git a/source/java/org/alfresco/web/bean/VersionedDocumentDetailsBean.java b/source/java/org/alfresco/web/bean/VersionedDocumentDetailsBean.java new file mode 100644 index 0000000000..b4b5d55aa7 --- /dev/null +++ b/source/java/org/alfresco/web/bean/VersionedDocumentDetailsBean.java @@ -0,0 +1,530 @@ +/*--+ + | Copyright European Community 2006 - Licensed under the EUPL V.1.0 + | + | http://ec.europa.eu/idabc/en/document/6523 + | + +--*/ +package org.alfresco.web.bean; + +import java.io.Serializable; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import javax.faces.context.FacesContext; +import javax.faces.event.ActionEvent; + +import org.alfresco.i18n.I18NUtil; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.version.common.VersionLabelComparator; +import org.alfresco.service.cmr.ml.ContentFilterLanguagesService; +import org.alfresco.service.cmr.ml.EditionService; +import org.alfresco.service.cmr.ml.MultilingualContentService; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.InvalidNodeRefException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.version.Version; +import org.alfresco.service.cmr.version.VersionHistory; +import org.alfresco.service.cmr.version.VersionService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ParameterCheck; +import org.alfresco.web.app.Application; +import org.alfresco.web.app.servlet.DownloadContentServlet; +import org.alfresco.web.bean.repository.MapNode; +import org.alfresco.web.bean.repository.Node; +import org.alfresco.web.bean.repository.Repository; +import org.alfresco.web.ui.common.Utils; +import org.alfresco.web.ui.common.component.UIActionLink; + +/** + * Bean with generic function helping the rendering of the versioned properties + * + * @author Yanick Pignot + */ +public class VersionedDocumentDetailsBean +{ + /** Dependencies */ + protected VersionService versionService; + protected EditionService editionService; + protected NodeService nodeService; + protected MultilingualContentService multilingualContentService; + protected ContentFilterLanguagesService contentFilterLanguagesService; + + private static final Comparator VERSION_LABEL_COMPARATOR = new VersionLabelComparator(); + + /** Determine if the version is a translation of a old edition */ + private boolean fromPreviousEditon; + + /** The version selected by the user */ + private Version documentVersion; + private VersionHistory versionHistory; + + /** The multilingual information of the selected version selected by the user */ + private Version documentEdition; + private VersionHistory editionHistory; + + + public void init() + { + fromPreviousEditon = false; + documentVersion = null; + versionHistory = null; + documentEdition = null; + editionHistory = null; + } + + /** + * Set which version of the current node that the user want to display the properties + */ + public void setBrowsingVersion(ActionEvent event) + { + init(); + + // Get the properties of the action event + UIActionLink link = (UIActionLink)event.getComponent(); + Map params = link.getParameterMap(); + + String versionLabel = params.get("versionLabel"); + String id = params.get("id"); + String lang = params.get("lang"); + + setBrowsingVersion(id, versionLabel, lang); + } + + + /** + * Implementation of setBrowsingVersion action event to be use with the needed parameters. + */ + private void setBrowsingVersion(String id, String versionLabel, String lang) + { + // test if the mandatories parameter are valid + ParameterCheck.mandatoryString("The id of the node", id); + ParameterCheck.mandatoryString("The version of the node", versionLabel); + + try + { + // try to get the nodeRef with the given ID. This node is not a versioned node. + NodeRef currentNodeRef = new NodeRef(Repository.getStoreRef(), id); + + // the threatment is different if the node is a translation or a mlContainer + if(nodeService.getType(currentNodeRef).equals(ContentModel.TYPE_MULTILINGUAL_CONTAINER)) + { + // test if the lang parameter is valid + ParameterCheck.mandatoryString("The lang of the node", lang); + + fromPreviousEditon = true; + + versionLabel = cleanVersionLabel(versionLabel); + + // set the edition information of the mlContainer of the selected translation version + this.editionHistory = editionService.getEditions(currentNodeRef); + this.documentEdition = editionHistory.getVersion(versionLabel); + + // set the version to display + this.documentVersion = getBrowsingVersionForMLContainer(currentNodeRef, versionLabel, lang); + } + else + { + fromPreviousEditon = false; + + // set the version history + this.versionHistory = versionService.getVersionHistory(currentNodeRef); + // set the version to display + this.documentVersion = getBrowsingVersionForDocument(currentNodeRef, versionLabel); + } + } + catch (InvalidNodeRefException refErr) + { + Utils.addErrorMessage(MessageFormat.format(Application.getMessage( + FacesContext.getCurrentInstance(), Repository.ERROR_NODEREF), new Object[] {id}) ); + } + } + + /** + * Navigates to next item in the list of versioned content for the current VersionHistory + */ + @SuppressWarnings("unchecked") + public void nextItem(ActionEvent event) + { + // Get the properties of the action event + UIActionLink link = (UIActionLink)event.getComponent(); + Map params = link.getParameterMap(); + + String versionLabel = params.get("versionLabel"); + + // if the version is not specified, get the next version + if(versionLabel == null) + { + List nextVersions = new ArrayList(this.versionHistory.getSuccessors(this.documentVersion)); + + // if the version history doesn't contains successor, get the root version (the first one) + if(nextVersions == null || nextVersions.size() < 1) + { + this.documentVersion = versionHistory.getRootVersion(); + } + else + { + Collections.sort(nextVersions, VERSION_LABEL_COMPARATOR); + this.documentVersion = nextVersions.get(0); + } + } + else + { + this.documentVersion = this.versionHistory.getVersion(versionLabel); + } + } + + /** + * Navigates to previous item in the list of versioned content for the current VersionHistory + */ + @SuppressWarnings("unchecked") + public void previousItem(ActionEvent event) + { + // Get the properties of the action event + UIActionLink link = (UIActionLink)event.getComponent(); + Map params = link.getParameterMap(); + + String versionLabel = params.get("versionLabel"); + + // if the version is not specified, get the next version + if(versionLabel == null) + { + Version prevVersion = this.versionHistory.getPredecessor(this.documentVersion); + + // if the version history doesn't contains predecessor, get the last version + if(prevVersion == null) + { + List allVersions = new ArrayList(this.versionHistory.getAllVersions()); + Collections.sort(allVersions, VERSION_LABEL_COMPARATOR); + + this.documentVersion = allVersions.get(0); + } + else + { + this.documentVersion = prevVersion; + } + } + else + { + this.documentVersion = this.versionHistory.getVersion(versionLabel); + } + } + + /** + * Returns a list of objects representing the translations of the given version of the mlContainer + * + * @return List of translations + */ + @SuppressWarnings("unchecked") + public List getTranslations() + { + // get the version of the mlContainer and its translations + List translationsList = editionService.getVersionedTranslations(this.documentEdition); + + Map translationNodeRef; + + // if translation size == 0, the edition is the current edition and the translations are not yet attached. + if(translationsList.size() == 0) + { + // the selection edition is the current: use the multilingual content service + translationNodeRef = multilingualContentService.getTranslations(this.documentEdition.getVersionedNodeRef()); + } + else + { + translationNodeRef = new HashMap(translationsList.size()); + + // get the last version of the translation in the given lang of the edition + for (VersionHistory history : translationsList) + { + // get the list of versions and sort them ascending according their version label + List orderedVersions = new ArrayList(history.getAllVersions()); + Collections.sort(orderedVersions, VERSION_LABEL_COMPARATOR); + + // the last version is the first version of the list + Version lastVersion = orderedVersions.get(0); + + // fill the list of translation + if(lastVersion != null) + { + NodeRef versionNodeRef = lastVersion.getFrozenStateNodeRef(); + Locale locale = (Locale) nodeService.getProperty(versionNodeRef, ContentModel.PROP_LOCALE); + translationNodeRef.put(locale, versionNodeRef); + } + } + } + + // the list of client-side translation to return + List translations = new ArrayList(translationNodeRef.size()); + + for (Map.Entry entry : translationNodeRef.entrySet()) + { + Locale locale = entry.getKey(); + NodeRef nodeRef = entry.getValue(); + + // create a map node representation of the translation + MapNode mapNode = new MapNode(nodeRef); + + String lgge = (locale != null) ? + // convert the locale into new ISO codes + contentFilterLanguagesService.convertToNewISOCode(locale.getLanguage()).toUpperCase() + : null; + + mapNode.put("name", nodeService.getProperty(nodeRef, ContentModel.PROP_NAME)); + mapNode.put("language", lgge); + mapNode.put("url", DownloadContentServlet.generateBrowserURL(nodeRef, mapNode.getName())); + + mapNode.put("notEmpty", new Boolean(!nodeService.hasAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION))); + + // add the client side version to the list + translations.add(mapNode); + + } + + return translations; + } + + + /** + * Returns a list of objects representing the versions of the + * current document + * + * @return List of previous versions + */ + public List getVersionHistory() + { + List versions = new ArrayList(); + + for (Version version : this.versionHistory.getAllVersions()) + { + // create a map node representation of the version + MapNode clientVersion = new MapNode(version.getFrozenStateNodeRef()); + clientVersion.put("versionLabel", version.getVersionLabel()); + clientVersion.put("notes", version.getDescription()); + clientVersion.put("author", version.getCreator()); + clientVersion.put("versionDate", version.getCreatedDate()); + + if(getFrozenStateDocument().hasAspect(ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION)) + { + clientVersion.put("url", null); + } + else + { + clientVersion.put("url", DownloadContentServlet.generateBrowserURL(version.getFrozenStateNodeRef(), + clientVersion.getName())); + } + + // add the client side version to the list + versions.add(clientVersion); + } + + return versions; + } + + /** + * @return true if the version is a translation of a previous edition + */ + public boolean isFromPreviousEditon() + { + return fromPreviousEditon; + } + + /** + * Returns the URL to download content for the current document + * + * @return Content url to download the current document + */ + public String getUrl() + { + return DownloadContentServlet.generateBrowserURL(getFrozenStateNodeRef(), getName()); + } + + /** + * @return the versioned node selected by the user + */ + public Node getFrozenStateDocument() + { + return new Node(getFrozenStateNodeRef()); + } + + /** + * @return the versioned node ref selected by the user + */ + public NodeRef getFrozenStateNodeRef() + { + return documentVersion.getFrozenStateNodeRef(); + } + + /** + * @return the edition of the mlContainer of the selected verion of the translation + */ + public Node getMultilingualContainerDocument() + { + return new Node(documentEdition.getFrozenStateNodeRef()); + } + + /** + * @return the name of selected version + */ + public String getName() + { + String name = (String) nodeService.getProperty(getFrozenStateNodeRef(), ContentModel.PROP_NAME); + return name; + } + + /** + * @return the file type image URL of the version + */ + public String getFileType32() + { + String fileType = Utils.getFileTypeImage(getName(), false); + return fileType; + } + + public boolean isEmptyTranslation() + { + return nodeService.hasAspect(getFrozenStateNodeRef(), ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION); + } + + /** + * @return the version to display of the document selected by the user + */ + public Version getVersion() + { + return this.documentVersion; + } + + /** + * @return the next page to display according to which page the dialog is coming from + */ + public String getOutcome() + { + return (this.fromPreviousEditon) ? "showMLContainerDetails" : "showDocDetails"; + } + + /** + * Util method which remove the eventual '(actual)' label from the version label. + */ + private String cleanVersionLabel(String versionLabel) + { + // remove the (actual vesrion) label if needed + int idx = versionLabel.indexOf(' '); + if(idx > -1) + { + versionLabel = versionLabel.substring(0, idx); + } + return versionLabel; + } + + /** + * Util method which returns the given version of a node + */ + private Version getBrowsingVersionForDocument(NodeRef document, String versionLabel) + { + return this.versionService.getVersionHistory(document).getVersion(versionLabel); + } + + /** + * Util method which returns the current version of a node + */ + private Version getBrowsingCurrentVersionForMLContainer(NodeRef document, String lang) + { + NodeRef translation = multilingualContentService.getTranslationForLocale(document, I18NUtil.parseLocale(lang)); + this.versionHistory = versionService.getVersionHistory(translation); + + return versionService.getCurrentVersion(translation); + } + + /** + * Util method which return the last version of a translation of a given edition of a mlContainer according its language + */ + @SuppressWarnings("unchecked") + private Version getBrowsingVersionForMLContainer(NodeRef document, String editionLabel, String lang) + { + // get the list of translations of the given edition of the mlContainer + List translations = editionService.getVersionedTranslations(this.documentEdition); + + // if translation size == 0, the edition is the current edition and the translations are not yet attached. + if(translations.size() == 0) + { + // the selection edition is the current. + return getBrowsingCurrentVersionForMLContainer(document, lang); + } + else + { + Version versionToReturn = null; + + // get the last version of the translation in the given lang of the edition + for (VersionHistory history : translations) + { + // get the list of versions and sort them ascending according their version label + List orderedVersions = new ArrayList(history.getAllVersions()); + Collections.sort(orderedVersions, VERSION_LABEL_COMPARATOR); + + // the last version is the first version of the list + Version lastVersion = orderedVersions.get(0); + + if(lastVersion != null) + { + Map properties = editionService.getVersionedMetadatas(lastVersion); + Locale locale = (Locale) properties.get(ContentModel.PROP_LOCALE); + + if(locale.getLanguage().equalsIgnoreCase(lang)) + { + versionToReturn = lastVersion; + this.versionHistory = history; + break; + } + } + } + return versionToReturn; + } + + } + + + /** + * @param versionService the Version Service to set + */ + public void setVersionService(VersionService versionService) + { + this.versionService = versionService; + } + + /** + * @param editionService the Edition Service to set + */ + public void setEditionService(EditionService editionService) + { + this.editionService = editionService; + } + + /** + * @param nodeService the Node Service to set + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @param contentFilterLanguagesService the Content Filter Languages Service to set + */ + public void setContentFilterLanguagesService(ContentFilterLanguagesService contentFilterLanguagesService) + { + this.contentFilterLanguagesService = contentFilterLanguagesService; + } + + /** + * @param Multilingual Content Service the Multilingual Content Service to set + */ + public void setMultilingualContentService(MultilingualContentService multilingualContentService) + { + this.multilingualContentService = multilingualContentService; + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/web/bean/clipboard/WorkspaceClipboardItem.java b/source/java/org/alfresco/web/bean/clipboard/WorkspaceClipboardItem.java index c55dce49f7..e95ca1a25c 100644 --- a/source/java/org/alfresco/web/bean/clipboard/WorkspaceClipboardItem.java +++ b/source/java/org/alfresco/web/bean/clipboard/WorkspaceClipboardItem.java @@ -15,11 +15,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ package org.alfresco.web.bean.clipboard; @@ -35,6 +35,7 @@ import org.alfresco.model.ApplicationModel; import org.alfresco.model.ContentModel; import org.alfresco.repo.avm.AVMNodeConverter; import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.ml.MultilingualContentService; import org.alfresco.service.cmr.model.FileExistsException; import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.repository.ChildAssociationRef; @@ -52,20 +53,20 @@ import org.alfresco.web.ui.repo.component.shelf.UIClipboardShelfItem; /** * Class representing a 'workspace' store protocol clipboard item - * + * * @author Kevin Roast */ public class WorkspaceClipboardItem extends AbstractClipboardItem { private static final String WORKSPACE_PASTE_VIEW_ID = "/jsp/browse/browse.jsp"; private static final String AVM_PASTE_VIEW_ID = "/jsp/wcm/browse-sandbox.jsp"; - + private static final String MSG_LINK_TO = "link_to"; - + // File extension to use for link nodes private static final String LINK_NODE_EXTENSION = ".url"; - - + + /** * @param ref * @param mode @@ -74,7 +75,7 @@ public class WorkspaceClipboardItem extends AbstractClipboardItem { super(ref, mode); } - + /** * @see org.alfresco.web.bean.clipboard.ClipboardItem#supportsLink() */ @@ -82,7 +83,7 @@ public class WorkspaceClipboardItem extends AbstractClipboardItem { return true; } - + /** * @see org.alfresco.web.bean.clipboard.ClipboardItem#canCopyToViewId(java.lang.String) */ @@ -109,28 +110,31 @@ public class WorkspaceClipboardItem extends AbstractClipboardItem { NavigationBean navigator = (NavigationBean)FacesHelper.getManagedBean(fc, NavigationBean.BEAN_NAME); NodeRef destRef = new NodeRef(Repository.getStoreRef(), navigator.getCurrentNodeId()); - + DictionaryService dd = getServiceRegistry().getDictionaryService(); NodeService nodeService = getServiceRegistry().getNodeService(); FileFolderService fileFolderService = getServiceRegistry().getFileFolderService(); CopyService copyService = getServiceRegistry().getCopyService(); - + MultilingualContentService multilingualContentService = getServiceRegistry().getMultilingualContentService(); + // TODO: Should we be using primary parent here? // We are assuming that the item exists in only a single parent and that the source for // the clipboard operation (e.g. the source folder) is specifically that parent node. // So does not allow for more than one possible parent node - or for linked objects! - // This code should be refactored to use a parent ID when appropriate. + // This code should be refactored to use a parent ID when appropriate. ChildAssociationRef assocRef = nodeService.getPrimaryParent(getNodeRef()); - + // initial name to attempt the copy of the item with String name = getName(); + String translationPrefix = ""; + if (action == UIClipboardShelfItem.ACTION_PASTE_LINK) { // copy as link was specifically requested by the user - String linkTo = Application.getMessage(fc, MSG_LINK_TO); + String linkTo = Application.getMessage(fc, MSG_LINK_TO); name = linkTo + ' ' + name; } - + boolean operationComplete = false; while (operationComplete == false) { @@ -147,7 +151,7 @@ public class WorkspaceClipboardItem extends AbstractClipboardItem // LINK operation if (logger.isDebugEnabled()) logger.debug("Attempting to link node ID: " + getNodeRef() + " into node: " + destRef.toString()); - + // we create a special Link Object node that has a property to reference the original // create the node using the nodeService (can only use FileFolderService for content) if (checkExists(name + LINK_NODE_EXTENSION, destRef) == false) @@ -164,7 +168,7 @@ public class WorkspaceClipboardItem extends AbstractClipboardItem assocRef.getQName(), ApplicationModel.TYPE_FILELINK, props); - + // apply the titled aspect - title and description Map titledProps = new HashMap(2, 1.0f); titledProps.put(ContentModel.PROP_TITLE, name); @@ -180,7 +184,7 @@ public class WorkspaceClipboardItem extends AbstractClipboardItem assocRef.getQName(), ApplicationModel.TYPE_FOLDERLINK, props); - + // apply the uifacets aspect - icon, title and description props Map uiFacetsProps = new HashMap(4, 1.0f); uiFacetsProps.put(ApplicationModel.PROP_ICON, "space-icon-link"); @@ -188,7 +192,7 @@ public class WorkspaceClipboardItem extends AbstractClipboardItem uiFacetsProps.put(ContentModel.PROP_DESCRIPTION, name); nodeService.addAspect(childRef.getChildRef(), ApplicationModel.ASPECT_UIFACETS, uiFacetsProps); } - + // if we get here without an exception, the clipboard link operation was successful operationComplete = true; } @@ -198,7 +202,7 @@ public class WorkspaceClipboardItem extends AbstractClipboardItem // COPY operation if (logger.isDebugEnabled()) logger.debug("Attempting to copy node: " + getNodeRef() + " into node ID: " + destRef.toString()); - + // first check that we are not attempting to copy a duplicate into the same parent if (destRef.equals(assocRef.getParentRef()) && name.equals(getName())) { @@ -206,7 +210,7 @@ public class WorkspaceClipboardItem extends AbstractClipboardItem String copyOf = Application.getMessage(fc, MSG_COPY_OF); name = copyOf + ' ' + name; } - + if (dd.isSubClass(getType(), ContentModel.TYPE_CONTENT) || dd.isSubClass(getType(), ContentModel.TYPE_FOLDER)) { @@ -216,6 +220,11 @@ public class WorkspaceClipboardItem extends AbstractClipboardItem destRef, name); } + else if(dd.isSubClass(getType(), ContentModel.TYPE_MULTILINGUAL_CONTAINER)) + { + // copy the mlContainer and its translations + multilingualContentService.copyTranslationContainer(getNodeRef(), destRef, translationPrefix); + } else { // copy the node @@ -229,7 +238,7 @@ public class WorkspaceClipboardItem extends AbstractClipboardItem true); } } - + // if we get here without an exception, the clipboard copy operation was successful operationComplete = true; } @@ -239,7 +248,7 @@ public class WorkspaceClipboardItem extends AbstractClipboardItem // MOVE operation if (logger.isDebugEnabled()) logger.debug("Attempting to move node: " + getNodeRef() + " into node ID: " + destRef.toString()); - + if (dd.isSubClass(getType(), ContentModel.TYPE_CONTENT) || dd.isSubClass(getType(), ContentModel.TYPE_FOLDER)) { @@ -249,6 +258,11 @@ public class WorkspaceClipboardItem extends AbstractClipboardItem destRef, name); } + else if(dd.isSubClass(getType(), ContentModel.TYPE_MULTILINGUAL_CONTAINER)) + { + // copy the mlContainer and its translations + multilingualContentService.moveTranslationContainer(getNodeRef(), destRef); + } else { // move the node @@ -258,7 +272,7 @@ public class WorkspaceClipboardItem extends AbstractClipboardItem ContentModel.ASSOC_CONTAINS, assocRef.getQName()); } - + // if we get here without an exception, the clipboard move operation was successful operationComplete = true; } @@ -266,7 +280,7 @@ public class WorkspaceClipboardItem extends AbstractClipboardItem catch (FileExistsException fileExistsErr) { if (getMode() != ClipboardStatus.COPY) - { + { // we should not rename an item when it is being moved - so exit throw fileExistsErr; } @@ -284,6 +298,7 @@ public class WorkspaceClipboardItem extends AbstractClipboardItem try { if (tx != null) {tx.rollback();} } catch (Exception tex) {} String copyOf = Application.getMessage(fc, MSG_COPY_OF); name = copyOf + ' ' + name; + translationPrefix = copyOf + ' ' + translationPrefix; } else { @@ -297,15 +312,15 @@ public class WorkspaceClipboardItem extends AbstractClipboardItem else if (AVM_PASTE_VIEW_ID.equals(viewId)) { AVMBrowseBean avmBrowseBean = (AVMBrowseBean)FacesHelper.getManagedBean(fc, AVMBrowseBean.BEAN_NAME); - + String destPath = avmBrowseBean.getCurrentPath(); NodeRef destRef = AVMNodeConverter.ToNodeRef(-1, destPath); - + CrossRepositoryCopyService crossRepoCopyService = getServiceRegistry().getCrossRepositoryCopyService(); - + // initial name to attempt the copy of the item with String name = getName(); - + boolean operationComplete = false; while (operationComplete == false) { @@ -320,10 +335,10 @@ public class WorkspaceClipboardItem extends AbstractClipboardItem // COPY operation if (logger.isDebugEnabled()) logger.debug("Attempting to copy node: " + getNodeRef() + " into node ID: " + destRef.toString()); - + // inter-store copy operation crossRepoCopyService.copy(getNodeRef(), destRef, name); - + // if we get here without an exception, the clipboard copy operation was successful operationComplete = true; } @@ -336,7 +351,7 @@ public class WorkspaceClipboardItem extends AbstractClipboardItem catch (FileExistsException fileExistsErr) { if (getMode() != ClipboardStatus.COPY) - { + { // we should not rename an item when it is being moved - so exit throw fileExistsErr; } diff --git a/source/java/org/alfresco/web/bean/content/DeleteContentDialog.java b/source/java/org/alfresco/web/bean/content/DeleteContentDialog.java index 6001e76064..a5fd621630 100644 --- a/source/java/org/alfresco/web/bean/content/DeleteContentDialog.java +++ b/source/java/org/alfresco/web/bean/content/DeleteContentDialog.java @@ -15,11 +15,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ package org.alfresco.web.bean.content; @@ -28,6 +28,8 @@ import java.text.MessageFormat; import javax.faces.context.FacesContext; +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.ml.MultilingualContentService; import org.alfresco.web.app.AlfrescoNavigationHandler; import org.alfresco.web.app.Application; import org.alfresco.web.bean.dialog.BaseDialogBean; @@ -37,16 +39,20 @@ import org.apache.commons.logging.LogFactory; /** * Bean implementation for the "Delete Content" dialog - * + * * @author gavinc */ public class DeleteContentDialog extends BaseDialogBean { + + + protected MultilingualContentService multilingualContentService; + private static final Log logger = LogFactory.getLog(DeleteContentDialog.class); - + // ------------------------------------------------------------------------------ // Dialog implementation - + @Override protected String finishImpl(FacesContext context, String outcome) throws Exception @@ -55,31 +61,43 @@ public class DeleteContentDialog extends BaseDialogBean Node node = this.browseBean.getDocument(); if (node != null) { - if (logger.isDebugEnabled()) - logger.debug("Trying to delete content node: " + node.getId()); - - // delete the node - this.nodeService.deleteNode(node.getNodeRef()); + if(ContentModel.TYPE_MULTILINGUAL_CONTAINER.equals(node.getType())) + { + if (logger.isDebugEnabled()) + logger.debug("Trying to delete multilingual container: " + node.getId() + " and its translations" ); + + // delete the mlContainer and its translations + multilingualContentService.deleteTranslationContainer(node.getNodeRef()); + } + else + { + if (logger.isDebugEnabled()) + logger.debug("Trying to delete content node: " + node.getId()); + + // delete the node + this.nodeService.deleteNode(node.getNodeRef()); + } + } else { logger.warn("WARNING: delete called without a current Document!"); } - + return outcome; } - + @Override protected String doPostCommitProcessing(FacesContext context, String outcome) { // clear action context this.browseBean.setDocument(null); - + // setting the outcome will show the browse view again return AlfrescoNavigationHandler.CLOSE_DIALOG_OUTCOME + AlfrescoNavigationHandler.OUTCOME_SEPARATOR + "browse"; } - + @Override protected String getErrorMessageId() { @@ -91,21 +109,51 @@ public class DeleteContentDialog extends BaseDialogBean { return false; } - + // ------------------------------------------------------------------------------ // Bean Getters and Setters /** * Returns the confirmation to display to the user before deleting the content. - * + * * @return The formatted message to display */ public String getConfirmMessage() { - String fileConfirmMsg = Application.getMessage(FacesContext.getCurrentInstance(), - "delete_file_confirm"); - - return MessageFormat.format(fileConfirmMsg, - new Object[] {this.browseBean.getDocument().getName()}); + String fileConfirmMsg = null; + + Node document = this.browseBean.getDocument(); + + if(document.getType().equals(ContentModel.TYPE_MULTILINGUAL_CONTAINER)) + { + fileConfirmMsg = Application.getMessage(FacesContext.getCurrentInstance(), + "delete_ml_container_confirm"); + } + else if(document.hasAspect(ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION)) + { + fileConfirmMsg = Application.getMessage(FacesContext.getCurrentInstance(), + "delete_empty_translation_confirm"); + } + else if(document.hasAspect(ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)) + { + fileConfirmMsg = Application.getMessage(FacesContext.getCurrentInstance(), + "delete_translation_confirm"); + } + else + { + fileConfirmMsg = Application.getMessage(FacesContext.getCurrentInstance(), + "delete_file_confirm"); + } + + return MessageFormat.format(fileConfirmMsg, + new Object[] {document.getName()}); + } + + /** + * @param multilingualContentService the Multilingual Content Service to set + */ + public void setMultilingualContentService(MultilingualContentService multilingualContentService) + { + this.multilingualContentService = multilingualContentService; } } diff --git a/source/java/org/alfresco/web/bean/ml/AddTranslationDialog.java b/source/java/org/alfresco/web/bean/ml/AddTranslationDialog.java index 2dddf6fab1..2f6cca34c1 100644 --- a/source/java/org/alfresco/web/bean/ml/AddTranslationDialog.java +++ b/source/java/org/alfresco/web/bean/ml/AddTranslationDialog.java @@ -31,13 +31,9 @@ import javax.faces.model.SelectItem; import org.alfresco.i18n.I18NUtil; import org.alfresco.service.cmr.ml.MultilingualContentService; -import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.web.app.servlet.DownloadContentServlet; import org.alfresco.web.bean.UserPreferencesBean; import org.alfresco.web.bean.content.AddContentDialog; -import org.alfresco.web.bean.repository.Node; -import org.alfresco.web.ui.common.Utils; /** * Dialog bean to upload a new document and to add it to an existing MLContainer. @@ -81,31 +77,11 @@ public class AddTranslationDialog extends AddContentDialog { // add the new file to the repository outcome = super.finishImpl(context, outcome); - + // add a new translation multilingualContentService.addTranslation(this.createdNode, this.mlTranslation, I18NUtil.parseLocale(this.language)); - - // Get the content data of the translation - ContentData contentData = fileFolderService.getReader(this.createdNode).getContentData(); - - Node createdNode = new Node(this.createdNode); - - Map browseProp = createdNode.getProperties(); - browseProp.put("size", contentData.getSize()); - browseProp.put("mimetype", contentData.getMimetype()); - browseProp.put("cm:content", contentData); - browseProp.put("fileType32", Utils.getFileTypeImage(createdNode.getName(), false)); - browseProp.put("url", DownloadContentServlet.generateDownloadURL(this.createdNode, createdNode.getName())); - - this.browseBean.setDocument(createdNode); - - return outcome; - } - @Override - protected String getDefaultFinishOutcome() - { - return "showDocDetails"; + return "browse"; } @Override diff --git a/source/java/org/alfresco/web/bean/ml/AddTranslationWithoutContentDialog.java b/source/java/org/alfresco/web/bean/ml/AddTranslationWithoutContentDialog.java index 35fa820ab1..5f225d2bef 100644 --- a/source/java/org/alfresco/web/bean/ml/AddTranslationWithoutContentDialog.java +++ b/source/java/org/alfresco/web/bean/ml/AddTranslationWithoutContentDialog.java @@ -33,13 +33,10 @@ import javax.faces.model.SelectItem; import org.alfresco.i18n.I18NUtil; import org.alfresco.model.ContentModel; import org.alfresco.service.cmr.ml.MultilingualContentService; -import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.web.app.servlet.DownloadContentServlet; import org.alfresco.web.bean.UserPreferencesBean; import org.alfresco.web.bean.dialog.BaseDialogBean; import org.alfresco.web.bean.repository.Node; -import org.alfresco.web.ui.common.Utils; /** * Dialog bean to add a new translation without content. I means, a new node is created @@ -55,23 +52,22 @@ public class AddTranslationWithoutContentDialog extends BaseDialogBean // the translation being to be created protected NodeRef newTranslation; - private String name; private String title; private String description; private String author; private String language; - + private boolean showOtherProperties; @Override public void init(Map parameters) { super.init(parameters); - name = null; title = null; description = null; author = null; language = null; + showOtherProperties = true; } @Override @@ -84,29 +80,23 @@ public class AddTranslationWithoutContentDialog extends BaseDialogBean Locale locale = I18NUtil.parseLocale(language); // add the empty translation - newTranslation = multilingualContentService.addEmptyTranslation(refNode, name, locale); + newTranslation = multilingualContentService.addEmptyTranslation(refNode, null, locale); // set the properties nodeService.setProperty(newTranslation, ContentModel.PROP_DESCRIPTION, description); nodeService.setProperty(newTranslation, ContentModel.PROP_AUTHOR, author); nodeService.setProperty(newTranslation, ContentModel.PROP_TITLE, title); - // Get the content data of the new translation - ContentData newTranslationContentData = fileFolderService.getReader(newTranslation).getContentData(); - - // set the current browse node - Node browse = new Node(newTranslation); - - Map browseProp = browse.getProperties(); - browseProp.put("size", newTranslationContentData.getSize()); - browseProp.put("mimetype", newTranslationContentData.getMimetype()); - browseProp.put("cm:content", newTranslationContentData); - browseProp.put("fileType32", Utils.getFileTypeImage(name, false)); - browseProp.put("url", DownloadContentServlet.generateDownloadURL(newTranslation, name)); - - this.browseBean.setDocument(browse); - - return outcome; + // redirect the user according the value of (show other properties) + if(showOtherProperties) + { + this.browseBean.setDocument(new Node(this.newTranslation)); + return "dialog:setContentProperties"; + } + else + { + return "browse"; + } } /** @@ -173,22 +163,6 @@ public class AddTranslationWithoutContentDialog extends BaseDialogBean this.language = language; } - /** - * @return the name - */ - public String getName() - { - return name; - } - - /** - * @param name the name to set - */ - public void setName(String name) - { - this.name = name; - } - /** * @return the title */ @@ -212,4 +186,20 @@ public class AddTranslationWithoutContentDialog extends BaseDialogBean { return userPreferencesBean.getAvailablesContentFilterLanguages(this.browseBean.getDocument().getNodeRef(), false); } + + /** + * @return the showOtherProperties + */ + public boolean isShowOtherProperties() + { + return showOtherProperties; + } + + /** + * @param showOtherProperties the showOtherProperties to set + */ + public void setShowOtherProperties(boolean showOtherProperties) + { + this.showOtherProperties = showOtherProperties; + } } diff --git a/source/java/org/alfresco/web/bean/ml/EditMLContainerDialog.java b/source/java/org/alfresco/web/bean/ml/EditMLContainerDialog.java index 49e6daae86..98af031ffa 100644 --- a/source/java/org/alfresco/web/bean/ml/EditMLContainerDialog.java +++ b/source/java/org/alfresco/web/bean/ml/EditMLContainerDialog.java @@ -15,11 +15,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ package org.alfresco.web.bean.ml; @@ -29,6 +29,7 @@ import java.util.Map; import javax.faces.context.FacesContext; +import org.alfresco.model.ContentModel; import org.alfresco.service.cmr.ml.MultilingualContentService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; @@ -38,7 +39,7 @@ import org.alfresco.web.bean.repository.Node; /** * Dialog bean to edit an existing multilingual container. - * + * * @author Yannick Pignot */ public class EditMLContainerDialog extends BaseDialogBean @@ -69,7 +70,7 @@ public class EditMLContainerDialog extends BaseDialogBean @Override protected String finishImpl(FacesContext context, String outcome) throws Exception - { + { // get the container node ref NodeRef container = editableNode.getNodeRef(); @@ -92,10 +93,20 @@ public class EditMLContainerDialog extends BaseDialogBean */ protected Node initEditableNode() { - return new Node( - multilingualContentService.getTranslationContainer( - this.browseBean.getDocument().getNodeRef()) - ); + Node currentNode = this.browseBean.getDocument(); + + if(ContentModel.TYPE_MULTILINGUAL_CONTAINER.equals(currentNode.getType())) + { + return currentNode; + } + else + { + return new Node( + multilingualContentService.getTranslationContainer( + currentNode.getNodeRef()) + ); + } + } /** diff --git a/source/java/org/alfresco/web/bean/ml/MultilingualUtils.java b/source/java/org/alfresco/web/bean/ml/MultilingualUtils.java new file mode 100644 index 0000000000..a7e273be22 --- /dev/null +++ b/source/java/org/alfresco/web/bean/ml/MultilingualUtils.java @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.web.bean.ml; + +import java.util.Locale; +import java.util.Map; + +import javax.faces.context.FacesContext; + +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.ml.MultilingualContentService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.web.app.servlet.FacesHelper; +import org.alfresco.web.bean.repository.Node; + +/** + * Util class for the management of multilingual documents on the web client side + * + * @author yanipig + */ +public class MultilingualUtils +{ + + /** + * Returns true if the current user has enough right to add a content to the space + * where the pivot translation is located in. + * + * @param multlingualDocument + * @param fc + * @return + */ + public static boolean canAddChildrenToPivotSpace(Node multlingualDocument, FacesContext fc) + { + MultilingualContentService mlservice = getMultilingualContentService(fc); + NodeService nodeService = getNodeService(fc); + + // get the pivot translation and get the space where it's located + NodeRef pivot = mlservice.getPivotTranslation(multlingualDocument.getNodeRef()); + NodeRef space = nodeService.getPrimaryParent(pivot).getParentRef(); + + // return if the current user can add a content to the space of the pivot + return new Node(space).hasPermission(PermissionService.ADD_CHILDREN); + } + + /** + * Returns true if the current user can delete each translation of the mlContainer of the given node + * + * @param multlingualDocument + * @param fc + * @return + */ + public static boolean canDeleteEachTranslation(Node multlingualDocument, FacesContext fc) + { + boolean can = true; + + MultilingualContentService mlservice = getMultilingualContentService(fc); + + Map translations = mlservice.getTranslations(multlingualDocument.getNodeRef()); + for (Map.Entry entry : translations.entrySet()) + { + Node translation = new Node(entry.getValue()); + + if(translation.hasPermission(PermissionService.DELETE_NODE) == false + || translation.isLocked() == true + || translation.hasAspect(ContentModel.ASPECT_WORKING_COPY) == true + ) + { + can = false; + break; + } + } + + return can; + } + + /** + * Returns true if the current user can move each translation of the mlContainer of the given node + * + * @param multlingualDocument + * @param fc + * @return + */ + public static boolean canMoveEachTranslation(Node multlingualDocument, FacesContext fc) + { + boolean can = true; + + MultilingualContentService mlservice = getMultilingualContentService(fc); + + Map translations = mlservice.getTranslations(multlingualDocument.getNodeRef()); + for (Map.Entry entry : translations.entrySet()) + { + Node translation = new Node(entry.getValue()); + + if(translation.hasPermission(PermissionService.DELETE_NODE) == false) + { + can = false; + break; + } + } + + return can; + } + + /** + * Returns true if the current user can delete each translation and create + * * a new content in the space + * + * @param multlingualDocument + * @param fc + * @return + */ + public static boolean canStartNewEditon(Node multlingualDocument, FacesContext fc) + { + boolean canDelete = MultilingualUtils.canMoveEachTranslation(multlingualDocument, fc); + boolean canCreate = MultilingualUtils.canAddChildrenToPivotSpace(multlingualDocument, fc); + + return canDelete && canCreate; + } + + private static MultilingualContentService getMultilingualContentService(FacesContext fc) + { + return (MultilingualContentService) FacesHelper.getManagedBean(fc, "MultilingualContentService"); + } + + private static NodeService getNodeService(FacesContext fc) + { + return (NodeService) FacesHelper.getManagedBean(fc, "NodeService"); + } + +} diff --git a/source/java/org/alfresco/web/bean/ml/NewEditionWizard.java b/source/java/org/alfresco/web/bean/ml/NewEditionWizard.java index 2137b6f46c..86bfb02979 100644 --- a/source/java/org/alfresco/web/bean/ml/NewEditionWizard.java +++ b/source/java/org/alfresco/web/bean/ml/NewEditionWizard.java @@ -32,8 +32,11 @@ import java.util.Locale; import java.util.Map; import javax.faces.context.FacesContext; -import javax.faces.model.SelectItem; +import javax.faces.event.ActionEvent; +import javax.faces.model.DataModel; +import javax.faces.model.ListDataModel; +import org.alfresco.i18n.I18NUtil; import org.alfresco.model.ContentModel; import org.alfresco.repo.version.VersionModel; import org.alfresco.service.cmr.lock.LockService; @@ -44,36 +47,47 @@ import org.alfresco.service.cmr.ml.MultilingualContentService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.version.Version; +import org.alfresco.service.cmr.version.VersionService; import org.alfresco.service.cmr.version.VersionType; +import org.alfresco.util.ParameterCheck; import org.alfresco.web.app.AlfrescoNavigationHandler; +import org.alfresco.web.app.Application; import org.alfresco.web.bean.repository.Node; import org.alfresco.web.bean.wizard.BaseWizardBean; +import org.alfresco.web.ui.common.component.UIActionLink; /** * Wizard bean to create a new edition from an existing MLContainer. * - * @author yanipig + * @author Yanick Pignot */ public class NewEditionWizard extends BaseWizardBean { + public static final String ID_MESSAGE_MINOR_CHANGE = "minor_change"; + public static final String ID_MESSAGE_MAJOR_CHANGE = "major_change"; + protected EditionService editionService; protected MultilingualContentService multilingualContentService; protected ContentFilterLanguagesService contentFilterLanguagesService; protected LockService lockService; + protected VersionService versionService; protected NodeRef mlContainerToVersion; - private List selectableTranslations; - private String startingItemNodeString; + private List selectableTranslations; + private List translationsCheckedOut; + private String editionNotes; private boolean minorChange; private boolean otherProperties; - private List translationCheckedOut; + private boolean skipFirstStep; private String language; private String title; - private String author; private boolean hasTranslationCheckedOut; private NodeRef startingElement; + private String author; + private String selectedLanguage; + @Override public void init(Map parameters) @@ -81,32 +95,50 @@ public class NewEditionWizard extends BaseWizardBean super.init(parameters); // reset the fileds - - startingItemNodeString = null; editionNotes = null; minorChange = true; otherProperties = false; - translationCheckedOut = null; - language = "lang"; - title = "title"; - author = "author"; + translationsCheckedOut = null; selectableTranslations = null; - // set the mlContainer to version - NodeRef currentNodeRef = this.browseBean.getDocument().getNodeRef(); - if(ContentModel.TYPE_MULTILINGUAL_CONTAINER.equals(nodeService.getType(currentNodeRef))) + if(!skipFirstStep) { - mlContainerToVersion = currentNodeRef; - } - else - { - mlContainerToVersion = multilingualContentService.getTranslationContainer(currentNodeRef); + // this properties is set by the skipFirstStep action event method. + + language = null; + title = null; + + // set the current mlContainer + setMLContainer(); } - translationCheckedOut = getTranslationCheckedOut(); - hasTranslationCheckedOut = getHasTranslationCheckedOut(); + // init the list of the available translations and the list of translations checked out + initTranslationList(); + // true if they are at leat one translation checked out + hasTranslationCheckedOut = this.translationsCheckedOut.size() > 0; + } + + /** + * Force the the lang of the new pivot translation for the new edition and skip the first step + */ + public void skipFirstStep(ActionEvent event) + { + skipFirstStep = true; + + // Get the lang of the new + UIActionLink link = (UIActionLink)event.getComponent(); + Map params = link.getParameterMap(); + String lang = params.get("lang"); + + // test if the parameter is valid + ParameterCheck.mandatoryString("The lang of the node", lang); + + // set the current mlContainer + setMLContainer(); + + setStartingItem(lang.toLowerCase()); } @Override @@ -127,6 +159,7 @@ public class NewEditionWizard extends BaseWizardBean // create the edition and get the reference of the new starting translation NodeRef newPivot = editionService.createEdition(startingElement, versionProperties); + // redirect the user at the 'modify translation properties' page if desire. if (otherProperties == true) { this.browseBean.setDocument(new Node(newPivot)); @@ -137,9 +170,30 @@ public class NewEditionWizard extends BaseWizardBean outcome = AlfrescoNavigationHandler.CLOSE_DIALOG_OUTCOME + AlfrescoNavigationHandler.OUTCOME_SEPARATOR + "browse"; } + skipFirstStep = false; + return outcome; } + /** + * Returns the properties for checked out translations JSF DataModel + * + * @return JSF DataModel representing the translations checked out + */ + public DataModel getTranslationsCheckedOutDataModel() + { + return getDataModel(this.translationsCheckedOut); + } + + /** + * Returns the properties for available translations JSF DataModel + * + * @return JSF DataModel representing the available translation for a new edition + */ + public DataModel getAvailableTranslationsDataModel() + { + return getDataModel(this.selectableTranslations); + } /** * Determines whether there are any translation checked out. @@ -148,8 +202,6 @@ public class NewEditionWizard extends BaseWizardBean */ public boolean getHasTranslationCheckedOut() { - hasTranslationCheckedOut = getTranslationCheckedOut().size() > 0; - return hasTranslationCheckedOut; } @@ -165,117 +217,94 @@ public class NewEditionWizard extends BaseWizardBean return super.getNextButtonDisabled() || hasTranslationCheckedOut; } + /** + * @param language of the starting translation to set as the new pivot of the new edition + */ + private void setStartingItem(String language) + { + // get the starting point translation with its locale + startingElement = multilingualContentService.getTranslationForLocale(mlContainerToVersion, I18NUtil.parseLocale(language)); + + // set the futur properties of the new starting element (only usefull for the summary step) + setLanguage(language); + setTitle((String) nodeService.getProperty(startingElement, ContentModel.PROP_TITLE)); + setAuthor((String) nodeService.getProperty(startingElement, ContentModel.PROP_AUTHOR)); + } /** - * Return the list of cecked out document found in the mlContainer. - * - * @return the list of checked out translation + * Util metho which construct a data model with rows passed in parameter */ - public List getTranslationCheckedOut() + private DataModel getDataModel(Object rows) { - if(translationCheckedOut == null ) + DataModel translationDataModel = new ListDataModel(); + + translationDataModel.setWrappedData(rows); + + return translationDataModel; + } + + /** + * Util method which init the lists of translations + */ + private void initTranslationList() + { + this.translationsCheckedOut = new ArrayList(); + this.selectableTranslations = new ArrayList(); + + // get all translations of the mlContainer + Map translations = multilingualContentService.getTranslations(mlContainerToVersion); + + // fill the data table rows list + for(Map.Entry entry : translations.entrySet()) { - // first call, init the list + NodeRef nodeRef = entry.getValue(); - this.translationCheckedOut = new ArrayList(); + String name = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); + String langCode = ((Locale) nodeService.getProperty(nodeRef, ContentModel.PROP_LOCALE)).getLanguage(); + String langText = contentFilterLanguagesService.getLabelByCode(langCode); + String lockOwner = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_LOCK_OWNER); - // get all translations of the mlContainer - Map translations = multilingualContentService.getTranslations(mlContainerToVersion); + // create the row with the current translation + TranslationWrapper wrapper = new TranslationWrapper(name, langCode, lockOwner, langText); - // fill the select itms - for(Map.Entry entry : translations.entrySet()) + if(!nodeService.hasAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION)) { - NodeRef nodeRef = entry.getValue(); - - if(nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE)) + // add it to the selectable list if it is not empty + this.selectableTranslations.add(wrapper); + } + if(nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE)) + { + LockStatus lockStatus = lockService.getLockStatus(nodeRef); + if (lockStatus != LockStatus.NO_LOCK) { - LockStatus lockStatus = lockService.getLockStatus(nodeRef); - if (lockStatus != LockStatus.NO_LOCK) - { - // if the node is locked, add it to the locked translation list - String name = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); - Locale lang = (Locale) nodeService.getProperty(nodeRef, ContentModel.PROP_LOCALE); - String lockOwner = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_WORKING_COPY_OWNER); - - this.translationCheckedOut.add(new SelectItem( - "(" + lang.getLanguage() + ")", - name, - lockOwner - )); - } + // if the node is locked, add it to the locked translation list + this.translationsCheckedOut.add(wrapper); } } - } - return this.translationCheckedOut; + } } - - /** - * Return the list of available translations to begin the starting translations of the new edition. - * - * @return the list of available translations - */ - public List getSelectableTranslations() + private void setMLContainer() { - if(selectableTranslations == null) + // set the mlContainer to version + NodeRef currentNodeRef = this.browseBean.getDocument().getNodeRef(); + + if(ContentModel.TYPE_MULTILINGUAL_CONTAINER.equals(nodeService.getType(currentNodeRef))) { - // first call, init the list - - selectableTranslations = new ArrayList(); - - // get all translations of the mlContainer - Map translations = multilingualContentService.getTranslations(mlContainerToVersion); - - // fill the select items - for(Map.Entry entry : translations.entrySet()) - { - NodeRef nodeRef = entry.getValue(); - - //add each non empty translation - if(!nodeService.hasAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION)) - { - String name = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); - Locale lang = (Locale) nodeService.getProperty(nodeRef, ContentModel.PROP_LOCALE); - selectableTranslations.add(new SelectItem( - nodeRef.toString(), - name + " - " + contentFilterLanguagesService.getLabelByCode(lang.getLanguage()) - )); - } - } + mlContainerToVersion = currentNodeRef; + } + else + { + mlContainerToVersion = multilingualContentService.getTranslationContainer(currentNodeRef); } - return selectableTranslations; - } - - - - - /** - * @param multilingualContentService the Multilingual Content Service to set - */ - public void setMultilingualContentService(MultilingualContentService multilingualContentService) - { - this.multilingualContentService = multilingualContentService; - } - - - /** - * @param nodeService the Node Service to set - */ - public void setNodeService(NodeService nodeService) - { - this.nodeService = nodeService; - } - - - /** - * @param editionService the Edition Service to set - */ - public void setEditionService(EditionService editionService) - { - this.editionService = editionService; + if(!skipFirstStep) + { + // init the pivot language (it will be selected by default) + selectedLanguage = ((Locale) nodeService.getProperty(mlContainerToVersion, ContentModel.PROP_LOCALE)).getLanguage(); + } } /** @@ -326,49 +355,6 @@ public class NewEditionWizard extends BaseWizardBean this.otherProperties = otherProperties; } - /** - * @return the starting translation being the new pivot of tne new edition - */ - public String getStartingItemNodeString() - { - return startingItemNodeString; - } - - /** - * @param startingItemNodeString the starting translation to set as the new pivot of tne new edition - */ - public void setStartingItemNodeString(String startingItemNodeString) - { - // get the starting point translation with its id - startingElement = new NodeRef(startingItemNodeString); - - // set the futur properties of the new starting element (only usefull for the summary step) - setLanguage((Locale) nodeService.getProperty(startingElement, ContentModel.PROP_LOCALE)); - setAuthor((String) nodeService.getProperty(startingElement, ContentModel.PROP_AUTHOR)); - setTitle((String) nodeService.getProperty(startingElement, ContentModel.PROP_TITLE)); - - this.startingItemNodeString = startingItemNodeString; - } - - - /** - * @param contentFilterLanguagesService the Content Filter Languages Service to set - */ - public void setContentFilterLanguagesService(ContentFilterLanguagesService contentFilterLanguagesService) - { - this.contentFilterLanguagesService = contentFilterLanguagesService; - } - - - /** - * @param lockService the Lock Service to set - */ - public void setLockService(LockService lockService) - { - this.lockService = lockService; - } - - /** * @return the author */ @@ -378,21 +364,12 @@ public class NewEditionWizard extends BaseWizardBean } - /** - * @param author the author to set - */ - public void setAuthor(String author) - { - this.author = author; - } - - /** * @return the language */ public String getLanguage() { - return language; + return contentFilterLanguagesService.getLabelByCode(language); } @@ -429,19 +406,170 @@ public class NewEditionWizard extends BaseWizardBean this.title = title; } + /** + * @param author the author to set + */ + public void setAuthor(String author) + { + this.author = author; + } + /** * @return the versionLabel */ public String getVersionLabel() { - String toReturn = "Version Label"; + String label = versionService.getCurrentVersion(mlContainerToVersion).getVersionLabel(); + + String nextLabel = null; + + try + { + int dotPosition = label.indexOf('.'); + + int major = Integer.parseInt(label.substring(0, dotPosition)); + int minor = Integer.parseInt(label.substring(dotPosition + 1)); + + if(minorChange) + { + nextLabel = major + "." + (minor + 1); + } + else + { + nextLabel = (major + 1) + ".0"; + } + } + catch(Exception e) + { + nextLabel = ""; + } if(minorChange) { - toReturn += " (minor change)"; + String minorString = Application.getMessage( + FacesContext.getCurrentInstance(), + ID_MESSAGE_MINOR_CHANGE); + + return nextLabel + " (" + minorString + ')' ; + } + else + { + return nextLabel; + } + } + + + + /** + * @return the selectedTranslationLanguage + */ + public String getSelectedTranslationLanguage() + { + return selectedLanguage; + } + + /** + * @param selectedTranslationLanguage the selectedTranslationLanguage to set + */ + public void setSelectedTranslationLanguage(String language) + { + // only the selected language is not set as null + if(language != null) + { + setStartingItem(language); } - return toReturn; + } + + + /** + * @param multilingualContentService the Multilingual Content Service to set + */ + public void setMultilingualContentService(MultilingualContentService multilingualContentService) + { + this.multilingualContentService = multilingualContentService; + } + + + /** + * @param nodeService the Node Service to set + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @param contentFilterLanguagesService the Content Filter Languages Service to set + */ + public void setContentFilterLanguagesService(ContentFilterLanguagesService contentFilterLanguagesService) + { + this.contentFilterLanguagesService = contentFilterLanguagesService; + } + + + /** + * @param lockService the Lock Service to set + */ + public void setLockService(LockService lockService) + { + this.lockService = lockService; + } + + /** + * @param editionService the Edition Service to set + */ + public void setEditionService(EditionService editionService) + { + this.editionService = editionService; + } + + /** + * @param versionService the version Service to set + */ + public void setVersionService(VersionService versionService) + { + this.versionService = versionService; + } + + + /** + * Simple wrapper class to represent a translation in the data table + */ + public static class TranslationWrapper + { + private String language; + private String name; + private String checkedOutBy; + private String languageLabel; + + public TranslationWrapper(String name, String language, String checkedOutBy, String languageLabel) + { + this.name = name; + this.language = language; + this.checkedOutBy = checkedOutBy; + this.languageLabel = languageLabel; + } + + public String getCheckedOutBy() + { + return checkedOutBy; + } + + public String getLanguage() + { + return language; + } + + public String getName() + { + return name; + } + + public String getLanguageLabel() + { + return this.languageLabel; + } } } diff --git a/source/web/WEB-INF/alfresco.tld b/source/web/WEB-INF/alfresco.tld index 01563cc745..1b6018e022 100644 --- a/source/web/WEB-INF/alfresco.tld +++ b/source/web/WEB-INF/alfresco.tld @@ -210,7 +210,7 @@ false true - + refreshOnBind false @@ -1814,7 +1814,7 @@ true true - + styleClass false @@ -2200,4 +2200,53 @@ + + + + + param + org.apache.myfaces.taglib.core.ParamTag + JSP + + Add a child UIParameter component to the UIComponent + associated with the closed parent UIComponent custom action. + + + + binding + false + false + + Value binding expression to a backing bean property + bound to the component instance for the UIComponent + created by this custom action. + + + + + id + false + true + + Component identifier of the UIParameter component to be created. + + + + + name + false + true + + Name of the parameter to be created. + + + + + value + true + true + Value of the parameter to be set. + + + diff --git a/source/web/WEB-INF/faces-config-beans.xml b/source/web/WEB-INF/faces-config-beans.xml index e8d60db371..d09ef95ee8 100644 --- a/source/web/WEB-INF/faces-config-beans.xml +++ b/source/web/WEB-INF/faces-config-beans.xml @@ -281,6 +281,10 @@ userPreferencesBean #{UserPreferencesBean} + + multilingualContentService + #{MultilingualContentService} + @@ -775,6 +779,10 @@ namespaceService #{NamespaceService} + + multilingualContentService + #{MultilingualContentService} + @@ -978,7 +986,7 @@ authenticationService - #{AuthenticationService} + #{authenticationService} permissionService @@ -2032,7 +2040,7 @@ #{AVMBrowseBean} - + The bean that backs up the Verify Broken Links Task Dialog @@ -2176,7 +2184,7 @@ editionService #{EditionService} - + multilingualContentService #{MultilingualContentService} @@ -2188,6 +2196,10 @@ contentFilterLanguagesService #{ContentFilterLanguagesService} + + versionService + #{VersionService} + lockService #{LockService} @@ -2545,7 +2557,7 @@ #{AVMSyncService} - + The bean that backs up the Create Web Content Wizard @@ -2853,7 +2865,7 @@ #{FilePickerBean} - + The bean that backs up the Snapshot Sandbox Dialog @@ -3441,7 +3453,7 @@ #{NodeService} - + The bean that backs up the Link Validation Dialog @@ -3478,7 +3490,7 @@ --> - + Bean that generates a mutlilingual text field component @@ -3521,7 +3533,7 @@ --> - + Bean that generates a multilingual text area component @@ -3871,7 +3883,7 @@ org.alfresco.web.bean.wcm.DeploymentProgressBean request - + Bean that returns link validation progress status @@ -3884,4 +3896,36 @@ #{AVMBrowseBean} + + + + + The bean that backs up the view of the Versioned Properties + + VersionedDocumentDetailsBean + + org.alfresco.web.bean.VersionedDocumentDetailsBean + + session + + versionService + #{VersionService} + + + editionService + #{EditionService} + + + nodeService + #{NodeService} + + + multilingualContentService + #{MultilingualContentService} + + + contentFilterLanguagesService + #{ContentFilterLanguagesService} + + diff --git a/source/web/WEB-INF/faces-config-navigation.xml b/source/web/WEB-INF/faces-config-navigation.xml index f16264246e..9781025c48 100644 --- a/source/web/WEB-INF/faces-config-navigation.xml +++ b/source/web/WEB-INF/faces-config-navigation.xml @@ -53,6 +53,10 @@ showMLContainerDetails /jsp/ml/ml-container-details.jsp + + showVersionedDetails + /jsp/dialog/versioned-details.jsp + showSpaceDetails /jsp/dialog/space-details.jsp diff --git a/source/web/images/icons/versioned_properties.gif b/source/web/images/icons/versioned_properties.gif new file mode 100644 index 0000000000..bca4d76267 Binary files /dev/null and b/source/web/images/icons/versioned_properties.gif differ diff --git a/source/web/images/icons/versioned_properties_large.gif b/source/web/images/icons/versioned_properties_large.gif new file mode 100644 index 0000000000..e8a16d91d1 Binary files /dev/null and b/source/web/images/icons/versioned_properties_large.gif differ diff --git a/source/web/jsp/dialog/document-details.jsp b/source/web/jsp/dialog/document-details.jsp index 721e3469c0..56b29300d1 100644 --- a/source/web/jsp/dialog/document-details.jsp +++ b/source/web/jsp/dialog/document-details.jsp @@ -304,16 +304,16 @@

- + - +
- + - +
<%-- list of translations --%> - + <%-- Language columns --%> - + - + <%-- view actions --%> + <%-- + Start the new edition wizard from this translation + --%> + + + + - - + +
- + <%-- Actions - Add Translation, Add Translation with Content --%> -
- - - - -
+ +
+ + + + +
+
<%-- Panel if the node has not the multilingual aspect--%> @@ -368,7 +377,9 @@ expanded='#{DocumentDetailsBean.panels["ml-info-panel"]}' expandedActionListener="#{DocumentDetailsBean.expandPanel}"> <%-- Action - Add Translation --%> -
+ +
+
@@ -433,11 +444,12 @@ - + + <%-- Version notes columns --%> - + @@ -467,10 +479,15 @@ - + + + + + + - + - \ No newline at end of file + diff --git a/source/web/jsp/dialog/versioned-details.jsp b/source/web/jsp/dialog/versioned-details.jsp new file mode 100644 index 0000000000..0be3dcbf13 --- /dev/null +++ b/source/web/jsp/dialog/versioned-details.jsp @@ -0,0 +1,335 @@ +<%-- + * Copyright (C) 2005-2007 Alfresco Software Limited. + + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" +--%> +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="/WEB-INF/alfresco.tld" prefix="a" %> +<%@ taglib uri="/WEB-INF/repo.tld" prefix="r" %> + +<%@ page buffer="64kb" contentType="text/html;charset=UTF-8" %> +<%@ page isELIgnored="false" %> +<%@ page import="org.alfresco.web.ui.common.PanelGenerator" %> +<%@ page import="org.alfresco.web.app.Application" %> +<%@ page import="javax.faces.context.FacesContext" %> + + + + + <% + FacesContext fc = FacesContext.getCurrentInstance(); + + // set locale for JSF framework usage + fc.getViewRoot().setLocale(Application.getLanguage(fc)); + %> + + <%-- load a bundle of properties with I18N strings --%> + + + + + <%-- Main outer table --%> + + + <%-- Title bar --%> + + + + + <%-- Main area --%> + + <%-- Shelf --%> + + + <%-- Work Area --%> + + +
+ <%@ include file="../parts/titlebar.jsp" %> +
+ <%@ include file="../parts/shelf.jsp" %> + + + <%-- Breadcrumb --%> + <%@ include file="../parts/breadcrumb.jsp" %> + + <%-- Status and Actions --%> + + + + + + + <%-- separator row with gradient shadow --%> + + + + + + + <%-- Error Messages --%> + + + + + + + <%-- Details --%> + + + + + + + <%-- separator row with bottom panel graphics --%> + + + + + + +
+ + <%-- Status and Actions inner contents table --%> + <%-- Generally this consists of an icon, textual summary and actions for the current object --%> + + + + + + <%-- Navigation --%> + + +
+ + +
+ '' +
+
+ : ' +
+
+
+ + + +
+
+ <%-- messages tag to show messages not handled by other specific message tags --%> + +
+ + + + + + +
+ + + + + + + + + + +
+ <%-- icon image for the doc --%> + + + + + + + + +
+
+ + +
+
+
+ <%-- properties for the doc --%> + + +
+
+ +
+ + <%-- Multilingual properties --%> + + + + + <%-- Panel if the node has the multilingual aspect--%> + + + <%-- properties for Ml container --%> +
+ +
+ + + +
+ + + +
+ <%-- list of translations --%> + + + <%-- Name and icon columns --%> + + + + + + + + + + + <%-- Language columns --%> + + + + + + + + <%-- view actions --%> + + + + + + + + + +
+
+ +
+ + + + + + <%-- Primary column for details view mode --%> + + + + + + + + + <%-- Version notes columns --%> + + + + + + + + <%-- Description columns --%> + + + + + + + + <%-- Created Date column for details view mode --%> + + + + + + + + + + <%-- view the contents of the specific version --%> + + + + + + + + + + + + + + + +
+ + <% PanelGenerator.generatePanelStart(out, request.getContextPath(), "greyround", "#F5F5F5"); %> + + + + +
+ + +
+ <% PanelGenerator.generatePanelEnd(out, request.getContextPath(), "greyround"); %> + +
+
+
+
+ +
+ +
+ +
diff --git a/source/web/jsp/ml/add-translation-without-content.jsp b/source/web/jsp/ml/add-translation-without-content.jsp index 45b57f4b6c..1b8f21247c 100644 --- a/source/web/jsp/ml/add-translation-without-content.jsp +++ b/source/web/jsp/ml/add-translation-without-content.jsp @@ -1,6 +1,6 @@ <%-- * Copyright (C) 2005-2007 Alfresco Software Limited. - + * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 @@ -15,11 +15,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" --%> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> @@ -31,12 +31,7 @@ - - - - - @@ -49,24 +44,30 @@ - + - + + + + + + + + + + - diff --git a/source/web/jsp/ml/ml-container-details.jsp b/source/web/jsp/ml/ml-container-details.jsp index 9bbbd666b7..7956f4380b 100644 --- a/source/web/jsp/ml/ml-container-details.jsp +++ b/source/web/jsp/ml/ml-container-details.jsp @@ -32,14 +32,14 @@ <%@ page isELIgnored="false" %> <%@ page import="org.alfresco.web.ui.common.PanelGenerator" %> - + <%-- load a bundle of properties with I18N strings --%> - + <%-- Main outer table --%> @@ -129,7 +129,7 @@ <%-- properties for Ml container --%> - + @@ -141,18 +141,18 @@ expanded='#{DocumentDetailsBean.panels["ml-properties-panel"]}' expandedActionListener="#{DocumentDetailsBean.expandPanel}"> - - + +
<%-- list of translations --%> + border="white" bgcolor="white" titleBorder="lbgrey" expandedTitleBorder="dotted" titleBgcolor="white" + expanded='#{DocumentDetailsBean.panels["ml-translation-panel"]}' expandedActionListener="#{DocumentDetailsBean.expandPanel}">
+ <%-- + Start the new edition wizard from this translation + --%> + + + + @@ -201,7 +208,7 @@ Editions details --%> - + @@ -283,35 +290,35 @@
+ expandedActionListener="#{DocumentDetailsBean.expandPanel}" styleClass="nodeWorkflowInfoTitle" > + var="tr" styleClass="recordSet" headerStyleClass="recordSetHeader" + rowStyleClass="recordSetRow" altRowStyleClass="recordSetRowAlt" width="100%" + pageSize="10" initialSortColumn="versionName" initialSortDescending="true" style="padding-left:12px;padding-top:10px;"> - <%-- Icon details view mode --%> + <%-- Icon details view mode --%> - <%-- Versionned name --%> + <%-- Versioned name --%> - + - <%-- Versionned description --%> - + <%-- Versioned description --%> + - <%-- Versionned creation date --%> + <%-- Versioned creation date --%> @@ -321,7 +328,7 @@ - <%-- Versionned modified date --%> + <%-- Versioned modified date --%> @@ -331,7 +338,7 @@ - <%-- Versionned language --%> + <%-- Versioned language --%> @@ -344,7 +351,11 @@ - + + + + + @@ -357,7 +368,7 @@
- +
diff --git a/source/web/jsp/ml/new-edition-wizard/choose-translation.jsp b/source/web/jsp/ml/new-edition-wizard/choose-translation.jsp index 43f6dbafb4..d321aa8bac 100644 --- a/source/web/jsp/ml/new-edition-wizard/choose-translation.jsp +++ b/source/web/jsp/ml/new-edition-wizard/choose-translation.jsp @@ -45,10 +45,77 @@ - - - - - - \ No newline at end of file + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/web/jsp/ml/new-edition-wizard/edition-details.jsp b/source/web/jsp/ml/new-edition-wizard/edition-details.jsp index 1bda7afc53..15acd8fde0 100644 --- a/source/web/jsp/ml/new-edition-wizard/edition-details.jsp +++ b/source/web/jsp/ml/new-edition-wizard/edition-details.jsp @@ -32,7 +32,7 @@ - + @@ -48,6 +48,6 @@ - +