Search in sources :

Example 1 with DeletedAttachment

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

the class DeleteAttachmentAction method action.

@Override
public boolean action(XWikiContext context) throws XWikiException {
    // CSRF prevention
    if (!csrfTokenCheck(context)) {
        return false;
    }
    XWikiRequest request = context.getRequest();
    XWikiResponse response = context.getResponse();
    XWikiDocument doc = context.getDoc();
    XWikiAttachment attachment = null;
    XWiki xwiki = context.getWiki();
    String filename;
    // Delete from the trash
    if (request.getParameter("trashId") != null) {
        long trashId = NumberUtils.toLong(request.getParameter("trashId"));
        DeletedAttachment da = xwiki.getAttachmentRecycleBinStore().getDeletedAttachment(trashId, context, true);
        // don't try to delete it and instead redirect to the attachment list.
        if (da != null) {
            com.xpn.xwiki.api.DeletedAttachment daapi = new com.xpn.xwiki.api.DeletedAttachment(da, context);
            if (!daapi.canDelete()) {
                throw new XWikiException(XWikiException.MODULE_XWIKI_ACCESS, XWikiException.ERROR_XWIKI_ACCESS_DENIED, "You are not allowed to delete an attachment from the trash " + "immediately after it has been deleted from the wiki");
            }
            if (!da.getDocName().equals(doc.getFullName())) {
                throw new XWikiException(XWikiException.MODULE_XWIKI_APP, XWikiException.ERROR_XWIKI_APP_URL_EXCEPTION, "The specified trash entry does not match the current document");
            }
            // TODO: Add a confirmation check
            xwiki.getAttachmentRecycleBinStore().deleteFromRecycleBin(trashId, context, true);
        }
        sendRedirect(response, Utils.getRedirect("attach", context));
        return false;
    }
    if (context.getMode() == XWikiContext.MODE_PORTLET) {
        filename = request.getParameter("filename");
    } else {
        // Note: We use getRequestURI() because the spec says the server doesn't decode it, as
        // we want to use our own decoding.
        String requestUri = request.getRequestURI();
        filename = getFileName();
    }
    XWikiDocument newdoc = doc.clone();
    // An attachment can be indicated either using an id, or using the filename.
    if (request.getParameter("id") != null) {
        int id = NumberUtils.toInt(request.getParameter("id"));
        if (newdoc.getAttachmentList().size() > id) {
            attachment = newdoc.getAttachmentList().get(id);
        }
    } else {
        attachment = newdoc.getAttachment(filename);
    }
    // No such attachment
    if (attachment == null) {
        response.setStatus(HttpServletResponse.SC_NOT_FOUND);
        ScriptContext scriptContext = getCurrentScriptContext();
        if (scriptContext != null) {
            scriptContext.setAttribute("message", localizePlainOrKey("core.action.deleteAttachment.failed", filename), ScriptContext.ENGINE_SCOPE);
            scriptContext.setAttribute("details", localizePlainOrKey("platform.core.action.deleteAttachment.noAttachment"), ScriptContext.ENGINE_SCOPE);
        }
        return true;
    }
    newdoc.setAuthorReference(context.getUserReference());
    // Set "deleted attachment" as the version comment.
    String comment;
    if (attachment.isImage(context)) {
        comment = localizePlainOrKey("core.comment.deleteImageComment", filename);
    } else {
        comment = localizePlainOrKey("core.comment.deleteAttachmentComment", filename);
    }
    try {
        newdoc.removeAttachment(attachment);
        xwiki.saveDocument(newdoc, comment, context);
    } catch (Exception ex) {
        response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        ScriptContext scriptContext = getCurrentScriptContext();
        if (scriptContext != null) {
            scriptContext.setAttribute("message", localizePlainOrKey("core.action.deleteAttachment.failed", filename), ScriptContext.ENGINE_SCOPE);
            scriptContext.setAttribute("details", ExceptionUtils.getRootCauseMessage(ex), ScriptContext.ENGINE_SCOPE);
        }
        return true;
    }
    // forward to attach page
    if (!((Boolean) context.get("ajax")).booleanValue()) {
        String redirect = Utils.getRedirect("attach", context);
        sendRedirect(response, redirect);
    }
    return false;
}
Also used : XWiki(com.xpn.xwiki.XWiki) ScriptContext(javax.script.ScriptContext) XWikiAttachment(com.xpn.xwiki.doc.XWikiAttachment) DeletedAttachment(com.xpn.xwiki.doc.DeletedAttachment) XWikiException(com.xpn.xwiki.XWikiException) XWikiDocument(com.xpn.xwiki.doc.XWikiDocument) XWikiException(com.xpn.xwiki.XWikiException)

Example 2 with DeletedAttachment

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

the class R910100XWIKI14871DataMigration method storeDeletedAttachment.

private void storeDeletedAttachment(File directory, long id, Session session) throws ParserConfigurationException, SAXException, IOException {
    this.logger.info("Storing attachment metadata [{}] in the database", directory);
    // Find document reference
    File documentDirectory = directory.getParentFile().getParentFile().getParentFile();
    DocumentReference documentReference = getDocumentReference(documentDirectory);
    if (getXWikiContext().getWikiReference().equals(documentReference.getWikiReference())) {
        // Parse ~DELETED_ATTACH_METADATA.xml
        File file = new File(directory, "~DELETED_ATTACH_METADATA.xml");
        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
        Document doc = dBuilder.parse(file);
        String filename = getElementText(doc, "filename", null);
        String deleter = getElementText(doc, "deleter", null);
        Date deleteDate = new Date(Long.valueOf(getElementText(doc, "datedeleted", null)));
        long docId = new XWikiDocument(documentReference).getId();
        // We need to make sure the deleted attachment is not already in the database with a different id (left
        // there by the attachment porter script for example)
        Query selectQuery = session.createQuery("SELECT id FROM DeletedAttachment WHERE docId=? AND filename=? AND date=?");
        selectQuery.setLong(0, docId);
        selectQuery.setString(1, filename);
        selectQuery.setTimestamp(2, new java.sql.Timestamp(deleteDate.getTime()));
        Long databaseId = (Long) selectQuery.uniqueResult();
        if (databaseId == null) {
            // Try without the milliseconds since most versions of MySQL don't support them
            selectQuery.setTimestamp(2, new java.sql.Timestamp(deleteDate.toInstant().getEpochSecond() * 1000));
            databaseId = (Long) selectQuery.uniqueResult();
        }
        DeletedAttachment dbAttachment;
        if (databaseId != null) {
            // Update the database metadata (probably left there by the attachment porter script)
            dbAttachment = new DeletedAttachment(docId, this.serializer.serialize(documentReference), filename, FileSystemStoreUtils.HINT, deleter, deleteDate, null, databaseId);
            session.update(dbAttachment);
        } else {
            // Insert new deleted attachment metadata in the DB
            dbAttachment = new DeletedAttachment(docId, this.serializer.serialize(documentReference), filename, FileSystemStoreUtils.HINT, deleter, deleteDate, null);
            databaseId = (Long) session.save(dbAttachment);
        }
        // Refactor file storage to be based on database id instead of date
        File newDirectory = new File(directory.getParentFile(), encode(dbAttachment.getFilename() + "-id" + databaseId));
        FileUtils.moveDirectory(directory, newDirectory);
    }
}
Also used : DocumentBuilderFactory(javax.xml.parsers.DocumentBuilderFactory) Query(org.hibernate.Query) Document(org.w3c.dom.Document) XWikiDocument(com.xpn.xwiki.doc.XWikiDocument) DeletedAttachment(com.xpn.xwiki.doc.DeletedAttachment) Date(java.util.Date) XWikiDocument(com.xpn.xwiki.doc.XWikiDocument) DocumentBuilder(javax.xml.parsers.DocumentBuilder) File(java.io.File) DocumentReference(org.xwiki.model.reference.DocumentReference)

Example 3 with DeletedAttachment

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

the class HibernateAttachmentRecycleBinStore method createDeletedAttachment.

private DeletedAttachment createDeletedAttachment(XWikiAttachment attachment, String deleter, Date date, AttachmentRecycleBinContentStore contentStore) throws XWikiException {
    DeletedAttachment trashdoc;
    String storeType = null;
    DeletedAttachmentContent deletedDocumentContent = null;
    if (contentStore != null) {
        storeType = contentStore.getHint();
    } else {
        deletedDocumentContent = new HibernateDeletedAttachmentContent(attachment);
    }
    trashdoc = new DeletedAttachment(attachment.getDocId(), attachment.getDoc().getFullName(), attachment.getFilename(), storeType, deleter, date, deletedDocumentContent);
    return trashdoc;
}
Also used : DeletedAttachmentContent(com.xpn.xwiki.doc.DeletedAttachmentContent) HibernateDeletedAttachmentContent(com.xpn.xwiki.internal.store.hibernate.HibernateDeletedAttachmentContent) DeletedAttachment(com.xpn.xwiki.doc.DeletedAttachment) HibernateDeletedAttachmentContent(com.xpn.xwiki.internal.store.hibernate.HibernateDeletedAttachmentContent)

Example 4 with DeletedAttachment

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

the class XWikiServletURLFactory method findDeletedAttachmentForDocRevision.

public long findDeletedAttachmentForDocRevision(XWikiDocument doc, String docRevision, String filename, XWikiContext context) throws XWikiException {
    XWikiAttachment attachment = null;
    XWikiDocument rdoc = context.getWiki().getDocument(doc, docRevision, context);
    if (context.getWiki().hasAttachmentRecycleBin(context) && filename != null) {
        attachment = rdoc.getAttachment(filename);
        if (attachment != null) {
            List<DeletedAttachment> deleted = context.getWiki().getAttachmentRecycleBinStore().getAllDeletedAttachments(attachment, context, true);
            Collections.reverse(deleted);
            for (DeletedAttachment entry : deleted) {
                if (entry.getDate().after(rdoc.getDate())) {
                    return entry.getId();
                }
            }
        }
    }
    return -1;
}
Also used : XWikiDocument(com.xpn.xwiki.doc.XWikiDocument) XWikiAttachment(com.xpn.xwiki.doc.XWikiAttachment) DeletedAttachment(com.xpn.xwiki.doc.DeletedAttachment)

Example 5 with DeletedAttachment

use of com.xpn.xwiki.doc.DeletedAttachment 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)

Aggregations

DeletedAttachment (com.xpn.xwiki.doc.DeletedAttachment)5 XWikiDocument (com.xpn.xwiki.doc.XWikiDocument)4 XWikiAttachment (com.xpn.xwiki.doc.XWikiAttachment)3 Date (java.util.Date)2 XWiki (com.xpn.xwiki.XWiki)1 XWikiException (com.xpn.xwiki.XWikiException)1 DeletedAttachmentContent (com.xpn.xwiki.doc.DeletedAttachmentContent)1 XWikiAttachmentToRemove (com.xpn.xwiki.doc.XWikiDocument.XWikiAttachmentToRemove)1 ParseGroovyFromString (com.xpn.xwiki.internal.render.groovy.ParseGroovyFromString)1 HibernateDeletedAttachmentContent (com.xpn.xwiki.internal.store.hibernate.HibernateDeletedAttachmentContent)1 IncludeServletAsString (com.xpn.xwiki.web.includeservletasstring.IncludeServletAsString)1 File (java.io.File)1 ArrayList (java.util.ArrayList)1 ScriptContext (javax.script.ScriptContext)1 DocumentBuilder (javax.xml.parsers.DocumentBuilder)1 DocumentBuilderFactory (javax.xml.parsers.DocumentBuilderFactory)1 Query (org.hibernate.Query)1 Document (org.w3c.dom.Document)1 DocumentRolledBackEvent (org.xwiki.bridge.event.DocumentRolledBackEvent)1 DocumentRollingBackEvent (org.xwiki.bridge.event.DocumentRollingBackEvent)1