diff --git a/config/alfresco/messages/webclient.properties b/config/alfresco/messages/webclient.properties index a8168cf8c4..3d22726d2d 100644 --- a/config/alfresco/messages/webclient.properties +++ b/config/alfresco/messages/webclient.properties @@ -1185,6 +1185,9 @@ delete_rendition_confirm=Are you sure you want to remove \"{0}\", \"{1}\", and i delete_folder=Delete Folder delete_avm_folder_info=To remove this folder and its contents from the sandbox, click OK. delete_avm_folder_confirm=Are you sure you want to remove \"{0}\" and its contents from the sandbox? +unlock_file=Unlock File +unlock_file_info=To remove the lock from this file click OK. +unlock_file_confirm=Are you sure you want to remove the lock from \"{0}\"? error_delete_folder=Unable to delete Folder due to system error: create_web_content=Create Web Content create_web_content_title=Create Web Content Wizard diff --git a/config/alfresco/web-client-config-dialogs.xml b/config/alfresco/web-client-config-dialogs.xml index 716bda1af8..1ddf0d3876 100644 --- a/config/alfresco/web-client-config-dialogs.xml +++ b/config/alfresco/web-client-config-dialogs.xml @@ -171,6 +171,10 @@ + + + + + + Delete + + org.alfresco.web.action.evaluator.WCMUnlockEvaluator + unlock + /images/icons/unlock.gif + #{AVMBrowseBean.setupContentAction} + dialog:unlockAvmFile + + #{actionContext.id} + + + @@ -329,6 +344,7 @@ + diff --git a/source/java/org/alfresco/web/action/evaluator/WCMUnlockEvaluator.java b/source/java/org/alfresco/web/action/evaluator/WCMUnlockEvaluator.java new file mode 100644 index 0000000000..d870079593 --- /dev/null +++ b/source/java/org/alfresco/web/action/evaluator/WCMUnlockEvaluator.java @@ -0,0 +1,83 @@ +/* + * 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.repo.avm.AVMNodeConverter; +import org.alfresco.service.cmr.avm.AVMService; +import org.alfresco.service.cmr.avm.locking.AVMLock; +import org.alfresco.service.cmr.avm.locking.AVMLockingService; +import org.alfresco.util.Pair; +import org.alfresco.web.app.servlet.FacesHelper; +import org.alfresco.web.bean.repository.Node; +import org.alfresco.web.bean.repository.Repository; +import org.alfresco.web.bean.wcm.AVMBrowseBean; +import org.alfresco.web.bean.wcm.AVMUtil; +import org.alfresco.web.bean.wcm.WebProject; + +/** + * UI Action Evaluator - return true if the node is not part of an in-progress + * WCM workflow and is locked + * + * @author Gavin Cornwell + */ +public class WCMUnlockEvaluator extends BaseActionEvaluator +{ + /** + * @see org.alfresco.web.action.ActionEvaluator#evaluate(org.alfresco.web.bean.repository.Node) + */ + public boolean evaluate(final Node node) + { + boolean proceed = false; + + FacesContext context = FacesContext.getCurrentInstance(); + AVMService avmService = Repository.getServiceRegistry(context).getAVMService(); + AVMLockingService avmLockingService = Repository.getServiceRegistry(context).getAVMLockingService(); + AVMBrowseBean avmBrowseBean = (AVMBrowseBean)FacesHelper.getManagedBean(context, AVMBrowseBean.BEAN_NAME); + + Pair p = AVMNodeConverter.ToAVMVersionPath(node.getNodeRef()); + String path = p.getSecond(); + + if (avmService.lookup(p.getFirst(), path) != null) + { + String relPath = AVMUtil.getStoreRelativePath(path); + + WebProject webProject = avmBrowseBean.getWebProject(); + if (webProject == null) + { + // when in a workflow context, the WebProject may not be accurate on the browsebean + // so get the web project associated with the path + webProject = new WebProject(path); + } + + // determine if the item is locked + AVMLock lock = avmLockingService.getLock(webProject.getStoreId(), relPath); + proceed = (lock != null); + } + + return proceed; + } +} diff --git a/source/java/org/alfresco/web/bean/wcm/CreateWebContentWizard.java b/source/java/org/alfresco/web/bean/wcm/CreateWebContentWizard.java index de7c8793ab..83863b8afc 100644 --- a/source/java/org/alfresco/web/bean/wcm/CreateWebContentWizard.java +++ b/source/java/org/alfresco/web/bean/wcm/CreateWebContentWizard.java @@ -54,6 +54,7 @@ import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransacti import org.alfresco.service.cmr.avm.AVMExistsException; import org.alfresco.service.cmr.avm.AVMNodeDescriptor; import org.alfresco.service.cmr.avm.AVMService; +import org.alfresco.service.cmr.avm.locking.AVMLock; import org.alfresco.service.cmr.avm.locking.AVMLockingService; import org.alfresco.service.cmr.avmsync.AVMDifference; import org.alfresco.service.cmr.avmsync.AVMSyncService; @@ -101,6 +102,7 @@ public class CreateWebContentWizard extends CreateContentWizard transient private Document instanceDataDocument = null; protected boolean formSelectDisabled = false; protected boolean startWorkflow = false; + protected List locksToReturnToMainStoreOnCancel = null; transient private AVMLockingService avmLockingService; transient private AVMService avmService; @@ -213,6 +215,7 @@ public class CreateWebContentWizard extends CreateContentWizard this.createMimeTypes = null; this.formChoices = null; this.filePickerBean.clearUploadedFiles(); + this.locksToReturnToMainStoreOnCancel = new ArrayList(4); // check for a form ID being passed in as a parameter if (this.parameters.get(UIUserSandboxes.PARAM_FORM_NAME) != null) @@ -347,6 +350,32 @@ public class CreateWebContentWizard extends CreateContentWizard return super.back(); } + @Override + public String cancel() + { + if (this.formInstanceData != null && this.renditions != null) + { + if (this.locksToReturnToMainStoreOnCancel.size() > 0) + { + for (String path : this.locksToReturnToMainStoreOnCancel) + { + String storeId = AVMUtil.getStoreId(path); + String storePath = AVMUtil.getStoreRelativePath(path); + String storeName = AVMUtil.getStoreName(path); + String mainStore = AVMUtil.getCorrespondingMainStoreName(storeName); + + if (logger.isDebugEnabled()) + logger.debug("transferring lock from " + storeName + " to " + mainStore + + " for path: " + path + " as user chose to cancel"); + + this.getAvmLockingService().modifyLock(storeId, storePath, null, mainStore, null, null); + } + } + } + + return super.cancel(); + } + @Override protected String finishImpl(final FacesContext context, String outcome) throws Exception @@ -482,10 +511,34 @@ public class CreateWebContentWizard extends CreateContentWizard if (logger.isDebugEnabled()) logger.debug("creating file " + fileName + " in " + path); + // get current username for lock checks + String username = Application.getCurrentUser(FacesContext.getCurrentInstance()).getUserName(); + // put the content of the file into the AVM store + String filePath = AVMNodeConverter.ExtendAVMPath(path, fileName); try { - getAvmService().createFile(path, fileName, new ByteArrayInputStream((this.content == null ? "" : this.content).getBytes("UTF-8"))); + String storeId = AVMUtil.getStoreId(filePath); + String storePath = AVMUtil.getStoreRelativePath(filePath); + String storeName = AVMUtil.getStoreName(filePath); + AVMLock lock = this.getAvmLockingService().getLock(storeId, storePath); + if (lock != null && lock.getStore().equals(storeName) == false) + { + if (lock.getOwners().contains(username)) + { + // lock already exists on path, check it's owned by the current user + if (logger.isDebugEnabled()) + logger.debug("transferring lock from " + lock.getStore() + " to " + storeName + " for path: " + filePath); + + // add the path to the list of locks to return to the preview store if cancel is pressed + this.locksToReturnToMainStoreOnCancel.add(filePath); + this.getAvmLockingService().modifyLock(storeId, storePath, null, storeName, null, null); + } + } + + // create the file + getAvmService().createFile(path, fileName, + new ByteArrayInputStream((this.content == null ? "" : this.content).getBytes("UTF-8"))); } catch (AVMExistsException avmee) { @@ -495,7 +548,7 @@ public class CreateWebContentWizard extends CreateContentWizard } // remember the created path - this.createdPath = AVMNodeConverter.ExtendAVMPath(path, fileName); + this.createdPath = filePath; // add titled aspect for the read/edit properties screens final NodeRef formInstanceDataNodeRef = AVMNodeConverter.ToNodeRef(-1, this.createdPath); @@ -517,6 +570,29 @@ public class CreateWebContentWizard extends CreateContentWizard try { path = ret.getOutputPathForRendition(this.formInstanceData, cwd); + + if (logger.isDebugEnabled()) + logger.debug("About to render path: " + path); + + String storeId = AVMUtil.getStoreId(path); + String storePath = AVMUtil.getStoreRelativePath(path); + String storeName = AVMUtil.getStoreName(path); + AVMLock lock = this.getAvmLockingService().getLock(storeId, storePath); + if (lock != null && lock.getStore().equals(storeName) == false) + { + // see if the lock belongs to the current user, if it does modify the lock to point to the preview store + if (lock.getOwners().contains(username)) + { + if (logger.isDebugEnabled()) + logger.debug("transferring lock from " + lock.getStore() + " to " + storeName + " for path: " + path); + + // add the path to the list of locks to return to the main store if cancel is pressed + this.locksToReturnToMainStoreOnCancel.add(path); + this.getAvmLockingService().modifyLock(storeId, storePath, null, storeName, null, null); + } + } + + // generate the rendition this.renditions.add(ret.render(this.formInstanceData, path)); } catch (Exception e) diff --git a/source/java/org/alfresco/web/bean/wcm/EditWebContentWizard.java b/source/java/org/alfresco/web/bean/wcm/EditWebContentWizard.java index 5089af7663..cfe82de090 100644 --- a/source/java/org/alfresco/web/bean/wcm/EditWebContentWizard.java +++ b/source/java/org/alfresco/web/bean/wcm/EditWebContentWizard.java @@ -1,259 +1,297 @@ -/* - * 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.wcm; - -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import org.alfresco.repo.content.MimetypeMap; -import org.alfresco.service.cmr.avm.locking.AVMLock; -import org.alfresco.service.cmr.repository.ContentWriter; -import org.alfresco.web.app.Application; -import org.alfresco.web.forms.Form; -import org.alfresco.web.forms.FormInstanceData; -import org.alfresco.web.forms.FormNotFoundException; -import org.alfresco.web.forms.Rendition; -import org.alfresco.web.forms.XMLUtil; -import org.alfresco.web.ui.common.Utils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * Bean implementation for the "Edit Web Content Wizard" dialog - */ -public class EditWebContentWizard extends CreateWebContentWizard -{ - private static final long serialVersionUID = 439996926303151006L; - - private static final Log logger = LogFactory.getLog(EditWebContentWizard.class); - - private AVMNode avmNode; - private Form form; - - private boolean wasFormLockPresent = false; - private boolean wasRenditionLockPresent = false; - - // ------------------------------------------------------------------------------ - // Wizard implementation - - @Override - public void init(final Map parameters) - { - super.init(parameters); - this.avmNode = this.avmBrowseBean.getAvmActionNode(); - if (this.avmNode == null) - { - throw new IllegalArgumentException("Edit Form wizard requires action node context."); - } - - if (logger.isDebugEnabled()) - logger.debug("path is " + this.avmNode.getPath()); - - this.createdPath = AVMUtil.getCorrespondingPathInPreviewStore(this.avmNode.getPath()); - this.formInstanceData = this.getFormsService().getFormInstanceData(-1, this.createdPath); - final WebProject webProject = new WebProject(this.createdPath); - try - { - this.formName = this.formInstanceData.getForm().getName(); - this.form = webProject.getForm(this.formName); - } - catch (FormNotFoundException fnfe) - { - Utils.addErrorMessage(fnfe.getMessage(), fnfe); - } - this.content = this.getAvmService().getContentReader(-1, this.createdPath).getContentString(); - this.fileName = this.formInstanceData.getName(); - this.mimeType = MimetypeMap.MIMETYPE_XML; - this.wasFormLockPresent = false; - this.wasRenditionLockPresent = false; - } - - @Override - public String cancel() - { - if (this.formInstanceData != null && this.renditions != null) - { - if (this.wasFormLockPresent == false) - { - // there wasn't a lock on the form at the start of the - // wizard so remove the one present now - if (logger.isDebugEnabled()) - logger.debug("removing form instance data lock from " + - AVMUtil.getCorrespondingPathInMainStore(this.createdPath) + - " as user chose to cancel"); - - this.getAvmLockingService().removeLock(AVMUtil.getStoreId(this.createdPath), - AVMUtil.getStoreRelativePath(this.createdPath)); - } - - if (this.wasRenditionLockPresent == false) - { - // there weren't locks on renditions at the start of - // the wizard so remove the ones present now - for (Rendition r : this.renditions) - { - if (logger.isDebugEnabled()) - logger.debug("removing lock from " + - AVMUtil.getCorrespondingPathInMainStore(r.getPath()) + - " as user chose to cancel"); - - this.getAvmLockingService().removeLock(AVMUtil.getStoreId(r.getPath()), - AVMUtil.getStoreRelativePath(r.getPath())); - } - } - } - - return super.cancel(); - } - - @Override - public String back() - { - if ("content".equals(Application.getWizardManager().getCurrentStepName())) - { - //override in order not to delete these items - this.formInstanceData = null; - this.renditions = null; - } - return super.back(); - } - - @Override - protected void saveContent() - throws Exception - { - if (logger.isDebugEnabled()) - logger.debug("saving " + this.createdPath); - - AVMLock lock = this.getAvmLockingService().getLock(AVMUtil.getStoreId(this.createdPath), - AVMUtil.getStoreRelativePath(this.createdPath)); - if (lock != null) - { - if (logger.isDebugEnabled()) - logger.debug("transferring lock from " + lock.getStore() + - " to " + AVMUtil.getStoreName(this.createdPath)); - - this.getAvmLockingService().modifyLock(AVMUtil.getStoreId(this.createdPath), - AVMUtil.getStoreRelativePath(this.createdPath), - null, - AVMUtil.getStoreName(this.createdPath), - null, - null); - - // set the lock present flag - this.wasFormLockPresent = true; - } - - final ContentWriter writer = this.getAvmService().getContentWriter(this.createdPath); - this.content = XMLUtil.toString(this.getInstanceDataDocument(), false); - writer.putContent(this.content); - - // XXXarielb might not need to do this reload - this.formInstanceData = this.getFormsService().getFormInstanceData(-1, this.createdPath); - for (final Rendition r : this.formInstanceData.getRenditions()) - { - lock = this.getAvmLockingService().getLock(AVMUtil.getStoreId(r.getPath()), - AVMUtil.getStoreRelativePath(r.getPath())); - if (lock != null) - { - if (logger.isDebugEnabled()) - logger.debug("transferring lock from " + lock.getStore() + - " to " + AVMUtil.getStoreName(r.getPath())); - - this.getAvmLockingService().modifyLock(AVMUtil.getStoreId(r.getPath()), - AVMUtil.getStoreRelativePath(r.getPath()), - null, - AVMUtil.getStoreName(r.getPath()), - null, - null); - - // set the lock present flag - this.wasRenditionLockPresent = true; - } - } - final List result = this.formInstanceData.regenerateRenditions(); - this.renditions = new LinkedList(); - for (FormInstanceData.RegenerateResult rr : result) - { - if (rr.getException() != null) - { - Utils.addErrorMessage("error regenerating rendition using " + rr.getRenderingEngineTemplate().getName() + - ": " + rr.getException().getMessage(), - rr.getException()); - } - else - { - final Rendition r = rr.getRendition(); - this.renditions.add(r); - - if (logger.isDebugEnabled()) - logger.debug("transferring lock for " + r.getPath() + - " back to " + AVMUtil.getCorrespondingMainStoreName(AVMUtil.getStoreName(r.getPath()))); - - this.getAvmLockingService().modifyLock(AVMUtil.getStoreId(r.getPath()), - AVMUtil.getStoreRelativePath(r.getPath()), - null, - AVMUtil.getCorrespondingMainStoreName(AVMUtil.getStoreName(r.getPath())), - null, - null); - } - } - - if (logger.isDebugEnabled()) - logger.debug("transferring form instance data lock back to " + - AVMUtil.getCorrespondingMainStoreName(AVMUtil.getStoreName(this.createdPath))); - - this.getAvmLockingService().modifyLock(AVMUtil.getStoreId(this.createdPath), - AVMUtil.getStoreRelativePath(this.createdPath), - null, - AVMUtil.getCorrespondingMainStoreName(AVMUtil.getStoreName(this.createdPath)), - null, - null); - } - - /** Indicates whether or not the wizard is currently in edit mode */ - @Override - public boolean getEditMode() - { - return true; - } - - @Override - public boolean getSubmittable() - { - return !AVMUtil.isWorkflowStore(AVMUtil.getStoreName(this.createdPath)); - } - - /** - * Overridden to avoid calling getWebProject since potentially there is no web project - * context in workflow scenario. - */ - @Override - public Form getForm() - { - return this.form; - } -} +/* + * 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.wcm; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.service.cmr.avm.locking.AVMLock; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.web.app.Application; +import org.alfresco.web.forms.Form; +import org.alfresco.web.forms.FormInstanceData; +import org.alfresco.web.forms.FormNotFoundException; +import org.alfresco.web.forms.Rendition; +import org.alfresco.web.forms.XMLUtil; +import org.alfresco.web.ui.common.Utils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Bean implementation for the "Edit Web Content Wizard" dialog + */ +public class EditWebContentWizard extends CreateWebContentWizard +{ + private static final long serialVersionUID = 439996926303151006L; + + private static final Log logger = LogFactory.getLog(EditWebContentWizard.class); + + private AVMNode avmNode; + private Form form; + + protected List locksPresentAtInit = null; + + // ------------------------------------------------------------------------------ + // Wizard implementation + + @Override + public void init(final Map parameters) + { + super.init(parameters); + this.avmNode = this.avmBrowseBean.getAvmActionNode(); + if (this.avmNode == null) + { + throw new IllegalArgumentException("Edit Form wizard requires action node context."); + } + + if (logger.isDebugEnabled()) + logger.debug("path is " + this.avmNode.getPath()); + + this.createdPath = AVMUtil.getCorrespondingPathInPreviewStore(this.avmNode.getPath()); + this.formInstanceData = this.getFormsService().getFormInstanceData(-1, this.createdPath); + final WebProject webProject = new WebProject(this.createdPath); + try + { + this.formName = this.formInstanceData.getForm().getName(); + this.form = webProject.getForm(this.formName); + } + catch (FormNotFoundException fnfe) + { + Utils.addErrorMessage(fnfe.getMessage(), fnfe); + } + this.content = this.getAvmService().getContentReader(-1, this.createdPath).getContentString(); + this.fileName = this.formInstanceData.getName(); + this.mimeType = MimetypeMap.MIMETYPE_XML; + + // calculate which locks are present at init time + this.locksPresentAtInit = new ArrayList(4); + AVMLock lock = this.getAvmLockingService().getLock(AVMUtil.getStoreId(this.createdPath), + AVMUtil.getStoreRelativePath(this.createdPath)); + if (lock != null) + { + this.locksPresentAtInit.add(this.createdPath); + + if (logger.isDebugEnabled()) + logger.debug("Lock exists for xml instance " + this.createdPath + " at initialisation"); + } + + for (final Rendition r : this.formInstanceData.getRenditions()) + { + String path = r.getPath(); + lock = this.getAvmLockingService().getLock(AVMUtil.getStoreId(path), + AVMUtil.getStoreRelativePath(path)); + if (lock != null) + { + this.locksPresentAtInit.add(path); + + if (logger.isDebugEnabled()) + logger.debug("Lock exists for rendition " + path + " at initialisation"); + } + } + } + + @Override + public String cancel() + { + if (this.formInstanceData != null && this.renditions != null) + { + if (this.locksPresentAtInit.contains(this.createdPath) == false) + { + // there wasn't a lock on the form at the start of the + // wizard so remove the one present now + if (logger.isDebugEnabled()) + logger.debug("removing form instance data lock from " + + AVMUtil.getCorrespondingPathInMainStore(this.createdPath) + + " as user chose to cancel and it wasn't present at initialisation"); + + this.getAvmLockingService().removeLock(AVMUtil.getStoreId(this.createdPath), + AVMUtil.getStoreRelativePath(this.createdPath)); + } + + for (Rendition r : this.renditions) + { + String path = r.getPath(); + + if (this.locksPresentAtInit.contains(path) == false) + { + // there wasn't a lock on the rendition at the start of + // the wizard so remove the one present now + if (logger.isDebugEnabled()) + logger.debug("removing lock from rendition " + + AVMUtil.getCorrespondingPathInMainStore(path) + + " as user chose to cancel and it wasn't present at initialisation"); + + this.getAvmLockingService().removeLock(AVMUtil.getStoreId(path), + AVMUtil.getStoreRelativePath(path)); + } + } + } + + return super.cancel(); + } + + @Override + public String back() + { + if ("content".equals(Application.getWizardManager().getCurrentStepName())) + { + //override in order not to delete these items + this.formInstanceData = null; + this.renditions = null; + } + return super.back(); + } + + @Override + protected void saveContent() + throws Exception + { + if (logger.isDebugEnabled()) + logger.debug("saving " + this.createdPath); + + AVMLock lock = this.getAvmLockingService().getLock(AVMUtil.getStoreId(this.createdPath), + AVMUtil.getStoreRelativePath(this.createdPath)); + if (lock != null) + { + if (logger.isDebugEnabled()) + logger.debug("transferring lock from " + lock.getStore() + + " to " + AVMUtil.getStoreName(this.createdPath)); + + this.getAvmLockingService().modifyLock(AVMUtil.getStoreId(this.createdPath), + AVMUtil.getStoreRelativePath(this.createdPath), + null, + AVMUtil.getStoreName(this.createdPath), + null, + null); + } + + final ContentWriter writer = this.getAvmService().getContentWriter(this.createdPath); + this.content = XMLUtil.toString(this.getInstanceDataDocument(), false); + writer.putContent(this.content); + + // XXXarielb might not need to do this reload + this.formInstanceData = this.getFormsService().getFormInstanceData(-1, this.createdPath); + for (final Rendition r : this.formInstanceData.getRenditions()) + { + lock = this.getAvmLockingService().getLock(AVMUtil.getStoreId(r.getPath()), + AVMUtil.getStoreRelativePath(r.getPath())); + if (lock != null) + { + if (logger.isDebugEnabled()) + logger.debug("transferring lock from " + lock.getStore() + + " to " + AVMUtil.getStoreName(r.getPath())); + + this.getAvmLockingService().modifyLock(AVMUtil.getStoreId(r.getPath()), + AVMUtil.getStoreRelativePath(r.getPath()), + null, + AVMUtil.getStoreName(r.getPath()), + null, + null); + } + } + + final List result = this.formInstanceData.regenerateRenditions(); + this.renditions = new LinkedList(); + for (FormInstanceData.RegenerateResult rr : result) + { + if (rr.getException() != null) + { + Utils.addErrorMessage("error regenerating rendition using " + rr.getRenderingEngineTemplate().getName() + + ": " + rr.getException().getMessage(), + rr.getException()); + + // if the renditions were locked before the regenerate, move the lock back to main store + String path = rr.getPath(); + + if (this.locksPresentAtInit.contains(path)) + { + if (logger.isDebugEnabled()) + logger.debug("transferring existing lock for " + path + + " back to " + AVMUtil.getCorrespondingMainStoreName(AVMUtil.getStoreName(path))); + + this.getAvmLockingService().modifyLock(AVMUtil.getStoreId(path), + AVMUtil.getStoreRelativePath(path), + null, + AVMUtil.getCorrespondingMainStoreName(AVMUtil.getStoreName(path)), + null, + null); + } + } + else + { + final Rendition r = rr.getRendition(); + this.renditions.add(r); + + if (logger.isDebugEnabled()) + logger.debug("transferring lock for " + r.getPath() + + " back to " + AVMUtil.getCorrespondingMainStoreName(AVMUtil.getStoreName(r.getPath()))); + + this.getAvmLockingService().modifyLock(AVMUtil.getStoreId(r.getPath()), + AVMUtil.getStoreRelativePath(r.getPath()), + null, + AVMUtil.getCorrespondingMainStoreName(AVMUtil.getStoreName(r.getPath())), + null, + null); + } + } + + if (logger.isDebugEnabled()) + logger.debug("transferring form instance data lock back to " + + AVMUtil.getCorrespondingMainStoreName(AVMUtil.getStoreName(this.createdPath))); + + this.getAvmLockingService().modifyLock(AVMUtil.getStoreId(this.createdPath), + AVMUtil.getStoreRelativePath(this.createdPath), + null, + AVMUtil.getCorrespondingMainStoreName(AVMUtil.getStoreName(this.createdPath)), + null, + null); + } + + /** Indicates whether or not the wizard is currently in edit mode */ + @Override + public boolean getEditMode() + { + return true; + } + + @Override + public boolean getSubmittable() + { + return !AVMUtil.isWorkflowStore(AVMUtil.getStoreName(this.createdPath)); + } + + /** + * Overridden to avoid calling getWebProject since potentially there is no web project + * context in workflow scenario. + */ + @Override + public Form getForm() + { + return this.form; + } +} diff --git a/source/java/org/alfresco/web/bean/wcm/UnlockFileDialog.java b/source/java/org/alfresco/web/bean/wcm/UnlockFileDialog.java new file mode 100644 index 0000000000..8546967fb9 --- /dev/null +++ b/source/java/org/alfresco/web/bean/wcm/UnlockFileDialog.java @@ -0,0 +1,145 @@ +/* + * 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.wcm; + +import java.text.MessageFormat; + +import javax.faces.context.FacesContext; + +import org.alfresco.service.cmr.avm.AVMService; +import org.alfresco.service.cmr.avm.locking.AVMLockingService; +import org.alfresco.web.app.Application; +import org.alfresco.web.bean.dialog.BaseDialogBean; +import org.alfresco.web.bean.repository.Repository; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Bean implementation for the AVM "Unlock File" dialog + * + * @author Gavin Cornwell + */ +public class UnlockFileDialog extends BaseDialogBean +{ + private static final long serialVersionUID = -2985826502913718770L; + + private static final Log logger = LogFactory.getLog(UnlockFileDialog.class); + + protected AVMService avmService; + protected AVMLockingService lockingService; + protected AVMBrowseBean avmBrowseBean; + + /** + * @param avmBrowseBean The avmBrowseBean to set. + */ + public void setAvmBrowseBean(AVMBrowseBean avmBrowseBean) + { + this.avmBrowseBean = avmBrowseBean; + } + + /** + * @param avmService The avmService to set. + */ + public void setAvmService(AVMService avmService) + { + this.avmService = avmService; + } + + protected AVMService getAvmService() + { + if (this.avmService == null) + { + this.avmService = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getAVMService(); + } + + return this.avmService; + } + + /** + * @param lockingService The avmLockingService to set. + */ + public void setAvmLockingService(AVMLockingService lockingService) + { + this.lockingService = lockingService; + } + + protected AVMLockingService getAvmLockingService() + { + if (this.lockingService == null) + { + this.lockingService = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getAVMLockingService(); + } + + return this.lockingService; + } + + // ------------------------------------------------------------------------------ + // Dialog implementation + + @Override + protected String finishImpl(final FacesContext context, final String outcome) + throws Exception + { + // get the content to delete + final AVMNode node = this.avmBrowseBean.getAvmActionNode(); + if (node == null) + { + logger.warn("WARNING: unlock called without a current AVM Node!"); + } + else + { + if (logger.isDebugEnabled()) + logger.debug("Trying to unlock AVM node: " + node.getPath()); + + this.getAvmLockingService().removeLock(this.avmBrowseBean.getWebProject().getStoreId(), + AVMUtil.getStoreRelativePath(node.getPath())); + } + + return outcome; + } + + @Override + public boolean getFinishButtonDisabled() + { + 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 unlockConfirmMsg = Application.getMessage(FacesContext.getCurrentInstance(), + "unlock_file_confirm"); + + return MessageFormat.format(unlockConfirmMsg, + new Object[] {this.avmBrowseBean.getAvmActionNode().getName()}); + } +} diff --git a/source/java/org/alfresco/web/forms/FormInstanceData.java b/source/java/org/alfresco/web/forms/FormInstanceData.java index 259487a2f2..38f4115267 100644 --- a/source/java/org/alfresco/web/forms/FormInstanceData.java +++ b/source/java/org/alfresco/web/forms/FormInstanceData.java @@ -47,29 +47,39 @@ public interface FormInstanceData private static final long serialVersionUID = -3827878774655260635L; private final RenderingEngineTemplate ret; + private final String path; private final Rendition r; private final Exception e; public RegenerateResult(final RenderingEngineTemplate ret, + final String path, final Rendition r) { this.ret = ret; this.r = r; this.e = null; + this.path = path; } public RegenerateResult(final RenderingEngineTemplate ret, + final String path, final Exception e) { this.ret = ret; this.e = e; this.r = null; + this.path = path; } public RenderingEngineTemplate getRenderingEngineTemplate() { return this.ret; } + + public String getPath() + { + return this.path; + } public Rendition getRendition() { diff --git a/source/java/org/alfresco/web/forms/FormInstanceDataImpl.java b/source/java/org/alfresco/web/forms/FormInstanceDataImpl.java index f01988acd3..0bd8277ff0 100644 --- a/source/java/org/alfresco/web/forms/FormInstanceDataImpl.java +++ b/source/java/org/alfresco/web/forms/FormInstanceDataImpl.java @@ -41,6 +41,8 @@ import org.alfresco.repo.domain.PropertyValue; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.avm.AVMNotFoundException; import org.alfresco.service.cmr.avm.AVMService; +import org.alfresco.service.cmr.avm.locking.AVMLock; +import org.alfresco.service.cmr.avm.locking.AVMLockingService; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.StoreRef; @@ -61,8 +63,8 @@ import org.xml.sax.SAXException; /* package */ class FormInstanceDataImpl implements FormInstanceData { private static final long serialVersionUID = -7806221587661854013L; - - private static final Log LOGGER = LogFactory.getLog(RenditionImpl.class); + + private static final Log logger = LogFactory.getLog(RenditionImpl.class); private final NodeRef nodeRef; private transient FormsService formsService; @@ -175,9 +177,10 @@ import org.xml.sax.SAXException; public List regenerateRenditions() throws FormNotFoundException { - if (LOGGER.isDebugEnabled()) - LOGGER.debug("regenerating renditions of " + this); + if (logger.isDebugEnabled()) + logger.debug("regenerating renditions of " + this); + AVMLockingService avmLockService = this.getServiceRegistry().getAVMLockingService(); final AVMService avmService = this.getServiceRegistry().getAVMService(); PropertyValue pv = avmService.getNodeProperty( AVMNodeConverter.ToAVMVersionPath(this.nodeRef).getFirst(), @@ -191,6 +194,8 @@ import org.xml.sax.SAXException; new HashSet(this.getForm().getRenderingEngineTemplates()); final List result = new LinkedList(); // regenerate existing renditions + boolean renditionLockedBefore = false; + String path = null; for (final Rendition r : this.getRenditions()) { final RenderingEngineTemplate ret = r.getRenderingEngineTemplate(); @@ -200,14 +205,36 @@ import org.xml.sax.SAXException; } try { - LOGGER.debug("regenerating rendition " + r + " using template " + ret); + if (logger.isDebugEnabled()) + logger.debug("regenerating rendition " + r + " using template " + ret); + + renditionLockedBefore = false; + path = r.getPath(); + AVMLock lock = avmLockService.getLock(AVMUtil.getStoreId(path), AVMUtil.getStoreRelativePath(path)); + if (lock != null) + { + renditionLockedBefore = true; + + if (logger.isDebugEnabled()) + logger.debug("Lock already exists for " + path); + } + ret.render(this, r); allRets.remove(ret); - result.add(new RegenerateResult(ret, r)); + result.add(new RegenerateResult(ret, path, r)); } catch (Exception e) { - result.add(new RegenerateResult(ret, e)); + result.add(new RegenerateResult(ret, path, e)); + + // remove lock if there wasn't one before + if (renditionLockedBefore == false) + { + avmLockService.removeLock(AVMUtil.getStoreId(path), AVMUtil.getStoreRelativePath(path)); + + if (logger.isDebugEnabled()) + logger.debug("Removed lock for " + path + " as it failed to generate"); + } } } @@ -216,15 +243,36 @@ import org.xml.sax.SAXException; { try { - final String path = ret.getOutputPathForRendition(this, originalParentAvmPath); - LOGGER.debug("regenerating rendition of " + this.getPath() + - " at " + path + " using template " + ret); + renditionLockedBefore = false; + path = ret.getOutputPathForRendition(this, originalParentAvmPath); - result.add(new RegenerateResult(ret, ret.render(this, path))); + if (logger.isDebugEnabled()) + logger.debug("regenerating rendition of " + this.getPath() + + " at " + path + " using template " + ret); + + AVMLock lock = avmLockService.getLock(AVMUtil.getStoreId(path), AVMUtil.getStoreRelativePath(path)); + if (lock != null) + { + renditionLockedBefore = true; + + if (logger.isDebugEnabled()) + logger.debug("Lock already exists for " + path); + } + + result.add(new RegenerateResult(ret, path, ret.render(this, path))); } catch (Exception e) { - result.add(new RegenerateResult(ret, e)); + result.add(new RegenerateResult(ret, path, e)); + + // remove lock if there wasn't one before + if (renditionLockedBefore == false) + { + avmLockService.removeLock(AVMUtil.getStoreId(path), AVMUtil.getStoreRelativePath(path)); + + if (logger.isDebugEnabled()) + logger.debug("Removed lock for " + path + " as it failed to generate"); + } } } return result; @@ -244,7 +292,8 @@ import org.xml.sax.SAXException; { if (avmService.lookup(-1, storeName + ':' + (String)path) == null) { - LOGGER.debug("ignoring dangling rendition at " + storeName + ':' + (String)path); + if (logger.isDebugEnabled()) + logger.debug("ignoring dangling rendition at " + storeName + ':' + (String)path); } else { @@ -255,9 +304,10 @@ import org.xml.sax.SAXException; { if (!this.equals(r.getPrimaryFormInstanceData())) { - LOGGER.debug("rendition " + r + - " points at form instance data " + r.getPrimaryFormInstanceData() + - " instead of " + this + ". Not including in renditions list."); + if (logger.isDebugEnabled()) + logger.debug("rendition " + r + + " points at form instance data " + r.getPrimaryFormInstanceData() + + " instead of " + this + ". Not including in renditions list."); continue; } } diff --git a/source/web/WEB-INF/faces-config-beans.xml b/source/web/WEB-INF/faces-config-beans.xml index f0cc5a1295..ea695d53a0 100644 --- a/source/web/WEB-INF/faces-config-beans.xml +++ b/source/web/WEB-INF/faces-config-beans.xml @@ -3673,6 +3673,27 @@ #{NodeService} + + + + The bean that backs up the Unlock File Dialog + + UnlockFileDialog + org.alfresco.web.bean.wcm.UnlockFileDialog + session + + avmService + #{AVMLockingAwareService} + + + avmLockingService + #{AVMLockingService} + + + avmBrowseBean + #{AVMBrowseBean} + + diff --git a/source/web/images/icons/unlock_large.gif b/source/web/images/icons/unlock_large.gif new file mode 100644 index 0000000000..0bfce88dad Binary files /dev/null and b/source/web/images/icons/unlock_large.gif differ