Search in sources :

Example 91 with XWikiAttachment

use of com.xpn.xwiki.doc.XWikiAttachment in project xwiki-platform by xwiki.

the class XWiki method rollback.

public XWikiDocument rollback(final XWikiDocument tdoc, String rev, XWikiContext context) throws XWikiException {
    LOGGER.debug("Rolling back [{}] to version [{}]", tdoc, rev);
    // Let's clone rolledbackDoc since we might modify it
    XWikiDocument rolledbackDoc = getDocument(tdoc, rev, context).clone();
    if ("1".equals(getConfiguration().getProperty("xwiki.store.rollbackattachmentwithdocuments", "1"))) {
        // Attachment handling strategy:
        // - Two lists: Old Attachments, Current Attachments
        // Goals:
        // 1. Attachments that are only in OA must be restored from the trash
        // 2. Attachments that are only in CA must be sent to the trash
        // 3. Attachments that are in both lists should be reverted to the right version
        // 4. Gotcha: deleted and re-uploaded attachments should be both trashed and restored.
        // Plan:
        // - Construct two lists: to restore, to revert
        // - Iterate over OA.
        // -- If the attachment is not in CA, add it to the restore list
        // -- If it is in CA, but the date of the first version of the current attachment is after the date of the
        // restored document version, add it the restore & move the current attachment to the recycle bin
        // -- Otherwise, add it to the revert list
        // - Iterate over CA
        // -- If the attachment is not in OA, delete it
        List<XWikiAttachment> oldAttachments = rolledbackDoc.getAttachmentList();
        List<XWikiAttachment> currentAttachments = tdoc.getAttachmentList();
        List<XWikiAttachment> toRestore = new ArrayList<>();
        List<XWikiAttachment> toRevert = new ArrayList<>();
        // First step, determine what to do with each attachment
        LOGGER.debug("Checking attachments");
        for (XWikiAttachment oldAttachment : oldAttachments) {
            String filename = oldAttachment.getFilename();
            XWikiAttachment equivalentAttachment = tdoc.getAttachment(filename);
            if (equivalentAttachment == null) {
                // Deleted attachment
                LOGGER.debug("Deleted attachment: [{}]", filename);
                toRestore.add(oldAttachment);
                continue;
            }
            XWikiAttachment equivalentAttachmentRevision = equivalentAttachment.getAttachmentRevision(oldAttachment.getVersion(), context);
            // because the nanoseconds component of the passed date is unknown.
            if (equivalentAttachmentRevision == null || equivalentAttachmentRevision.getDate().getTime() != oldAttachment.getDate().getTime()) {
                // Recreated attachment
                LOGGER.debug("Recreated attachment: [{}]", filename);
                // If the attachment trash is not available, don't lose the existing attachment
                if (getAttachmentRecycleBinStore() != null) {
                    getAttachmentRecycleBinStore().saveToRecycleBin(equivalentAttachment, context.getUser(), new Date(), context, true);
                    toRestore.add(oldAttachment);
                }
                continue;
            }
            if (!StringUtils.equals(oldAttachment.getVersion(), equivalentAttachment.getVersion())) {
                // Updated attachment
                LOGGER.debug("Updated attachment: [{}]", filename);
                toRevert.add(equivalentAttachment);
            }
        }
        for (XWikiAttachment attachment : currentAttachments) {
            if (rolledbackDoc.getAttachment(attachment.getFilename()) == null) {
                LOGGER.debug("New attachment: " + attachment.getFilename());
                // XWikiDocument#save() is actually the only way to delete an attachment cleanly
                rolledbackDoc.getAttachmentsToRemove().add(new XWikiAttachmentToRemove(attachment, true));
            }
        }
        // Revert updated attachments to the old version
        for (XWikiAttachment attachmentToRevert : toRevert) {
            String oldAttachmentVersion = rolledbackDoc.getAttachment(attachmentToRevert.getFilename()).getVersion();
            XWikiAttachment oldAttachmentRevision = attachmentToRevert.getAttachmentRevision(oldAttachmentVersion, context);
            if (oldAttachmentRevision == null) {
                // Previous version is lost, just leave the current version in place
                rolledbackDoc.setAttachment(attachmentToRevert);
                continue;
            }
            // We can't just leave the old version in place, since it will break the revision history, given the
            // current implementation, so we set the attachment version to the most recent version, mark the content
            // as dirty, and the storage will automatically bump up the version number.
            // This is a hack, to be fixed once the storage doesn't take care of updating the history and version,
            // and once the current attachment version can point to an existing version from the history.
            oldAttachmentRevision.setVersion(attachmentToRevert.getVersion());
            oldAttachmentRevision.setMetaDataDirty(true);
            oldAttachmentRevision.getAttachment_content().setContentDirty(true);
            rolledbackDoc.setAttachment(oldAttachmentRevision);
        }
        // Restore deleted attachments from the trash
        if (getAttachmentRecycleBinStore() != null) {
            for (XWikiAttachment attachmentToRestore : toRestore) {
                // There might be multiple versions of the attachment in the trash, search for the right one
                List<DeletedAttachment> deletedVariants = getAttachmentRecycleBinStore().getAllDeletedAttachments(attachmentToRestore, context, true);
                DeletedAttachment correctVariant = null;
                for (DeletedAttachment variant : deletedVariants) {
                    // Reverse chronological order
                    if (variant.getDate().before(rolledbackDoc.getDate())) {
                        break;
                    }
                    correctVariant = variant;
                }
                if (correctVariant == null) {
                    // Not found in the trash, nothing left to do
                    continue;
                }
                XWikiAttachment restoredAttachment = correctVariant.restoreAttachment();
                XWikiAttachment restoredAttachmentRevision = restoredAttachment.getAttachmentRevision(attachmentToRestore.getVersion(), context);
                if (restoredAttachmentRevision != null) {
                    restoredAttachmentRevision.setAttachment_archive(restoredAttachment.getAttachment_archive());
                    restoredAttachmentRevision.getAttachment_archive().setAttachment(restoredAttachmentRevision);
                    restoredAttachmentRevision.setVersion(restoredAttachment.getVersion());
                    restoredAttachmentRevision.setMetaDataDirty(true);
                    restoredAttachmentRevision.getAttachment_content().setContentDirty(true);
                    rolledbackDoc.setAttachment(restoredAttachmentRevision);
                } else {
                    // This particular version is lost, update to the one available
                    rolledbackDoc.setAttachment(restoredAttachment);
                }
            }
        } else {
            // No trash, can't restore. Remove the attachment references, so that the document is not broken
            for (XWikiAttachment attachmentToRestore : toRestore) {
                rolledbackDoc.getAttachmentList().remove(attachmentToRestore);
            }
        }
    }
    // Special treatment for deleted objects
    rolledbackDoc.addXObjectsToRemoveFromVersion(tdoc);
    // now we save the final document..
    rolledbackDoc.setOriginalDocument(tdoc);
    rolledbackDoc.setAuthorReference(context.getUserReference());
    rolledbackDoc.setRCSVersion(tdoc.getRCSVersion());
    rolledbackDoc.setVersion(tdoc.getVersion());
    rolledbackDoc.setContentDirty(true);
    ObservationManager om = getObservationManager();
    if (om != null) {
        // Notify listeners about the document that is going to be rolled back.
        // Note that for the moment the event being send is a bridge event, as we are still passing around
        // an XWikiDocument as source and an XWikiContext as data.
        om.notify(new DocumentRollingBackEvent(rolledbackDoc.getDocumentReference(), rev), rolledbackDoc, context);
    }
    saveDocument(rolledbackDoc, localizePlainOrKey("core.comment.rollback", rev), context);
    // Since the the store resets the original document, we need to temporarily put it back to send notifications.
    XWikiDocument newOriginalDocument = rolledbackDoc.getOriginalDocument();
    rolledbackDoc.setOriginalDocument(tdoc);
    try {
        if (om != null) {
            // Notify listeners about the document that was rolled back.
            // Note that for the moment the event being send is a bridge event, as we are still passing around an
            // XWikiDocument as source and an XWikiContext as data.
            om.notify(new DocumentRolledBackEvent(rolledbackDoc.getDocumentReference(), rev), rolledbackDoc, context);
        }
    } finally {
        rolledbackDoc.setOriginalDocument(newOriginalDocument);
    }
    return rolledbackDoc;
}
Also used : XWikiDocument(com.xpn.xwiki.doc.XWikiDocument) DocumentRollingBackEvent(org.xwiki.bridge.event.DocumentRollingBackEvent) ArrayList(java.util.ArrayList) DocumentRolledBackEvent(org.xwiki.bridge.event.DocumentRolledBackEvent) ObservationManager(org.xwiki.observation.ObservationManager) XWikiAttachment(com.xpn.xwiki.doc.XWikiAttachment) ParseGroovyFromString(com.xpn.xwiki.internal.render.groovy.ParseGroovyFromString) IncludeServletAsString(com.xpn.xwiki.web.includeservletasstring.IncludeServletAsString) XWikiAttachmentToRemove(com.xpn.xwiki.doc.XWikiDocument.XWikiAttachmentToRemove) DeletedAttachment(com.xpn.xwiki.doc.DeletedAttachment) Date(java.util.Date)

Example 92 with XWikiAttachment

use of com.xpn.xwiki.doc.XWikiAttachment in project xwiki-platform by xwiki.

the class XWiki method getDeletedAttachments.

/**
 * Retrieve all the deleted attachments that belonged to a certain document and had the specified name. Multiple
 * versions can be returned since the same file can be uploaded and deleted several times, creating different
 * instances in the trash. Note that this does not distinguish between different incarnations of a document name,
 * and it does not require that the document still exists, it returns all the attachments that at the time of their
 * deletion had a document with the specified name as their owner.
 *
 * @param docName the {@link DeletedAttachment#getDocName() name of the document} the attachment belonged to
 * @param filename the {@link DeletedAttachment#getFilename() name} of the attachment to search for
 * @param context see {@link XWikiContext}
 * @return A list with all the deleted attachments which belonged to the specified document and had the specified
 *         filename. If no such attachments are found in the trash, an empty list is returned.
 * @throws XWikiException if an error occurs while loading the attachments
 */
public List<DeletedAttachment> getDeletedAttachments(String docName, String filename, XWikiContext context) throws XWikiException {
    if (hasAttachmentRecycleBin(context)) {
        XWikiDocument doc = new XWikiDocument(getCurrentMixedDocumentReferenceResolver().resolve(docName));
        XWikiAttachment attachment = new XWikiAttachment(doc, filename);
        return getAttachmentRecycleBinStore().getAllDeletedAttachments(attachment, context, true);
    }
    return null;
}
Also used : XWikiDocument(com.xpn.xwiki.doc.XWikiDocument) XWikiAttachment(com.xpn.xwiki.doc.XWikiAttachment)

Example 93 with XWikiAttachment

use of com.xpn.xwiki.doc.XWikiAttachment in project xwiki-platform by xwiki.

the class XWikiHibernateStore method saveAttachmentList.

private void saveAttachmentList(XWikiDocument doc, XWikiContext context) throws XWikiException {
    try {
        getSession(context);
        List<XWikiAttachment> list = doc.getAttachmentList();
        for (XWikiAttachment attachment : list) {
            saveAttachment(attachment, context);
        }
    } catch (Exception e) {
        Object[] args = { doc.getDocumentReference() };
        throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SAVING_ATTACHMENT_LIST, "Exception while saving attachments attachment list of document {0}", e, args);
    }
}
Also used : XWikiAttachment(com.xpn.xwiki.doc.XWikiAttachment) XWikiException(com.xpn.xwiki.XWikiException) InitializationException(org.xwiki.component.phase.InitializationException) MigrationRequiredException(com.xpn.xwiki.store.migration.MigrationRequiredException) ObjectNotFoundException(org.hibernate.ObjectNotFoundException) QueryException(org.xwiki.query.QueryException) UnexpectedException(org.xwiki.store.UnexpectedException) ComponentLookupException(org.xwiki.component.manager.ComponentLookupException) SQLException(java.sql.SQLException) XWikiException(com.xpn.xwiki.XWikiException)

Example 94 with XWikiAttachment

use of com.xpn.xwiki.doc.XWikiAttachment in project xwiki-platform by xwiki.

the class Package method installDocument.

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);
            }
            // 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 {
                // 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;
}
Also used : XWikiDocument(com.xpn.xwiki.doc.XWikiDocument) List(java.util.List) ArrayList(java.util.ArrayList) LinkedList(java.util.LinkedList) XWikiAttachment(com.xpn.xwiki.doc.XWikiAttachment) XWikiException(com.xpn.xwiki.XWikiException) QueryException(org.xwiki.query.QueryException) DocumentException(org.dom4j.DocumentException) IOException(java.io.IOException) ResolveException(org.xwiki.extension.ResolveException) Date(java.util.Date) XWikiException(com.xpn.xwiki.XWikiException)

Example 95 with XWikiAttachment

use of com.xpn.xwiki.doc.XWikiAttachment in project xwiki-platform by xwiki.

the class FileSystemURLFactory method getURL.

/**
 * Store the requested attachment on the filesystem and return a {@code file://} URL where FOP can access that file.
 *
 * @param wiki the name of the owner document's wiki
 * @param spaces a serialized space reference which can contain one or several spaces (e.g. "space1.space2"). If
 *        a space name contains a dot (".") it must be passed escaped as in "space1\.with\.dot.space2"
 * @param name the name of the owner document
 * @param filename the name of the attachment
 * @param revision an optional attachment version
 * @param context the current request context
 * @return a {@code file://} URL where the attachment has been stored
 * @throws Exception if the attachment can't be retrieved from the database and stored on the filesystem
 */
private URL getURL(String wiki, String spaces, String name, String filename, String revision, XWikiContext context) throws Exception {
    Map<String, File> usedFiles = getFileMapping(context);
    List<String> spaceNames = this.legacySpaceResolver.resolve(spaces);
    String key = getAttachmentKey(spaceNames, name, filename, revision);
    if (!usedFiles.containsKey(key)) {
        File file = getTemporaryFile(key, context);
        LOGGER.debug("Temporary PDF export file [{}]", file.toString());
        XWikiDocument doc = context.getWiki().getDocument(new DocumentReference(StringUtils.defaultString(wiki, context.getWikiId()), spaceNames, name), context);
        XWikiAttachment attachment = doc.getAttachment(filename);
        if (StringUtils.isNotEmpty(revision)) {
            attachment = attachment.getAttachmentRevision(revision, context);
        }
        FileOutputStream fos = new FileOutputStream(file);
        IOUtils.copy(attachment.getContentInputStream(context), fos);
        fos.close();
        usedFiles.put(key, file);
    }
    return usedFiles.get(key).toURI().toURL();
}
Also used : XWikiDocument(com.xpn.xwiki.doc.XWikiDocument) FileOutputStream(java.io.FileOutputStream) XWikiAttachment(com.xpn.xwiki.doc.XWikiAttachment) File(java.io.File) DocumentReference(org.xwiki.model.reference.DocumentReference)

Aggregations

XWikiAttachment (com.xpn.xwiki.doc.XWikiAttachment)133 XWikiDocument (com.xpn.xwiki.doc.XWikiDocument)71 DocumentReference (org.xwiki.model.reference.DocumentReference)51 Test (org.junit.Test)40 XWikiContext (com.xpn.xwiki.XWikiContext)35 XWikiException (com.xpn.xwiki.XWikiException)25 ByteArrayInputStream (java.io.ByteArrayInputStream)20 Attachment (com.xpn.xwiki.api.Attachment)18 Date (java.util.Date)17 IOException (java.io.IOException)15 ArrayList (java.util.ArrayList)15 Document (com.xpn.xwiki.api.Document)14 XWiki (com.xpn.xwiki.XWiki)13 BaseObject (com.xpn.xwiki.objects.BaseObject)13 AttachmentReference (org.xwiki.model.reference.AttachmentReference)13 InputStream (java.io.InputStream)11 BaseClass (com.xpn.xwiki.objects.classes.BaseClass)10 File (java.io.File)10 URL (java.net.URL)7 List (java.util.List)7