diff --git a/src/main/java/com/celements/docform/DocFormRequestKeyParser.java b/src/main/java/com/celements/docform/DocFormRequestKeyParser.java index 8174b562f..5c00e5840 100644 --- a/src/main/java/com/celements/docform/DocFormRequestKeyParser.java +++ b/src/main/java/com/celements/docform/DocFormRequestKeyParser.java @@ -15,6 +15,8 @@ import java.util.function.Supplier; import java.util.regex.Pattern; +import javax.validation.constraints.NotNull; + import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,7 +49,7 @@ public class DocFormRequestKeyParser { */ private final DocumentReference defaultDocRef; - public DocFormRequestKeyParser(DocumentReference defaultDocRef) { + public DocFormRequestKeyParser(@NotNull DocumentReference defaultDocRef) { this.defaultDocRef = checkNotNull(defaultDocRef); } diff --git a/src/main/java/com/celements/filebase/references/FileReference.java b/src/main/java/com/celements/filebase/references/FileReference.java new file mode 100644 index 000000000..b66e1ca8e --- /dev/null +++ b/src/main/java/com/celements/filebase/references/FileReference.java @@ -0,0 +1,212 @@ +package com.celements.filebase.references; + +import static com.google.common.base.Preconditions.*; + +import java.io.Serializable; +import java.util.Objects; +import java.util.Optional; +import java.util.regex.Pattern; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.NotThreadSafe; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import javax.ws.rs.core.UriBuilder; + +import org.xwiki.model.reference.DocumentReference; + +import com.celements.model.util.ModelUtils; +import com.google.common.base.Strings; +import com.google.errorprone.annotations.Immutable; +import com.xpn.xwiki.web.Utils; + +@Immutable +public final class FileReference implements Serializable { + + private static final long serialVersionUID = 1L; + + public enum FileReferenceType { + ON_DISK, ATTACHMENT, EXTERNAL; + } + + @NotThreadSafe + public static final class Builder { + + private static final Pattern ATTACHMENT_LINK_PATTERN = Pattern.compile( + "([\\w\\-]*:)?([\\w\\-]*\\.[\\w\\-]*){1};.*"); + private static final Pattern ON_DISK_LINK_PATTERN = Pattern.compile("^:[/\\w\\-\\.]*"); + + private String name; + private FileReferenceType type; + private DocumentReference docRef; + private String fullPath; + private String queryString; + + @NotNull + private static String getAttachmentName(@NotEmpty String link) { + return link.split(";")[1]; + } + + @NotNull + private static String getPathFileName(@NotEmpty String link) { + String[] linkParts = link.split("/"); + return linkParts[linkParts.length - 1]; + } + + private static boolean isAttachmentLink(@Nullable String link) { + if (link != null) { + return ATTACHMENT_LINK_PATTERN.matcher(link.trim()).matches(); + } + return false; + } + + private static boolean isOnDiskLink(@Nullable String link) { + if (link != null) { + return ON_DISK_LINK_PATTERN.matcher(link.trim()).matches(); + } + return false; + } + + @NotNull + private static DocumentReference getPageDocRef(@NotNull String link) { + return Utils.getComponent(ModelUtils.class).resolveRef(link.split(";")[0], + DocumentReference.class); + } + + @NotNull + private static FileReferenceType getTypeOfLink(@NotEmpty String link) { + if (isOnDiskLink(link)) { + return FileReferenceType.ON_DISK; + } else if (isAttachmentLink(link)) { + return FileReferenceType.ATTACHMENT; + } + return FileReferenceType.EXTERNAL; + } + + @NotNull + public Builder setFileName(@NotNull String fileName) { + checkNotNull(fileName); + this.name = fileName; + return this; + } + + @NotNull + public Builder setType(@NotNull FileReferenceType type) { + checkNotNull(type); + this.type = type; + return this; + } + + public void setDocRef(@NotNull DocumentReference docRef) { + checkNotNull(docRef); + this.docRef = docRef; + } + + public void setFullPath(@NotEmpty String fullPath) { + checkArgument(!Strings.isNullOrEmpty(fullPath), "path may not be null or empty"); + this.fullPath = fullPath; + } + + public void setQueryString(@Nullable String queryString) { + this.queryString = Strings.emptyToNull(queryString); + } + + @NotNull + public FileReference build() { + return new FileReference(this); + } + + } + + private final String name; + private final FileReferenceType type; + private final DocumentReference docRef; + private final String fullPath; + private final String queryString; + + private FileReference(Builder builder) { + this.name = builder.name; + this.type = builder.type; + this.fullPath = builder.fullPath; + this.docRef = builder.docRef; + this.queryString = builder.queryString; + } + + @NotNull + public String getName() { + return name; + } + + @NotNull + public FileReferenceType getType() { + return type; + } + + @Nullable + public DocumentReference getDocRef() { + return docRef; + } + + @NotEmpty + public String getFullPath() { + return fullPath; + } + + @NotNull + public Optional getQueryString() { + return Optional.ofNullable(queryString); + } + + @NotNull + public UriBuilder getUri() { + return UriBuilder.fromPath(fullPath).replaceQuery(queryString); + } + + public boolean isAttachmentReference() { + return type == FileReferenceType.ATTACHMENT; + } + + public boolean isOnDiskReference() { + return type == FileReferenceType.ON_DISK; + } + + @Override + public int hashCode() { + return Objects.hash(name, type, docRef, fullPath); + } + + @Override + public boolean equals(@Nullable Object obj) { + return (obj instanceof FileReference) + && Objects.equals(((FileReference) obj).name, this.name) + && Objects.equals(((FileReference) obj).type, this.type) + && Objects.equals(((FileReference) obj).docRef, this.docRef) + && Objects.equals(((FileReference) obj).fullPath, this.fullPath); + } + + @Override + public String toString() { + return "FileReference [name=" + name + ", type=" + type + ", docRef=" + docRef + ", fullPath=" + + fullPath + "]"; + } + + @NotNull + public static FileReference of(@NotEmpty String link) { + checkArgument(!Strings.isNullOrEmpty(link), "link may not be empty"); + final String[] linkParts = link.split("\\?"); + Builder builder = new Builder(); + builder.setType(Builder.getTypeOfLink(linkParts[0])); + if (builder.type == FileReferenceType.ATTACHMENT) { + builder.setFileName(Builder.getAttachmentName(linkParts[0])); + builder.setDocRef(Builder.getPageDocRef(linkParts[0])); + } else { + builder.setFileName(Builder.getPathFileName(linkParts[0])); + builder.setFullPath(linkParts[0]); + } + if (linkParts.length > 1) { + builder.setQueryString(linkParts[1]); + } + return builder.build(); + } + +} diff --git a/src/main/java/com/celements/filebase/uri/FileNotExistException.java b/src/main/java/com/celements/filebase/uri/FileNotExistException.java new file mode 100644 index 000000000..c30041b3f --- /dev/null +++ b/src/main/java/com/celements/filebase/uri/FileNotExistException.java @@ -0,0 +1,32 @@ +package com.celements.filebase.uri; + +import javax.validation.constraints.NotNull; + +import com.celements.filebase.references.FileReference; + +public class FileNotExistException extends Exception { + + private static final long serialVersionUID = 1L; + + private final FileReference fileReference; + + public FileNotExistException(@NotNull FileReference fileRef) { + super("Url fileRef [" + fileRef + "] does not exist."); + this.fileReference = fileRef; + } + + public FileNotExistException(@NotNull String message, @NotNull FileReference fileRef) { + super(message); + this.fileReference = fileRef; + } + + public FileNotExistException(@NotNull String message, @NotNull FileReference fileRef, + Exception wrapExp) { + super(message, wrapExp); + this.fileReference = fileRef; + } + + public FileReference getFileReference() { + return fileReference; + } +} diff --git a/src/main/java/com/celements/filebase/uri/FileUriScriptService.java b/src/main/java/com/celements/filebase/uri/FileUriScriptService.java new file mode 100644 index 000000000..878fc2f61 --- /dev/null +++ b/src/main/java/com/celements/filebase/uri/FileUriScriptService.java @@ -0,0 +1,104 @@ +package com.celements.filebase.uri; + +import java.util.Optional; + +import javax.annotation.Nullable; +import javax.inject.Singleton; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import javax.ws.rs.core.UriBuilder; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.xwiki.component.annotation.Component; +import org.xwiki.component.annotation.Requirement; +import org.xwiki.script.service.ScriptService; + +import com.celements.filebase.references.FileReference; +import com.google.common.base.Strings; + +@Component(FileUriScriptService.NAME) +@Singleton +public class FileUriScriptService implements ScriptService { + + public static final String NAME = "fileUri"; + + private static final Logger LOGGER = LoggerFactory.getLogger(FileUriScriptService.class); + + @Requirement + private FileUrlServiceRole fileUriService; + + @NotNull + public FileReference createFileReference(@NotEmpty String link) { + return FileReference.of(link); + } + + @NotNull + public UriBuilder createFileUrl(@Nullable String fileRef) { + return createFileUrl(fileRef, null); + } + + @NotNull + public UriBuilder createFileUrl(@Nullable String fileRef, @Nullable String action) { + return createFileUrl(fileRef, action, null); + } + + @NotNull + public UriBuilder createFileUrl(@Nullable String fileRef, @Nullable String action, + @Nullable String queryString) { + if (!Strings.isNullOrEmpty(fileRef)) { + return createFileUrl(createFileReference(fileRef), action, queryString); + } + return UriBuilder.fromPath(""); + } + + @NotNull + public UriBuilder createFileUrl(@Nullable FileReference fileRef, @Nullable String action, + @Nullable String queryString) { + if (fileRef != null) { + try { + return fileUriService.createFileUri(fileRef, Optional.ofNullable(action), + Optional.ofNullable(queryString)); + } catch (FileNotExistException exp) { + LOGGER.info("createFileUrl for [{}] with action [{}] an queryString [{}] failed.", fileRef, + action, queryString, exp); + } + } + return UriBuilder.fromPath(""); + } + + @NotNull + public UriBuilder getFileURLPrefix() { + return fileUriService.getFileUrlPrefix(Optional.empty()); + } + + @NotNull + public UriBuilder getFileURLPrefix(@Nullable String action) { + return fileUriService.getFileUrlPrefix(Optional.ofNullable(action)); + } + + @NotNull + public UriBuilder createAbsoluteFileUri(@Nullable String fileRef, @Nullable String action) { + return createAbsoluteFileUri(fileRef, action, null); + } + + @NotNull + public UriBuilder createAbsoluteFileUri(@Nullable String fileRef, String action, + @Nullable String queryString) { + if (!Strings.isNullOrEmpty(fileRef)) { + return createAbsoluteFileUri(createFileReference(fileRef), action, queryString); + } + return UriBuilder.fromPath(""); + } + + @NotNull + public UriBuilder createAbsoluteFileUri(@Nullable FileReference fileRef, String action, + @Nullable String queryString) { + if (fileRef != null) { + return fileUriService.createAbsoluteFileUri(fileRef, Optional.ofNullable(action), + Optional.ofNullable(queryString)); + } + return UriBuilder.fromPath(""); + } + +} diff --git a/src/main/java/com/celements/filebase/uri/FileUriService.java b/src/main/java/com/celements/filebase/uri/FileUriService.java new file mode 100644 index 000000000..2a0587c31 --- /dev/null +++ b/src/main/java/com/celements/filebase/uri/FileUriService.java @@ -0,0 +1,160 @@ +package com.celements.filebase.uri; + +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Optional; + +import javax.validation.constraints.NotNull; +import javax.ws.rs.core.UriBuilder; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.xwiki.component.annotation.Component; +import org.xwiki.component.annotation.Requirement; +import org.xwiki.configuration.ConfigurationSource; + +import com.celements.configuration.CelementsFromWikiConfigurationSource; +import com.celements.filebase.IAttachmentServiceRole; +import com.celements.filebase.references.FileReference; +import com.celements.model.access.IModelAccessFacade; +import com.celements.model.access.exception.AttachmentNotExistsException; +import com.celements.model.access.exception.DocumentNotExistsException; +import com.celements.model.context.ModelContext; +import com.celements.model.util.ModelUtils; +import com.celements.web.service.LastStartupTimeStampRole; +import com.google.common.base.Strings; +import com.xpn.xwiki.doc.XWikiAttachment; +import com.xpn.xwiki.doc.XWikiDocument; +import com.xpn.xwiki.web.XWikiURLFactory; + +import groovy.lang.Singleton; + +@Component +@Singleton +public class FileUriService implements FileUrlServiceRole { + + private static final Logger LOGGER = LoggerFactory.getLogger(FileUriService.class); + + @Requirement + private ModelContext context; + + @Requirement + private IModelAccessFacade modelAccess; + + @Requirement + private ModelUtils modelUtils; + + @Requirement + private IAttachmentServiceRole attachmentSrv; + + @Requirement + private LastStartupTimeStampRole lastStartupTimeStamp; + + @Requirement(CelementsFromWikiConfigurationSource.NAME) + private ConfigurationSource configSrc; + + private String getDefaultAction() { + return Optional.ofNullable( + Strings.emptyToNull(configSrc.getProperty("celements.fileuri.defaultaction"))) + .orElse(context.getXWikiContext().getWiki().getXWikiPreference("celdefaultAttAction", + "celements.attachmenturl.defaultaction", "file", context.getXWikiContext())); + } + + @Override + public @NotNull UriBuilder getFileUrlPrefix(@NotNull Optional action) { + URL baseUrl = getUrlFactory().createResourceURL("", false, context.getXWikiContext()); + try { + return UriBuilder.fromUri(baseUrl.toURI()) + .replacePath("/" + action.orElse(getDefaultAction()) + "/") + .path(baseUrl.getPath()); + } catch (URISyntaxException exp) { + LOGGER.error("Failed to get file url prefix.", exp); + return UriBuilder.fromPath(baseUrl.toString()); + } + } + + @Override + @NotNull + public UriBuilder createAbsoluteFileUri(@NotNull FileReference fileRef, + @NotNull Optional action, Optional queryString) { + try { + return UriBuilder.fromUri(getUrlFactory().getServerURL(context.getXWikiContext()).toURI()) + .uri(createFileUri(fileRef, action, queryString).build()); + } catch (MalformedURLException | FileNotExistException | URISyntaxException exp) { + LOGGER.error("Failed to getServerURL for [{}].", fileRef, exp); + return UriBuilder.fromPath(""); + } + } + + private XWikiURLFactory getUrlFactory() { + return context.getXWikiContext().getURLFactory(); + } + + @Override + @NotNull + public UriBuilder createFileUri(@NotNull FileReference fileRef, @NotNull Optional action, + @NotNull Optional queryString) throws FileNotExistException { + final UriBuilder baseUrl = createFileUri(fileRef, action); + if (queryString.isPresent()) { + return baseUrl.replaceQuery(Optional.ofNullable(baseUrl.build().getQuery()) + .map(qS -> qS + "&" + queryString.get()) + .orElse(queryString.get())); + } + return baseUrl; + } + + @NotNull + UriBuilder createFileUri(@NotNull FileReference fileRef, + @NotNull Optional action) + throws FileNotExistException { + UriBuilder uriBuilder; + if (fileRef.isAttachmentReference()) { + uriBuilder = createAttachmentUri(fileRef, action); + } else if (fileRef.isOnDiskReference()) { + uriBuilder = createOnDiskUri(fileRef, action); + } else { + uriBuilder = fileRef.getUri(); + } + return addContextUri(uriBuilder); + } + + UriBuilder addContextUri(UriBuilder uriBuilder) { + Optional currentDoc = context.getCurrentDoc().toJavaUtil(); + if (currentDoc.isPresent() && uriBuilder.toString().startsWith("?")) { + uriBuilder.replacePath(currentDoc.get().getURL("view", context.getXWikiContext())); + } + return uriBuilder; + } + + private UriBuilder createAttachmentUri(@NotNull FileReference fileRef, Optional action) + throws FileNotExistException { + String attName = fileRef.getName(); + try { + XWikiDocument doc = modelAccess.getDocument(fileRef.getDocRef()); + XWikiAttachment att = attachmentSrv.getAttachmentNameEqual(doc, attName); + + return UriBuilder.fromPath(doc.getAttachmentURL(attName, action.orElse(getDefaultAction()), + context.getXWikiContext())) + .replaceQuery("version=" + lastStartupTimeStamp.getLastChangedTimeStamp(att.getDate())); + } catch (DocumentNotExistsException exp) { + LOGGER.error("Error getting attachment URL for doc '{}' and file {}", fileRef.getDocRef(), + attName, exp); + throw new FileNotExistException(fileRef); + } catch (AttachmentNotExistsException anee) { + LOGGER.info("Attachment not found for link [{}] and action [{}]", fileRef, action, anee); + throw new FileNotExistException(fileRef); + } + } + + UriBuilder createOnDiskUri(@NotNull FileReference fileRef, Optional action) { + String path = fileRef.getFullPath().trim().substring(1); + UriBuilder uri = UriBuilder.fromPath( + context.getXWikiContext().getWiki().getSkinFile(path, true, context.getXWikiContext()) + .replace("/skin/", + "/" + action.orElse(getDefaultAction()) + "/")); + uri.queryParam("version", lastStartupTimeStamp.getFileModificationDate(path)); + return uri; + } + +} diff --git a/src/main/java/com/celements/filebase/uri/FileUrlServiceRole.java b/src/main/java/com/celements/filebase/uri/FileUrlServiceRole.java new file mode 100644 index 000000000..f43e7ba52 --- /dev/null +++ b/src/main/java/com/celements/filebase/uri/FileUrlServiceRole.java @@ -0,0 +1,26 @@ +package com.celements.filebase.uri; + +import java.net.URL; + +import javax.annotation.Nullable; +import javax.validation.constraints.NotNull; + +import org.xwiki.component.annotation.ComponentRole; + +import com.celements.filebase.references.FileReference; + +@ComponentRole +public interface FileUrlServiceRole { + + @NotNull + URL getFileUrl(@NotNull FileReference fileRef, @Nullable String action) + throws FileNotExistException; + + @NotNull + URL getFileUrl(@NotNull FileReference fileRef, @Nullable String action, + @Nullable String queryString) throws FileNotExistException; + + @NotNull + URL getFileUrlPrefix(@NotNull String action); + +} diff --git a/src/main/java/com/celements/javascript/ExtJsFileParameter.java b/src/main/java/com/celements/javascript/ExtJsFileParameter.java index 1b40fbde8..5f7aa6eb6 100644 --- a/src/main/java/com/celements/javascript/ExtJsFileParameter.java +++ b/src/main/java/com/celements/javascript/ExtJsFileParameter.java @@ -2,6 +2,7 @@ import static com.google.common.base.Preconditions.*; +import java.util.Objects; import java.util.Optional; import javax.annotation.Nullable; @@ -125,10 +126,24 @@ public boolean isLazyLoad() { return lazyLoad; } + @Override + public int hashCode() { + return Objects.hash(jsFileEntry, queryString, lazyLoad, action); + } + + @Override + public boolean equals(@Nullable Object obj) { + return (obj instanceof ExtJsFileParameter) + && Objects.equals(((ExtJsFileParameter) obj).jsFileEntry, this.jsFileEntry) + && Objects.equals(((ExtJsFileParameter) obj).queryString, this.queryString) + && Objects.equals(((ExtJsFileParameter) obj).lazyLoad, this.lazyLoad) + && Objects.equals(((ExtJsFileParameter) obj).action, this.action); + } + @Override public String toString() { - return "ExtJsFileParameter [action=" + action + ", jsFileEntry=" + jsFileEntry - + ", queryString=" + queryString + ", lazyLoad=" + lazyLoad + "]"; + return "ExtJsFileParameter [action=" + action + ", queryString=" + queryString + ", lazyLoad=" + + lazyLoad + ", jsFileEntry=" + jsFileEntry + "]"; } } diff --git a/src/main/java/com/celements/web/plugin/cmd/AttachmentURLCommand.java b/src/main/java/com/celements/web/plugin/cmd/AttachmentURLCommand.java index ebc3e9b31..99b71547d 100644 --- a/src/main/java/com/celements/web/plugin/cmd/AttachmentURLCommand.java +++ b/src/main/java/com/celements/web/plugin/cmd/AttachmentURLCommand.java @@ -20,12 +20,17 @@ package com.celements.web.plugin.cmd; import java.net.MalformedURLException; +import java.util.Optional; + +import javax.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xwiki.context.Execution; import com.celements.filebase.IAttachmentServiceRole; +import com.celements.filebase.references.FileReference; +import com.celements.filebase.uri.FileUrlServiceRole; import com.celements.model.access.exception.AttachmentNotExistsException; import com.celements.web.service.LastStartupTimeStampRole; import com.xpn.xwiki.XWikiContext; @@ -35,29 +40,52 @@ import com.xpn.xwiki.web.Utils; import com.xpn.xwiki.web.XWikiURLFactory; +/** + * @deprecated since 5.4 instead use {@link FileUrlServiceRole} + */ +@Deprecated public class AttachmentURLCommand { private static final Logger LOGGER = LoggerFactory.getLogger(AttachmentURLCommand.class); + /** + * @deprecated since 5.4 instead use + * {@link FileUrlServiceRole#createFileUri(String, Optional)} + */ + @Deprecated public String getAttachmentURL(String link, XWikiContext context) { return getAttachmentURL(link, getDefaultAction(), context); } - protected String getDefaultAction() { + private String getDefaultAction() { return getContext().getWiki().getXWikiPreference("celdefaultAttAction", "celements.attachmenturl.defaultaction", "file", getContext()); } + /** + * @deprecated since 5.4 instead use {@link FileUrlServiceRole#getFileURLPrefix()} + */ + @Deprecated public String getAttachmentURLPrefix() { return getAttachmentURLPrefix(getDefaultAction()); } + /** + * @deprecated since 5.4 instead use {@link FileUrlServiceRole#getFileUrlPrefix(String)} + */ + @Deprecated public String getAttachmentURLPrefix(String action) { XWikiURLFactory urlf = getContext().getURLFactory(); return urlf.createResourceURL("", true, getContext()).toString().replace("/skin/", "/" + action + "/"); } + /** + * @deprecated since 5.4 instead use + * {@link RessourceUrlServiceRole#createRessourceUrl(String, Optional)} + */ + @Deprecated + @Nullable public String getAttachmentURL(String link, String action, XWikiContext context) { String url = link; if (isAttachmentLink(link)) { @@ -91,14 +119,29 @@ private LastStartupTimeStampRole getLastStartupTimeStamp() { return Utils.getComponent(LastStartupTimeStampRole.class); } + /** + * @deprecated since 5.4 instead use + * {@link FileReference#getName()} + */ + @Deprecated public String getAttachmentName(String link) { return link.split(";")[1]; } + /** + * @deprecated since 5.4 instead use + * {@link FileReference#getDocRef()} + */ + @Deprecated public String getPageFullName(String link) { return link.split(";")[0]; } + /** + * @deprecated since 5.4 instead use + * {@link FileReference#isAttachmentReference()} + */ + @Deprecated public boolean isAttachmentLink(String link) { boolean isAttachmentLink = false; if (link != null) { @@ -108,6 +151,11 @@ public boolean isAttachmentLink(String link) { return isAttachmentLink; } + /** + * @deprecated since 5.4 instead use + * {@link FileReference#isOnDiskReference()} + */ + @Deprecated public boolean isOnDiskLink(String link) { boolean isAttachmentLink = false; if (link != null) { @@ -117,6 +165,11 @@ public boolean isOnDiskLink(String link) { return isAttachmentLink; } + /** + * @deprecated since 5.4 instead use + * {@link FileUriServiceRole#getExternalFileURL(FileReference, Optional)} + */ + @Deprecated public String getExternalAttachmentURL(String fileName, String action, XWikiContext context) { try { return context.getURLFactory().getServerURL(context).toExternalForm() + getAttachmentURL( diff --git a/src/main/java/com/celements/web/service/CelementsWebScriptService.java b/src/main/java/com/celements/web/service/CelementsWebScriptService.java index 74a0a4b01..089e0d730 100644 --- a/src/main/java/com/celements/web/service/CelementsWebScriptService.java +++ b/src/main/java/com/celements/web/service/CelementsWebScriptService.java @@ -54,6 +54,7 @@ import com.celements.appScript.IAppScriptService; import com.celements.common.classes.IClassesCompositorComponent; import com.celements.filebase.FileBaseScriptService; +import com.celements.filebase.uri.FileUriScriptService; import com.celements.lastChanged.ILastChangedRole; import com.celements.mandatory.IMandatoryDocumentCompositorRole; import com.celements.metatag.BaseObjectMetaTagProvider; @@ -99,6 +100,8 @@ @Component("celementsweb") public class CelementsWebScriptService implements ScriptService { + private static final String DELETED_ATTACHMENTS_HQL = "select datt.id from DeletedAttachment as datt order by datt.filename asc"; + private static final String CEL_GLOBALVAL_PREFIX = "celements.globalvalues."; private static final String IMAGE_MAP_COMMAND = "com.celements.web.ImageMapCommand"; @@ -373,22 +376,43 @@ public String displayImageMapConfigs() { return getImageMapCommand().displayAllImageMapConfigs(); } + /** + * @deprecated since 5.4 instead use {@link FileUriScriptService#createFileUrl(String)} + */ + @Deprecated public String getSkinFile(String fileName) { return new AttachmentURLCommand().getAttachmentURL(fileName, getContext()); } + /** + * @deprecated since 5.4 instead use {@link FileUriScriptService#createFileUrl(String, String)} + */ + @Deprecated public String getSkinFile(String fileName, String action) { return new AttachmentURLCommand().getAttachmentURL(fileName, action, getContext()); } + /** + * @deprecated since 5.4 instead use {@link FileUriScriptService#getFileURLPrefix()} + */ + @Deprecated public String getAttachmentURLPrefix() { return new AttachmentURLCommand().getAttachmentURLPrefix(); } + /** + * @deprecated since 5.4 instead use {@link FileUriScriptService#getFileURLPrefix(String)} + */ + @Deprecated public String getAttachmentURLPrefix(String action) { return new AttachmentURLCommand().getAttachmentURLPrefix(action); } + /** + * @deprecated since 5.4 instead use + * {@link FileUriScriptService#createAbsoluteFileUri(String, String)} + */ + @Deprecated public String getSkinFileExternal(String fileName, String action) { return new AttachmentURLCommand().getExternalAttachmentURL(fileName, action, getContext()); } @@ -485,8 +509,8 @@ public String renderDocument(DocumentReference docRef, String lang, boolean remo renderCommand.initRenderingEngine(rendererNameList); return renderCommand.renderDocument(docRef, lang); } catch (XWikiException exp) { - LOGGER.error("renderCelementsDocument: Failed to render [" + docRef + "] lang [" + lang - + "].", exp); + LOGGER.error("renderCelementsDocument: Failed to render [{}] in lang [{}].", docRef, lang, + exp); } return ""; } @@ -513,8 +537,9 @@ public String renderInheritableDocument(DocumentReference docRef, String lang) { try { return webUtilsService.renderInheritableDocument(docRef, lang); } catch (XWikiException exp) { - LOGGER.error("renderInheritableDocument: Failed to render inheritable [" + docRef - + "] in lang [" + lang + "]."); + LOGGER.error( + "renderInheritableDocument: Failed to render inheritable [{}] in lang [{}].", docRef, + lang, exp); } return ""; } @@ -563,7 +588,8 @@ public List getDeletedDocuments(String orderby, boolean hideOverwritten) } private String getDeletedDocsHql(String orderby, boolean hideOverwritten) { - String deletedDocsHql = "select distinct ddoc.fullName" + " from XWikiDeletedDocument as ddoc"; + String deletedDocsHql = "select distinct ddoc.fullName" + + " from XWikiDeletedDocument as ddoc"; if (hideOverwritten) { deletedDocsHql += " where ddoc.fullName not in (select doc.fullName from" + " XWikiDocument as doc)"; @@ -628,7 +654,7 @@ public Integer permanentlyEmptyTrash(int waitDays) { public List getDeletedAttachments() { List resultList = Collections.emptyList(); try { - Query query = queryManager.createQuery(getDeletedAttachmentsHql(), Query.HQL); + Query query = queryManager.createQuery(DELETED_ATTACHMENTS_HQL, Query.HQL); resultList = query.execute(); } catch (QueryException queryExp) { LOGGER.error("Failed to parse or execute deletedAttachments hql query.", queryExp); @@ -636,10 +662,6 @@ public List getDeletedAttachments() { return resultList; } - private String getDeletedAttachmentsHql() { - return "select datt.id from DeletedAttachment as datt order by datt.filename asc"; - } - /** * permanentlyEmptyAttachmentTrash delete all documents after waitDays and minWaitDays * @@ -724,7 +746,7 @@ public com.xpn.xwiki.api.Object getSkinConfigFieldInheritor(String fallbackClass String key) { BaseCollection skinConfigBaseColl = new SkinConfigObjCommand().getSkinConfigFieldInheritor( fallbackClassName).getObject(key); - if ((skinConfigBaseColl != null) && (skinConfigBaseColl instanceof BaseObject)) { + if (skinConfigBaseColl instanceof BaseObject) { BaseObject skinConfigObj = (BaseObject) skinConfigBaseColl; return skinConfigObj.newObjectApi(skinConfigObj, getContext()); } else { diff --git a/src/main/resources/META-INF/components.txt b/src/main/resources/META-INF/components.txt index f13db9fdd..67c502284 100644 --- a/src/main/resources/META-INF/components.txt +++ b/src/main/resources/META-INF/components.txt @@ -161,3 +161,5 @@ com.celements.cells.classes.GroupCellClass com.celements.cells.classes.PageDepCellConfigClass com.celements.cells.classes.TranslationBoxCellConfigClass com.celements.cells.classes.CellAttributeClass +com.celements.filebase.uri.FileUriService +com.celements.filebase.uri.FileUriScriptService diff --git a/src/test/java/com/celements/auth/user/CelementsUserServiceTest.java b/src/test/java/com/celements/auth/user/CelementsUserServiceTest.java index e8acd2842..d0f0a64a6 100644 --- a/src/test/java/com/celements/auth/user/CelementsUserServiceTest.java +++ b/src/test/java/com/celements/auth/user/CelementsUserServiceTest.java @@ -30,6 +30,7 @@ import com.celements.model.classes.ClassDefinition; import com.celements.model.classes.fields.ClassField; import com.celements.model.object.xwiki.XWikiObjectFetcher; +import com.celements.model.reference.RefBuilder; import com.celements.query.IQueryExecutionServiceRole; import com.celements.rights.access.EAccessLevel; import com.celements.web.classes.oldcore.XWikiGroupsClass; @@ -50,7 +51,8 @@ public class CelementsUserServiceTest extends AbstractComponentTest { private CelementsUserService service; - private final DocumentReference userDocRef = new DocumentReference("xwikidb", "XWiki", "msladek"); + private final DocumentReference userDocRef = RefBuilder.create() + .wiki("XWiki").space("XWiki").doc("msladek").build(DocumentReference.class); @Before public void prepareTest() throws Exception { diff --git a/src/test/java/com/celements/docform/DocFormRequestKeyParserTest.java b/src/test/java/com/celements/docform/DocFormRequestKeyParserTest.java index d1e8a4c9d..6d666c0a1 100644 --- a/src/test/java/com/celements/docform/DocFormRequestKeyParserTest.java +++ b/src/test/java/com/celements/docform/DocFormRequestKeyParserTest.java @@ -30,7 +30,8 @@ public class DocFormRequestKeyParserTest extends AbstractComponentTest { @Before public void prepare() throws Exception { db = getContext().getDatabase(); - defaultDocRef = new DocumentReference(db, "Space", "DefaultDoc"); + defaultDocRef = RefBuilder.create().wiki(db).space("space").doc("DefaultDoc") + .build(DocumentReference.class); parser = new DocFormRequestKeyParser(defaultDocRef); } diff --git a/src/test/java/com/celements/docform/DocFormRequestKeyTest.java b/src/test/java/com/celements/docform/DocFormRequestKeyTest.java index 6d92a98c3..524598ca0 100644 --- a/src/test/java/com/celements/docform/DocFormRequestKeyTest.java +++ b/src/test/java/com/celements/docform/DocFormRequestKeyTest.java @@ -14,6 +14,7 @@ import org.xwiki.model.reference.ClassReference; import org.xwiki.model.reference.DocumentReference; +import com.celements.model.reference.RefBuilder; import com.google.common.collect.ImmutableList; public class DocFormRequestKeyTest { @@ -103,7 +104,7 @@ private List getSortedKeys() { } private DocumentReference getDocRef(String name) { - return new DocumentReference("db", "space", name); + return RefBuilder.create().wiki("db").space("space").doc(name).build(DocumentReference.class); } private ClassReference getClassRef(String name) { diff --git a/src/test/java/com/celements/docform/DocFormScriptServiceTest.java b/src/test/java/com/celements/docform/DocFormScriptServiceTest.java index b2deba545..fe9618272 100644 --- a/src/test/java/com/celements/docform/DocFormScriptServiceTest.java +++ b/src/test/java/com/celements/docform/DocFormScriptServiceTest.java @@ -19,6 +19,7 @@ import com.celements.docform.IDocForm.ResponseState; import com.celements.model.access.IModelAccessFacade; import com.celements.model.access.exception.DocumentSaveException; +import com.celements.model.reference.RefBuilder; import com.celements.rights.access.IRightsAccessFacadeRole; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -37,7 +38,8 @@ public class DocFormScriptServiceTest extends AbstractComponentTest { public void prepareTest() throws Exception { registerComponentMocks(IModelAccessFacade.class, IRightsAccessFacadeRole.class); docFormService = (DocFormScriptService) Utils.getComponent(ScriptService.class, "docform"); - docRef = new DocumentReference("db", "space", "doc"); + docRef = RefBuilder.create().wiki("db").space("space").doc("doc") + .build(DocumentReference.class); getContext().setRequest(new XWikiServletRequestStub()); } diff --git a/src/test/java/com/celements/filebase/references/FileReferenceTest.java b/src/test/java/com/celements/filebase/references/FileReferenceTest.java new file mode 100644 index 000000000..92fa1e1aa --- /dev/null +++ b/src/test/java/com/celements/filebase/references/FileReferenceTest.java @@ -0,0 +1,60 @@ +package com.celements.filebase.references; + +import static org.junit.Assert.*; + +import org.junit.Before; +import org.junit.Test; + +import com.celements.common.test.AbstractComponentTest; + +public class FileReferenceTest extends AbstractComponentTest { + + @Before + public void setUp_FileReferenceTest() throws Exception {} + + @Test + public void test_isAttachmentLink_null() { + assertThrows(IllegalArgumentException.class, () -> FileReference.of(null)); + } + + @Test + public void test_isAttachmentLink_empty() { + assertThrows(IllegalArgumentException.class, () -> FileReference.of("")); + } + + @Test + public void test_isAttachmentLink_url() { + assertFalse( + FileReference.of("/download/Space/Page/attachment.jpg").isAttachmentReference()); + } + + @Test + public void test_isAttachmentLink_is() { + assertTrue(FileReference.of("Space.Page;attachment.jpg").isAttachmentReference()); + } + + @Test + public void test_isAttachmentLink_isSpecialChars() { + assertTrue(FileReference.of("Teilnehmer.f8Nx9vyPOX8O2;Hans-002-Bearbeitet-2.jpg") + .isAttachmentReference()); + } + + @Test + public void test_isAttachmentLink_isWithDb() { + assertTrue(FileReference.of("db:Space.Page;attachment.jpg").isAttachmentReference()); + } + + @Test + public void test_isOnDiskLink_true() { + assertTrue(FileReference.of(":bla.js").isOnDiskReference()); + assertTrue(FileReference.of(" :celJS/bla.js").isOnDiskReference()); + } + + @Test + public void test_isOnDiskLink_false() { + assertFalse(FileReference.of("bla.js").isOnDiskReference()); + assertFalse(FileReference.of("x:celJS/bla.js").isOnDiskReference()); + assertFalse(FileReference.of("x:A.B;bla.js").isOnDiskReference()); + } + +} diff --git a/src/test/java/com/celements/filebase/uri/FileUriScriptServiceTest.java b/src/test/java/com/celements/filebase/uri/FileUriScriptServiceTest.java new file mode 100644 index 000000000..6b9523603 --- /dev/null +++ b/src/test/java/com/celements/filebase/uri/FileUriScriptServiceTest.java @@ -0,0 +1,130 @@ +package com.celements.filebase.uri; + +import static com.celements.common.test.CelementsTestUtils.*; +import static org.easymock.EasyMock.*; +import static org.junit.Assert.*; + +import java.net.URL; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Optional; + +import javax.ws.rs.core.UriBuilder; + +import org.junit.Before; +import org.junit.Test; +import org.xwiki.configuration.ConfigurationSource; +import org.xwiki.script.service.ScriptService; + +import com.celements.common.test.AbstractComponentTest; +import com.celements.configuration.CelementsFromWikiConfigurationSource; +import com.celements.filebase.references.FileReference; +import com.xpn.xwiki.XWiki; +import com.xpn.xwiki.XWikiContext; +import com.xpn.xwiki.web.Utils; +import com.xpn.xwiki.web.XWikiURLFactory; + +public class FileUriScriptServiceTest extends AbstractComponentTest { + + private FileUriScriptService fileUriSrv; + private XWikiContext context; + private XWikiURLFactory mockURLFactory; + private XWiki wiki; + private ConfigurationSource configSrcMock; + + @Before + public void setUp_FileUriScriptServiceTest() throws Exception { + configSrcMock = registerComponentMock(ConfigurationSource.class, + CelementsFromWikiConfigurationSource.NAME); + context = getContext(); + wiki = getWikiMock(); + mockURLFactory = createMockAndAddToDefault(XWikiURLFactory.class); + context.setURLFactory(mockURLFactory); + fileUriSrv = (FileUriScriptService) Utils.getComponent(ScriptService.class, + FileUriScriptService.NAME); + } + + @Test + public void test_createFileUrl_null() throws Exception { + assertEquals("", fileUriSrv.createFileUrl((FileReference) null, null, null).toString()); + assertEquals("", fileUriSrv.createFileUrl((String) null, null, null).toString()); + assertEquals("", fileUriSrv.createFileUrl((String) null, null).toString()); + assertEquals("", fileUriSrv.createFileUrl((String) null).toString()); + } + + @Test + public void test_createFileUrl_file_action() throws Exception { + String resultURL = "/skin/resources/celJS/prototype.js"; + expect(wiki.getSkinFile(eq("celJS/prototype.js"), eq(true), same(context))) + .andReturn(resultURL); + Date lastModificationDate = new SimpleDateFormat("YYYYmmddHHMMss").parse("20201123101535"); + expect(wiki.getResourceLastModificationDate(eq("resources/celJS/prototype.js"))).andReturn( + lastModificationDate); + expectDefaultAction(Optional.of("download")); + replayDefault(); + assertEquals("/file/resources/celJS/prototype.js?version=20191230101135", + fileUriSrv.createFileUrl(":celJS/prototype.js", "file").toString()); + verifyDefault(); + } + + @Test + public void test_createFileUrl_file_action_query() throws Exception { + String resultURL = "/skin/resources/celJS/prototype.js"; + expect(wiki.getSkinFile(eq("celJS/prototype.js"), eq(true), same(context))) + .andReturn(resultURL); + Date lastModificationDate = new SimpleDateFormat("YYYYmmddHHMMss").parse("20201123101535"); + expect(wiki.getResourceLastModificationDate(eq("resources/celJS/prototype.js"))).andReturn( + lastModificationDate); + expectDefaultAction(Optional.of("download")); + replayDefault(); + assertEquals("/file/resources/celJS/prototype.js?version=20191230101135&bla=asfd", + fileUriSrv.createFileUrl(":celJS/prototype.js", "file", "bla=asfd").toString()); + verifyDefault(); + } + + @Test + public void test_createAbsoluteFileUri_null() { + assertNotNull(fileUriSrv.createAbsoluteFileUri((FileReference) null, null, null)); + assertEquals("", fileUriSrv.createAbsoluteFileUri((FileReference) null, null, null).toString()); + assertNotNull(fileUriSrv.createAbsoluteFileUri((String) null, null, null)); + assertEquals("", fileUriSrv.createAbsoluteFileUri((String) null, null, null).toString()); + assertNotNull(fileUriSrv.createAbsoluteFileUri((String) null, null)); + assertEquals("", fileUriSrv.createAbsoluteFileUri((String) null, null).toString()); + } + + @Test + public void test_createAbsoluteFileUri_fileRef() throws Exception { + String serverUrl = "http://localhost"; + URL viewURL = new URL(serverUrl); + expect(mockURLFactory.getServerURL(same(context))).andReturn(viewURL); + replayDefault(); + String filePath = "/a/b/c.txt"; + FileReference fileRef = fileUriSrv.createFileReference(filePath); + UriBuilder fileUri = fileUriSrv.createAbsoluteFileUri(fileRef, null, null); + assertNotNull(fileUri); + assertEquals(serverUrl + filePath, fileUri.build().toURL().toExternalForm()); + verifyDefault(); + } + + @Test + public void test_getExternalFileURL_string() throws Exception { + String filePath = "/a/b/c.txt"; + String serverUrl = "http://localhost"; + URL viewURL = new URL(serverUrl); + expect(mockURLFactory.getServerURL(same(context))).andReturn(viewURL); + replayDefault(); + UriBuilder fileUri = fileUriSrv.createAbsoluteFileUri(filePath, null, null); + assertNotNull(fileUri); + assertEquals("Absolute file uri must enclose all information for externalForm.", + serverUrl + filePath, fileUri.build().toURL().toExternalForm()); + verifyDefault(); + } + + private void expectDefaultAction(Optional action) { + expect(configSrcMock.getProperty(eq("celements.fileuri.defaultaction"))) + .andReturn(action.orElse("file")); + expect(wiki.getXWikiPreference(eq("celdefaultAttAction"), eq( + "celements.attachmenturl.defaultaction"), eq("file"), same(context))) + .andReturn(action.orElse("file")).atLeastOnce(); + } +} diff --git a/src/test/java/com/celements/filebase/uri/FileUriServiceTest.java b/src/test/java/com/celements/filebase/uri/FileUriServiceTest.java new file mode 100644 index 000000000..4cf278607 --- /dev/null +++ b/src/test/java/com/celements/filebase/uri/FileUriServiceTest.java @@ -0,0 +1,325 @@ +package com.celements.filebase.uri; + +import static com.celements.common.test.CelementsTestUtils.*; +import static org.easymock.EasyMock.*; +import static org.junit.Assert.*; + +import java.net.URL; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Optional; + +import javax.ws.rs.core.UriBuilder; + +import org.junit.Before; +import org.junit.Test; +import org.xwiki.configuration.ConfigurationSource; +import org.xwiki.model.reference.AttachmentReference; +import org.xwiki.model.reference.DocumentReference; + +import com.celements.common.test.AbstractComponentTest; +import com.celements.configuration.CelementsFromWikiConfigurationSource; +import com.celements.filebase.references.FileReference; +import com.celements.model.access.IModelAccessFacade; +import com.celements.model.access.exception.AttachmentNotExistsException; +import com.celements.model.reference.RefBuilder; +import com.xpn.xwiki.XWiki; +import com.xpn.xwiki.XWikiContext; +import com.xpn.xwiki.doc.XWikiAttachment; +import com.xpn.xwiki.doc.XWikiDocument; +import com.xpn.xwiki.web.Utils; +import com.xpn.xwiki.web.XWikiURLFactory; + +public class FileUriServiceTest extends AbstractComponentTest { + + private IModelAccessFacade modelAccessMock; + private XWikiContext context; + private XWikiURLFactory mockURLFactory; + private ConfigurationSource configSrcMock; + private XWiki wiki; + private FileUriService fileUriServ; + + @Before + public void setUp_RessourceUrlServiceTest() throws Exception { + modelAccessMock = registerComponentMock(IModelAccessFacade.class); + configSrcMock = registerComponentMock(ConfigurationSource.class, + CelementsFromWikiConfigurationSource.NAME); + context = getContext(); + wiki = getWikiMock(); + mockURLFactory = createMockAndAddToDefault(XWikiURLFactory.class); + context.setURLFactory(mockURLFactory); + fileUriServ = (FileUriService) Utils.getComponent(FileUrlServiceRole.class); + } + + @Test + public void test_createFileUrl_fullURL() throws Exception { + FileReference fileRef = FileReference.of("http://www.bla.com/bla.txt"); + assertEquals("http://www.bla.com/bla.txt", + fileUriServ.createFileUri(fileRef, Optional.empty()).toString()); + } + + @Test + public void test_createFileUrl_partURL() throws Exception { + FileReference fileRef = FileReference.of("/xwiki/bin/download/A/B/bla.txt"); + assertEquals("/xwiki/bin/download/A/B/bla.txt", fileUriServ.createFileUri(fileRef, + Optional.empty()).toString()); + } + + @Test + public void test_getExternalFileURL_partURL() throws Exception { + FileReference fileRef = FileReference.of("/xwiki/bin/download/A/B/bla.txt"); + URL viewURL = new URL("http://localhost"); + expect(mockURLFactory.getServerURL(same(context))).andReturn(viewURL); + replayDefault(); + assertEquals("http://localhost/xwiki/bin/download/A/B/bla.txt", + fileUriServ.createAbsoluteFileUri(fileRef, Optional.empty(), Optional.empty()).build() + .toURL().toExternalForm().toString()); + verifyDefault(); + } + + @Test + public void test_getExternalFileURL_partURL_external() throws Exception { + FileReference fileRef = FileReference.of("http://myTesthost.ch/xwiki/bin/download/A/B/bla.txt"); + URL viewURL = new URL("http://localhost"); + expect(mockURLFactory.getServerURL(same(context))).andReturn(viewURL); + replayDefault(); + assertEquals("http://myTesthost.ch/xwiki/bin/download/A/B/bla.txt", + fileUriServ.createAbsoluteFileUri(fileRef, Optional.empty(), Optional.empty()).build() + .toURL().toExternalForm().toString()); + verifyDefault(); + } + + @Test + public void test_createFileUrl_dynamicParamURL() throws Exception { + String mySpaceName = "mySpace"; + String myDocName = "myDoc"; + DocumentReference myDocRef = new DocumentReference(context.getDatabase(), mySpaceName, + myDocName); + XWikiDocument doc = new XWikiDocument(myDocRef); + context.setDoc(doc); + URL viewURL = new URL("http://localhost/mySpace/myDoc"); + expect(mockURLFactory.createURL(eq(mySpaceName), eq(myDocName), eq("view"), (String) isNull(), + (String) isNull(), eq(context.getDatabase()), same(context))).andReturn(viewURL); + expect(mockURLFactory.getURL(eq(viewURL), same(context))).andReturn(viewURL.getPath()); + replayDefault(); + FileReference fileRef = FileReference.of("?xpage=bla&bli=blu"); + assertEquals("/mySpace/myDoc?xpage=bla&bli=blu", fileUriServ.createFileUri(fileRef, + Optional.empty()).toString()); + verifyDefault(); + } + + @Test + public void test_createFileUrl_fullInternalLink() throws Exception { + String resultURL = "http://celements2web.localhost/file/A/B/bla.txt"; + DocumentReference abDocRef = new RefBuilder().wiki("celements2web").space("A").doc("B") + .build(DocumentReference.class); + XWikiDocument abDoc = new XWikiDocument(abDocRef); + List attachList = new ArrayList<>(); + XWikiAttachment blaAtt = new XWikiAttachment(); + String attName = "bla.txt"; + blaAtt.setFilename(attName); + blaAtt.setDoc(abDoc); + attachList.add(blaAtt); + abDoc.setAttachmentList(attachList); + URL tstURL = new URL(resultURL); + expect(mockURLFactory.createAttachmentURL(eq("bla.txt"), eq("A"), eq("B"), eq("file"), + (String) eq(null), eq("celements2web"), same(context))).andReturn(tstURL); + expect(mockURLFactory.getURL(eq(tstURL), same(context))).andReturn(resultURL); + expect(modelAccessMock.getDocument(eq(abDocRef))).andReturn(abDoc).atLeastOnce(); + expectDefaultAction(Optional.empty()); + expect(modelAccessMock.getAttachmentNameEqual(same(abDoc), eq(attName))).andReturn(blaAtt) + .atLeastOnce(); + replayDefault(); + FileReference fileRef = FileReference.of("celements2web:A.B;bla.txt"); + String attachmentURL = fileUriServ.createFileUri(fileRef, Optional.empty()).toString(); + assertNotNull(attachmentURL); + assertTrue("expecting " + resultURL + " but got " + attachmentURL, + attachmentURL.matches(resultURL + "\\?version=\\d{14}")); + verifyDefault(); + } + + @Test + public void test_createFileUrl_partInternalLink() throws Exception { + String resultURL = "http://mydomain.ch/file/A/B/bla.txt"; + URL tstURL = new URL(resultURL); + expect(mockURLFactory.createAttachmentURL(eq("bla.txt"), eq("A"), eq("B"), eq("file"), + (String) eq(null), eq(context.getDatabase()), same(context))).andReturn(tstURL); + expect(mockURLFactory.getURL(eq(tstURL), same(context))).andReturn(resultURL); + DocumentReference abDocRef = new RefBuilder().wiki(getContext().getDatabase()).space("A") + .doc("B").build(DocumentReference.class); + XWikiDocument abDoc = new XWikiDocument(abDocRef); + List attachList = new ArrayList<>(); + XWikiAttachment blaAtt = new XWikiAttachment(); + String attName = "bla.txt"; + blaAtt.setFilename(attName); + blaAtt.setDoc(abDoc); + attachList.add(blaAtt); + abDoc.setAttachmentList(attachList); + expect(modelAccessMock.getDocument(eq(abDocRef))).andReturn(abDoc).atLeastOnce(); + expectDefaultAction(Optional.empty()); + expect(modelAccessMock.getAttachmentNameEqual(same(abDoc), eq(attName))).andReturn(blaAtt) + .atLeastOnce(); + replayDefault(); + FileReference fileRef = FileReference.of("A.B;bla.txt"); + String attachmentURL = fileUriServ.createFileUri(fileRef, Optional.empty()).toString(); + assertNotNull(attachmentURL); + assertTrue("expecting " + resultURL + " but got " + attachmentURL, + attachmentURL.matches(resultURL + "\\?version=\\d{14}")); + verifyDefault(); + } + + @Test + public void test_createFileUrl_partInternalLink_notExists() throws Exception { + DocumentReference abDocRef = new RefBuilder().wiki(getContext().getDatabase()).space("A") + .doc("B").build(DocumentReference.class); + XWikiDocument abDoc = new XWikiDocument(abDocRef); + expect(modelAccessMock.getDocument(eq(abDocRef))).andReturn(abDoc).atLeastOnce(); + String attName = "bla.txt"; + AttachmentReference attRef = new RefBuilder().with(abDocRef).att(attName) + .build(AttachmentReference.class); + expect(modelAccessMock.getAttachmentNameEqual(same(abDoc), eq(attName))) + .andThrow(new AttachmentNotExistsException(attRef)).atLeastOnce(); + replayDefault(); + FileReference fileRef = FileReference.of("A.B;bla.txt"); + assertThrows(FileNotExistException.class, + () -> fileUriServ.createFileUri(fileRef, Optional.empty())); + verifyDefault(); + } + + @Test + public void test_createFileUrl_onDiskLink() throws Exception { + String resultURL = "/appname/skin/resources/celJS/bla.js"; + expect(wiki.getSkinFile(eq("celJS/bla.js"), eq(true), same(context))).andReturn(resultURL); + expect(wiki.getResourceLastModificationDate(eq("resources/celJS/bla.js"))).andReturn( + new Date()); + expectDefaultAction(Optional.of("download")); + replayDefault(); + FileReference fileRef = FileReference.of(" :celJS/bla.js"); + String attachmentURL = fileUriServ.createFileUri(fileRef, Optional.empty(), Optional.empty()) + .toString(); + String expectedURL = "/appname/download/resources/celJS/bla.js"; + assertNotNull(attachmentURL); + assertTrue("expecting " + expectedURL + " but got " + attachmentURL, + attachmentURL.matches(expectedURL + "\\?version=\\d{14}")); + verifyDefault(); + } + + @Test + public void test_createFileUrl_Rubish() throws Exception { + FileReference fileRef = FileReference.of("http://A.B;bla.txt"); + assertEquals("http://A.B;bla.txt", + fileUriServ.createFileUri(fileRef, Optional.empty(), Optional.empty()).toString()); + } + + @Test + public void test_getFileURLPrefix() throws Exception { + expectDefaultAction(Optional.empty()); + expect(mockURLFactory.createResourceURL(eq(""), eq(false), same(context))).andReturn(new URL( + "http://test.fabian.dev:10080/resources/")); + replayDefault(); + assertEquals("http://test.fabian.dev:10080/file/resources/", + fileUriServ.getFileUrlPrefix(Optional.empty()).toString()); + verifyDefault(); + } + + @Test + public void test_createFileUrl_onDisk_queryString() throws Exception { + String resultURL = "/appname/skin/resources/celJS/bla.js"; + expect(wiki.getSkinFile(eq("celJS/bla.js"), eq(true), same(context))).andReturn(resultURL); + expect(wiki.getResourceLastModificationDate(eq("resources/celJS/bla.js"))).andReturn( + new Date()); + expectDefaultAction(Optional.of("download")); + String queryString = "asf=oiu"; + replayDefault(); + FileReference fileRef = FileReference.of(":celJS/bla.js"); + String attachmentURL = fileUriServ.createFileUri(fileRef, Optional.empty(), + Optional.of(queryString)).toString(); + String expectedURL = "/appname/download/resources/celJS/bla.js"; + assertTrue(attachmentURL, + attachmentURL.matches(expectedURL + "\\?version=\\d{14}\\&" + queryString)); + verifyDefault(); + } + + @Test + public void test_createFileUrl_partInternalLink_queryString() throws Exception { + String resultURL = "http://mydomain.ch/testAction/A/B/bla.txt"; + URL tstURL = new URL(resultURL); + expect(mockURLFactory.createAttachmentURL(eq("bla.txt"), eq("A"), eq("B"), eq("testAction"), + (String) eq(null), eq(context.getDatabase()), same(context))).andReturn(tstURL); + expect(mockURLFactory.getURL(eq(tstURL), same(context))).andReturn(resultURL); + DocumentReference abDocRef = new RefBuilder().wiki(getContext().getDatabase()).space("A") + .doc("B").build(DocumentReference.class); + XWikiDocument abDoc = new XWikiDocument(abDocRef); + List attachList = new ArrayList<>(); + XWikiAttachment blaAtt = new XWikiAttachment(); + String attName = "bla.txt"; + blaAtt.setFilename(attName); + blaAtt.setDoc(abDoc); + attachList.add(blaAtt); + abDoc.setAttachmentList(attachList); + expect(modelAccessMock.getDocument(eq(abDocRef))).andReturn(abDoc).atLeastOnce(); + expect(modelAccessMock.getAttachmentNameEqual(same(abDoc), eq(attName))).andReturn(blaAtt) + .atLeastOnce(); + expectDefaultAction(Optional.empty()); + String queryString = "asf=oiu"; + replayDefault(); + FileReference fileRef = FileReference.of("A.B;bla.txt"); + String attachmentURL = fileUriServ.createFileUri(fileRef, Optional.of("testAction"), + Optional.of(queryString)).toString(); + assertTrue(attachmentURL, + attachmentURL.matches(resultURL + "\\?version=\\d{14}\\&" + queryString)); + verifyDefault(); + } + + @Test + public void test_addContextUrl() throws Exception { + String mySpaceName = "mySpace"; + String myDocName = "myDoc"; + DocumentReference myDocRef = new DocumentReference(context.getDatabase(), mySpaceName, + myDocName); + XWikiDocument doc = new XWikiDocument(myDocRef); + context.setDoc(doc); + URL viewURL = new URL("http://localhost/mySpace/myDoc"); + expect(mockURLFactory.createURL(eq(mySpaceName), eq(myDocName), eq("view"), (String) isNull(), + (String) isNull(), eq(context.getDatabase()), same(context))).andReturn(viewURL); + expect(mockURLFactory.getURL(eq(viewURL), same(context))).andReturn(viewURL.getPath()); + UriBuilder uri = UriBuilder.fromPath("").replaceQuery("sdf=asdf"); + replayDefault(); + assertEquals("/mySpace/myDoc?sdf=asdf", fileUriServ.addContextUri(uri).toString()); + verifyDefault(); + } + + @Test + public void test_createOnDiskUrl() throws Exception { + String mySpaceName = "mySpace"; + String myDocName = "myDoc"; + DocumentReference myDocRef = new DocumentReference(context.getDatabase(), mySpaceName, + myDocName); + XWikiDocument doc = new XWikiDocument(myDocRef); + context.setDoc(doc); + Date lastModificationDate = new SimpleDateFormat("YYYYmmddHHMMss").parse("20201123101535"); + expect(wiki.getResourceLastModificationDate(eq("resources/celRes/test/bla.css"))).andReturn( + lastModificationDate); + String resultURL = "http://celements2web.localhost/skin/celRes/test/bla.css"; + expect(wiki.getSkinFile(eq("celRes/test/bla.css"), eq(true), same(context))) + .andReturn(resultURL); + expectDefaultAction(Optional.empty()); + replayDefault(); + FileReference fileRef = FileReference.of(":celRes/test/bla.css"); + assertEquals( + "http://celements2web.localhost/createOnDiskUrl/celRes/test/bla.css?version=20191230101135", + fileUriServ.createOnDiskUri(fileRef, Optional.of("createOnDiskUrl")).toString()); + verifyDefault(); + } + + private void expectDefaultAction(Optional action) { + expect(configSrcMock.getProperty(eq("celements.fileuri.defaultaction"))) + .andReturn(action.orElse("file")); + expect(wiki.getXWikiPreference(eq("celdefaultAttAction"), eq( + "celements.attachmenturl.defaultaction"), eq("file"), same(context))) + .andReturn(action.orElse("file")).atLeastOnce(); + } + +} diff --git a/src/test/java/com/celements/web/plugin/cmd/AttachmentURLCommandTest.java b/src/test/java/com/celements/web/plugin/cmd/AttachmentURLCommandTest.java index ce622685d..00fb4ed6c 100644 --- a/src/test/java/com/celements/web/plugin/cmd/AttachmentURLCommandTest.java +++ b/src/test/java/com/celements/web/plugin/cmd/AttachmentURLCommandTest.java @@ -19,10 +19,10 @@ */ package com.celements.web.plugin.cmd; +import static com.celements.common.test.CelementsTestUtils.*; import static org.easymock.EasyMock.*; import static org.junit.Assert.*; -import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Date; @@ -32,15 +32,15 @@ import org.junit.Test; import org.xwiki.model.reference.DocumentReference; -import com.celements.common.test.AbstractBridgedComponentTestCase; +import com.celements.common.test.AbstractComponentTest; import com.xpn.xwiki.XWiki; import com.xpn.xwiki.XWikiContext; -import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.doc.XWikiAttachment; import com.xpn.xwiki.doc.XWikiDocument; import com.xpn.xwiki.web.XWikiURLFactory; -public class AttachmentURLCommandTest extends AbstractBridgedComponentTestCase { +@Deprecated +public class AttachmentURLCommandTest extends AbstractComponentTest { private XWikiContext context; private XWiki wiki; @@ -57,19 +57,19 @@ public void setUp_AttachmentURLCommandTest() throws Exception { } @Test - public void testGetAttachmentURL_fullURL() { + public void test_getAttachmentURL_fullURL() throws Exception { assertEquals("http://www.bla.com/bla.txt", attUrlCmd.getAttachmentURL( "http://www.bla.com/bla.txt", context)); } @Test - public void testGetAttachmentURL_partURL() { + public void test_getAttachmentURL_partURL() throws Exception { assertEquals("/xwiki/bin/download/A/B/bla.txt", attUrlCmd.getAttachmentURL( "/xwiki/bin/download/A/B/bla.txt", context)); } @Test - public void testGetAttachmentURL_dynamicParamURL() throws MalformedURLException { + public void test_getAttachmentURL_dynamicParamURL() throws Exception { String mySpaceName = "mySpace"; String myDocName = "myDoc"; DocumentReference myDocRef = new DocumentReference(context.getDatabase(), mySpaceName, @@ -89,7 +89,7 @@ public void testGetAttachmentURL_dynamicParamURL() throws MalformedURLException } @Test - public void testGetAttachmentURL_fullInternalLink() throws XWikiException, MalformedURLException { + public void test_getAttachmentURL_fullInternalLink() throws Exception { String resultURL = "http://celements2web.localhost/file/A/B/bla.txt"; XWikiDocument abdoc = new XWikiDocument(); abdoc.setFullName("A.B"); @@ -114,7 +114,7 @@ public void testGetAttachmentURL_fullInternalLink() throws XWikiException, Malfo } @Test - public void testGetAttachmentURL_partInternalLink() throws XWikiException, MalformedURLException { + public void test_getAttachmentURL_partInternalLink() throws Exception { String resultURL = "http://mydomain.ch/file/A/B/bla.txt"; URL tstURL = new URL(resultURL); expect(mockURLFactory.createAttachmentURL(eq("bla.txt"), eq("A"), eq("B"), eq("file"), @@ -138,8 +138,7 @@ public void testGetAttachmentURL_partInternalLink() throws XWikiException, Malfo } @Test - public void testGetAttachmentURL_partInternalLink_notExists() throws XWikiException, - MalformedURLException { + public void test_getAttachmentURL_partInternalLink_notExists() throws Exception { XWikiDocument abdoc = new XWikiDocument(); abdoc.setFullName("A.B"); expect(wiki.getDocument(eq("A.B"), same(context))).andReturn(abdoc).anyTimes(); @@ -148,10 +147,11 @@ public void testGetAttachmentURL_partInternalLink_notExists() throws XWikiExcept replayDefault(); assertNull(attUrlCmd.getAttachmentURL("A.B;bla.txt", context)); verifyDefault(); + } @Test - public void testGetAttachmentURL_onDiskLink() throws XWikiException, MalformedURLException { + public void test_getAttachmentURL_onDiskLink() throws Exception { String resultURL = "/appname/skin/resources/celJS/bla.js"; expect(wiki.getSkinFile(eq("celJS/bla.js"), eq(true), same(context))).andReturn(resultURL); expect(wiki.getResourceLastModificationDate(eq("resources/celJS/bla.js"))).andReturn( @@ -166,55 +166,55 @@ public void testGetAttachmentURL_onDiskLink() throws XWikiException, MalformedUR } @Test - public void isAttachmentLink_null() { + public void test_isAttachmentLink_null() { assertFalse(attUrlCmd.isAttachmentLink(null)); } @Test - public void isAttachmentLink_empty() { + public void test_isAttachmentLink_empty() { assertFalse(attUrlCmd.isAttachmentLink("")); } @Test - public void isAttachmentLink_url() { + public void test_isAttachmentLink_url() { assertFalse(attUrlCmd.isAttachmentLink("/download/Space/Page/attachment.jpg")); } @Test - public void isAttachmentLink_is() { + public void test_isAttachmentLink_is() { assertTrue(attUrlCmd.isAttachmentLink("Space.Page;attachment.jpg")); } @Test - public void isAttachmentLink_isSpecialChars() { + public void test_isAttachmentLink_isSpecialChars() { assertTrue(attUrlCmd.isAttachmentLink("Teilnehmer.f8Nx9vyPOX8O2;Hans-002-Bearbeitet-2.jpg")); } @Test - public void isAttachmentLink_isWithDb() { + public void test_isAttachmentLink_isWithDb() { assertTrue(attUrlCmd.isAttachmentLink("db:Space.Page;attachment.jpg")); } @Test - public void testGetAttachmentURL_Rubish() { + public void test_getAttachmentURL_Rubish() throws Exception { assertEquals("http://A.B;bla.txt", attUrlCmd.getAttachmentURL("http://A.B;bla.txt", context)); } @Test - public void testIsOnDiskLink_true() { + public void test_isOnDiskLink_true() { assertTrue(attUrlCmd.isOnDiskLink(":bla.js")); assertTrue(attUrlCmd.isOnDiskLink(" :celJS/bla.js")); } @Test - public void testIsOnDiskLink_false() { + public void test_isOnDiskLink_false() { assertFalse(attUrlCmd.isOnDiskLink("bla.js")); assertFalse(attUrlCmd.isOnDiskLink("x:celJS/bla.js")); assertFalse(attUrlCmd.isOnDiskLink("x:A.B;bla.js")); } @Test - public void testGetAttachmentURLPrefix() throws Exception { + public void test_getAttachmentURLPrefix() throws Exception { expect(wiki.getXWikiPreference(eq("celdefaultAttAction"), eq( "celements.attachmenturl.defaultaction"), eq("file"), same(context))).andReturn("file"); expect(mockURLFactory.createResourceURL(eq(""), eq(true), same(context))).andReturn(new URL( diff --git a/src/test/java/com/celements/web/plugin/cmd/PasswordRecoveryAndEmailValidationCommandTest.java b/src/test/java/com/celements/web/plugin/cmd/PasswordRecoveryAndEmailValidationCommandTest.java index 7549e478c..0f04d38a8 100644 --- a/src/test/java/com/celements/web/plugin/cmd/PasswordRecoveryAndEmailValidationCommandTest.java +++ b/src/test/java/com/celements/web/plugin/cmd/PasswordRecoveryAndEmailValidationCommandTest.java @@ -41,6 +41,7 @@ import com.celements.common.test.TestMessageTool; import com.celements.model.access.IModelAccessFacade; import com.celements.model.access.exception.DocumentSaveException; +import com.celements.model.reference.RefBuilder; import com.celements.web.classes.oldcore.XWikiUsersClass; import com.celements.web.service.IWebUtilsService; import com.xpn.xwiki.api.Attachment; @@ -53,7 +54,8 @@ public class PasswordRecoveryAndEmailValidationCommandTest extends AbstractComponentTest { - private final DocumentReference userDocRef = new DocumentReference("db", "XWiki", "msladek"); + private final DocumentReference userDocRef = RefBuilder.create() + .wiki("db").space("XWiki").doc("msladek").build(DocumentReference.class); private PasswordRecoveryAndEmailValidationCommand cmd;