/* * Copyright (C) 2001, 2002 Red Hat Inc. All Rights Reserved. * * The contents of this file are subject to the CCM Public * License (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of * the License at http://www.redhat.com/licenses/ccmpl.html * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * */ package com.arsdigita.cms.docmgr.ui; import com.arsdigita.bebop.ColumnPanel; import com.arsdigita.bebop.Component; import com.arsdigita.bebop.Form; import com.arsdigita.bebop.FormData; import com.arsdigita.bebop.FormProcessException; import com.arsdigita.bebop.Label; import com.arsdigita.bebop.PageState; import com.arsdigita.bebop.SimpleContainer; import com.arsdigita.bebop.Tree; import com.arsdigita.bebop.event.FormInitListener; import com.arsdigita.bebop.event.FormProcessListener; import com.arsdigita.bebop.event.FormSectionEvent; import com.arsdigita.bebop.event.FormValidationListener; import com.arsdigita.bebop.event.PrintEvent; import com.arsdigita.bebop.event.PrintListener; import com.arsdigita.bebop.form.FileUpload; import com.arsdigita.bebop.form.Option; import com.arsdigita.bebop.form.SingleSelect; import com.arsdigita.bebop.form.MultipleSelect; import com.arsdigita.bebop.form.Submit; import com.arsdigita.bebop.form.TextArea; import com.arsdigita.bebop.form.TextField; import com.arsdigita.bebop.parameters.ArrayParameter; import com.arsdigita.bebop.parameters.BigDecimalParameter; import com.arsdigita.bebop.parameters.StringParameter; import com.arsdigita.bebop.parameters.TrimmedStringParameter; import com.arsdigita.categorization.Category; import com.arsdigita.categorization.CategoryCollection; import com.arsdigita.cms.ContentBundle; import com.arsdigita.cms.ContentSection; import com.arsdigita.cms.ContentSectionCollection; import com.arsdigita.cms.FileAsset; import com.arsdigita.cms.docmgr.Initializer; import com.arsdigita.cms.docmgr.DocFolder; import com.arsdigita.cms.docmgr.DocMgr; import com.arsdigita.cms.docmgr.Document; import com.arsdigita.dispatcher.ObjectNotFoundException; import com.arsdigita.dispatcher.MultipartHttpServletRequest; import com.arsdigita.domain.DataObjectNotFoundException; import com.arsdigita.kernel.Group; import com.arsdigita.kernel.Kernel; import com.arsdigita.kernel.KernelExcursion; import com.arsdigita.kernel.User; import com.arsdigita.kernel.permissions.ObjectPermissionCollection; import com.arsdigita.kernel.permissions.PermissionDescriptor; import com.arsdigita.kernel.permissions.PermissionManager; import com.arsdigita.kernel.permissions.PermissionService; import com.arsdigita.kernel.permissions.PrivilegeDescriptor; import com.arsdigita.mimetypes.MimeType; import com.arsdigita.persistence.OID; import com.arsdigita.util.Assert; import com.arsdigita.util.UncheckedWrapperException; import com.arsdigita.versioning.Versions; import com.arsdigita.web.Web; import javax.servlet.http.HttpServletRequest; import java.math.BigDecimal; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Iterator; import java.util.TooManyListenersException; /** * This component allows to change the file name and the description of a file. It also serves to * associate keywords to a file (knowledge object). * * @author Stefan Deusch * @author Crag Wolfe */ class FileEditForm extends Form implements FormValidationListener, FormProcessListener, FormInitListener, DMConstants { private final static org.apache.log4j.Logger s_log = org.apache.log4j.Logger.getLogger( FileEditForm.class); private final static String FILE_EDIT = "file-edit"; private final static String FILE_EDIT_CATS = "file-edit-cats"; private final static String FILE_EDIT_FNAME = "file-edit-name"; private final static String FILE_EDIT_AUDIENCE = "file-edit-audience"; private final static String FILE_EDIT_AUTHOR = "file-edit-author"; private final static String FILE_EDIT_DESCRIPTION = "file-edit-description"; private final static String FILE_EDIT_TITLE = "file-edit-title"; private StringParameter m_FileAudience; private StringParameter m_FileName; private TrimmedStringParameter m_FileAuthor; private StringParameter m_FileDesc; private ArrayParameter m_FileCats; private StringParameter m_FileTitle; // m_parent is FileInfoPropertiesPane in the edit case, // and BrowsePane in the create case private Component m_parent; boolean m_creation = false; // only used for creation form private FileUpload m_fileUpload; private Tree m_tree; private final static String FILE_UPLOAD = "file-upload"; private final static String FILE_UPLOAD_FORM = "file-upload-form"; //private final static String FILE_UPLOAD_INPUT_DESCRIPTION = "file-description"; private Submit m_submit; public FileEditForm(Component parent) { this(parent, false, null); } /** * Constructor */ public FileEditForm(Component parent, boolean creation, Tree tree) { super(FILE_EDIT, new ColumnPanel(2)); m_parent = parent; m_creation = creation; m_tree = tree; m_FileAudience = new StringParameter(FILE_EDIT_AUDIENCE); m_FileAuthor = new TrimmedStringParameter(FILE_EDIT_AUTHOR); m_FileDesc = new StringParameter(FILE_EDIT_DESCRIPTION); m_FileCats = new ArrayParameter(FILE_EDIT_CATS); if (m_creation) { setMethod(Form.POST); setEncType("multipart/form-data"); add(new Label(FILE_UPLOAD_ADD_FILE)); m_fileUpload = new FileUpload(FILE_UPLOAD); add(m_fileUpload); } else { add(new Label(FILE_NAME_REQUIRED)); m_FileName = new StringParameter(FILE_EDIT_FNAME); TextField fnameEntry = new TextField(m_FileName); add(fnameEntry); } add(new Label("Title"));//TODO m_FileTitle = new StringParameter(FILE_EDIT_TITLE); TextField fTitleEntry = new TextField(m_FileTitle); add(fTitleEntry); add(new Label(FILE_INTENDED_AUDIENCE)); SingleSelect audienceEntry = new SingleSelect(m_FileAudience); try { audienceEntry.addPrintListener(new IntendedAudienceSelectPrinter()); } catch (TooManyListenersException tmlex) { s_log.error(tmlex.getMessage()); tmlex.printStackTrace(); } add(audienceEntry); add(new Label(new AuthorLabelPrinter())); TextField authorEntry = new TextField(m_FileAuthor); add(authorEntry); add(new Label(FILE_CATEGORIES)); MultipleSelect catSelect = new MultipleSelect(FILE_EDIT_CATS); catSelect.setSize(20); try { BigDecimalParameter fileIDParam = null; if (!m_creation) { fileIDParam = getFileIDParam(); } catSelect.addPrintListener(new CategoriesPrintListener(getContentSection(), fileIDParam)); } catch (java.util.TooManyListenersException tmex) { throw new UncheckedWrapperException(tmex.getMessage()); } add(catSelect); add(new Label(FILE_DESCRIPTION)); TextArea descArea = new TextArea(m_FileDesc); descArea.setRows(10); descArea.setCols(40); add(descArea); m_submit = new Submit("file-edit-save"); m_submit.setButtonLabel(FILE_SAVE); add(new Label()); // spacer SimpleContainer sc = new SimpleContainer(); sc.add(m_submit); sc.add(new Submit(CANCEL)); add(sc); addInitListener(this); addProcessListener(this); addValidationListener(this); } /** * Initializer to pre-fill name and description */ public void init(FormSectionEvent e) throws FormProcessException { if (m_creation) { initCreate(e); } else { initEdit(e); } } public void initEdit(FormSectionEvent e) throws FormProcessException { PageState state = e.getPageState(); FormData data = e.getFormData(); BigDecimal id = getSelectedDocID(state); Document doc = DMUtils.getFile(id); ArrayList assignedCats = new ArrayList(); // Iterator i = doc.getCategories(); CategoryCollection cats = doc.getCategoryCollection(); Category cat; if (cats.next()) { cat = cats.getCategory(); String catID = cat.getID().toString(); assignedCats.add(catID); s_log.debug("init: " + catID); } data.put(FILE_EDIT_FNAME, URLDecoder.decode(doc.getName())); data.put(FILE_EDIT_TITLE, doc.getTitle()); data.put(FILE_EDIT_AUTHOR, doc.getAuthor()); data.put(FILE_EDIT_DESCRIPTION, doc.getDescription()); data.put(FILE_EDIT_CATS, assignedCats.toArray()); initAudienceFormData(data, doc); } private void initCreate(FormSectionEvent e) throws FormProcessException { if (m_parent instanceof BrowsePane) { PageState state = e.getPageState(); FormData data = e.getFormData(); HttpServletRequest request = state.getRequest(); ArrayParameter catIDParam = new ArrayParameter("catID"); String[] catIDs = (String[]) catIDParam.transformValue(request); if (catIDs != null) { data.put(FILE_EDIT_CATS, catIDs); } } } /** * read form and update */ public void process(FormSectionEvent e) throws FormProcessException { Document doc = null; if (m_submit.isSelected(e.getPageState())) { if (m_creation) { doc = processCreate(e); } else { doc = processEdit(e); } } backCancel(e.getPageState(), (doc != null ? doc.getID() : null)); } private Document processEdit(FormSectionEvent e) throws FormProcessException { PageState state = e.getPageState(); FormData data = e.getFormData(); Document doc = DMUtils.getFile(getSelectedDocID(state)); setDocumentAttributes(data, doc); doc.setCategories((String[]) data.get(FILE_EDIT_CATS)); doc.setLastModifiedLocal(new java.util.Date()); doc.save(); // creates a new revision setDocumentPermission(data, doc); return doc; } private Document processCreate(FormSectionEvent e) throws FormProcessException { PageState state = e.getPageState(); final FormData data = e.getFormData(); final HttpServletRequest req = state.getRequest(); final String fname = getUploadFileName(e); String titleTmp = (String) data.get(FILE_EDIT_TITLE); if (titleTmp == null || titleTmp.trim().length() == 0) { titleTmp = fname; MimeType mime = MimeType.guessMimeTypeFromFile(fname); int index; if (mime != null && (index = fname.lastIndexOf('.')) > -1) { titleTmp = fname.substring(0, index); } else { titleTmp = fname; } } final String title = titleTmp; String fpath = (String) data.get(FILE_UPLOAD); java.io.File src = null; if (fpath != null && fpath.length() > 0) { HttpServletRequest mreq = e.getPageState().getRequest(); Assert.isTrue(mreq instanceof MultipartHttpServletRequest, "I got a " + mreq + " when I was " + "expecting a MultipartHttpServletRequest"); src = ((MultipartHttpServletRequest) mreq).getFile(FILE_UPLOAD); } if (s_log.isDebugEnabled()) { s_log.debug("file == '" + src + "'"); } DocFolder p = null; String selKey = (String) m_tree.getSelectedKey(state); if (selKey == null) { p = DMUtils.getRootFolder(state); } else { BigDecimal folderID = new BigDecimal(selKey); try { p = new DocFolder(folderID); } catch (DataObjectNotFoundException nf) { throw new ObjectNotFoundException((String) FOLDER_PARENTNOTFOUND_ERROR.localize(req)); } } final DocFolder parent = p; // insert the file in the data base below parent final Document f1 = new Document(); // FR: define the bundle here //final ContentBundle bundle = new ContentBundle(ContentBundle.BASE_DATA_OBJECT_TYPE); f1.setTitle(title); f1.setName(URLEncoder.encode(fname)); f1.setLanguage("en"); final ContentBundle bundle = new ContentBundle(f1); final FileAsset fa = new FileAsset(); new KernelExcursion() { protected void excurse() { // Create all the objects inside the kernel excursion // so persistence doesn't barf... setParty(Kernel.getSystemParty()); //f1.setTitle(title); //f1.setName(URLEncoder.encode(fname)); //f1.setLanguage("en"); fa.setName("temp"); f1.setFile(fa); bundle.setDefaultLanguage(f1.getLanguage()); //bundle.addInstance(f1); bundle.setName(fname); bundle.setParent(parent); bundle.setContentSection(parent.getContentSection()); PermissionService.setContext(bundle, parent); PermissionService.setContext(f1, bundle); PermissionService.setContext(fa, f1); bundle.save(); f1.save(); } }.run(); try { fa.loadFromFile(fname, src, "txt"); } catch (java.io.IOException ex) { ex.printStackTrace(); throw new FormProcessException(ex.getMessage()); } Versions.tag(f1.getOID(), (FILE_UPLOAD_INITIAL_TRANSACTION_DESCRIPTION .localize(req) .toString())); setDocumentAttributes(data, f1); // title must be set before name f1.setTitle(title); f1.setName(URLEncoder.encode(fname)); f1.setRepository(DocFolder.getRepository(p)); f1.setLastModifiedLocal(f1.getLastModifiedDate()); //f1.save(); // context has been set, now add additional permissions setDocumentPermission(data, f1); f1.setCategories((String[]) data.get(FILE_EDIT_CATS)); bundle.save(); return f1; } private void setDocumentAttributes(FormData data, Document doc) { if (!m_creation) { String ftitle = (String) data.get(FILE_EDIT_TITLE); String fname = (String) data.get(FILE_EDIT_FNAME); // title must be set before name doc.setTitle((ftitle != null && ftitle.length() > 0) ? ftitle : fname); doc.setName(URLEncoder.encode(fname)); } String fauthor = (String) data.get(FILE_EDIT_AUTHOR); String fdesc = (String) data.get(FILE_EDIT_DESCRIPTION); if (fauthor != null) { doc.setAuthor(fauthor); } doc.setDescription(fdesc); } protected String getUploadFileName(FormSectionEvent e) { FormData data = e.getFormData(); String filename = (String) data.get(FILE_UPLOAD); return DMUtils.extractFileName(filename, e.getPageState()); } /** * Test if the new name already exists in the current folder */ public void validate(FormSectionEvent event) throws FormProcessException { if (m_submit.isSelected(event.getPageState())) { FormData data = event.getFormData(); //validate length of author String author = (String) data.get(FILE_EDIT_AUTHOR); if (author != null && author.length() > 200) { data.addError(FILE_EDIT_AUTHOR, "This parameter is too long. It must be fewer than 200 characters."); } //validate length of description String desc = (String) data.get(FILE_EDIT_DESCRIPTION); if (desc != null && desc.length() > 4000) { data.addError(FILE_EDIT_DESCRIPTION, "This parameter is too long. It must be fewer than 4000 characters."); } if (m_creation) { validateCreate(event); } else { validateEdit(event); } } } private void validateCreate(FormSectionEvent e) throws FormProcessException { PageState state = e.getPageState(); FormData data = e.getFormData(); HttpServletRequest req = state.getRequest(); String fname = (String) data.get(FILE_UPLOAD); if (fname == null || fname.length() == 0) { data.addError(FILE_UPLOAD, "This parameter is required."); } fname = DMUtils.extractFileName(getUploadFileName(e), state); // XXX Not localized as the other errors are. if (fname.length() > 200) { data.addError(FILE_UPLOAD, "This filename is too long. It must be fewer than 200 characters."); } String title = (String) data.get(FILE_EDIT_TITLE); if (title != null && title.length() > 200) { data.addError(FILE_EDIT_TITLE, "This title is too long. It must be fewer than 200 characters."); } DocFolder parent = null; String selKey = (String) m_tree.getSelectedKey(state); if (selKey == null) { parent = DMUtils.getRootFolder(state); } else { BigDecimal folderID = new BigDecimal(selKey); try { parent = new DocFolder(folderID); } catch (DataObjectNotFoundException nf) { throw new ObjectNotFoundException(FOLDER_PARENTNOTFOUND_ERROR .localize(req).toString()); } } // Now we have the parent, make sure the user is allowed to create here parent.assertPrivilege(PrivilegeDescriptor.CREATE); try { parent.retrieveSubResource(fname); data.addError(FILE_UPLOAD, RESOURCE_EXISTS_ERROR .localize(req).toString()); } catch (DataObjectNotFoundException nf) { // ok here }// catch(InvalidNameException ex) { } private void validateEdit(FormSectionEvent event) throws FormProcessException { // PageState state = event.getPageState(); FormData data = event.getFormData(); String fname = (String) data.get(FILE_EDIT_FNAME); if (fname == null || fname.trim().length() == 0) { data.addError(FILE_EDIT_FNAME, "This parameter is required"); } String title = (String) data.get(FILE_EDIT_TITLE); if (title == null || title.length() == 0) { data.addError(FILE_EDIT_TITLE, "This parameter is required."); } else if (title.length() > 200) { data.addError(FILE_EDIT_TITLE, "This title is too long. It must be fewer than 200 characters."); } // HttpServletRequest req = state.getRequest(); // Document doc = DMUtils.getFile(getSelectedDocID(state)); // // Construct a name with the optional extension // String name = doc.getName(); // if (!doc.isValidNewName(name)) { // data.addError(FILE_EDIT_FNAME, // "Not a valid new name for this file"); // } // Verify that the new name does not correspond to an existing // resource (file or folder) // XXX we need this, but leaving it broken for now... --hbrock /* if (!name.equals(file.getName())) { try { Folder parent = (Folder) file.getParent(); parent.getResourceID(name); data.addError(FILE_EDIT_FNAME, (String)RESOURCE_EXISTS_ERROR.localize(req)); } catch(DataObjectNotFoundException nfe) { // good, so we can rename it } catch (InvalidNameException ex) { data.addError(FILE_EDIT_FNAME, ex.getMessage()); } } */ } /* Initialize form data for document's intended audience. */ private void initAudienceFormData(FormData data, Document doc) { ObjectPermissionCollection opc = PermissionService.getDirectGrantedPermissions(doc.getOID()); long numPermissions = opc.size(); if (numPermissions > 1) { s_log.error("there should only be 1 direct permission for " + "a document"); } if (numPermissions == 0) { data.put(FILE_EDIT_AUDIENCE, "workspace"); opc.close(); return; } boolean isPublic = false; while (opc.next()) { if (opc.getGranteeID().intValue() == PermissionManager.VIRTUAL_PUBLIC_ID) { isPublic = true; } } opc.close(); if (isPublic) { data.put(FILE_EDIT_AUDIENCE, "public"); } else { data.put(FILE_EDIT_AUDIENCE, "internal"); } } /* Grant/revoke direct permissions as needed. Note that workspace permissions is always implied. */ private void setDocumentPermission(FormData data, Document doc) throws FormProcessException { final String intendedAudience = (String) data.get(FILE_EDIT_AUDIENCE); if (intendedAudience == null) { throw new FormProcessException("Intended Audience cannot be null"); } final PermissionDescriptor publicDescriptor = new PermissionDescriptor(PrivilegeDescriptor.READ, doc.getOID(), new OID(User.BASE_DATA_OBJECT_TYPE, PermissionManager.VIRTUAL_PUBLIC_ID)); final PermissionDescriptor internalDescriptor = new PermissionDescriptor(PrivilegeDescriptor.READ, doc.getOID(), new OID(Group.BASE_DATA_OBJECT_TYPE, DocMgr.getConfig(). getInternalGroupID())); new KernelExcursion() { protected void excurse() { //Party currentParty = Kernel.getWebContext().getParty(); setParty(Kernel.getSystemParty()); if ("public".equals(intendedAudience)) { PermissionService.grantPermission(publicDescriptor); PermissionService.revokePermission(internalDescriptor); } else if ("internal".equals(intendedAudience)) { PermissionService.revokePermission(publicDescriptor); PermissionService.grantPermission(internalDescriptor); } else { // workspace only PermissionService.revokePermission(publicDescriptor); PermissionService.revokePermission(internalDescriptor); } } }.run(); } protected class AuthorLabelPrinter implements PrintListener { public AuthorLabelPrinter() { // Empty } public void prepare(PrintEvent e) { Label label = (Label) e.getTarget(); PageState pageState = e.getPageState(); String name = Web.getWebContext().getUser().getName(); label.setLabel("Author: (if not " + name + ")"); } } protected class IntendedAudienceSelectPrinter implements PrintListener { public IntendedAudienceSelectPrinter() { } public void prepare(PrintEvent e) { SingleSelect select = (SingleSelect) e.getTarget(); select.clearOptions(); select.addOption(new Option("workspace", Web.getWebContext().getApplication() .getParentApplication() == null ? Web.getWebContext(). getApplication().getDisplayName() : Web.getWebContext().getApplication(). getParentApplication().getDisplayName() + " members")); select.addOption(new Option("internal", new Label(FILE_INTENDED_AUDIENCE_INTERNAL))); select.addOption(new Option("public", new Label(FILE_INTENDED_AUDIENCE_PUBLIC))); } } private ContentSection getContentSection() { ContentSectionCollection csl = ContentSection.getAllSections(); csl.addEqualsFilter("label", DocMgr.getConfig().getContentSection()); if (!csl.next()) { csl.close(); return null; } ContentSection docsContentSection = csl.getContentSection(); csl.close(); return docsContentSection; } private BigDecimal getSelectedDocID(PageState state) { if (m_creation) { s_log.warn("doc create step shouldn't need selected id"); return null; } return (BigDecimal) state.getValue(getFileIDParam()); } private BigDecimalParameter getFileIDParam() { if (m_parent instanceof FileInfoPropertiesPane) { return ((FileInfoPropertiesPane) m_parent).getFileIDParam(); } else { if (m_parent instanceof BrowseFileInfoPropertiesPane) { ((BrowseFileInfoPropertiesPane) m_parent).getFileIDParam(); } } return null; } private void backCancel(PageState state, BigDecimal docID) { if (m_parent instanceof BrowsePane) { if (docID != null) { ((BrowsePane) m_parent).displayFilePropPanel(state, docID); } else { ((BrowsePane) m_parent).displayFolderContentPanel(state); } } else { if (m_parent instanceof FileInfoPropertiesPane) { ((FileInfoPropertiesPane) m_parent).displayPropertiesAndActions(state); } else { if (m_parent instanceof BrowseFileInfoPropertiesPane) { ((BrowseFileInfoPropertiesPane) m_parent).displayPropertiesPane(state); } } } } }