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;
}
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;
}
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);
}
}
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;
}
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();
}
Aggregations