From a1011154a51870dd7a8c79141e3216337a513189 Mon Sep 17 00:00:00 2001 From: tosmers Date: Wed, 2 Dec 2015 17:01:25 +0000 Subject: [PATCH] modifies DestinationFolderForm and FileEditForm, adds DocRepoRequestLocal and FileInfoHistoryPane and FileInfoPropertiesPane git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@3759 8810af33-2d31-482b-a856-94f89814c4df --- .../main/java/org/libreccm/security/User.java | 6 +- .../docrepo/ui/DestinationFolderForm.java | 6 +- .../docrepo/ui/DocRepoRequestLocal.java | 57 +++++ .../arsdigita/docrepo/ui/FileEditForm.java | 55 +++-- .../docrepo/ui/FileInfoHistoryPane.java | 91 +++++++ .../docrepo/ui/FileInfoPropertiesPane.java | 222 ++++++++++++++++++ .../java/org/libreccm/docrepo/Repository.java | 17 +- .../docrepo/RepositoryRepository.java | 75 ++++++ .../java/org/libreccm/docrepo/Resource.java | 16 +- .../org/libreccm/docrepo/ResourceManager.java | 46 ++++ .../libreccm/docrepo/ResourceRepository.java | 15 +- .../Resources.properties | 1 + .../Resources_de.properties | 1 + .../Resources_en.properties | 1 + 14 files changed, 580 insertions(+), 29 deletions(-) create mode 100644 ccm-docrepo/src/main/java/com/arsdigita/docrepo/ui/DocRepoRequestLocal.java create mode 100644 ccm-docrepo/src/main/java/com/arsdigita/docrepo/ui/FileInfoHistoryPane.java create mode 100644 ccm-docrepo/src/main/java/com/arsdigita/docrepo/ui/FileInfoPropertiesPane.java create mode 100644 ccm-docrepo/src/main/java/org/libreccm/docrepo/RepositoryRepository.java diff --git a/ccm-core/src/main/java/org/libreccm/security/User.java b/ccm-core/src/main/java/org/libreccm/security/User.java index dc4901dac..56fe95984 100644 --- a/ccm-core/src/main/java/org/libreccm/security/User.java +++ b/ccm-core/src/main/java/org/libreccm/security/User.java @@ -59,9 +59,9 @@ import javax.xml.bind.annotation.XmlTransient; @NamedQuery(name = "User.findByName", query = "SELECT u FROM User u WHERE u.name = :name"), @NamedQuery(name = "User.findByEmailAddress", - query = "SELECT u FROM User u " - + "WHERE u.primaryEmailAddress.address = :emailAddress" - )}) + query = "SELECT u FROM User u WHERE " + + "u.primaryEmailAddress.address = :emailAddress") +}) @XmlRootElement(name = "user", namespace = CORE_XML_NS) //Supressing a few warnings from PMD because they misleading here. //User is perfectly fine class name, and the complexity is not to high... diff --git a/ccm-docrepo/src/main/java/com/arsdigita/docrepo/ui/DestinationFolderForm.java b/ccm-docrepo/src/main/java/com/arsdigita/docrepo/ui/DestinationFolderForm.java index 83055ce36..65567abf6 100644 --- a/ccm-docrepo/src/main/java/com/arsdigita/docrepo/ui/DestinationFolderForm.java +++ b/ccm-docrepo/src/main/java/com/arsdigita/docrepo/ui/DestinationFolderForm.java @@ -167,6 +167,7 @@ public class DestinationFolderForm extends Form implements FormInitListener, * Processes the destination folder form after it has been triggered. * * @param event The section event to be triggered + * * @throws FormProcessException */ public void process(FormSectionEvent event) throws FormProcessException { @@ -296,11 +297,10 @@ public class DestinationFolderForm extends Form implements FormInitListener, HashMap map = new HashMap(); map.put(new Long("-1"), treeElement); - // Todo: beibehalten? - Repository repository = (Repository) Web.getWebContext().getApplication(); + Repository repository = (Repository) Web. + getWebContext().getApplication(); Folder rootFolder = repository.getRootFolder(); - // Todo: Method getSubFolders? List resources = rootFolder.getImmediateChildren(); for (Resource resource : resources) { diff --git a/ccm-docrepo/src/main/java/com/arsdigita/docrepo/ui/DocRepoRequestLocal.java b/ccm-docrepo/src/main/java/com/arsdigita/docrepo/ui/DocRepoRequestLocal.java new file mode 100644 index 000000000..223d5c2eb --- /dev/null +++ b/ccm-docrepo/src/main/java/com/arsdigita/docrepo/ui/DocRepoRequestLocal.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2015 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package com.arsdigita.docrepo.ui; + +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.RequestLocal; +import org.apache.log4j.Logger; +import org.libreccm.cdi.utils.CdiLookupException; +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.docrepo.File; +import org.libreccm.docrepo.Resource; +import org.libreccm.docrepo.ResourceRepository; + +/** + * Same as the {@link RequestLocal} but overrides the + * {@code initialValue} uniquely for {@code DocRepo} classes. + * + * @author Tobias Osmers + * @version 02/12/2015 + */ +public class DocRepoRequestLocal extends RequestLocal + implements Constants { + + Logger log = Logger.getLogger(DocRepoRequestLocal.class); + + @Override + protected Object initialValue(PageState state) { + Long id = (Long) state.getValue(FILE_ID_PARAM); + File file = null; + final CdiUtil cdiUtil = new CdiUtil(); + final ResourceRepository resourceRepository; + try { + resourceRepository = cdiUtil.findBean(ResourceRepository.class); + Resource resource = resourceRepository.findById(id); + file = resource.isFile() ? (File) resource : null; + } catch(CdiLookupException ex) { + log.error("Failed to find bean for the ResourceRepository.", ex); + } + return file; + } +} diff --git a/ccm-docrepo/src/main/java/com/arsdigita/docrepo/ui/FileEditForm.java b/ccm-docrepo/src/main/java/com/arsdigita/docrepo/ui/FileEditForm.java index bd646d182..8160a3b14 100644 --- a/ccm-docrepo/src/main/java/com/arsdigita/docrepo/ui/FileEditForm.java +++ b/ccm-docrepo/src/main/java/com/arsdigita/docrepo/ui/FileEditForm.java @@ -21,6 +21,7 @@ package com.arsdigita.docrepo.ui; import com.arsdigita.bebop.ColumnPanel; 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; @@ -33,14 +34,15 @@ import com.arsdigita.bebop.form.TextArea; import com.arsdigita.bebop.form.TextField; import com.arsdigita.bebop.parameters.NotEmptyValidationListener; import com.arsdigita.bebop.parameters.StringParameter; +import com.arsdigita.docrepo.util.GlobalizationUtil; import org.apache.log4j.Logger; import org.libreccm.cdi.utils.CdiLookupException; import org.libreccm.cdi.utils.CdiUtil; import org.libreccm.docrepo.File; +import org.libreccm.docrepo.Resource; +import org.libreccm.docrepo.ResourceManager; import org.libreccm.docrepo.ResourceRepository; -import javax.servlet.http.HttpServletRequest; - //import com.arsdigita.docrepo.File; //import com.arsdigita.docrepo.Folder; @@ -133,6 +135,42 @@ public class FileEditForm extends Form implements FormValidationListener, } } + /** + * Tests if the new name already exists in the current folder when event + * has been triggered. Is been called before processed. + * + * @param event The event + * + * @throws FormProcessException + */ + public void validate(FormSectionEvent event) throws FormProcessException { + PageState state = event.getPageState(); + FormData data = event.getFormData(); + + Long resourceId = (Long) state.getValue(FILE_ID_PARAM); + File file = null; + ResourceManager resourceManager = null; + final CdiUtil cdiUtil = new CdiUtil(); + final ResourceRepository resourceRepository; + try { + resourceRepository = cdiUtil.findBean(ResourceRepository.class); + Resource resource = resourceRepository.findById(resourceId); + file = resource.isFile() ? (File) resource : null; + resourceManager = cdiUtil.findBean(ResourceManager.class); + } catch (CdiLookupException ex) { + log.error("Failed to find bean for the ResourceManager.", ex); + } + + if (resourceManager != null && file != null) { + String fname = (String) data.get(FILE_EDIT_FNAME); + + if (!resourceManager.isValidNewResourceName(fname, file)) { + throw new FormProcessException(GlobalizationUtil.globalize( + "ui.file.name.invalid")); + } + } + } + /** * Read form and update when event has been triggered.+ * @@ -140,7 +178,6 @@ public class FileEditForm extends Form implements FormValidationListener, */ public void process(FormSectionEvent event) { PageState state = event.getPageState(); - HttpServletRequest req = state.getRequest(); FormData data = event.getFormData(); String fname = (String) data.get(FILE_EDIT_FNAME); @@ -155,8 +192,6 @@ public class FileEditForm extends Form implements FormValidationListener, if (file != null) { file.setName(fname); file.setDescription(fdesc); - // Todo: How to change? - //file.applyTag(FILE_EDIT_ACTION_DESCRIPTION.localize(req).toString()); resourceRepository.save(file); } else { log. error(String.format("Couldn't find file %d in the " + @@ -167,14 +202,4 @@ public class FileEditForm extends Form implements FormValidationListener, } m_parent.displayPropertiesAndActions(state); } - - /** - * Tests if the new name already exists in the current folder when event - * has been triggered. - * - * @param event The event - */ - public void validate(FormSectionEvent event) { - // Todo: redundant i think - } } \ No newline at end of file diff --git a/ccm-docrepo/src/main/java/com/arsdigita/docrepo/ui/FileInfoHistoryPane.java b/ccm-docrepo/src/main/java/com/arsdigita/docrepo/ui/FileInfoHistoryPane.java new file mode 100644 index 000000000..04563df4e --- /dev/null +++ b/ccm-docrepo/src/main/java/com/arsdigita/docrepo/ui/FileInfoHistoryPane.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.docrepo.ui; + +import com.arsdigita.bebop.Component; +import com.arsdigita.bebop.Page; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.RequestLocal; +import com.arsdigita.bebop.SegmentedPanel; +import com.arsdigita.bebop.SimpleContainer; +import com.arsdigita.docrepo.File; +import org.apache.log4j.Logger; + +/** + * This component shows the version history of a document. It allows + * to download historical versions. + * + * @author Stefan Deusch + * @author Tobias Osmers + */ +public class FileInfoHistoryPane extends SimpleContainer implements Constants { + + private static Logger log = Logger.getLogger(FileInfoHistoryPane.class); + + private Component m_history; + // share file instance for all sub components + private RequestLocal requestLocal; + + /** + * Constructor. Constructs the info-history pane for a file. + */ + public FileInfoHistoryPane() { + + requestLocal = new DocRepoRequestLocal(); + + SegmentedPanel main = new SegmentedPanel(); + main.setClassAttr("main"); + + m_history = makeHistoryPane(main); + + add(main); + } + + /** + * Creates a new history pane. + * + * @param panel The segment panel. + * @return The new segment panel with a created history pane. + */ + private Component makeHistoryPane(SegmentedPanel panel) { + return panel.addSegment(FILE_REVISION_HISTORY_HEADER, + new FileRevisionsTable(this)); + } + + /** + * Registers the given page. + * + * @param page The page. + */ + @Override + public void register(Page page) { + page.addGlobalStateParam(FILE_ID_PARAM); + super.register(page); + } + + /** + * Returns a file. + * + * @param state The page state + * @return A file + */ + public File getFile(PageState state) { + return (File) requestLocal.get(state); + } +} diff --git a/ccm-docrepo/src/main/java/com/arsdigita/docrepo/ui/FileInfoPropertiesPane.java b/ccm-docrepo/src/main/java/com/arsdigita/docrepo/ui/FileInfoPropertiesPane.java new file mode 100644 index 000000000..848b20708 --- /dev/null +++ b/ccm-docrepo/src/main/java/com/arsdigita/docrepo/ui/FileInfoPropertiesPane.java @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.docrepo.ui; + +import com.arsdigita.bebop.ActionLink; +import com.arsdigita.bebop.Component; +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.Page; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.SegmentedPanel; +import com.arsdigita.bebop.SimpleContainer; +import com.arsdigita.bebop.event.ActionEvent; +import com.arsdigita.bebop.event.ActionListener; + +import java.util.ArrayList; + +/** + * This component shows all the properties of a file with links + * to administrative actions to change those. + * + * @author Stefan Deusch + */ +public class FileInfoPropertiesPane extends SimpleContainer implements + Constants { + + private ArrayList m_componentList; + + private Component m_properties; + private Component m_upload; + private Component m_sendColleague; + private Component m_edit; + private Component m_action; + + private BasePage m_page; + + /** + * Constructor. Creates different panes and adds them to a list of + * components. + * + * @param page The BasePage + */ + public FileInfoPropertiesPane(BasePage page) { + m_page = page; + + SegmentedPanel main = new SegmentedPanel(); + main.setClassAttr("main"); + + m_componentList = new ArrayList<>(); + + m_properties = makePropertiesPane(main); + m_componentList.add(m_properties); + + m_edit = makeEditPane(main); + m_componentList.add(m_edit); + + m_action = makeActionPane(main); + m_componentList.add(m_action); + + m_upload = makeUploadPane(main); + m_componentList.add(m_upload); + + m_sendColleague = makeSendColleaguePane(main); + m_componentList.add(m_sendColleague); + + add(main); + } + + /** + * Makes the properties pane + * + * @param main The main segment panel + * + * @return A component with the properties pane + */ + private Component makePropertiesPane(SegmentedPanel main) { + SimpleContainer container= new SimpleContainer(); + + container.add(new FilePropertiesPanel()); + ActionLink link = new ActionLink(new Label(FILE_EDIT_LINK)); + link.setClassAttr("actionLink"); + link.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + PageState state = e.getPageState(); + displayEditForm(state); + } + }); + container.add(link); + return main.addSegment(FILE_PROPERTIES_HEADER, container); + } + + /** + * Makes the edit pane + * + * @param main The main segment panel + * + * @return A component with the edit pane + */ + private Component makeEditPane(SegmentedPanel main) { + return main.addSegment(FILE_EDIT_HEADER, + new FileEditForm(this)); + } + + /** + * Makes the action pane + * + * @param main The main segment panel + * + * @return A component with the action pane + */ + private Component makeActionPane(SegmentedPanel main) { + return main.addSegment(FILE_ACTION_HEADER, + new FileActionPane(this)); + } + + /** + * Makes the upload pane + * + * @param main The main segment panel + * + * @return A component with the upload pane + */ + private Component makeUploadPane(SegmentedPanel main) { + return main.addSegment(FILE_UPLOAD_HEADER, + new VersionUploadForm(this)); + } + + /** + * Makes the send to colleagues pane + * + * @param main The main segment panel + * + * @return A component with the send to colleague pane + */ + private Component makeSendColleaguePane(SegmentedPanel main) { + return main.addSegment(FILE_SEND_COLLEAGUE_HEADER, + new FileSendColleagueForm(this)); + } + + /** + * Registers the page with the properties of a file + * + * @param p The page + */ + public void register(Page p) { + for (Object aM_componentList : m_componentList) { + p.setVisibleDefault((Component) aM_componentList, false); + } + p.setVisibleDefault( m_properties, true); + p.setVisibleDefault( m_action, true); + + p.addGlobalStateParam(FILE_ID_PARAM); + + super.register(p); + } + + + /** + * Visibility of components management methods + * + * @param state The page state + */ + private void hideAll(PageState state) { + for (Object aM_componentList : m_componentList) { + ((Component) aM_componentList).setVisible(state, false); + } + } + + /** + * Displays the properties and actions of a file + * + * @param state The page state + */ + public void displayPropertiesAndActions(PageState state) { + m_page.goUnmodal(state); + hideAll(state); + m_properties.setVisible(state, true); + m_action.setVisible(state, true); + } + + /** + * Displays the edit form for the file + * + * @param state The page state + */ + public void displayEditForm(PageState state) { + m_page.goModal(state, m_edit); + } + + /** + * Displays the upload form for a file + * + * @param state The page state + */ + public void displayUploadForm(PageState state) { + m_page.goModal(state, m_upload); + } + + /** + * Displays the send to colleague form + * + * @param state The page state + */ + public void displaySendColleagueForm(PageState state) { + m_page.goModal(state, m_sendColleague); + } +} diff --git a/ccm-docrepo/src/main/java/org/libreccm/docrepo/Repository.java b/ccm-docrepo/src/main/java/org/libreccm/docrepo/Repository.java index 917065a7e..adbfda96d 100644 --- a/ccm-docrepo/src/main/java/org/libreccm/docrepo/Repository.java +++ b/ccm-docrepo/src/main/java/org/libreccm/docrepo/Repository.java @@ -18,13 +18,15 @@ */ package org.libreccm.docrepo; +import org.hibernate.validator.constraints.NotBlank; import org.libreccm.security.User; import org.libreccm.web.CcmApplication; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; import javax.persistence.OneToOne; import javax.persistence.Table; @@ -37,6 +39,10 @@ import javax.persistence.Table; */ @Entity @Table(schema = "CCM_DOCREPO", name = "REPOSITORIES") +@NamedQueries({ + @NamedQuery(name = "DocRepo.findRepositoriesForOwner", + query = "SELECT r FROM Repository r WHERE r.owner = :owner") +}) public class Repository extends CcmApplication { private static final long serialVersionUID = 6673243021462798036L; @@ -44,21 +50,24 @@ public class Repository extends CcmApplication { /** * Name des {@code Repository}s. */ - @Column(name = "NAME") + @Column(name = "NAME", length = 512, unique = true, nullable = false) + @NotBlank private String name; /** * The root of the {@code Repository}. */ @OneToOne - @JoinColumn(name = "ROOT_FOLDER") + @JoinColumn(name = "ROOT_FOLDER_ID", unique = true, nullable = false) + @NotBlank private Folder rootFolder; /** * The owner of the {@code Repository}. */ @OneToOne - @JoinColumn(name = "OWNER_ID") + @JoinColumn(name = "OWNER_ID", nullable = false) + @NotBlank private User owner; /** diff --git a/ccm-docrepo/src/main/java/org/libreccm/docrepo/RepositoryRepository.java b/ccm-docrepo/src/main/java/org/libreccm/docrepo/RepositoryRepository.java new file mode 100644 index 000000000..65fed8e88 --- /dev/null +++ b/ccm-docrepo/src/main/java/org/libreccm/docrepo/RepositoryRepository.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2015 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.libreccm.docrepo; + +import org.libreccm.auditing.AbstractAuditedEntityRepository; +import org.libreccm.security.User; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import javax.persistence.EntityManager; +import javax.persistence.TypedQuery; +import java.util.List; + +/** + * Repository class for retrieving, storing and deleting {@code Repository}s. + * + * @author Tobias Osmers + * @version 25/11/2015 + */ +@RequestScoped +public class RepositoryRepository extends + AbstractAuditedEntityRepository { + + @Inject + private EntityManager entityManager; + + @Override + public Long getEntityId(Repository entity) { + return entity.getObjectId(); + } + + @Override + public Class getEntityClass() { + return Repository.class; + } + + @Override + public boolean isNew(Repository entity) { + if (entity == null) { + throw new IllegalArgumentException("Entity to save can't be null."); + } + return entity.getObjectId() == 0; + } + + /** + * Retrieve all {@link Repository}s a given {@link User} ownes. + * + * @param owner The owner of the {@link Repository}s + * + * @return The {@link Repository}s owned by the given {@link User} + */ + public List findForOwner(User owner) { + final TypedQuery query = entityManager.createNamedQuery( + "DocRepo.findRepositoriesForOwner", Repository.class); + query.setParameter("owner", owner); + + return query.getResultList(); + } +} diff --git a/ccm-docrepo/src/main/java/org/libreccm/docrepo/Resource.java b/ccm-docrepo/src/main/java/org/libreccm/docrepo/Resource.java index 1ea8713a6..45d8eada7 100644 --- a/ccm-docrepo/src/main/java/org/libreccm/docrepo/Resource.java +++ b/ccm-docrepo/src/main/java/org/libreccm/docrepo/Resource.java @@ -52,11 +52,17 @@ import java.util.List; @Table(schema = "CCM_DOCREPO", name = "RESOURCES") @NamedQueries({ @NamedQuery(name = "DocRepo.findResourceByPath", - query = "SELECT r FROM DocRepoResource r WHERE r.path = :pathName"), + query = "SELECT r FROM DocRepoResource r WHERE " + + "r.path = :pathName"), + @NamedQuery(name = "DocRepo.findResourcesByName", + query = "SELECT r FROM DocRepoResource r WHERE " + + "r.name = :name"), @NamedQuery(name = "DocRepo.findCreatedResourcesFromUser", - query = "SELECT r FROM DocRepoResource r WHERE r.creationUser = :user"), + query = "SELECT r FROM DocRepoResource r WHERE " + + "r.creationUser = :user"), @NamedQuery(name = "DocRepo.findModifiedResourcesFromUser", - query = "SELECT r FROM DocRepoResource r WHERE r.lastModifiedUser = :user")}) + query = "SELECT r FROM DocRepoResource r WHERE " + + "r.lastModifiedUser = :user")}) public abstract class Resource extends CcmObject { private static final long serialVersionUID = -910317798106611214L; @@ -296,4 +302,8 @@ public abstract class Resource extends CcmObject { public boolean isRoot() { return isFolder() && getParent() == null; } + + public boolean isFile() { + return !isFolder(); + } } diff --git a/ccm-docrepo/src/main/java/org/libreccm/docrepo/ResourceManager.java b/ccm-docrepo/src/main/java/org/libreccm/docrepo/ResourceManager.java index b59fddf3b..7a72cfd35 100644 --- a/ccm-docrepo/src/main/java/org/libreccm/docrepo/ResourceManager.java +++ b/ccm-docrepo/src/main/java/org/libreccm/docrepo/ResourceManager.java @@ -19,6 +19,7 @@ package org.libreccm.docrepo; import org.apache.log4j.Logger; +import org.apache.oro.text.perl.Perl5Util; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; @@ -36,6 +37,12 @@ public class ResourceManager { @Inject private ResourceRepository resourceRepository; + /** + * Copies a given {@link Resource} to a given {@link Folder}. + * + * @param original The {@link Resource} to be copied + * @param folder The {@link Folder} to copy to + */ public void copyToFolder(Resource original, Folder folder) { Resource copy = original.isFolder() ? new Folder() : new File(); copy.setName(original.getName()); @@ -52,6 +59,45 @@ public class ResourceManager { resourceRepository.save(copy); } + /** + * Determines weather the given name is a valid new name for the also + * given {@link Resource}. + * + * Verifies that the string only contains valid characters for + * {@link Resource} names. The following name patterns are allowed: + * + * [a-z][A-Z][0-9][-., ] + * + * In addition, names cannot begin with ".", i.e. we do NOT support file + * names like ".profile" at the moment. The current implementation does + * not allow international characters for resource names. + * + * @param name The resource name for validation + * @param resource The resource for which the new name needs to be validated + * + * @return true for a system-valid resource name, otherwise false + */ + public boolean isValidNewResourceName(String name, Resource resource) { + Perl5Util perl5Util = new Perl5Util(); + final String INVALID_START_PATTERN = "/^[.]+/"; + final String INVALID_NAME_PATTERN = "/[^a-zA-Z0-9\\_\\.\\-\\ ]+/"; + final String EXTENSION_PATTERN = "/^([^\\.].*)(\\.\\w+)$/"; + // adds an extension if non-existent + if (!perl5Util.match(EXTENSION_PATTERN, name)) { + if (perl5Util.match(EXTENSION_PATTERN, resource.getName())) { + name += perl5Util.group(2); + } + } + + // checks pattern of the name + boolean validName = !(perl5Util.match(INVALID_START_PATTERN, name) || + perl5Util.match(INVALID_NAME_PATTERN, name)); + + // checks duplication of the name, database access (mind performance) + validName &= resourceRepository.findByName(name).isEmpty(); + + return validName; + } } diff --git a/ccm-docrepo/src/main/java/org/libreccm/docrepo/ResourceRepository.java b/ccm-docrepo/src/main/java/org/libreccm/docrepo/ResourceRepository.java index b1f1e9d87..520632292 100644 --- a/ccm-docrepo/src/main/java/org/libreccm/docrepo/ResourceRepository.java +++ b/ccm-docrepo/src/main/java/org/libreccm/docrepo/ResourceRepository.java @@ -56,7 +56,6 @@ public class ResourceRepository extends AbstractAuditedEntityRepository findByName(final String name) { + final TypedQuery query = entityManager.createNamedQuery( + "DocRepo.findResourcesByName", Resource.class); + query.setParameter("name", name); + + return query.getResultList(); + } } diff --git a/ccm-docrepo/src/main/resources/com.arsdigita.docrepo/Resources.properties b/ccm-docrepo/src/main/resources/com.arsdigita.docrepo/Resources.properties index 69bdc1cc5..9c6058bf8 100644 --- a/ccm-docrepo/src/main/resources/com.arsdigita.docrepo/Resources.properties +++ b/ccm-docrepo/src/main/resources/com.arsdigita.docrepo/Resources.properties @@ -74,3 +74,4 @@ ui.workspace.sign_out=Sign out ui.view.admin=View of the Admin ui.view.user=The view of the User ui.folder.choose_destination=Please choose a destination +ui.file.name.invalid=The filename is not valid. diff --git a/ccm-docrepo/src/main/resources/com.arsdigita.docrepo/Resources_de.properties b/ccm-docrepo/src/main/resources/com.arsdigita.docrepo/Resources_de.properties index b907c122e..05ce68921 100644 --- a/ccm-docrepo/src/main/resources/com.arsdigita.docrepo/Resources_de.properties +++ b/ccm-docrepo/src/main/resources/com.arsdigita.docrepo/Resources_de.properties @@ -74,3 +74,4 @@ ui.workspace.sign_out=Abmelden ui.view.admin=Ansicht des Admin ui.view.user=Die Ansicht des Benutzers ui.folder.choose_destination=Bitte w\u00e4hle ein Ziel +ui.file.name.invalid=Der Dateiname ist nicht gültig. diff --git a/ccm-docrepo/src/main/resources/com.arsdigita.docrepo/Resources_en.properties b/ccm-docrepo/src/main/resources/com.arsdigita.docrepo/Resources_en.properties index 69bdc1cc5..9c6058bf8 100644 --- a/ccm-docrepo/src/main/resources/com.arsdigita.docrepo/Resources_en.properties +++ b/ccm-docrepo/src/main/resources/com.arsdigita.docrepo/Resources_en.properties @@ -74,3 +74,4 @@ ui.workspace.sign_out=Sign out ui.view.admin=View of the Admin ui.view.user=The view of the User ui.folder.choose_destination=Please choose a destination +ui.file.name.invalid=The filename is not valid.