public int compare(XWikiAttachment attachmentOne, XWikiAttachment attachmentTwo) { return attachmentOne .getFilename() .toLowerCase() .replace('_', '-') .compareTo(attachmentTwo.getFilename().toLowerCase().replace('_', '-')); }
public XWikiPageClassLoader(String jarWikiPage, ClassLoader parent, XWikiContext context) throws XWikiException { super(new URL[0], parent); XWikiDocument doc = context.getWiki().getDocument(jarWikiPage, context); if (!doc.isNew()) { List attachList = doc.getAttachmentList(); for (int i = 0; i < attachList.size(); i++) { XWikiAttachment attach = (XWikiAttachment) attachList.get(i); String filename = attach.getFilename(); if (filename.endsWith(".jar")) { String downloadURL = doc.getExternalAttachmentURL(filename, "download", context); try { addURL(new URL(downloadURL)); if (LOGGER.isDebugEnabled()) { LOGGER.debug( "Adding [" + downloadURL + "] JAR from page [" + jarWikiPage + "] to Groovy classloader"); } } catch (Exception e) { LOGGER.warn( "Failed to add [" + downloadURL + "] JAR from page [" + jarWikiPage + "], ignoring it."); } } } } }
/** * Make sure the attachment is associated with a document. * * @param attachment the attachment to check. * @throws IllegalArgumentException if attachment.getDoc() yields null. */ private static void checkAttachedToDocument(final XWikiAttachment attachment) { if (attachment.getDoc() == null) { throw new IllegalArgumentException( "In order to use this function, the attachment [" + attachment.getFilename() + "] must be associated with a document."); } }
/** * @see "XWIKI-9399: Attachment version is incremented when a document is rolled back even if the * attachment did not change" */ @Test public void rollbackDoesNotSaveUnchangedAttachment() throws Exception { String version = "1.1"; String fileName = "logo.png"; Date date = new Date(); XWikiAttachment currentAttachment = mock(XWikiAttachment.class); when(currentAttachment.getAttachmentRevision(version, context)).thenReturn(currentAttachment); when(currentAttachment.getDate()).thenReturn(new Timestamp(date.getTime())); when(currentAttachment.getVersion()).thenReturn(version); when(currentAttachment.getFilename()).thenReturn(fileName); XWikiAttachment oldAttachment = mock(XWikiAttachment.class); when(oldAttachment.getFilename()).thenReturn(fileName); when(oldAttachment.getVersion()).thenReturn(version); when(oldAttachment.getDate()).thenReturn(date); DocumentReference documentReference = new DocumentReference("wiki", "Space", "Page"); XWikiDocument document = mock(XWikiDocument.class); when(document.getDocumentReference()).thenReturn(documentReference); when(document.getAttachmentList()).thenReturn(Arrays.asList(currentAttachment)); when(document.getAttachment(fileName)).thenReturn(currentAttachment); XWikiDocument result = mock(XWikiDocument.class); when(result.clone()).thenReturn(result); when(result.getDocumentReference()).thenReturn(documentReference); when(result.getAttachmentList()).thenReturn(Arrays.asList(oldAttachment)); when(result.getAttachment(fileName)).thenReturn(oldAttachment); String revision = "3.5"; when(xwiki.getVersioningStore().loadXWikiDoc(document, revision, context)).thenReturn(result); AttachmentRecycleBinStore attachmentRecycleBinStore = mock(AttachmentRecycleBinStore.class); xwiki.setAttachmentRecycleBinStore(attachmentRecycleBinStore); DocumentReference reference = document.getDocumentReference(); this.mocker.registerMockComponent(ContextualLocalizationManager.class); when(xwiki.getStore().loadXWikiDoc(any(XWikiDocument.class), same(context))) .thenReturn(new XWikiDocument(reference)); xwiki.rollback(document, revision, context); verify(attachmentRecycleBinStore, never()) .saveToRecycleBin( same(currentAttachment), any(String.class), any(Date.class), same(context), eq(true)); verify(oldAttachment, never()).setMetaDataDirty(true); }
/** * Set the response HTTP headers common to both partial (Range) and full responses. * * @param attachment the attachment to get content from * @param request the current client request * @param response the response to write to. * @param context the current request context */ private static void setCommonHeaders( final XWikiAttachment attachment, final XWikiRequest request, final XWikiResponse response, final XWikiContext context) { // Choose the right content type String mimetype = attachment.getMimeType(context); response.setContentType(mimetype); try { response.setCharacterEncoding(""); } catch (IllegalCharsetNameException ex) { response.setCharacterEncoding(XWiki.DEFAULT_ENCODING); } String ofilename = Util.encodeURI(attachment.getFilename(), context).replaceAll("\\+", "%20"); // The inline attribute of Content-Disposition tells the browser that they should display // the downloaded file in the page (see http://www.ietf.org/rfc/rfc1806.txt for more // details). We do this so that JPG, GIF, PNG, etc are displayed without prompting a Save // dialog box. However, all mime types that cannot be displayed by the browser do prompt a // Save dialog box (exe, zip, xar, etc). String dispType = "inline"; // Determine whether the user who attached the file has Programming Rights or not. boolean hasPR = false; String author = attachment.getAuthor(); try { hasPR = context .getWiki() .getRightService() .hasAccessLevel("programming", author, "XWiki.XWikiPreferences", context); } catch (Exception e) { hasPR = false; } // If the mimetype is not authorized to be displayed inline, let's force its content disposition // to download. if ((!hasPR && !isAuthorized(mimetype)) || "1".equals(request.getParameter("force-download"))) { dispType = ATTACHMENT; } // Use RFC 2231 for encoding filenames, since the normal HTTP headers only allows ASCII // characters. // See http://tools.ietf.org/html/rfc2231 for more details. response.addHeader("Content-disposition", dispType + "; filename*=utf-8''" + ofilename); response.setDateHeader("Last-Modified", attachment.getDate().getTime()); // Advertise that downloads can be resumed response.setHeader("Accept-Ranges", "bytes"); }
/** * Transforms the given image (i.e. shrinks the image and changes its quality) before it is * downloaded. * * @param image the image to be downloaded * @param width the desired image width; this value is taken into account only if it is greater * than zero and less than the current image width * @param height the desired image height; this value is taken into account only if it is greater * than zero and less than the current image height * @param quality the desired compression quality * @param context the XWiki context * @return the transformed image * @throws Exception if transforming the image fails */ private XWikiAttachment downloadImage( XWikiAttachment image, int width, int height, float quality, XWikiContext context) throws Exception { initCache(context); boolean keepAspectRatio = Boolean.valueOf(context.getRequest().getParameter("keepAspectRatio")); XWikiAttachment thumbnail = (this.imageCache == null) ? shrinkImage(image, width, height, keepAspectRatio, quality, context) : downloadImageFromCache(image, width, height, keepAspectRatio, quality, context); // If the image has been transformed, update the file name extension to match the image format. String fileName = thumbnail.getFilename(); String extension = StringUtils.lowerCase(StringUtils.substringAfterLast(fileName, String.valueOf('.'))); if (thumbnail != image && !Arrays.asList("jpeg", "jpg", "png").contains(extension)) { // The scaled image is PNG, so correct the extension in order to output the correct MIME type. thumbnail.setFilename(StringUtils.substringBeforeLast(fileName, ".") + ".png"); } return thumbnail; }
private int installDocument( DocumentInfo doc, boolean isAdmin, boolean backup, XWikiContext context) throws XWikiException { if (this.preserveVersion && this.withVersions) { // Right now importing an archive and the history revisions it contains // without overriding the existing document is not supported. // We fallback on adding a new version to the existing history without importing the // archive's revisions. this.withVersions = false; } int result = DocumentInfo.INSTALL_OK; if (LOGGER.isDebugEnabled()) { LOGGER.debug("Package installing document " + doc.getFullName() + " " + doc.getLanguage()); } if (doc.getAction() == DocumentInfo.ACTION_SKIP) { addToSkipped(doc.getFullName() + ":" + doc.getLanguage(), context); return DocumentInfo.INSTALL_OK; } int status = doc.testInstall(isAdmin, context); if (status == DocumentInfo.INSTALL_IMPOSSIBLE) { addToErrors(doc.getFullName() + ":" + doc.getLanguage(), context); return DocumentInfo.INSTALL_IMPOSSIBLE; } if (status == DocumentInfo.INSTALL_OK || status == DocumentInfo.INSTALL_ALREADY_EXIST && doc.getAction() == DocumentInfo.ACTION_OVERWRITE) { XWikiDocument previousdoc = null; if (status == DocumentInfo.INSTALL_ALREADY_EXIST) { previousdoc = context.getWiki().getDocument(doc.getFullName(), context); // if this document is a translation: we should only delete the translation if (doc.getDoc().getTranslation() != 0) { previousdoc = previousdoc.getTranslatedDocument(doc.getLanguage(), context); } // we should only delete the previous document // if we are overridding the versions and/or if this is a backup pack if (!this.preserveVersion || this.withVersions) { try { // This is not a real document delete, it's a upgrade. To be sure to not // generate DELETE notification we directly use {@link XWikiStoreInterface} context.getWiki().getStore().deleteXWikiDoc(previousdoc, context); } catch (Exception e) { // let's log the error but not stop result = DocumentInfo.INSTALL_ERROR; addToErrors(doc.getFullName() + ":" + doc.getLanguage(), context); if (LOGGER.isErrorEnabled()) { LOGGER.error("Failed to delete document " + previousdoc.getDocumentReference()); } if (LOGGER.isDebugEnabled()) { LOGGER.debug("Failed to delete document " + previousdoc.getDocumentReference(), e); } } } else if (previousdoc.hasElement(XWikiDocument.HAS_ATTACHMENTS)) { // We conserve the old attachments in the new documents List<XWikiAttachment> newDocAttachments = doc.getDoc().getAttachmentList(); for (XWikiAttachment att : previousdoc.getAttachmentList()) { if (doc.getDoc().getAttachment(att.getFilename()) == null) { // We add the attachment to new document newDocAttachments.add(att); // But then we add it in the "to remove list" of the document // So the attachment will be removed from the database when XWiki#saveDocument // will be called doc.getDoc().removeAttachment(att); } } } doc.getDoc().addXObjectsToRemoveFromVersion(previousdoc); doc.getDoc().setOriginalDocument(previousdoc); } try { if (!backup) { doc.getDoc().setAuthorReference(context.getUserReference()); doc.getDoc().setContentAuthorReference(context.getUserReference()); // if the import is not a backup pack we set the date to now Date date = new Date(); doc.getDoc().setDate(date); doc.getDoc().setContentUpdateDate(date); } if (!this.withVersions) { doc.getDoc().setVersion("1.1"); } // Does the document to be imported already exists in the wiki ? boolean isNewDocument = previousdoc == null; // Conserve existing history only if asked for it and if this history exists boolean conserveExistingHistory = this.preserveVersion && !isNewDocument; // Does the document from the package contains history revisions ? boolean packageHasHistory = this.documentContainsHistory(doc); // Reset to initial (1.1) version when we don't want to conserve existing history and either // we don't // want the package history or this latter one is empty boolean shouldResetToInitialVersion = !conserveExistingHistory && (!this.withVersions || !packageHasHistory); if (conserveExistingHistory) { // Insert the archive from the existing document doc.getDoc().setDocumentArchive(previousdoc.getDocumentArchive(context)); } else { // Reset or replace history // if there was not history in the source package then we should reset the version number // to 1.1 if (shouldResetToInitialVersion) { // Make sure the save will not increment the version to 2.1 doc.getDoc().setContentDirty(false); doc.getDoc().setMetaDataDirty(false); } } String saveMessage = context.getMessageTool().get("core.importer.saveDocumentComment"); context.getWiki().saveDocument(doc.getDoc(), saveMessage, context); addToInstalled(doc.getFullName() + ":" + doc.getLanguage(), context); if ((this.withVersions && packageHasHistory) || conserveExistingHistory) { // we need to force the saving the document archive. if (doc.getDoc().getDocumentArchive() != null) { context .getWiki() .getVersioningStore() .saveXWikiDocArchive(doc.getDoc().getDocumentArchive(context), true, context); } } if (shouldResetToInitialVersion) { // If we override and do not import version, (meaning reset document to 1.1) // We need manually reset possible existing revision for the document // This means making the history empty (it does not affect the version number) doc.getDoc().resetArchive(context); } } catch (XWikiException e) { addToErrors(doc.getFullName() + ":" + doc.getLanguage(), context); if (LOGGER.isErrorEnabled()) { LOGGER.error("Failed to save document " + doc.getFullName(), e); } result = DocumentInfo.INSTALL_ERROR; } } return result; }