From 931685b753cf54dfd873bcba5fa7a40f8f4d8b8f Mon Sep 17 00:00:00 2001 From: Kevin Roast Date: Thu, 11 May 2006 18:01:53 +0000 Subject: [PATCH] . Final elements of the Soft Delete UI - Recover and delete listed items - Report screens for listed item results - Recover and delete all items (admin only) - Recover listed items to a different location git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2856 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/messages/webclient.properties | 14 +- config/alfresco/web-client-config-actions.xml | 3 - .../org/alfresco/web/bean/TrashcanBean.java | 338 +++++++++++++++++- .../web/WEB-INF/faces-config-navigation.xml | 4 + source/web/css/main.css | 18 + source/web/jsp/trashcan/delete-listed.jsp | 2 +- source/web/jsp/trashcan/item-details.jsp | 2 +- source/web/jsp/trashcan/recover-item.jsp | 3 +- source/web/jsp/trashcan/recover-listed.jsp | 34 +- source/web/jsp/trashcan/recovery-report.jsp | 169 +++++++++ source/web/jsp/trashcan/trash-list.jsp | 6 +- 11 files changed, 565 insertions(+), 28 deletions(-) create mode 100644 source/web/jsp/trashcan/recovery-report.jsp diff --git a/config/alfresco/messages/webclient.properties b/config/alfresco/messages/webclient.properties index eb6f45e818..2e2b6b2027 100644 --- a/config/alfresco/messages/webclient.properties +++ b/config/alfresco/messages/webclient.properties @@ -773,6 +773,7 @@ title_recover_listed=Recover Listed Items title_delete_listed=Delete Listed Items title_delete_item=Delete Item title_recover_item=Recover Item +title_recovery_report=Recovery Report deleted_items=Deleted Items manage_deleted_items=Manage Deleted Items manage_deleted_items_description=Remove or recover previously deleted items @@ -801,15 +802,19 @@ recover_all_items_info=Recover all files and spaces from the deleted file store recover_all_items_confirm=Are you sure you want to recover all the deleted files and spaces from the deleted file store? delete_listed_items=Delete Listed Items delete_listed_items_info=Permanently delete the listed files and spaces from the deleted file store -delete_listed_items_confirm=Are you sure you want to permanently delete the listed deleted files and spaces from the deleted file store? The items cannot be recovered once this action has been performed. +delete_listed_items_confirm=Are you sure you want to permanently delete the following deleted files and spaces from the deleted file store? The items cannot be recovered once this action has been performed. recover_listed_items=Recover Listed Items recover_listed_items_info=Recover the listed files and spaces from the deleted file store -recover_listed_items_confirm=Are you sure you want to recover the listed deleted files and spaces from the deleted file store? +recover_listed_items_confirm=Are you sure you want to recover the following deleted files and spaces from the deleted file store? recovered_item_success=The item \"{0}\" has been successfully recovered. recovered_item_parent=Failed to recover the item \"{0}\" as the original parent folder is missing, please select a new folder destination. +recovered_item_parent_short=Parent folder missing recovered_item_permission=Failed to recover the item \"{0}\" as you do not have the appropriate permissions to restore the item to the original parent folder, please select a new folder destination. +recovered_item_permission_short=No permissions to write recovered_item_integrity=Failed to recover the item \"{0}\" as there is now an item in the original parent folder with the same name, please select a new folder destination. +recovered_item_integrity_short=Item with same name exists recovered_item_failure=Failed to recover the item \"{0}\" due to error: {1} +recovered_item_failure_short=Failed delete_item_success=The item \"{0}\" has been permanently deleted. title_deleted_item_details=Deleted Item Details deleteditem_details_description=Details of the deleted item @@ -822,6 +827,11 @@ date_filter_all=All date_filter_today=Today date_filter_week=Last 7 days date_filter_month=Last 30 days +recovery_report=Recovered Items Report +recovery_report_info=The results of recovering the selected items +recovery_report_success=The following items were recovered successfully +recovery_report_failed=The following items failed to recover +recovery_report_reason=Failure Reason # Admin Console messages title_admin_console=Administration Console diff --git a/config/alfresco/web-client-config-actions.xml b/config/alfresco/web-client-config-actions.xml index b77e6510cd..84c6596a86 100644 --- a/config/alfresco/web-client-config-actions.xml +++ b/config/alfresco/web-client-config-actions.xml @@ -374,9 +374,6 @@ - - Write - manage_deleted_items /images/icons/trashcan.gif dialog:manageDeletedItems diff --git a/source/java/org/alfresco/web/bean/TrashcanBean.java b/source/java/org/alfresco/web/bean/TrashcanBean.java index eab0d28bdd..13bc4cc332 100644 --- a/source/java/org/alfresco/web/bean/TrashcanBean.java +++ b/source/java/org/alfresco/web/bean/TrashcanBean.java @@ -47,6 +47,7 @@ import org.alfresco.service.namespace.QName; import org.alfresco.util.CachingDateFormat; import org.alfresco.web.app.Application; import org.alfresco.web.app.context.IContextListener; +import org.alfresco.web.app.context.UIContextService; import org.alfresco.web.bean.repository.MapNode; import org.alfresco.web.bean.repository.Node; import org.alfresco.web.bean.repository.NodePropertyResolver; @@ -64,6 +65,23 @@ import org.alfresco.web.ui.common.component.data.UIRichList; */ public class TrashcanBean implements IContextListener { + private static final String MSG_DELETED_ITEMS_FOR = "deleted_items_for"; + private static final String MSG_DELETED_ITEMS = "deleted_items"; + private static final String MSG_RECOVERED_ITEM_INTEGRITY = "recovered_item_integrity"; + private static final String MSG_RECOVERED_ITEM_PERMISSION = "recovered_item_permission"; + private static final String MSG_RECOVERED_ITEM_PARENT = "recovered_item_parent"; + private static final String MSG_RECOVERED_ITEM_FAILURE = "recovered_item_failure"; + private static final String MSG_RECOVERED_ITEM_INTEGRITY_S = "recovered_item_integrity_short"; + private static final String MSG_RECOVERED_ITEM_PERMISSION_S = "recovered_item_permission_short"; + private static final String MSG_RECOVERED_ITEM_PARENT_S = "recovered_item_parent_short"; + private static final String MSG_RECOVERED_ITEM_FAILURE_S = "recovered_item_failure_short"; + private static final String MSG_RECOVERED_ITEM_SUCCESS = "recovered_item_success"; + private static final String MSG_RECOVERY_REASON = "recovery_report_reason"; + private static final String MSG_ORIGINAL_LOCATION = "original_location"; + private static final String MSG_NAME = "name"; + + private static final String PROP_RECOVERSTATUS = "recoverstatus"; + private static final String FILTER_DATE_ALL = "all"; private static final String FILTER_DATE_TODAY = "today"; private static final String FILTER_DATE_WEEK = "week"; @@ -71,15 +89,8 @@ public class TrashcanBean implements IContextListener private static final String FILTER_USER_ALL = "all"; private static final String FILTER_USER_USER = "user"; - private static final String MSG_DELETED_ITEMS_FOR = "deleted_items_for"; - private static final String MSG_DELETED_ITEMS = "deleted_items"; - private static final String MSG_RECOVERED_ITEM_INTEGRITY = "recovered_item_integrity"; - private static final String MSG_RECOVERED_ITEM_PERMISSION = "recovered_item_permission"; - private static final String MSG_RECOVERED_ITEM_PARENT = "recovered_item_parent"; - private static final String MSG_RECOVERED_ITEM_FAILURE = "recovered_item_failure"; - private static final String MSG_RECOVERED_ITEM_SUCCESS = "recovered_item_success"; - private static final String OUTCOME_DIALOGCLOSE = "dialog:close"; + private static final String OUTCOME_RECOVERY_REPORT = "recoveryReport"; private static final String RICHLIST_ID = "trashcan-list"; private static final String RICHLIST_MSG_ID = "trashcan" + ':' + RICHLIST_ID; @@ -121,6 +132,10 @@ public class TrashcanBean implements IContextListener /** Currently listed items */ private List listedItems = Collections.emptyList(); + private List successItems = Collections.emptyList(); + + private List failureItems = Collections.emptyList(); + /** Current action context Node */ private Node actionNode; @@ -331,6 +346,38 @@ public class TrashcanBean implements IContextListener { this.listedItems = listedItems; } + + /** + * @return HTML table of the listed items + */ + public String getListedItemsTable() + { + return buildItemsTable(getListedItems(), "recoveredItemsList", false); + } + + /** + * @return HTML table of the items successfully recovered + */ + public String getSuccessItemsTable() + { + return buildItemsTable(this.successItems, "recoveredItemsList", false); + } + + /** + * @return HTML table of the items that failed to recover + */ + public String getFailureItemsTable() + { + return buildItemsTable(this.failureItems, "failedItemsList", true); + } + + /** + * @return count of the items that failed to recover + */ + public int getFailureItemsCount() + { + return this.failureItems.size(); + } /** * @param node The item context for the current action @@ -492,10 +539,6 @@ public class TrashcanBean implements IContextListener // ------------------------------------------------------------------------------ // Action handlers - // TODO: - // need the following Action Handlers: - // deleteAllItemsOK, recoverAllItemsOK, recoverListedItemsOK, deleteListedItemsOK - /** * Search the deleted item store by name */ @@ -689,6 +732,159 @@ public class TrashcanBean implements IContextListener return outcome; } + /** + * Action handler to recover the list items + */ + public String recoverListedItemsOK() + { + FacesContext fc = FacesContext.getCurrentInstance(); + + UserTransaction tx = null; + try + { + tx = Repository.getUserTransaction(FacesContext.getCurrentInstance(), true); + tx.begin(); + + // restore the nodes - the user may have requested a restore to a different parent + List nodeRefs = new ArrayList(this.listedItems.size()); + for (Node node : this.listedItems) + { + nodeRefs.add(node.getNodeRef()); + } + List reports; + if (this.destination == null) + { + reports = this.nodeArchiveService.restoreArchivedNodes(nodeRefs); + } + else + { + reports = this.nodeArchiveService.restoreArchivedNodes(nodeRefs, this.destination, null, null); + } + + saveReportDetail(reports); + + tx.commit(); + } + catch (Throwable err) + { + try { if (tx != null) {tx.rollback();} } catch (Exception tex) {} + // most exceptions will be caught and returned as RestoreNodeReport objects by the service + String reason = err.getMessage(); + String msg = MessageFormat.format( + Application.getMessage(fc, Repository.ERROR_GENERIC), reason); + FacesMessage facesMsg = new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, msg); + fc.addMessage(null, facesMsg); + } + + // clear the UI state in preparation for finishing the action + contextUpdated(); + + return OUTCOME_RECOVERY_REPORT; + } + + /** + * Action handler called to recover all items from the store (Admin only) + */ + public String recoverAllItemsOK() + { + FacesContext fc = FacesContext.getCurrentInstance(); + + UserTransaction tx = null; + try + { + tx = Repository.getUserTransaction(FacesContext.getCurrentInstance(), true); + tx.begin(); + + // restore all nodes - the user may have requested a restore to a different parent + List reports; + if (this.destination == null) + { + reports = this.nodeArchiveService.restoreAllArchivedNodes(Repository.getStoreRef()); + } + else + { + reports = this.nodeArchiveService.restoreAllArchivedNodes(Repository.getStoreRef(), this.destination, null, null); + } + + // TODO: wrap all this in a UserTransaction - it performs a lot of getProperties()! + saveReportDetail(reports); + + tx.commit(); + } + catch (Throwable err) + { + try { if (tx != null) {tx.rollback();} } catch (Exception tex) {} + // most exceptions will be caught and returned as RestoreNodeReport objects by the service + String reason = err.getMessage(); + String msg = MessageFormat.format( + Application.getMessage(fc, Repository.ERROR_GENERIC), reason); + FacesMessage facesMsg = new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, msg); + fc.addMessage(null, facesMsg); + } + + // clear the UI state in preparation for finishing the action + contextUpdated(); + + return OUTCOME_RECOVERY_REPORT; + } + + /** + * @return outcome to close the main list screen and reset other beans ready for display + */ + public String close() + { + // call beans to update UI context for other screens + UIContextService.getInstance(FacesContext.getCurrentInstance()).notifyBeans(); + return OUTCOME_DIALOGCLOSE; + } + + /** + * Action handler to delete the listed items + */ + public String deleteListedItemsOK() + { + try + { + List nodeRefs = new ArrayList(this.listedItems.size()); + for (Node node : this.listedItems) + { + nodeRefs.add(node.getNodeRef()); + } + this.nodeArchiveService.purgeArchivedNodes(nodeRefs); + } + catch (Throwable err) + { + Utils.addErrorMessage(MessageFormat.format(Application.getMessage( + FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err); + } + + // clear the UI state in preparation for finishing the action + contextUpdated(); + + return OUTCOME_DIALOGCLOSE; + } + + /** + * Action handler to delete all items + */ + public String deleteAllItemsOK() + { + try + { + this.nodeArchiveService.purgeAllArchivedNodes(Repository.getStoreRef()); + } + catch (Throwable err) + { + Utils.addErrorMessage(MessageFormat.format(Application.getMessage( + FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err); + } + + // clear the UI state in preparation for finishing the action + contextUpdated(); + + return OUTCOME_DIALOGCLOSE; + } + /** * Action handler to initially setup the trashcan screen */ @@ -825,6 +1021,124 @@ public class TrashcanBean implements IContextListener return query; } + /** + * Save the detail of the items that were successfully or unsuccessfully restored + * + * @param reports The List of RestoreNodeReport objects to walk for results + */ + private void saveReportDetail(List reports) + { + // store the results ready for the next dialog page + this.successItems = new ArrayList(reports.size()); + this.failureItems = new ArrayList(reports.size()); + for (RestoreNodeReport report : reports) + { + if (RestoreStatus.SUCCESS == report.getStatus()) + { + Node node = new Node(report.getRestoredNodeRef()); + node.getProperties().put(PROP_RECOVERSTATUS, report.getStatus()); + this.successItems.add(node); + } + else + { + Node node = new Node(report.getArchivedNodeRef()); + node.getProperties().put(PROP_RECOVERSTATUS, report.getStatus()); + this.failureItems.add(node); + } + } + } + + /** + * Build an HTML table of the items that are to be or have been recovered. + * + * @param items List of Node objects to display in the table + * @param cssClass CSS style to apply to the table + * @param report Set true to report the reason for any failure. This flag requires that the Node + * object has a pseudo property "recoverstatus" containing the RestoreStatus. + * + * @return HTML table of node info + */ + private String buildItemsTable(List items, String cssClass, boolean report) + { + FacesContext fc = FacesContext.getCurrentInstance(); + String contextPath = fc.getExternalContext().getRequestContextPath(); + + StringBuilder buf = new StringBuilder(1024); + + // outer table + buf.append(""); + // title row + buf.append(""); + if (report) + { + buf.append(""); + } + buf.append(""); + for (Node node : items) + { + // listed item rows + buf.append(""); + if (report) + { + buf.append(""); + } + buf.append(""); + } + // end table + buf.append("
"); + buf.append(Application.getMessage(fc, MSG_NAME)); + buf.append(""); + buf.append(Application.getMessage(fc, MSG_ORIGINAL_LOCATION)); + buf.append(""); + buf.append(Application.getMessage(fc, MSG_RECOVERY_REASON)); + buf.append("
"); + String img; + if (this.dictionaryService.isSubClass(node.getType(), ContentModel.TYPE_FOLDER)) + { + String icon = (String)node.getProperties().get("app:icon"); + img = "/images/icons/" + (icon != null ? icon + "-16.gif" : BrowseBean.SPACE_SMALL_DEFAULT + ".gif"); + } + else + { + img = Utils.getFileTypeImage(node.getName(), false); + } + buf.append(""); + buf.append(""); + buf.append(node.getName()); + buf.append(""); + Path path = (Path)node.getProperties().get(ContentModel.PROP_ARCHIVED_ORIGINAL_PATH); + if (path != null) + { + buf.append(Repository.getDisplayPath(path)); + } + buf.append(""); + String msg; + switch ((RestoreStatus)node.getProperties().get(PROP_RECOVERSTATUS)) + { + case FAILURE_INVALID_PARENT: + msg = MSG_RECOVERED_ITEM_PARENT_S; + break; + + case FAILURE_PERMISSION: + msg = MSG_RECOVERED_ITEM_PERMISSION_S; + break; + + case FAILURE_INTEGRITY: + msg = MSG_RECOVERED_ITEM_INTEGRITY_S; + break; + + default: + msg = MSG_RECOVERED_ITEM_FAILURE_S; + break; + } + buf.append(Application.getMessage(fc, msg)); + buf.append("
"); + + return buf.toString(); + } + private boolean isAdminUser() { return Application.getCurrentUser(FacesContext.getCurrentInstance()).isAdmin(); diff --git a/source/web/WEB-INF/faces-config-navigation.xml b/source/web/WEB-INF/faces-config-navigation.xml index d004f78ee2..9379bce93f 100644 --- a/source/web/WEB-INF/faces-config-navigation.xml +++ b/source/web/WEB-INF/faces-config-navigation.xml @@ -927,6 +927,10 @@ itemDetails /jsp/trashcan/item-details.jsp + + recoveryReport + /jsp/trashcan/recovery-report.jsp + diff --git a/source/web/css/main.css b/source/web/css/main.css index b66583be3c..e14fded8b0 100644 --- a/source/web/css/main.css +++ b/source/web/css/main.css @@ -402,6 +402,24 @@ a.topToolbarLinkHighlight, a.topToolbarLinkHighlight:link, a.topToolbarLinkHighl border-color: #003366; } +.recoveredItemsList +{ + padding: 2px; + background-color: #EEEEEE; + border-width: 1px; + border-style: solid; + border-color: #AAAAAA; +} + +.failedItemsList +{ + padding: 2px; + background-color: #EEEEEE; + border-width: 1px; + border-style: solid; + border-color: #e00028; +} + .wizardSectionHeading { padding: 2px; diff --git a/source/web/jsp/trashcan/delete-listed.jsp b/source/web/jsp/trashcan/delete-listed.jsp index fb8f24e343..6808b9a951 100644 --- a/source/web/jsp/trashcan/delete-listed.jsp +++ b/source/web/jsp/trashcan/delete-listed.jsp @@ -104,7 +104,7 @@ - + diff --git a/source/web/jsp/trashcan/item-details.jsp b/source/web/jsp/trashcan/item-details.jsp index 4ac3d3140d..76f329a9e5 100644 --- a/source/web/jsp/trashcan/item-details.jsp +++ b/source/web/jsp/trashcan/item-details.jsp @@ -158,7 +158,7 @@
- +
diff --git a/source/web/jsp/trashcan/recover-item.jsp b/source/web/jsp/trashcan/recover-item.jsp index bc8fea3246..8cd74f48cf 100644 --- a/source/web/jsp/trashcan/recover-item.jsp +++ b/source/web/jsp/trashcan/recover-item.jsp @@ -121,13 +121,12 @@ - + value="#{TrashcanBean.destination}" styleClass="selector" /> diff --git a/source/web/jsp/trashcan/recover-listed.jsp b/source/web/jsp/trashcan/recover-listed.jsp index a59cca65a0..1b1b2b1c20 100644 --- a/source/web/jsp/trashcan/recover-listed.jsp +++ b/source/web/jsp/trashcan/recover-listed.jsp @@ -96,17 +96,43 @@ <% PanelGenerator.generatePanelStart(out, request.getContextPath(), "white", "white"); %> - +
- - + + + + + + + + + +
+
- + +
+ <% PanelGenerator.generatePanelStart(out, request.getContextPath(), "yellowInner", "#ffffcc"); %> + + + + + +
+ +
+ <% PanelGenerator.generatePanelEnd(out, request.getContextPath(), "yellowInner"); %> +
+ :  + + +
<%-- Error Messages --%> diff --git a/source/web/jsp/trashcan/recovery-report.jsp b/source/web/jsp/trashcan/recovery-report.jsp new file mode 100644 index 0000000000..037663530a --- /dev/null +++ b/source/web/jsp/trashcan/recovery-report.jsp @@ -0,0 +1,169 @@ +<%-- + Copyright (C) 2005 Alfresco, Inc. + + Licensed under the Mozilla Public License version 1.1 + with a permitted attribution clause. You may obtain a + copy of the License at + + http://www.alfresco.org/legal/license.txt + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + either express or implied. See the License for the specific + language governing permissions and limitations under the + License. +--%> +<%@ 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="32kb" contentType="text/html;charset=UTF-8" %> +<%@ page isELIgnored="false" %> +<%@ page import="org.alfresco.web.ui.common.PanelGenerator" %> + + + + + + <%-- load a bundle of properties with I18N strings --%> + + + <%-- set the form name here --%> + + + <%-- 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 --%> + + + + + + + <%-- 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 --%> + + + + + +
+ + +
+
+
+ +
+ + + + + + + +
+ <% PanelGenerator.generatePanelStart(out, request.getContextPath(), "white", "white"); %> + + + + + + + + <%-- show this panel if some items failed to recover --%> + + + + + + + + + + + + +
+ +
+ +
+ +
+ +
+ <%-- Error Messages --%> + <%-- messages tag to show messages not handled by other specific message tags --%> + + +
+ <% PanelGenerator.generatePanelEnd(out, request.getContextPath(), "white"); %> +
+ <% PanelGenerator.generatePanelStart(out, request.getContextPath(), "blue", "#D3E6FE"); %> + + + + +
+ +
+ <% PanelGenerator.generatePanelEnd(out, request.getContextPath(), "blue"); %> +
+
+
+ +
+ +
+ +
diff --git a/source/web/jsp/trashcan/trash-list.jsp b/source/web/jsp/trashcan/trash-list.jsp index e657e751f2..62ae75ba1b 100644 --- a/source/web/jsp/trashcan/trash-list.jsp +++ b/source/web/jsp/trashcan/trash-list.jsp @@ -113,9 +113,9 @@
- <%-- Current object actions --%> -   - + <%-- Admin only global actions --%> +   +