mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
fixing columns in create form wizard to not move around wildy once files are uploaded fixing some bugs in repeats as a result of the chiba1.3 upgrade. - xpaths were not being resolved for newly created instances (and shouldn't be) - xpaths for nested repeats weren't behaving properly fixed caching issue in edit which was causing the wrong form to be loaded for instance data using alfresco namespace as defined in NamespaceService passing namespace uris and prefixes to js to avoid errors if they should change fixed a bug where the renditions property of forminstancedata was getting duplicate renditions (and thus regenerating way too much stuff when doing an edit) using an italic 'description not set' when description isn't set in several screens. using the avm service to set properties in renderingenginetemplateimpl. upgrading to xalan-2.7.0 in order to be able to use bsf for extension functions. adding the path that was not found to AVMNotFoundExceptions. very helpful when debugging. substantial refactoring of rendering engines in preparation for integration with TemplateService. - implementing a common model as a hash of QNames to objects. for method, providing a simple method wrapper called TemplateProcessorMethod which takes an array of Objects as a parameter, and returns an object. it is up to the template processors to properly convert arguments. a QName is used for the variable name rather than a string in order to include a namespace prefix (needed for xsl, and generally better looking). - for xsl, using javascript bindings for formdatafunctions, which using liveconnect within rhino to call into the xsl rendering engine to evaluate the function. it ends up generating this js block into a xalan:component within the xsl tempalte: // gets the handle to the backing java object which can invoke the function based on id var _xsltp_invoke = java.lang.Class.forName('org.alfresco.web.forms.XSLTRenderingEngine$ProcessorMethodInvoker').newInstance(); // utility to convert js arrays into java function _xsltp_to_java_array(js_array) { var java_array = java.lang.reflect.Array.newInstance(java.lang.Object, js_array.length); for (var i = 0; i < js_array.length; i++) { java_array[i] = js_array[i]; } return java_array; } // js handles to each of the form data functions which uses _xsltp_invoke to call the actual method function _getAVMPath() { return _xsltp_invoke.invokeMethod('_getAVMPath8829055', _xsltp_to_java_array(arguments)); } function parseXMLDocuments() { return _xsltp_invoke.invokeMethod('parseXMLDocuments12235190', _xsltp_to_java_array(arguments)); } function parseXMLDocument() { return _xsltp_invoke.invokeMethod('parseXMLDocument15280968', _xsltp_to_java_array(arguments)); } xml model data is inferred as a root namespace document within the model hash provided. - for freemarker, things pretty much work as they did before, just i now need to convert values by hand to TemplateModels fixed a bug with hidden iframe upload. seems like the complexity of actually cloning the file input element is unnecessary and that simply attaching the node in two places within the dom works just fine. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@4764 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
573 lines
18 KiB
Java
573 lines
18 KiB
Java
/*
|
|
* 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.
|
|
*/
|
|
package org.alfresco.web.bean.wcm;
|
|
|
|
import java.io.File;
|
|
import java.io.FileNotFoundException;
|
|
import java.text.MessageFormat;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
import javax.faces.context.FacesContext;
|
|
import javax.faces.event.ActionEvent;
|
|
import javax.transaction.UserTransaction;
|
|
|
|
import org.alfresco.model.ContentModel;
|
|
import org.alfresco.model.WCMAppModel;
|
|
import org.alfresco.repo.avm.AVMNodeConverter;
|
|
import org.alfresco.repo.content.MimetypeMap;
|
|
import org.alfresco.repo.domain.PropertyValue;
|
|
import org.alfresco.service.cmr.avm.AVMService;
|
|
import org.alfresco.service.cmr.avmsync.AVMDifference;
|
|
import org.alfresco.service.cmr.avmsync.AVMSyncService;
|
|
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
|
import org.alfresco.service.cmr.repository.ContentReader;
|
|
import org.alfresco.service.cmr.repository.ContentService;
|
|
import org.alfresco.service.cmr.repository.ContentWriter;
|
|
import org.alfresco.service.cmr.repository.NodeRef;
|
|
import org.alfresco.service.cmr.repository.NodeService;
|
|
import org.alfresco.util.Pair;
|
|
import org.alfresco.web.app.AlfrescoNavigationHandler;
|
|
import org.alfresco.web.app.Application;
|
|
import org.alfresco.web.app.servlet.DownloadContentServlet;
|
|
import org.alfresco.web.bean.CheckinCheckoutBean;
|
|
import org.alfresco.web.bean.FileUploadBean;
|
|
import org.alfresco.web.bean.repository.Repository;
|
|
import org.alfresco.web.forms.Form;
|
|
import org.alfresco.web.forms.FormInstanceData;
|
|
import org.alfresco.web.forms.FormInstanceDataImpl;
|
|
import org.alfresco.web.forms.FormProcessor;
|
|
import org.alfresco.web.forms.Rendition;
|
|
import org.alfresco.web.forms.RenditionImpl;
|
|
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;
|
|
import org.w3c.dom.Document;
|
|
|
|
/**
|
|
* Bean backing the edit pages for a AVM node content.
|
|
*
|
|
* @author Ariel Backenroth
|
|
* @author Kevin Roast
|
|
*/
|
|
public class AVMEditBean
|
|
{
|
|
private static final Log LOGGER = LogFactory.getLog(AVMEditBean.class);
|
|
|
|
private static final String MSG_ERROR_UPDATE = "error_update";
|
|
private static final String MSG_UPLOAD_SUCCESS = "file_upload_success";
|
|
|
|
private String documentContent = null;
|
|
private Document instanceDataDocument = null;
|
|
private String editorOutput = null;
|
|
|
|
private File file = null;
|
|
private String fileName = null;
|
|
protected FormProcessor.Session formProcessorSession = null;
|
|
private Form form = null;
|
|
|
|
/** AVM service bean reference */
|
|
protected AVMService avmService;
|
|
|
|
/** AVM sync service bean reference */
|
|
protected AVMSyncService avmSyncService;
|
|
|
|
/** AVM Browse Bean reference */
|
|
protected AVMBrowseBean avmBrowseBean;
|
|
|
|
/** The ContentService bean reference */
|
|
protected ContentService contentService;
|
|
|
|
/** The NodeService bean reference */
|
|
protected NodeService nodeService;
|
|
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// Bean property getters and setters
|
|
|
|
/**
|
|
* @param avmService The AVMService to set.
|
|
*/
|
|
public void setAvmService(AVMService avmService)
|
|
{
|
|
this.avmService = avmService;
|
|
}
|
|
|
|
/**
|
|
* @param avmSyncService The AVMSyncService to set.
|
|
*/
|
|
public void setAvmSyncService(AVMSyncService avmSyncService)
|
|
{
|
|
this.avmSyncService = avmSyncService;
|
|
}
|
|
|
|
/**
|
|
* @param avmBrowseBean The AVMBrowseBean to set.
|
|
*/
|
|
public void setAvmBrowseBean(AVMBrowseBean avmBrowseBean)
|
|
{
|
|
this.avmBrowseBean = avmBrowseBean;
|
|
}
|
|
|
|
/**
|
|
* @param contentService The ContentService to set.
|
|
*/
|
|
public void setContentService(ContentService contentService)
|
|
{
|
|
this.contentService = contentService;
|
|
}
|
|
|
|
/**
|
|
* @param nodeService The nodeService to set.
|
|
*/
|
|
public void setNodeService(NodeService nodeService)
|
|
{
|
|
this.nodeService = nodeService;
|
|
}
|
|
|
|
/**
|
|
* @return Returns the current AVM node context.
|
|
*/
|
|
public AVMNode getAvmNode()
|
|
{
|
|
return this.avmBrowseBean.getAvmActionNode();
|
|
}
|
|
|
|
/**
|
|
* @return Large file icon for current AVM node
|
|
*/
|
|
public String getFileType32()
|
|
{
|
|
return Utils.getFileTypeImage(getAvmNode().getName(), false);
|
|
}
|
|
|
|
/**
|
|
* @return Small file icon for current AVM node
|
|
*/
|
|
public String getFileType16()
|
|
{
|
|
return Utils.getFileTypeImage(getAvmNode().getName(), true);
|
|
}
|
|
|
|
/**
|
|
* @return Content URL for current AVM node
|
|
*/
|
|
public String getUrl()
|
|
{
|
|
return DownloadContentServlet.generateDownloadURL(AVMNodeConverter.ToNodeRef(-1, getAvmNode().getPath()),
|
|
getAvmNode().getName());
|
|
}
|
|
|
|
/**
|
|
* @return Returns the document content used for HTML in-line editing.
|
|
*/
|
|
public String getDocumentContent()
|
|
{
|
|
return this.documentContent;
|
|
}
|
|
|
|
/**
|
|
* @param documentContent The document content for HTML in-line editing.
|
|
*/
|
|
public void setDocumentContent(String documentContent)
|
|
{
|
|
this.documentContent = documentContent;
|
|
}
|
|
|
|
/**
|
|
* @return Returns output from the in-line editor page.
|
|
*/
|
|
public String getEditorOutput()
|
|
{
|
|
return this.editorOutput;
|
|
}
|
|
|
|
/**
|
|
* @param editorOutput The output from the in-line editor page
|
|
*/
|
|
public void setEditorOutput(String editorOutput)
|
|
{
|
|
this.editorOutput = editorOutput;
|
|
}
|
|
|
|
/**
|
|
* @return Returns the name of the file
|
|
*/
|
|
public String getFileName()
|
|
{
|
|
// try and retrieve the file and filename from the file upload bean
|
|
// representing the file we previously uploaded.
|
|
FacesContext ctx = FacesContext.getCurrentInstance();
|
|
FileUploadBean fileBean = (FileUploadBean)ctx.getExternalContext().getSessionMap().
|
|
get(FileUploadBean.FILE_UPLOAD_BEAN_NAME);
|
|
if (fileBean != null)
|
|
{
|
|
this.file = fileBean.getFile();
|
|
this.fileName = fileBean.getFileName();
|
|
}
|
|
|
|
return this.fileName;
|
|
}
|
|
|
|
/**
|
|
* @return Returns the message to display when a file has been uploaded
|
|
*/
|
|
public String getFileUploadSuccessMsg()
|
|
{
|
|
String msg = Application.getMessage(FacesContext.getCurrentInstance(), MSG_UPLOAD_SUCCESS);
|
|
return MessageFormat.format(msg, new Object[] {getFileName()});
|
|
}
|
|
|
|
/**
|
|
* @return Returns the form when in the context of editing an xml asset.
|
|
*/
|
|
public Form getForm()
|
|
{
|
|
if (this.form == null)
|
|
{
|
|
final PropertyValue pv =
|
|
this.avmService.getNodeProperty(-1,
|
|
this.getAvmNode().getPath(),
|
|
WCMAppModel.PROP_PARENT_FORM_NAME);
|
|
|
|
final String formName = (String)pv.getValue(DataTypeDefinition.TEXT);
|
|
final WebProject wp = new WebProject(this.getAvmNode().getPath());
|
|
this.form = wp.getForm(formName);
|
|
LOGGER.debug("loaded form " + this.form +
|
|
", form name " + formName +
|
|
", for " + this.getAvmNode().getPath());
|
|
}
|
|
return this.form;
|
|
}
|
|
|
|
/**
|
|
* @return Returns the wrapper instance data for feeding the xml
|
|
* content to the form processor.
|
|
*/
|
|
public Document getInstanceDataDocument()
|
|
{
|
|
if (this.instanceDataDocument == null)
|
|
{
|
|
final String content = this.getEditorOutput();
|
|
try
|
|
{
|
|
this.instanceDataDocument = (content != null
|
|
? XMLUtil.parse(content)
|
|
: XMLUtil.newDocument());
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Utils.addErrorMessage("error parsing document", e);
|
|
return XMLUtil.newDocument();
|
|
}
|
|
}
|
|
return this.instanceDataDocument;
|
|
}
|
|
|
|
/**
|
|
* Returns the form processor session.
|
|
*/
|
|
public FormProcessor.Session getFormProcessorSession()
|
|
{
|
|
return this.formProcessorSession;
|
|
}
|
|
|
|
/**
|
|
* Sets the form processor session.
|
|
*/
|
|
public void setFormProcessorSession(final FormProcessor.Session formProcessorSession)
|
|
{
|
|
this.formProcessorSession = formProcessorSession;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// Action event handlers
|
|
|
|
/**
|
|
* Action handler called to calculate which editing screen to display based on the mimetype
|
|
* of a document. If appropriate, the in-line editing screen will be shown.
|
|
*/
|
|
public void setupEditAction(ActionEvent event)
|
|
{
|
|
this.avmBrowseBean.setupContentAction(event);
|
|
|
|
// retrieve the content reader for this node
|
|
String avmPath = getAvmNode().getPath();
|
|
if (this.avmService.hasAspect(-1, avmPath, WCMAppModel.ASPECT_RENDITION))
|
|
{
|
|
if (LOGGER.isDebugEnabled())
|
|
LOGGER.debug(avmPath + " is a rendition, editing primary rendition instead");
|
|
try
|
|
{
|
|
final FormInstanceData fid =
|
|
new RenditionImpl(AVMNodeConverter.ToNodeRef(-1, avmPath)).getPrimaryFormInstanceData();
|
|
avmPath = fid.getPath();
|
|
if (LOGGER.isDebugEnabled())
|
|
LOGGER.debug("Editing primary form instance data " + avmPath);
|
|
this.avmBrowseBean.setAvmActionNode(new AVMNode(this.avmService.lookup(-1, avmPath)));
|
|
}
|
|
catch (FileNotFoundException fnfe)
|
|
{
|
|
this.avmService.removeAspect(avmPath, WCMAppModel.ASPECT_RENDITION);
|
|
this.avmService.removeAspect(avmPath, WCMAppModel.ASPECT_FORM_INSTANCE_DATA);
|
|
Utils.addErrorMessage(fnfe.getMessage(), fnfe);
|
|
}
|
|
}
|
|
|
|
if (this.avmService.hasAspect(-1, avmPath, WCMAppModel.ASPECT_FORM_INSTANCE_DATA))
|
|
{
|
|
// reset the preview layer
|
|
String storeName = AVMConstants.getStoreName(avmPath);
|
|
storeName = AVMConstants.getCorrespondingPreviewStoreName(storeName);
|
|
final String path = AVMConstants.buildStoreRootPath(storeName);
|
|
if (LOGGER.isDebugEnabled())
|
|
LOGGER.debug("reseting layer " + path);
|
|
this.avmSyncService.resetLayer(path);
|
|
}
|
|
|
|
if (LOGGER.isDebugEnabled())
|
|
LOGGER.debug("Editing AVM node: " + avmPath);
|
|
ContentReader reader = this.avmService.getContentReader(-1, avmPath);
|
|
if (reader != null)
|
|
{
|
|
String mimetype = reader.getMimetype();
|
|
String outcome = null;
|
|
// calculate which editor screen to display
|
|
if (MimetypeMap.MIMETYPE_TEXT_PLAIN.equals(mimetype) ||
|
|
MimetypeMap.MIMETYPE_XML.equals(mimetype) ||
|
|
MimetypeMap.MIMETYPE_TEXT_CSS.equals(mimetype) ||
|
|
MimetypeMap.MIMETYPE_JAVASCRIPT.equals(mimetype))
|
|
{
|
|
// make content available to the editing screen
|
|
setEditorOutput(reader.getContentString());
|
|
|
|
// navigate to appropriate screen
|
|
outcome = ((MimetypeMap.MIMETYPE_XML.equals(mimetype) &&
|
|
this.avmService.hasAspect(-1, avmPath, WCMAppModel.ASPECT_FORM_INSTANCE_DATA))
|
|
? "dialog:editAvmXmlInline"
|
|
: "dialog:editAvmTextInline");
|
|
}
|
|
else if (MimetypeMap.MIMETYPE_HTML.equals(mimetype))
|
|
{
|
|
// make content available to the editing screen
|
|
setDocumentContent(reader.getContentString());
|
|
setEditorOutput(null);
|
|
|
|
// navigate to appropriate screen
|
|
outcome = "dialog:editAvmHtmlInline";
|
|
}
|
|
else
|
|
{
|
|
// normal downloadable document
|
|
outcome = "dialog:editAvmFile";
|
|
}
|
|
|
|
final FacesContext fc = FacesContext.getCurrentInstance();
|
|
fc.getApplication().getNavigationHandler().handleNavigation(fc, null, outcome);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Action called upon completion of the Edit File download page
|
|
*/
|
|
public String editFileOK()
|
|
{
|
|
String outcome = null;
|
|
|
|
AVMNode node = getAvmNode();
|
|
if (node != null)
|
|
{
|
|
resetState();
|
|
|
|
outcome = AlfrescoNavigationHandler.CLOSE_DIALOG_OUTCOME;
|
|
}
|
|
|
|
return outcome;
|
|
}
|
|
|
|
/**
|
|
* Action handler called to set the content of a node from an inline editing page.
|
|
*/
|
|
public String editInlineOK()
|
|
{
|
|
UserTransaction tx = null;
|
|
final AVMNode avmNode = getAvmNode();
|
|
if (avmNode == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
final String avmPath = avmNode.getPath();
|
|
LOGGER.debug("saving " + avmPath);
|
|
try
|
|
{
|
|
tx = Repository.getUserTransaction(FacesContext.getCurrentInstance());
|
|
tx.begin();
|
|
|
|
// get an updating writer that we can use to modify the content on the current node
|
|
final ContentWriter writer = this.avmService.getContentWriter(avmPath);
|
|
if (this.avmService.hasAspect(-1, avmPath, WCMAppModel.ASPECT_FORM_INSTANCE_DATA))
|
|
{
|
|
this.editorOutput = XMLUtil.toString(this.instanceDataDocument);
|
|
}
|
|
writer.putContent(this.editorOutput);
|
|
|
|
// commit the transaction
|
|
tx.commit();
|
|
|
|
// regenerate form content
|
|
if (this.avmService.hasAspect(-1, avmPath, WCMAppModel.ASPECT_FORM_INSTANCE_DATA))
|
|
{
|
|
final FormInstanceData fid = new FormInstanceDataImpl(AVMNodeConverter.ToNodeRef(-1, avmPath))
|
|
{
|
|
@Override
|
|
public Form getForm() { return AVMEditBean.this.getForm(); }
|
|
};
|
|
LOGGER.debug("regenerating renditions of " + fid);
|
|
for (Rendition rendition : fid.getRenditions())
|
|
{
|
|
try
|
|
{
|
|
rendition.regenerate(fid);
|
|
}
|
|
catch (Throwable t)
|
|
{
|
|
LOGGER.error("error regenerating " + rendition.getName() +
|
|
": " + t.getMessage(), t);
|
|
Utils.addErrorMessage("error regenerating " + rendition.getName() +
|
|
": " + t.getMessage(), t);
|
|
}
|
|
}
|
|
final NodeRef[] uploadedFiles = this.formProcessorSession.getUploadedFiles();
|
|
LOGGER.debug("updating " + uploadedFiles.length + " uploaded files");
|
|
final List<AVMDifference> diffList = new ArrayList<AVMDifference>(uploadedFiles.length);
|
|
for (NodeRef uploadedFile : uploadedFiles)
|
|
{
|
|
final String path = AVMNodeConverter.ToAVMVersionPath(uploadedFile).getSecond();
|
|
diffList.add(new AVMDifference(-1, path,
|
|
-1, AVMConstants.getCorrespondingPathInMainStore(path),
|
|
AVMDifference.NEWER));
|
|
}
|
|
this.avmSyncService.update(diffList, null, true, true, true, true, null, null);
|
|
}
|
|
|
|
AVMConstants.updateVServerWebapp(avmNode.getPath(), false);
|
|
|
|
resetState();
|
|
|
|
return AlfrescoNavigationHandler.CLOSE_DIALOG_OUTCOME;
|
|
}
|
|
catch (Throwable err)
|
|
{
|
|
// rollback the transaction
|
|
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
|
|
Utils.addErrorMessage(Application.getMessage(
|
|
FacesContext.getCurrentInstance(), CheckinCheckoutBean.MSG_ERROR_UPDATE) + err.getMessage());
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Action called upon completion of the Update File page
|
|
*/
|
|
public String updateFileOK()
|
|
{
|
|
String outcome = null;
|
|
|
|
UserTransaction tx = null;
|
|
|
|
AVMNode node = getAvmNode();
|
|
if (node != null && this.getFileName() != null)
|
|
{
|
|
try
|
|
{
|
|
FacesContext context = FacesContext.getCurrentInstance();
|
|
tx = Repository.getUserTransaction(context);
|
|
tx.begin();
|
|
|
|
// get an updating writer that we can use to modify the content on the current node
|
|
ContentWriter writer = this.contentService.getWriter(node.getNodeRef(), ContentModel.PROP_CONTENT, true);
|
|
|
|
// also update the mime type in case a different type of file is uploaded
|
|
String mimeType = Repository.getMimeTypeForFileName(context, this.fileName);
|
|
writer.setMimetype(mimeType);
|
|
|
|
writer.putContent(this.file);
|
|
|
|
// commit the transaction
|
|
tx.commit();
|
|
|
|
// clear action context
|
|
resetState();
|
|
|
|
outcome = AlfrescoNavigationHandler.CLOSE_DIALOG_OUTCOME;
|
|
}
|
|
catch (Throwable err)
|
|
{
|
|
// rollback the transaction
|
|
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
|
|
Utils.addErrorMessage(Application.getMessage(
|
|
FacesContext.getCurrentInstance(), MSG_ERROR_UPDATE) + err.getMessage(), err);
|
|
}
|
|
}
|
|
|
|
return outcome;
|
|
}
|
|
|
|
/**
|
|
* Deals with the cancel button being pressed on the upload file page
|
|
*/
|
|
public String cancel()
|
|
{
|
|
// reset the state
|
|
resetState();
|
|
|
|
return AlfrescoNavigationHandler.CLOSE_DIALOG_OUTCOME;
|
|
}
|
|
|
|
private void resetState()
|
|
{
|
|
// clean up and clear action context
|
|
clearUpload();
|
|
setDocumentContent(null);
|
|
setEditorOutput(null);
|
|
this.instanceDataDocument = null;
|
|
this.formProcessorSession = null;
|
|
this.form = null;
|
|
}
|
|
|
|
/**
|
|
* Clear form state and upload file bean
|
|
*/
|
|
private void clearUpload()
|
|
{
|
|
// delete the temporary file we uploaded earlier
|
|
if (this.file != null)
|
|
{
|
|
this.file.delete();
|
|
}
|
|
|
|
this.file = null;
|
|
this.fileName = null;
|
|
|
|
// remove the file upload bean from the session
|
|
FacesContext ctx = FacesContext.getCurrentInstance();
|
|
ctx.getExternalContext().getSessionMap().remove(FileUploadBean.FILE_UPLOAD_BEAN_NAME);
|
|
}
|
|
}
|