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