Index: core/xwiki-core/src/test/java/com/xpn/xwiki/doc/XWikiDocumentTest.java =================================================================== --- core/xwiki-core/src/test/java/com/xpn/xwiki/doc/XWikiDocumentTest.java (revision 23481) +++ core/xwiki-core/src/test/java/com/xpn/xwiki/doc/XWikiDocumentTest.java (working copy) @@ -741,7 +741,10 @@ doc3.setContent("[[" + DOCWIKI + ":" + DOCSPACE + "." + DOCNAME + "]]"); doc3.setSyntaxId("xwiki/2.0"); - this.mockXWiki.stubs().method("copyDocument").will(returnValue(true)); + //this.mockXWiki.stubs().method("copyDocument").will(returnValue(true)); + this.mockXWiki.stubs().method("getDocument").with(eq("newwikiname:newspace.newpage"), ANYTHING) + .will(returnValue(new XWikiDocument())); + this.mockXWikiStoreInterface.stubs().method("getTranslationList").will(returnValue(new ArrayList())); this.mockXWiki.stubs().method("getDocument").with(eq("1"), ANYTHING).will(returnValue(doc1)); this.mockXWiki.stubs().method("getDocument").with(eq("2"), ANYTHING).will(returnValue(doc2)); this.mockXWiki.stubs().method("getDocument").with(eq("3"), ANYTHING).will(returnValue(doc3)); Index: core/xwiki-core/src/main/java/com/xpn/xwiki/doc/XWikiDocument.java =================================================================== --- core/xwiki-core/src/main/java/com/xpn/xwiki/doc/XWikiDocument.java (revision 23481) +++ core/xwiki-core/src/main/java/com/xpn/xwiki/doc/XWikiDocument.java (working copy) @@ -4082,9 +4082,63 @@ return; } + // Make a list of documents to be saved for easy saving in single transaction. + ArrayList docsToSave = new ArrayList(); + // Step 1: Copy the document and all its translations under a new name - context.getWiki().copyDocument(getFullName(), newDocumentName, false, context); + // get (hopefully) empty target document. + XWikiDocument newDoc = context.getWiki().getDocument(newDocumentName, context); + // 1.1 error checking + if(!newDoc.isNew()){ + throw new XWikiException(XWikiException.MODULE_XWIKI_DOC, + XWikiException.ERROR_XWIKI_UNKNOWN, + "You are trying to rename this document to "+ + "the name of a document which already exists." + ); + } + // 1.2 for the origional document and all translations, create a map of new by old. + HashMap newDocAndTranslationsByOld = + new java.util.LinkedHashMap(); + newDocAndTranslationsByOld.put(this, (XWikiDocument) newDoc.clone()); + for(String language : this.getTranslationList(context)) { + newDocAndTranslationsByOld.put(this.getTranslatedDocument(language, context), + newDoc.getTranslatedDocument(language, context)); + } + // 1.3 loop over the default language document and all translations, + // make them clones of the origionals, set name to newDocumentName, set correct ID + // and clone archives (with id property of archives changed) + for(XWikiDocument oldDocOrTrans : newDocAndTranslationsByOld.keySet()){ + XWikiDocument newDocOrTrans = newDocAndTranslationsByOld.get(oldDocOrTrans); + + //load objects from database where document contains only pointers + oldDocOrTrans.loadAttachments(context); + oldDocOrTrans.loadArchive(context); + + newDocOrTrans.clone(oldDocOrTrans); + + //fullname must be corrected after it is changed by clone + newDocOrTrans.setFullName(newDocumentName, context); + newDocOrTrans.getxWikiClass().setName(newDocumentName); + newDocOrTrans.getId();//getId corrects document ID (changed by clone) + + //copy archive so origional can be deleted by deleteDocument + //clone only adds a new reference to documentArchive. + //If Content and MetaData are clean, archive (with new id#) is resaved. + XWikiDocumentArchive archive = newDocOrTrans.getDocumentArchive(); + if (archive != null) { + newDocOrTrans.setDocumentArchive(archive.clone(newDocOrTrans.getId(), context)); + } + + newDocOrTrans.setContentDirty(false);//don't want to make a new version + newDocOrTrans.setMetaDataDirty(false);//also see above. + docsToSave.add(newDocOrTrans); + } + // 1.4 Save each document. Should save in one transaction... + for(XWikiDocument docToSave : docsToSave){ + context.getWiki().saveDocument(docToSave, context); + } + // Step 2: For each backlink to rename, parse the backlink document and replace the links // with the new name. // Note: we ignore invalid links here. Invalid links should be shown to the user so