Search in sources :

Example 1 with XWikiAttachmentToRemove

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

the class XWikiHibernateStore method saveXWikiDoc.

@Override
public void saveXWikiDoc(XWikiDocument doc, XWikiContext inputxcontext, boolean bTransaction) throws XWikiException {
    XWikiContext context = getExecutionXContext(inputxcontext, true);
    try {
        MonitorPlugin monitor = Util.getMonitorPlugin(context);
        try {
            // Start monitoring timer
            if (monitor != null) {
                monitor.startTimer(HINT);
            }
            doc.setStore(this);
            // Make sure the database name is stored
            doc.setDatabase(context.getWikiId());
            // If the comment is larger than the max size supported by the Storage, then abbreviate it
            String comment = doc.getComment();
            if (comment != null && comment.length() > 1023) {
                doc.setComment(StringUtils.abbreviate(comment, 1023));
            }
            if (bTransaction) {
                checkHibernate(context);
                SessionFactory sfactory = injectCustomMappingsInSessionFactory(doc, context);
                bTransaction = beginTransaction(sfactory, context);
            }
            Session session = getSession(context);
            session.setFlushMode(FlushMode.COMMIT);
            // These informations will allow to not look for attachments and objects on loading
            doc.setElement(XWikiDocument.HAS_ATTACHMENTS, !doc.getAttachmentList().isEmpty());
            doc.setElement(XWikiDocument.HAS_OBJECTS, !doc.getXObjects().isEmpty());
            // Let's update the class XML since this is the new way to store it
            // TODO If all the properties are removed, the old xml stays?
            BaseClass bclass = doc.getXClass();
            if (bclass != null) {
                if (bclass.getFieldList().isEmpty()) {
                    doc.setXClassXML("");
                } else {
                    // Don't format the XML to reduce the size of the stored data as much as possible
                    doc.setXClassXML(bclass.toXMLString(false));
                }
                bclass.setDirty(false);
            }
            if (doc.hasElement(XWikiDocument.HAS_ATTACHMENTS)) {
                saveAttachmentList(doc, context);
            }
            // Remove attachments planned for removal
            if (!doc.getAttachmentsToRemove().isEmpty()) {
                for (XWikiAttachmentToRemove attachmentToRemove : doc.getAttachmentsToRemove()) {
                    XWikiAttachment attachment = attachmentToRemove.getAttachment();
                    XWikiAttachmentStoreInterface store = getXWikiAttachmentStoreInterface(attachment);
                    store.deleteXWikiAttachment(attachment, false, context, false);
                }
                doc.clearAttachmentsToRemove();
            }
            // Handle the latest text file
            if (doc.isContentDirty() || doc.isMetaDataDirty()) {
                Date ndate = new Date();
                doc.setDate(ndate);
                if (doc.isContentDirty()) {
                    doc.setContentUpdateDate(ndate);
                    doc.setContentAuthorReference(doc.getAuthorReference());
                }
                doc.incrementVersion();
                if (context.getWiki().hasVersioning(context)) {
                    context.getWiki().getVersioningStore().updateXWikiDocArchive(doc, false, context);
                }
                doc.setContentDirty(false);
                doc.setMetaDataDirty(false);
            } else {
                if (doc.getDocumentArchive() != null) {
                    // This is especially needed if we load a document from XML
                    if (context.getWiki().hasVersioning(context)) {
                        context.getWiki().getVersioningStore().saveXWikiDocArchive(doc.getDocumentArchive(), false, context);
                        // If the version does not exist it means it's a new version so add it to the history
                        if (!containsVersion(doc, doc.getRCSVersion(), context)) {
                            context.getWiki().getVersioningStore().updateXWikiDocArchive(doc, false, context);
                        }
                    }
                } else {
                    // with a valid context
                    try {
                        if (context.getWiki().hasVersioning(context)) {
                            doc.getDocumentArchive(context);
                            // history
                            if (!containsVersion(doc, doc.getRCSVersion(), context)) {
                                context.getWiki().getVersioningStore().updateXWikiDocArchive(doc, false, context);
                            }
                        }
                    } catch (XWikiException e) {
                    // this is a non critical error
                    }
                }
            }
            // Verify if the document already exists
            Query query = session.createQuery("select xwikidoc.id from XWikiDocument as xwikidoc where xwikidoc.id = :id");
            query.setLong("id", doc.getId());
            if (query.uniqueResult() == null) {
                if (doc.isContentDirty() || doc.isMetaDataDirty()) {
                    // Reset the creationDate to reflect the date of the first save, not the date of the object
                    // creation
                    doc.setCreationDate(new Date());
                }
                session.save(doc);
            } else {
                session.update(doc);
            // TODO: this is slower!! How can it be improved?
            // session.saveOrUpdate(doc);
            }
            // Remove objects planned for removal
            if (doc.getXObjectsToRemove().size() > 0) {
                for (BaseObject removedObject : doc.getXObjectsToRemove()) {
                    deleteXWikiCollection(removedObject, context, false, false);
                }
                doc.setXObjectsToRemove(new ArrayList<BaseObject>());
            }
            if (bclass != null) {
                bclass.setDocumentReference(doc.getDocumentReference());
                // Store this XWikiClass in the context so that we can use it in case of recursive usage of classes
                context.addBaseClass(bclass);
            }
            if (doc.hasElement(XWikiDocument.HAS_OBJECTS)) {
                // TODO: Delete all objects for which we don't have a name in the Map
                for (List<BaseObject> objects : doc.getXObjects().values()) {
                    for (BaseObject obj : objects) {
                        if (obj != null) {
                            obj.setDocumentReference(doc.getDocumentReference());
                            /* If the object doesn't have a GUID, create it before saving */
                            if (StringUtils.isEmpty(obj.getGuid())) {
                                obj.setGuid(null);
                            }
                            saveXWikiCollection(obj, context, false);
                        }
                    }
                }
            }
            if (context.getWiki().hasBacklinks(context)) {
                try {
                    saveLinks(doc, context, true);
                } catch (Exception e) {
                    this.logger.error("Failed to save links for document [{}]", doc.getDocumentReferenceWithLocale(), e);
                }
            }
            // Update space table
            updateXWikiSpaceTable(doc, session);
            if (bTransaction) {
                endTransaction(context, true);
            }
            doc.setNew(false);
            // We need to ensure that the saved document becomes the original document
            doc.setOriginalDocument(doc.clone());
        } catch (Exception e) {
            Object[] args = { this.defaultEntityReferenceSerializer.serialize(doc.getDocumentReference()) };
            throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SAVING_DOC, "Exception while saving document {0}", e, args);
        } finally {
            try {
                if (bTransaction) {
                    endTransaction(context, false);
                }
            } catch (Exception e) {
            }
            // End monitoring timer
            if (monitor != null) {
                monitor.endTimer(HINT);
            }
        }
    } finally {
        restoreExecutionXContext();
    }
}
Also used : SessionFactory(org.hibernate.SessionFactory) Query(org.hibernate.Query) MonitorPlugin(com.xpn.xwiki.monitor.api.MonitorPlugin) XWikiContext(com.xpn.xwiki.XWikiContext) XWikiAttachment(com.xpn.xwiki.doc.XWikiAttachment) Date(java.util.Date) 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) BaseObject(com.xpn.xwiki.objects.BaseObject) BaseClass(com.xpn.xwiki.objects.classes.BaseClass) XWikiAttachmentToRemove(com.xpn.xwiki.doc.XWikiDocument.XWikiAttachmentToRemove) XWikiException(com.xpn.xwiki.XWikiException) Session(org.hibernate.Session)

Example 2 with XWikiAttachmentToRemove

use of com.xpn.xwiki.doc.XWikiDocument.XWikiAttachmentToRemove 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 3 with XWikiAttachmentToRemove

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

the class XWiki method saveDocument.

/**
 * @param document the document to save
 * @param comment the comment to associated to the new version of the saved document
 * @param isMinorEdit true if the new version is a minor version
 * @param context see {@link XWikiContext}
 */
public void saveDocument(XWikiDocument document, String comment, boolean isMinorEdit, XWikiContext context) throws XWikiException {
    String currentWiki = context.getWikiId();
    try {
        // Switch to document wiki
        context.setWikiId(document.getDocumentReference().getWikiReference().getName());
        // Setting comment & minor edit before saving
        document.setComment(StringUtils.defaultString(comment));
        document.setMinorEdit(isMinorEdit);
        // We need to save the original document since saveXWikiDoc() will reset it and we
        // need that original document for the notification below.
        XWikiDocument originalDocument = document.getOriginalDocument();
        // (which is not a good practice)
        if (originalDocument == null) {
            originalDocument = getDocument(new DocumentReference(document.getDocumentReference(), document.getLocale()), context);
            document.setOriginalDocument(originalDocument);
        }
        ObservationManager om = getObservationManager();
        if (om != null) {
            CancelableEvent documentEvent;
            if (originalDocument.isNew()) {
                documentEvent = new DocumentCreatingEvent(document.getDocumentReference());
            } else {
                documentEvent = new DocumentUpdatingEvent(document.getDocumentReference());
            }
            om.notify(documentEvent, document, context);
            // If the action has been canceled by the user then don't perform any save and throw an exception
            if (documentEvent.isCanceled()) {
                throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_HIBERNATE_SAVING_DOC, String.format("An Event Listener has cancelled the document save for [%s]. Reason: [%s]", document.getDocumentReference(), documentEvent.getReason()));
            }
        }
        // Put attachments to remove in recycle bin
        if (hasAttachmentRecycleBin(context)) {
            for (XWikiAttachmentToRemove attachment : document.getAttachmentsToRemove()) {
                if (attachment.isToRecycleBin()) {
                    getAttachmentRecycleBinStore().saveToRecycleBin(attachment.getAttachment(), context.getUser(), new Date(), context, true);
                }
            }
        }
        // Actually save the document.
        getStore().saveXWikiDoc(document, context);
        // Since the store#saveXWikiDoc resets originalDocument, we need to temporarily put it
        // back to send notifications.
        XWikiDocument newOriginal = document.getOriginalDocument();
        try {
            document.setOriginalDocument(originalDocument);
            if (om != null) {
                if (originalDocument.isNew()) {
                    om.notify(new DocumentCreatedEvent(document.getDocumentReference()), document, context);
                } else {
                    om.notify(new DocumentUpdatedEvent(document.getDocumentReference()), document, context);
                }
            }
        } catch (Exception ex) {
            LOGGER.error("Failed to send document save notification for document [" + getDefaultEntityReferenceSerializer().serialize(document.getDocumentReference()) + "]", ex);
        } finally {
            document.setOriginalDocument(newOriginal);
        }
    } finally {
        context.setWikiId(currentWiki);
    }
}
Also used : DocumentUpdatingEvent(org.xwiki.bridge.event.DocumentUpdatingEvent) DocumentCreatedEvent(org.xwiki.bridge.event.DocumentCreatedEvent) ObservationManager(org.xwiki.observation.ObservationManager) DocumentUpdatedEvent(org.xwiki.bridge.event.DocumentUpdatedEvent) ParseGroovyFromString(com.xpn.xwiki.internal.render.groovy.ParseGroovyFromString) IncludeServletAsString(com.xpn.xwiki.web.includeservletasstring.IncludeServletAsString) DocumentCreatingEvent(org.xwiki.bridge.event.DocumentCreatingEvent) CancelableEvent(org.xwiki.observation.event.CancelableEvent) Date(java.util.Date) WikiManagerException(org.xwiki.wiki.manager.WikiManagerException) IOException(java.io.IOException) JobException(org.xwiki.job.JobException) ParseException(org.xwiki.rendering.parser.ParseException) QueryException(org.xwiki.query.QueryException) URIException(org.apache.commons.httpclient.URIException) InvocationTargetException(java.lang.reflect.InvocationTargetException) HibernateException(org.hibernate.HibernateException) ComponentLookupException(org.xwiki.component.manager.ComponentLookupException) NamingException(javax.naming.NamingException) FileNotFoundException(java.io.FileNotFoundException) MalformedURLException(java.net.MalformedURLException) XWikiDocument(com.xpn.xwiki.doc.XWikiDocument) XWikiAttachmentToRemove(com.xpn.xwiki.doc.XWikiDocument.XWikiAttachmentToRemove) DocumentReference(org.xwiki.model.reference.DocumentReference) LocalDocumentReference(org.xwiki.model.reference.LocalDocumentReference)

Aggregations

XWikiAttachmentToRemove (com.xpn.xwiki.doc.XWikiDocument.XWikiAttachmentToRemove)3 Date (java.util.Date)3 XWikiAttachment (com.xpn.xwiki.doc.XWikiAttachment)2 XWikiDocument (com.xpn.xwiki.doc.XWikiDocument)2 ParseGroovyFromString (com.xpn.xwiki.internal.render.groovy.ParseGroovyFromString)2 IncludeServletAsString (com.xpn.xwiki.web.includeservletasstring.IncludeServletAsString)2 ComponentLookupException (org.xwiki.component.manager.ComponentLookupException)2 ObservationManager (org.xwiki.observation.ObservationManager)2 QueryException (org.xwiki.query.QueryException)2 XWikiContext (com.xpn.xwiki.XWikiContext)1 XWikiException (com.xpn.xwiki.XWikiException)1 DeletedAttachment (com.xpn.xwiki.doc.DeletedAttachment)1 MonitorPlugin (com.xpn.xwiki.monitor.api.MonitorPlugin)1 BaseObject (com.xpn.xwiki.objects.BaseObject)1 BaseClass (com.xpn.xwiki.objects.classes.BaseClass)1 MigrationRequiredException (com.xpn.xwiki.store.migration.MigrationRequiredException)1 FileNotFoundException (java.io.FileNotFoundException)1 IOException (java.io.IOException)1 InvocationTargetException (java.lang.reflect.InvocationTargetException)1 MalformedURLException (java.net.MalformedURLException)1