Index: xwiki-platform-core/src/test/java/com/xpn/xwiki/doc/XWikiDocumentTest.java =================================================================== --- xwiki-platform-core/src/test/java/com/xpn/xwiki/doc/XWikiDocumentTest.java (revision 4076) +++ xwiki-platform-core/src/test/java/com/xpn/xwiki/doc/XWikiDocumentTest.java (working copy) @@ -94,6 +94,26 @@ assertEquals("Title", this.document.getDisplayTitle(this.context)); } + public void testMinorMajorVersions() { + // there is no version in doc yet, so 1.1 + assertEquals("1", this.document.getVersion()); + + this.document.setMinorEdit(false); + this.document.incrementVersion(); + // no version => incrementVersion sets 1.1 + assertEquals("1.1", this.document.getVersion()); + + this.document.setMinorEdit(false); + this.document.incrementVersion(); + // increment major version + assertEquals("2.1", this.document.getVersion()); + + this.document.setMinorEdit(true); + this.document.incrementVersion(); + // increment minor version + assertEquals("2.2", this.document.getVersion()); + } + public void testAuthorAfterDocumentCopy() throws XWikiException { String author = "Albatross"; Index: xwiki-platform-core/src/test/java/com/xpn/xwiki/doc/XWikiDocumentArchiveTest.java =================================================================== --- xwiki-platform-core/src/test/java/com/xpn/xwiki/doc/XWikiDocumentArchiveTest.java (revision 4076) +++ xwiki-platform-core/src/test/java/com/xpn/xwiki/doc/XWikiDocumentArchiveTest.java (working copy) @@ -19,15 +19,30 @@ */ package com.xpn.xwiki.doc; -import junit.framework.TestCase; +import java.util.Arrays; +import java.util.Date; +import org.apache.tools.ant.filters.StringInputStream; +import org.jmock.cglib.MockObjectTestCase; +import org.suigeneris.jrcs.rcs.Archive; + +import com.xpn.xwiki.XWikiContext; +import com.xpn.xwiki.XWikiException; + /** - * Unit tests for {@link com.xpn.xwiki.doc.XWikiDocumentArchive}. + * Unit tests for {@link XWikiDocumentArchive}. * * @version $Id: $ */ -public class XWikiDocumentArchiveTest extends TestCase +public class XWikiDocumentArchiveTest extends MockObjectTestCase { + private XWikiContext context; + protected void setUp() throws Exception + { + super.setUp(); + this.context = new XWikiContext(); + } + /** * JRCS uses the user.name system property to set the author of a change. Verify that it * works if the user name has a space in its name. This used to fail and this test is here to @@ -37,31 +52,6 @@ */ public void testUpdateArchiveWhenSpaceInUsername() throws Exception { - String originalText = "\"\\n\\n" - + "\\nKnowledgeBase\\nWebHome\\n" - + "\\nen\\n0\\n" - + "Main.Notes\\nXWiki.Admin\\nXWiki.Admin" - + "\\n\\nXWiki.Admin\\n" - + "1165874272000\\n1172011434000\\n" - + "1172011434000\\n1.8\\n" - + "\\n\\n\\n" - + "\\n\\n\\nXWiki.TagClass" - + "\\n\\n\\n" - + "\\n\\n" - + "\\n\\n" - + "\\n\\ntags\\nTags\\n" - + "0\\n1\\n" - + "input\\n1\\n30\\n" - + " \\n ,|\\n\\n" - + "1\\ncom.xpn.xwiki.objects.classes.StaticListClass" - + "\\n\\n\\nKnowledgeBase.WebHome\\n" - + "0\\nXWiki.TagClass\\n\\n\\n" - + "\\n\\n1 Wiki Knowledge Base\\r\\n\\r\\nThis is the " - + "Wiki Knowledge Base, where you can start writing about your favorite subjects.\\r\\n" - + "\\r\\nTo create new pages, click edit button and write links using brackets around " - + "words.\\r\\n\\r\\n* [Example Link 1]\\r\\n* [Example Link 2]\\n" - + "\\n\""; - String originalArchive = "head\t1.1;\n" + "access;\n" + "symbols;\n" + @@ -147,18 +137,95 @@ "* [Example Link 2]\n" + "\n" + "@"; - + XWikiDocumentArchive archive = new XWikiDocumentArchive(123456789L); archive.setArchive(originalArchive); - + // Set a username with a space System.setProperty("user.name", "Vincent Massol"); + + XWikiDocument doc = new XWikiDocument("KnowledgeBase", "WebHome"); + doc.setContent(doc.getContent() + "\nsomething added"); + archive.updateArchive("XWiki.XWikiGuest", new Date(), "some comment", false, doc, context); - archive.updateArchive("Main.WebHome", originalText); - // Try to construct again the archive from the last modification. This will happen when // XWiki loads a document from the database for example. We verify here that a username // with a space works. - new XWikiDocumentArchive(123456789L).setArchive(archive.getArchive()); + new XWikiDocumentArchive(123456789L).setArchive(archive.getArchive(context)); } + + public void testUpdateLoad() throws XWikiException { + XWikiDocument doc = new XWikiDocument("Test", "Test"); + doc.setContent("content 1.1"); + + XWikiContext context = new XWikiContext(); + + XWikiDocumentArchive archive = new XWikiDocumentArchive(doc.getId()); + assertEquals(0, archive.getNodes().size()); + + String author = "XWiki.some author"; + archive.updateArchive(author, new Date(), "initial, 1.1", false, doc, context); + String archive11 = archive.getArchive(context); + assertEquals(1, archive.getNodes().size()); + assertEquals(1, archive.getUpdeteNodeInfos().size()); + assertEquals(1, archive.getUpdeteNodeContents().size()); + + XWikiDocumentArchive archive2 = new XWikiDocumentArchive(doc.getId()); + archive2.setArchive(archive11); + assertEquals(archive11, archive2.getArchive(context)); + assertEquals(1, archive2.getNodes().size()); + assertEquals(1, archive2.getUpdeteNodeInfos().size()); + assertEquals(1, archive2.getUpdeteNodeContents().size()); + + doc.setContent("content\n1.2"); + archive.updateArchive(author, new Date(), "1.2", true, doc, context); + String archive12 = archive.getArchive(context); + assertEquals(2, archive.getNodes().size()); + assertEquals(2, archive.getUpdeteNodeInfos().size()); + assertEquals(2, archive.getUpdeteNodeContents().size()); + + XWikiDocumentArchive archive3 = new XWikiDocumentArchive(doc.getId()); + archive3.setArchive(archive12); + assertEquals(2, archive3.getNodes().size()); + assertEquals(2, archive3.getUpdeteNodeInfos().size()); + assertEquals(2, archive3.getUpdeteNodeContents().size()); + + doc.setContent("major change\ncontent\n2.1"); + archive.updateArchive(author, new Date(), "2.1", false, doc, context); + assertEquals(3, archive.getNodes().size()); + assertEquals(3, archive.getUpdeteNodeInfos().size()); + assertEquals(3, archive.getUpdeteNodeContents().size()); + } + + public void testRemoveVersions() throws XWikiException { + XWikiContext context = new XWikiContext(); + + XWikiDocument doc = new XWikiDocument("Test", "Test"); + XWikiDocumentArchive archive = new XWikiDocumentArchive(doc.getId()); + doc.setDocumentArchive(archive); + String author = "XWiki.some author"; + + doc.setContent("content 1.1"); + archive.updateArchive(author, new Date(), "initial, 1.1", false, doc, context); + XWikiDocument doc11 = (XWikiDocument) doc.clone(); + + doc.setContent("content 2.1\nqwe"); + archive.updateArchive(author, new Date(), "2.1", false, doc, context); + + doc.setContent("content 2.2\nqweq"); + archive.updateArchive(author, new Date(), "2.2", true, doc, context); + + doc.setContent("content 2.3\nqweqe"); + archive.updateArchive(author, new Date(), "2.3", true, doc, context); + + /* TODO: need many mocking + archive.removeVersions(new Version(2,1), new Version(2,2), doc, context); + + assertEquals(2, archive.getNodes().size()); + assertEquals(2, archive.getDeleteNodeInfo().size()); + assertNull(archive.getNode(new Version(2,1))); + assertNull(archive.getNode(new Version(2,2))); + + assertEquals(doc11, archive.loadDocument(doc, new Version(1,1), context));*/ + } } Index: xwiki-platform-core/src/main/java/com/xpn/xwiki/store/XWikiHibernateBaseStore.java =================================================================== --- xwiki-platform-core/src/main/java/com/xpn/xwiki/store/XWikiHibernateBaseStore.java (revision 4076) +++ xwiki-platform-core/src/main/java/com/xpn/xwiki/store/XWikiHibernateBaseStore.java (working copy) @@ -32,7 +32,6 @@ import java.util.Map; import java.util.Iterator; import java.lang.reflect.Proxy; -import java.lang.reflect.InvocationHandler; public class XWikiHibernateBaseStore { private static final Log log = LogFactory.getLog(XWikiHibernateBaseStore.class); @@ -789,4 +788,51 @@ ""; return custommapping; } + + /** spring-jcr like Callback interface for working in hibernate */ + public interface HibernateCallBack { + Object doInHibernate(Session session) throws Exception; + } + + /** spring-jcr like execute method for operations in hibernate + * @throws XWikiException */ + public Object execute(XWikiContext context, boolean bTransaction, boolean doCommit, HibernateCallBack cb) throws XWikiException { + MonitorPlugin monitor = Util.getMonitorPlugin(context); + try { + // Start monitoring timer + if (monitor!=null) + monitor.startTimer("hibernate"); + + if (bTransaction) { + checkHibernate(context); + bTransaction = beginTransaction(context); + } + + return cb.doInHibernate(getSession(context)); + } catch (Exception e) { + if (e instanceof XWikiException) + throw (XWikiException)e; + throw new XWikiException( XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_UNKNOWN, + "Exception while hibernate execute", e); + } finally { + try { + if (bTransaction) + endTransaction(context, doCommit); + if (monitor!=null) + monitor.endTimer("hibernate"); + } catch (Exception e) {} + } + } + + /** spring-jcr like execute method for read-only operations in hibernate + * @throws XWikiException */ + public Object executeRead(XWikiContext context, boolean bTransaction, HibernateCallBack cb) throws XWikiException { + return execute(context, bTransaction, false, cb); + } + + /** spring-jcr like execute method for write operations in hibernate + * @throws XWikiException */ + public Object executeWrite(XWikiContext context, boolean bTransaction, HibernateCallBack cb) throws XWikiException { + return execute(context, bTransaction, true, cb); + } } Index: xwiki-platform-core/src/main/java/com/xpn/xwiki/store/XWikiHibernateStore.java =================================================================== --- xwiki-platform-core/src/main/java/com/xpn/xwiki/store/XWikiHibernateStore.java (revision 4076) +++ xwiki-platform-core/src/main/java/com/xpn/xwiki/store/XWikiHibernateStore.java (working copy) @@ -26,6 +26,7 @@ import com.xpn.xwiki.doc.XWikiDocument; import com.xpn.xwiki.doc.XWikiLink; import com.xpn.xwiki.doc.XWikiLock; +import com.xpn.xwiki.doc.rcs.XWikiRCSNodeInfo; import com.xpn.xwiki.monitor.api.MonitorPlugin; import com.xpn.xwiki.objects.*; import com.xpn.xwiki.objects.classes.BaseClass; @@ -235,7 +236,7 @@ } doc.incrementVersion(); if (context.getWiki().hasVersioning(doc.getFullName(), context)) - context.getWiki().getVersioningStore().updateXWikiDocArchive(doc, doc.toXML(context), false, context); + context.getWiki().getVersioningStore().updateXWikiDocArchive(doc, false, context); doc.setContentDirty(false); doc.setMetaDataDirty(false); @@ -531,7 +532,11 @@ deleteXWikiObject(obj, context, false); } } - + // Delete history + session.createQuery("delete from "+XWikiRCSNodeInfo.class.getName()+" where id.docId=?") + .setLong(0, doc.getId()) + .executeUpdate(); + session.delete(doc); // We need to ensure that the deleted document becomes the original document Index: xwiki-platform-core/src/main/java/com/xpn/xwiki/store/XWikiVersioningStoreInterface.java =================================================================== --- xwiki-platform-core/src/main/java/com/xpn/xwiki/store/XWikiVersioningStoreInterface.java (revision 4076) +++ xwiki-platform-core/src/main/java/com/xpn/xwiki/store/XWikiVersioningStoreInterface.java (working copy) @@ -1,18 +1,25 @@ package com.xpn.xwiki.store; +import org.suigeneris.jrcs.rcs.Version; + import com.xpn.xwiki.XWikiContext; import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.doc.XWikiDocument; import com.xpn.xwiki.doc.XWikiDocumentArchive; -import org.suigeneris.jrcs.rcs.Version; +import com.xpn.xwiki.doc.rcs.XWikiRCSNodeContent; +import com.xpn.xwiki.doc.rcs.XWikiRCSNodeId; +import com.xpn.xwiki.doc.rcs.XWikiRCSNodeInfo; public interface XWikiVersioningStoreInterface { public void loadXWikiDocArchive(XWikiDocumentArchive archivedoc, boolean bTransaction, XWikiContext context) throws XWikiException; public void saveXWikiDocArchive(XWikiDocumentArchive archivedoc, boolean bTransaction, XWikiContext context) throws XWikiException; - public void updateXWikiDocArchive(XWikiDocument doc, String text, boolean bTransaction, XWikiContext context) throws XWikiException; + public void updateXWikiDocArchive(XWikiDocument doc, boolean bTransaction, XWikiContext context) throws XWikiException; public Version[] getXWikiDocVersions(XWikiDocument doc, XWikiContext context) throws XWikiException; - // public Archive getXWikiDocRCSArchive(XWikiDocument doc, XWikiContext context) throws XWikiException; public XWikiDocument loadXWikiDoc(XWikiDocument doc, String version, XWikiContext context) throws XWikiException; public void resetRCSArchive(XWikiDocument doc, boolean bTransaction, XWikiContext context) throws XWikiException; public XWikiDocumentArchive getXWikiDocumentArchive(XWikiDocument doc, XWikiContext context) throws XWikiException; + /** + * Load {@link XWikiRCSNodeContent} by demand. Used in {@link XWikiRCSNodeInfo#getContent(XWikiContext)} + */ + public XWikiRCSNodeContent loadRCSNodeContent(XWikiContext context, XWikiRCSNodeId id, boolean bTransaction) throws XWikiException; } Index: xwiki-platform-core/src/main/java/com/xpn/xwiki/store/jcr/XWikiJcrPropertyVersioningStore.java =================================================================== --- xwiki-platform-core/src/main/java/com/xpn/xwiki/store/jcr/XWikiJcrPropertyVersioningStore.java (revision 4076) +++ xwiki-platform-core/src/main/java/com/xpn/xwiki/store/jcr/XWikiJcrPropertyVersioningStore.java (working copy) @@ -1,34 +1,38 @@ package com.xpn.xwiki.store.jcr; +import java.lang.reflect.InvocationTargetException; +import java.util.Collection; +import java.util.Iterator; + +import javax.jcr.Node; +import javax.jcr.PathNotFoundException; +import javax.jcr.Property; + +import org.suigeneris.jrcs.rcs.Version; + import com.xpn.xwiki.XWiki; import com.xpn.xwiki.XWikiContext; import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.doc.XWikiDocument; import com.xpn.xwiki.doc.XWikiDocumentArchive; -import com.xpn.xwiki.monitor.api.MonitorPlugin; +import com.xpn.xwiki.doc.rcs.XWikiRCSNodeContent; +import com.xpn.xwiki.doc.rcs.XWikiRCSNodeId; +import com.xpn.xwiki.doc.rcs.XWikiRCSNodeInfo; import com.xpn.xwiki.store.XWikiVersioningStoreInterface; -import com.xpn.xwiki.util.Util; -import org.suigeneris.jrcs.rcs.Archive; -import org.suigeneris.jrcs.rcs.Version; -import javax.jcr.Node; -import javax.jcr.PathNotFoundException; -import javax.jcr.Property; -import java.lang.reflect.InvocationTargetException; - /** Versions store in jcr property '@archive' of xwiki:document */ public class XWikiJcrPropertyVersioningStore extends XWikiJcrBaseStore implements XWikiVersioningStoreInterface { public XWikiJcrPropertyVersioningStore(XWiki xwiki, XWikiContext context) throws SecurityException, NoSuchMethodException, ClassNotFoundException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException { super(xwiki, context); } - public void saveXWikiDocArchive(final XWikiDocumentArchive archivedoc, boolean bTransaction, XWikiContext context) throws XWikiException { + public void saveXWikiDocArchive(final XWikiDocumentArchive archivedoc, boolean bTransaction, final XWikiContext context) throws XWikiException { try { executeWrite(context, new JcrCallBack() { public Object doInJcr(XWikiJcrSession session) throws Exception { Node docNode = getDocNodeById(session, archivedoc.getId()); if (docNode==null) return null; - String s = archivedoc.getArchive(); + String s = archivedoc.getArchive(context); docNode.setProperty("archive", s); session.save(); return null; @@ -62,12 +66,14 @@ } // From XWikiHibernateVersioningStore: - public void resetRCSArchive(XWikiDocument doc, boolean bTransaction, XWikiContext context) throws XWikiException { try { XWikiDocumentArchive archivedoc = new XWikiDocumentArchive(doc.getId()); loadXWikiDocArchive(archivedoc, bTransaction, context); - archivedoc.resetArchive(doc.getFullName(), doc.getContent(), doc.getVersion()); + archivedoc.resetArchive(); + archivedoc.getDeleteNodeInfo().clear(); + doc.setMinorEdit(false); + updateXWikiDocArchive(doc, false, context); saveXWikiDocArchive(archivedoc, bTransaction, context); } catch (Exception e) { Object[] args = { doc.getFullName() }; @@ -78,10 +84,10 @@ - public void updateXWikiDocArchive(XWikiDocument doc, String text, boolean bTransaction, XWikiContext context) throws XWikiException { + public void updateXWikiDocArchive(XWikiDocument doc, boolean bTransaction, XWikiContext context) throws XWikiException { try { XWikiDocumentArchive archivedoc = getXWikiDocumentArchive(doc, context); - archivedoc.updateArchive(doc.getFullName(), text); + archivedoc.updateArchive(doc.getContentAuthor(), doc.getDate(), doc.getComment(), doc.isMinorEdit(), doc, context); saveXWikiDocArchive(archivedoc, bTransaction, context); } catch (Exception e) { Object[] args = { doc.getFullName() }; @@ -91,6 +97,8 @@ } public XWikiDocument loadXWikiDoc(XWikiDocument basedoc, String version, XWikiContext context) throws XWikiException { + return null; + /* TODO: it will be rewrited XWikiDocument doc = new XWikiDocument(basedoc.getSpace(), basedoc.getName()); doc.setDatabase(basedoc.getDatabase()); MonitorPlugin monitor = Util.getMonitorPlugin(context); @@ -139,20 +147,21 @@ if (monitor!=null) monitor.endTimer("jcr"); } - return doc; + return doc;*/ } public Version[] getXWikiDocVersions(XWikiDocument doc, XWikiContext context) throws XWikiException { try { - Archive archive = getXWikiDocumentArchive(doc, context).getRCSArchive(); + XWikiDocumentArchive archive = getXWikiDocumentArchive(doc, context); if (archive==null) return new Version[0]; - - org.suigeneris.jrcs.rcs.Node[] nodes = archive.changeLog(); - Version[] versions = new Version[nodes.length]; - for (int i=0;i 0) { + for (Iterator it = nodeInfos.iterator(); it.hasNext();) { + XWikiRCSNodeInfo nodeinfo = (XWikiRCSNodeInfo) it.next(); + XWikiJRCSNode node = new XWikiJRCSNode(nodeinfo.getId().getVersion(), null); + node.setAuthor(nodeinfo.getAuthor()); + node.setDate(nodeinfo.getDate()); + node.setLog(nodeinfo.getComment()); + node.setDiff(nodeinfo.isDiff()); + XWikiRCSNodeContent content = nodeinfo.getContent(context); + node.setText(content.getPatch().getContent()); + nodes.put(node.getVersion(), node); + } + XWikiJRCSNode last = null; + for (Iterator it = nodes.keySet().iterator(); it.hasNext();) { + Version ver = (Version) it.next(); + XWikiJRCSNode node = (XWikiJRCSNode) nodes.get(ver); + if (last != null) { + last.setRCSNext(node); + } + last = node; + if (head == null) { + head = node; + } + } + } + } + /** + * Used to deserialize {@link XWikiDocumentArchive}. + * @param archivetext - archive text in JRCS format + * @throws ParseException if syntax errors + */ + public XWikiRCSArchive(String archivetext) throws ParseException + { + super("", new StringInputStream(archivetext)); + } + /** + * Helper class for convert from {@link XWikiRCSNodeInfo} to JRCS {@link Node}. + */ + private static class XWikiJRCSNode extends TrunkNode + { + /** bug if author=="". */ + public static String sauthorIfEmpty = "_"; + /** mark that node contains not patch. */ + public static String sfullVersion = "full"; + /** + * @param vernum - version of node + * @param next - next node (with smaller version) in history + * @throws InvalidTrunkVersionNumberException if version is invalid + */ + public XWikiJRCSNode(Version vernum, TrunkNode next) + throws InvalidTrunkVersionNumberException + { + super(vernum, next); + } + /** @param other - create class from copying this node */ + public XWikiJRCSNode(Node other) + { + this(other.version, null); + this.date = other.getDate(); + this.author = other.getAuthor(); + this.state = other.getState(); + this.log = other.getLog(); + this.locker = other.getLocker(); + this.setText(other.getText()); + } + /** @param date - date of modification */ + public void setDate(Date date) { + this.date = date; + } + /** {@inheritDoc} */ + public void setAuthor(String user) + { + // + if (user == null || "".equals(user)) { + super.setAuthor(sauthorIfEmpty); + } else { + super.setAuthor(user); + } + } + /** @return is this node store diff or full version */ + public boolean isDiff() { + // we need something filed in Node. locker is free + return !sfullVersion.equals(getState()); + } + /** @param isdiff - true if node stores a diff, false - if full version */ + public void setDiff(boolean isdiff) { + setState(isdiff ? "diff" : sfullVersion); + } + } + + /** + * @return Collection of pairs [{@link XWikiRCSNodeInfo}, {@link XWikiRCSNodeContent}] + * @param docId - docId which will be wrote in {@link XWikiRCSNodeId#setDocId(long)} + */ + public Collection getNodes(long docId) { + Collection result = new ArrayList(nodes.values().size()); + for (Iterator it = nodes.values().iterator(); it.hasNext();) { + XWikiJRCSNode node = new XWikiJRCSNode((Node) it.next()); + XWikiRCSNodeInfo nodeinfo = new XWikiRCSNodeInfo(); + nodeinfo.setId(new XWikiRCSNodeId(docId, node.getVersion())); + nodeinfo.setAuthor(XWikiJRCSNode.sauthorIfEmpty.equals(node.getAuthor()) + ? "" : node.getAuthor()); + nodeinfo.setComment(node.getLog()); + nodeinfo.setDate(node.getDate()); + nodeinfo.setDiff(node.isDiff()); + XWikiRCSNodeContent content = new XWikiRCSNodeContent(nodeinfo.getId()); + content.setPatch(new XWikiPatch(ToString.arrayToString(node.getText()), node.isDiff())); + nodeinfo.setContent(content); + result.add(nodeinfo); + result.add(content); + } + // ensure latest version is full + ((XWikiRCSNodeInfo) result.iterator().next()).setDiff(false); + return result; + } +} Property changes on: xwiki-platform-core/src/main/java/com/xpn/xwiki/doc/rcs/XWikiRCSArchive.java ___________________________________________________________________ Name: svn:eol-style + native Index: xwiki-platform-core/src/main/java/com/xpn/xwiki/doc/rcs/XWikiRCSNodeId.java =================================================================== --- xwiki-platform-core/src/main/java/com/xpn/xwiki/doc/rcs/XWikiRCSNodeId.java (revision 0) +++ xwiki-platform-core/src/main/java/com/xpn/xwiki/doc/rcs/XWikiRCSNodeId.java (revision 0) @@ -0,0 +1,174 @@ +/* + * Copyright 2007, XpertNet SARL, and individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ +package com.xpn.xwiki.doc.rcs; + +import java.io.Serializable; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; +import org.suigeneris.jrcs.rcs.Version; + +/** + * Composite ID component used in {@link XWikiRCSNodeInfo} & {@link XWikiRCSNodeContent}. + * Mutable. + * @version $Id: $ + */ +public class XWikiRCSNodeId implements Serializable, Cloneable +{ + // composite-key + /** + * = {@link XWikiDocument#getId()}. + * part of composite key + */ + private long docId; + /** + * version of document. + * part of composite key + */ + private Version version = new Version(1, 1); + + /** + * default constructor used in Hibernate to load this class. + */ + public XWikiRCSNodeId() { } + + /** + * @param docId = {@link XWikiDocument#getId()} + * @param version - version of document + */ + public XWikiRCSNodeId(long docId, Version version) + { + super(); + this.docId = docId; + this.version = version; + } + + /*/** + * Clone-constructor. + * @param node - clone from what node + * + public XWikiRCSNodeId(XWikiRCSNodeId node) + { + this(node.getDocId(), node.getVersion()); + }*/ + + /** + * @return {@link XWikiDocument#getId()} + */ + public long getDocId() + { + return docId; + } + + /** + * @param docId = {@link XWikiDocument#getId()} + */ + public void setDocId(long docId) + { + this.docId = docId; + } + + /** + * @return version of document + */ + public Version getVersion() + { + return version; + } + /** + * @return 1st number in version + * used in Hibernate to store this class + */ + protected int getVersion1() + { + return version.at(0); + } + /** + * @return 2nd number in version + * used in Hibernate to store this class + */ + protected int getVersion2() + { + return version.at(1); + } + /** + * @param ver - version of document + */ + public void setVersion(Version ver) + { + this.version = ver; + } + /** + * @param v1 = 1st number in version + * used in Hibernate to load this class + */ + protected void setVersion1(int v1) + { + this.version = new Version(v1, version.at(1)); + } + /** + * @param v2 = 2nd number in version + * used in Hibernate to load this class + */ + protected void setVersion2(int v2) + { + this.version = new Version(version.at(0), v2); + } + + /** + * {@inheritDoc} + */ + public int hashCode() + { + return HashCodeBuilder.reflectionHashCode(this); + } + + /** + * {@inheritDoc} + */ + public boolean equals(Object obj) + { + return EqualsBuilder.reflectionEquals(this, obj); + } + + /** + * {@inheritDoc} + * @throws CloneNotSupportedException + */ + public Object clone() + { + try { + // there are no mutable fields, so simple do super.clone() + return super.clone(); + } catch (CloneNotSupportedException e) { + // clone is supported. exception is nonsense. + throw new RuntimeException(e); + } + } + + /** + * {@inheritDoc} + */ + public String toString() + { + return ToStringBuilder.reflectionToString(this); + } +} Property changes on: xwiki-platform-core/src/main/java/com/xpn/xwiki/doc/rcs/XWikiRCSNodeId.java ___________________________________________________________________ Name: svn:eol-style + native Index: xwiki-platform-core/src/main/java/com/xpn/xwiki/doc/rcs/XWikiPatch.java =================================================================== --- xwiki-platform-core/src/main/java/com/xpn/xwiki/doc/rcs/XWikiPatch.java (revision 0) +++ xwiki-platform-core/src/main/java/com/xpn/xwiki/doc/rcs/XWikiPatch.java (revision 0) @@ -0,0 +1,150 @@ +/* + * Copyright 2007, XpertNet SARL, and individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ +package com.xpn.xwiki.doc.rcs; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.suigeneris.jrcs.util.ToString; + +import com.xpn.xwiki.XWikiContext; +import com.xpn.xwiki.XWikiException; +import com.xpn.xwiki.doc.XWikiDocument; + +/** + * Contains differences between revisions. + * One field (diff for xml or full xml) for now. + * Created for easy migrate to future XWikiPatch system + * @version $Id: $ + */ +public class XWikiPatch +{ + /** string serialization for patch. */ + private String content; + /** is content a difference, or full version. */ + private boolean isDiff; + /** Default constructor, need for hibernate. */ + public XWikiPatch() { } + /** + * @param content - patch content + * @param isDiff - is patch a difference or full version + */ + public XWikiPatch(String content, boolean isDiff) + { + setContent(content); + setDiff(isDiff); + } + /** + * @return string serialization for patch + */ + public String getContent() + { + return content; + } + /** + * @param content - string serialization for patch + */ + public void setContent(String content) + { + this.content = content; + } + /** + * @return is content a difference, or full version + */ + public boolean isDiff() + { + return isDiff; + } + /** + * @param isDiff - is content a difference, or full version + */ + public void setDiff(boolean isDiff) + { + this.isDiff = isDiff; + } + /** + * Create full patch for document. + * @param doc - document to patch + * @param context - used for serialization document to xml + * @return self + * @throws XWikiException if any error + */ + public XWikiPatch setFullVersion(XWikiDocument doc, XWikiContext context) throws XWikiException + { + setDiff(false); + setContent(doc.toXML(context)); + return this; + } + /** + * Create difference patch for document curdoc, using origdoc as previous version. + * @param curdoc - current document + * @param origdoc - original document + * @param context - used for serialization documents to xml + * @return self + * @throws XWikiException if any error + */ + public XWikiPatch setDiffVersion(XWikiDocument curdoc, XWikiDocument origdoc, + XWikiContext context) throws XWikiException + { + return setDiffVersion(curdoc, origdoc.toXML(context), context); + } + /** + * Create difference patch for document curdoc, using origdoc as previous version. + * @param curdoc - current document + * @param origdocxml - xml of original document + * @param context - used for serialization document to xml + * @return self + * @throws XWikiException if any error + */ + public XWikiPatch setDiffVersion(XWikiDocument curdoc, String origdocxml, + XWikiContext context) throws XWikiException + { + setDiff(true); + try { + setContent(XWikiPatchUtils.getDiff(curdoc.toXML(context), origdocxml)); + } catch (Exception e) { + Object[] args = {curdoc.getFullName()}; + throw new XWikiException(XWikiException.MODULE_XWIKI_DIFF, + XWikiException.ERROR_XWIKI_DIFF_XML_ERROR, + "Failed to create diff for doc {}", e, args); + } + return this; + } + /** + * patch text. + * @param origtext - text to patch + * @throws XWikiException if exception while patching + */ + public void patch(List origtext) throws XWikiException + { + if (!isDiff()) { + origtext.clear(); + origtext.addAll(new ArrayList(Arrays.asList(ToString.stringToArray(getContent())))); + } else { + try { + XWikiPatchUtils.patch(origtext, getContent()); + } catch (Exception e) { + throw new XWikiException(XWikiException.MODULE_XWIKI_DIFF, + XWikiException.ERROR_XWIKI_DIFF_XML_ERROR, "Exception while patching", e); + } + } + } +} Property changes on: xwiki-platform-core/src/main/java/com/xpn/xwiki/doc/rcs/XWikiPatch.java ___________________________________________________________________ Name: svn:eol-style + native Index: xwiki-platform-core/src/main/java/com/xpn/xwiki/doc/rcs/XWikiPatchUtils.java =================================================================== --- xwiki-platform-core/src/main/java/com/xpn/xwiki/doc/rcs/XWikiPatchUtils.java (revision 0) +++ xwiki-platform-core/src/main/java/com/xpn/xwiki/doc/rcs/XWikiPatchUtils.java (revision 0) @@ -0,0 +1,122 @@ +/* + * Copyright 2007, XpertNet SARL, and individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ +package com.xpn.xwiki.doc.rcs; + +import java.util.List; +import java.util.StringTokenizer; + +import org.suigeneris.jrcs.diff.Diff; +import org.suigeneris.jrcs.diff.DifferentiationFailedException; +import org.suigeneris.jrcs.diff.PatchFailedException; +import org.suigeneris.jrcs.diff.Revision; +import org.suigeneris.jrcs.diff.delta.AddDelta; +import org.suigeneris.jrcs.diff.delta.Chunk; +import org.suigeneris.jrcs.diff.delta.DeleteDelta; +import org.suigeneris.jrcs.rcs.InvalidFileFormatException; +import org.suigeneris.jrcs.rcs.impl.Node; +import org.suigeneris.jrcs.util.ToString; + +/** + * diff & patch utility. + * @version $Id: $ + */ +public class XWikiPatchUtils +{ + /** prevent to create utility class. */ + private XWikiPatchUtils() { } + /** + * @param orig - original text + * @param rev - new text + * @return diff in JRCS format + * @throws DifferentiationFailedException if error when creating diff + */ + public static String getDiff(Object[] orig, Object[] rev) throws DifferentiationFailedException + { + return Diff.diff(orig, rev).toRCSString(); + } + /** + * @param orig - original text + * @param rev - new text + * @return diff in JRCS format + * @throws DifferentiationFailedException if error when creating diff + */ + public static String getDiff(String orig, String rev) throws DifferentiationFailedException { + return getDiff(ToString.stringToArray(orig), ToString.stringToArray(rev)); + } + + /** + * From {@link Node#patch(List, boolean)}. + * @param orig - text to patch, List<String> of lines. + * @param diff - diff to patch, {@link Diff} format + * @throws InvalidFileFormatException if diff is incorrect + * @throws PatchFailedException if error in patching + */ + public static void patch(List orig, String diff) + throws InvalidFileFormatException, PatchFailedException + { + Revision revision = new Revision(); + Object[] lines = ToString.stringToArray(diff); + for (int it = 0; it < lines.length; it++) { + String cmd = lines[it].toString(); + if (cmd.length()==0) + break; + + java.util.StringTokenizer t = new StringTokenizer(cmd, "ad ", true); + char action; + int n; + int count; + + try { + action = t.nextToken().charAt(0); + n = Integer.parseInt(t.nextToken()); + // skip the space + t.nextToken(); + count = Integer.parseInt(t.nextToken()); + } catch (Exception e) { + throw new InvalidFileFormatException("line:" + ":" + e.getClass().getName(), + e); + } + + if (action == 'd') { + revision.addDelta(new DeleteDelta(new Chunk(n - 1, count))); + } else if (action == 'a') { + revision.addDelta(new AddDelta(n, new Chunk(getTextLines(lines, + it + 1, it + 1 + count), 0, count, n - 1))); + it += count; + } else { + throw new InvalidFileFormatException(); + } + } + revision.applyTo(orig); + } + /** + * @param lines - some text + * @param from - from that line + * @param to - to that line + * @return selected lines of text + */ + private static Object[] getTextLines(Object[] lines, int from, int to) + { + Object[] ret = new Object[to-from+1]; + for (int i=from; i0) { + setRCSVersion(xda.getLatestVersion()); + XWikiRCSNodeInfo nodeinfo = xda.getLatestNode(); + setComment(nodeinfo.getComment()); + setMinorEdit(nodeinfo.isMinorEdit()); + setAuthor(nodeinfo.getAuthor()); + setDate(nodeinfo.getDate()); + } setDocumentArchive(xda); } @@ -898,6 +918,11 @@ } } + public XWikiRCSNodeInfo getRevisionInfo(String version, XWikiContext context) throws XWikiException + { + return getDocumentArchive(context).getNode(new Version(version)); + } + public boolean isMostRecent() { return mostRecent; @@ -1563,7 +1588,10 @@ if (comment != null) { setComment(comment); } - + + // Read the minor edit checkbox from the form + setMinorEdit(eform.isMinorEdit()); + String tags = eform.getTags(); if (tags != null) { setTags(tags, context); @@ -1779,6 +1807,7 @@ setxWikiClass((BaseClass) document.getxWikiClass().clone()); setxWikiClassXML(document.getxWikiClassXML()); setComment(document.getComment()); + setMinorEdit(document.isMinorEdit()); clonexWikiObjects(document); copyAttachments(document); @@ -1828,6 +1857,7 @@ doc.setxWikiClass((BaseClass) getxWikiClass().clone()); doc.setxWikiClassXML(getxWikiClassXML()); doc.setComment(getComment()); + doc.setMinorEdit(isMinorEdit()); doc.clonexWikiObjects(this); doc.copyAttachments(this); doc.elements = elements; @@ -1953,6 +1983,10 @@ if (!getComment().equals(doc.getComment())) { return false; } + + if (isMinorEdit() == doc.isMinorEdit()) { + return false; + } if (!getxWikiClass().equals(doc.getxWikiClass())) { return false; @@ -2071,7 +2105,7 @@ Document doc = new DOMDocument(); Element docel = new DOMElement("xwikidoc"); doc.setRootElement(docel); - + Element el = new DOMElement("web"); el.addText(getSpace()); docel.add(el); @@ -2150,13 +2184,17 @@ el = new DOMElement("comment"); el.addText(getComment()); docel.add(el); - + + el = new DOMElement("minorEdit"); + el.addText(String.valueOf(isMinorEdit())); + docel.add(el); + List alist = getAttachmentList(); for (int ai = 0; ai < alist.size(); ai++) { XWikiAttachment attach = (XWikiAttachment) alist.get(ai); docel.add(attach.toXML(bWithAttachmentContent, bWithVersions, context)); } - + if (bWithObjects) { // Add Class BaseClass bclass = getxWikiClass(); @@ -2217,7 +2255,7 @@ if (bWithVersions) { el = new DOMElement("versions"); try { - el.addText(getDocumentArchive(context).getArchive()); + el.addText(getDocumentArchive(context).getArchive( context )); } catch (XWikiException e) { return null; } @@ -2226,7 +2264,7 @@ return doc; } - + protected String encodedXMLStringAsUTF8(String xmlString) { if (xmlString == null) { @@ -2345,6 +2383,9 @@ setValidationScript(getElement(docel, "validationScript")); setComment(getElement(docel, "comment")); + String minorEdit = getElement(docel, "minorEdit"); + setMinorEdit(Boolean.valueOf(minorEdit).booleanValue()); + String strans = getElement(docel, "translation"); if ((strans == null) || strans.equals("")) { setTranslation(0); @@ -4056,6 +4097,26 @@ this.comment = comment; setMetaDataDirty(true); } + + public boolean isMinorEdit() + { + return isMinorEdit; + } + public void setMinorEdit(boolean isMinor) + { + this.isMinorEdit = isMinor; + setMetaDataDirty(true); + } + + // methods for easy table update. because default value == null + protected Boolean getMinorEdit1() + { + return Boolean.valueOf(isMinorEdit); + } + protected void setMinorEdit1(Boolean isMinor) + { + isMinorEdit = (isMinor!=null && isMinor.booleanValue()); + } public BaseObject newObject(String classname, XWikiContext context) throws XWikiException { Index: xwiki-platform-core/src/main/java/com/xpn/xwiki/doc/XWikiDocumentArchive.java =================================================================== --- xwiki-platform-core/src/main/java/com/xpn/xwiki/doc/XWikiDocumentArchive.java (revision 4076) +++ xwiki-platform-core/src/main/java/com/xpn/xwiki/doc/XWikiDocumentArchive.java (working copy) @@ -1,149 +1,367 @@ +/* + * Copyright 2005-2007, XpertNet SARL, and individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ package com.xpn.xwiki.doc; +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; +import java.util.TreeMap; +import java.util.TreeSet; + +import org.suigeneris.jrcs.rcs.Version; +import com.xpn.xwiki.XWikiContext; import com.xpn.xwiki.XWikiException; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.tools.ant.filters.StringInputStream; -import org.suigeneris.jrcs.rcs.Archive; -import org.suigeneris.jrcs.util.ToString; +import com.xpn.xwiki.doc.rcs.XWikiPatch; +import com.xpn.xwiki.doc.rcs.XWikiRCSArchive; +import com.xpn.xwiki.doc.rcs.XWikiRCSNodeContent; +import com.xpn.xwiki.doc.rcs.XWikiRCSNodeId; +import com.xpn.xwiki.doc.rcs.XWikiRCSNodeInfo; -public class XWikiDocumentArchive { - private static final Log log = LogFactory.getLog(XWikiDocumentArchive.class); - +/** + * Contains document history. + * Allows to load any version of document. + * @version $Id: $ + */ +public class XWikiDocumentArchive +{ + /** =docId. */ private long id; - private Archive archive; - - public XWikiDocumentArchive() { - } - + /** SortedMap from Version to XWikiRCSNodeInfo. */ + private SortedMap versionToNode = new TreeMap(); + /** SortedSet of Version - versions which has full document, not patch. + * Latest version is always full. */ + private SortedSet fullVersions = new TreeSet(); + + // store-specific information + /** Set of {@link XWikiRCSNodeInfo} which need to delete. */ + private Set deleteNodes = new TreeSet(); + /** Set of {@link XWikiRCSNodeInfo} which need to saveOrUpdate. */ + private Set updateNodeInfos = new TreeSet(); + /** Set of {@link XWikiRCSNodeContent} which need to update. */ + private Set updateNodeContents = new TreeSet(); + + /** @param id = {@link XWikiDocument#getId()} */ public XWikiDocumentArchive(long id) { + this(); setId(id); } - + /** default constructor. */ + public XWikiDocumentArchive() { } + + // helper methods + /** + * @param cur - current version + * @param isMinor - is modification is minor + * @return next version + */ + protected Version createNextVersion(Version cur, boolean isMinor) { + Version result; + if (cur == null) { + result = new Version(1, 1); + } else if (!isMinor) { + result = cur.getBase(1).next().newBranch(1); + } else { + result = cur.next(); + } + return result; + } + /** @param node - node added to versionToNode and fullNodes */ + protected void updateNode(XWikiRCSNodeInfo node) { + Version ver = node.getId().getVersion(); + versionToNode.put(ver, node); + if (!node.isDiff()) { + fullVersions.add(ver); + } else { + fullVersions.remove(ver); + } + } + /** + * @param ver - version of new node + * @return new {@link XWikiRCSNodeId} constructing from getId() and ver + */ + protected XWikiRCSNodeId newNodeId(Version ver) { + return new XWikiRCSNodeId(getId(), ver); + } + /** + * Make a patch. It is store only modified nodes(latest). New nodes need be saved after. + * @param newnode - new node information + * @param doc - document for that patch created + * @param context - used for loading node contents and generating xml + * @return node content for newnode + * @throws XWikiException if exception while loading content + */ + protected XWikiRCSNodeContent makePatch(XWikiRCSNodeInfo newnode, XWikiDocument doc, + XWikiContext context) throws XWikiException + { + XWikiRCSNodeContent result = new XWikiRCSNodeContent(); + result.setPatch(new XWikiPatch().setFullVersion(doc, context)); + newnode.setContent(result); + XWikiRCSNodeInfo latestnode = getLatestNode(); + if (latestnode != null) { + int nodescount = getNodes().size(); + int nodesperfull = context.getWiki() == null ? 5 : Integer.parseInt( + context.getWiki().getConfig().getProperty("xwiki.store.rcs.nodesPerFull", "5")); + if (nodesperfull <= 0 || (nodescount % nodesperfull) != 0) { + XWikiRCSNodeContent latestcontent = latestnode.getContent(context); + latestcontent.getPatch().setDiffVersion(doc, + latestcontent.getPatch().getContent(), context); + latestnode.setContent(latestcontent); + updateNode(latestnode); + getUpdeteNodeContents().add(latestcontent); + } + } + return result; + } + /** @return {@link XWikiDocument#getId()} - primary key */ public long getId() { return id; } - + /** @param id = {@link XWikiDocument#getId()} */ public void setId(long id) { this.id = id; } - - public Archive getRCSArchive() { - return archive; + /** @return collection of XWikiRCSNodeInfo order by version desc */ + public Collection getNodes() { + return versionToNode.values(); } - - public void setRCSArchive(Archive archive) { - this.archive = archive; + /** + * @return collection of XWikiRCSNodeInfo where vfrom>=version>=vto order by version desc + * @param vfrom - start version + * @param vto - end version + */ + public Collection getNodes(Version vfrom, Version vto) { + int[] ito = vto.getNumbers(); + ito[1]--; + return versionToNode.subMap(vfrom, new Version(ito)).values(); } - - public String getArchive() throws XWikiException { - if (archive == null) - return ""; - else { - StringBuffer buffer = new StringBuffer(); - archive.toString(buffer); - return buffer.toString(); + /** @param versions - collection of XWikiRCSNodeInfo */ + public void setNodes(Collection versions) { + resetArchive(); + for (Iterator it = versions.iterator(); it.hasNext();) { + updateNode((XWikiRCSNodeInfo) it.next()); } + if (getNodes().size() > 0) { + // ensure latest version is full + getLatestNode().setDiff(false); + updateNode(getLatestNode()); + } } - + /** + * @param context - used for load nodes content + * @return serialization of class + * used in {@link PackagePlugin}. + * @throws XWikiException if any error + */ + public String getArchive(XWikiContext context) throws XWikiException { + XWikiRCSArchive archive = new XWikiRCSArchive(getNodes(), context); + return archive.toString(); + } + /** + * Deserialize class. + * Used in {@link PackagePlugin}. + * @param text - archive in JRCS format + * @throws XWikiException if parse error + */ public void setArchive(String text) throws XWikiException { + XWikiRCSArchive archive; try { - if ((text!=null)&&(!text.trim().equals(""))) { - StringInputStream is = new StringInputStream(text); - archive = new Archive("", is); - } else - if (text == null){ - Object[] lines = new Object[1]; - lines[0] = ""; - archive = new Archive(lines, "", "1.0"); - } - else - { - Object[] lines = ToString.stringToArray(text); - archive = new Archive(lines, "", "1.0"); - } + archive = new XWikiRCSArchive(text); + } catch (Exception e) { + throw new XWikiException(XWikiException.MODULE_XWIKI_DIFF, + XWikiException.ERROR_XWIKI_DIFF_CONTENT_ERROR, + "Exception while constructing document archive", e); } - catch (Exception e) { - Object[] args = { "" }; - throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_ARCHIVEFORMAT, - "Exception while manipulating the archive for doc {0}", e, args); + resetArchive(); + Collection nodes = archive.getNodes(getId()); + for (Iterator it = nodes.iterator(); it.hasNext();) { + XWikiRCSNodeInfo nodeinfo = (XWikiRCSNodeInfo) it.next(); + XWikiRCSNodeContent nodecontent = (XWikiRCSNodeContent) it.next(); + updateNode(nodeinfo); + updateNodeInfos.add(nodeinfo); + updateNodeContents.add(nodecontent); } } - - public void updateArchive(String docname, String text) throws XWikiException { - - // JRCS used the user.name System property to set the author of a change. However JRCS - // has a bug when the user name has a space in the name - // (see http://www.suigeneris.org/issues/browse/JRCS-22). The workaround is to set the - // user.name System property to some user without a space in its name. In addition - // we're not using that information anywhere so it won't matter. When JRCS bug is fixed - // remove this hack. - - // Saving the property in case some other part of the code or some dependent framework - // needs it. - String originalUsername = System.getProperty("user.name"); - - System.setProperty("user.name", "xwiki"); - - try { - Object[] lines = ToString.stringToArray(text); - if (archive != null) - archive.addRevision(lines, ""); - else - archive = new Archive(lines, docname, "1.0"); + /** + * Update history with new document version. + * @param author - author of version + * @param date - date of version + * @param comment - version comment + * @param isMinor - is minor version + * @param doc - document for this version + * @param context - used for loading nodes content + * @throws XWikiException in any error + */ + public void updateArchive(String author, Date date, String comment, boolean isMinor, + XWikiDocument doc, XWikiContext context) throws XWikiException + { + XWikiRCSNodeInfo newnode = new XWikiRCSNodeInfo(newNodeId( + createNextVersion(getLatestVersion(), isMinor))); + newnode.setAuthor(author); + newnode.setComment(comment); + newnode.setDate(date); + XWikiRCSNodeContent newcontent = makePatch(newnode, doc, context); + + updateNode(newnode); + updateNodeInfos.add(newnode); + updateNodeContents.add(newcontent); + } + /** + * Remove document versions from vfrom to vto, inclusive. + * @param context - used for loading nodes content + * @param vfrom - start version + * @param vto - end version + * @param doc - document for this archive + * @throws XWikiException if any error + */ + public void removeVersions(Version vfrom, Version vto, XWikiDocument doc, XWikiContext context) + throws XWikiException + { + Version vfrom0 = vfrom; + Version vto0 = vto; + if (vfrom0.compareVersions(vto0) < 0) { + Version tmp = vfrom0; + vfrom0 = vto0; + vto0 = tmp; } - catch (Exception e) { - Object[] args = { docname }; - throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_STORE_ARCHIVEFORMAT, - "Exception while manipulating the archive for doc {0}", e, args); - } finally { - // Restore the user name to its original value - System.setProperty("user.name", originalUsername); + Version vfrom1 = getNextVersion(vfrom0); + Version vto1 = getPrevVersion(vto0); + if (vfrom1 == null && vto1 == null) { + resetArchive(); + return; } - - } - - public Object clone() { - XWikiDocumentArchive docarchive = null; - try { - docarchive = (XWikiDocumentArchive) getClass().newInstance(); - } catch (Exception e) { - // This should not happen + if (vfrom1 == null) { + // store full version in vto1 + XWikiDocument docto1 = loadDocument(doc, vto1, context); + XWikiRCSNodeInfo nito1 = getNode(vto1); + XWikiRCSNodeContent ncto1 = nito1.getContent(context); + ncto1.getPatch().setFullVersion(docto1, context); + nito1.setContent(ncto1); + updateNode(nito1); + getUpdeteNodeContents().add(ncto1); + } else if (vto1 != null) { + XWikiDocument docfrom1 = loadDocument(doc, vfrom1, context); + XWikiDocument docto1 = loadDocument(doc, vto1, context); + XWikiRCSNodeInfo nito1 = getNode(vto1); + XWikiRCSNodeContent ncto1 = nito1.getContent(context); + ncto1.getPatch().setDiffVersion(docfrom1, docto1, context); + nito1.setContent(ncto1); + updateNode(nito1); + getUpdeteNodeContents().add(ncto1); + } // if (vto1==null) => nothing to do, except delete + for (Iterator it = getNodes(vfrom0, vto0).iterator(); it.hasNext();) { + XWikiRCSNodeInfo ni = (XWikiRCSNodeInfo) it.next(); + fullVersions.remove(ni.getId().getVersion()); + deleteNodes.add(ni); + it.remove(); } - - docarchive.setId(getId()); - docarchive.setRCSArchive(getRCSArchive()); - return docarchive; } - - - public boolean equals(Object object) { - XWikiDocumentArchive doc = (XWikiDocumentArchive) object; - if (getId()!=doc.getId()) - return false; - - try { - if (!getArchive().equals(doc.getArchive())) - return false; - } catch (XWikiException e) { - return false; - } - - return true; + /** + * @return selected version of document + * @param origdoc - load old version for this document + * @param version - which version to load + * @param context - used for loading + * @throws XWikiException if any error + */ + public XWikiDocument loadDocument(XWikiDocument origdoc, Version version, XWikiContext context) + throws XWikiException + { + return context.getWiki().getVersioningStore().loadXWikiDoc(origdoc, + version.toString(), context); } - - public void resetArchive(String docname, String text, String version) throws XWikiException { - Object[] lines = ToString.stringToArray(text); - archive = new Archive(lines, docname, version); + /** + * @return {@link XWikiRCSNodeInfo} by version. null if none. + * @param version which version to get + */ + public XWikiRCSNodeInfo getNode(Version version) + { + return version == null ? null : (XWikiRCSNodeInfo) versionToNode.get(version); } - + /** @return latest version in history for document. null if none. */ + public Version getLatestVersion() + { + return versionToNode.size() == 0 ? null : (Version) versionToNode.firstKey(); + } + /** @return latest node in history for document. null if none. */ + public XWikiRCSNodeInfo getLatestNode() { + return getNode(getLatestVersion()); + } + /** + * @return next version in history. null if none + * @param ver - current version + */ + public Version getNextVersion(Version ver) { + // headMap is exclusive + SortedMap headmap = versionToNode.headMap(ver); + return (headmap.size() == 0) ? null : (Version) headmap.lastKey(); + } /** - * {@inheritDoc} - * @see Object#toString() + * @return previous version in history. null if none + * @param ver - current version */ - public String toString() + public Version getPrevVersion(Version ver) { + // tailMap is inclusive + SortedMap tailmap = versionToNode.tailMap(ver); + if (tailmap.size() <= 1) { + return null; + } + Iterator it = tailmap.keySet().iterator(); + it.next(); + return (Version) it.next(); + } + /** + * @param ver - for what version find nearest + * @return nearest version which contain full information (not patch) + */ + public Version getNearestFullVersion(Version ver) { - return "id = [" + getId() + "], archive = [" - + (getRCSArchive() == null ? "null" : getRCSArchive().toString()) + "]"; + if (fullVersions.contains(ver)) { + return ver; + } + SortedSet headSet = fullVersions.headSet(ver); + return (Version) ((headSet.size() == 0) ? null : headSet.last()); } + /** reset history. history becomes empty. */ + public void resetArchive() + { + versionToNode.clear(); + fullVersions.clear(); + deleteNodes.addAll(updateNodeInfos); + updateNodeInfos.clear(); + updateNodeContents.clear(); + } + /** @return mutable Set of {@link XWikiRCSNodeInfo} which are need for delete */ + public Set getDeleteNodeInfo() + { + return deleteNodes; + } + /** @return mutable Set of {@link XWikiRCSNodeInfo} which are need for saveOrUpdate */ + public Set getUpdeteNodeInfos() + { + return updateNodeInfos; + } + /** @return mutable Set of {@link XWikiRCSNodeContent} which are need for update */ + public Set getUpdeteNodeContents() + { + return updateNodeContents; + } } Index: xwiki-platform-core/src/main/java/com/xpn/xwiki/api/RevisionInfo.java =================================================================== --- xwiki-platform-core/src/main/java/com/xpn/xwiki/api/RevisionInfo.java (revision 0) +++ xwiki-platform-core/src/main/java/com/xpn/xwiki/api/RevisionInfo.java (revision 0) @@ -0,0 +1,68 @@ +/* + * Copyright 2007, XpertNet SARL, and individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ +package com.xpn.xwiki.api; + +import java.util.Date; + +import com.xpn.xwiki.XWikiContext; +import com.xpn.xwiki.doc.rcs.XWikiRCSNodeInfo; + +/** + * API object for get info about some version of Document. + * @version $Id: $ + */ +public class RevisionInfo extends Api +{ + /** used for get all information. */ + private XWikiRCSNodeInfo nodeInfo; + /** + * @param nodeInfo - from that nodeinfo all information is getting + * @param context - needed for {@link Api} + */ + public RevisionInfo(XWikiRCSNodeInfo nodeInfo, XWikiContext context) + { + super(context); + this.nodeInfo = nodeInfo; + } + /** @return version of this revision */ + public String getVersion() + { + return nodeInfo.getId().getVersion().toString(); + } + /** @return date of this revision */ + public Date getDate() + { + return nodeInfo.getDate(); + } + /** @return author of this revision */ + public String getAuthor() + { + return nodeInfo.getAuthor(); + } + /** @return revision comment */ + public String getComment() + { + return nodeInfo.getComment(); + } + /** @return is revision is minor */ + public boolean isMinorEdit() { + return nodeInfo.isMinorEdit(); + } +} Property changes on: xwiki-platform-core/src/main/java/com/xpn/xwiki/api/RevisionInfo.java ___________________________________________________________________ Name: svn:eol-style + native Index: xwiki-platform-core/src/main/java/com/xpn/xwiki/api/XWiki.java =================================================================== --- xwiki-platform-core/src/main/java/com/xpn/xwiki/api/XWiki.java (revision 4076) +++ xwiki-platform-core/src/main/java/com/xpn/xwiki/api/XWiki.java (working copy) @@ -34,7 +34,7 @@ import com.xpn.xwiki.stats.impl.DocumentStats; import com.xpn.xwiki.web.Utils; import com.xpn.xwiki.web.XWikiEngineContext; -import org.suigeneris.jrcs.diff.Chunk; +import org.suigeneris.jrcs.diff.delta.Chunk; import java.awt.image.BufferedImage; import java.io.IOException; @@ -2319,6 +2319,14 @@ { return xwiki.isEditCommentMandatory(context); } + + /** + * API to check if the minor edit feature is active + * minor edit are activated in xwiki.cfg or in the XWiki Preferences + */ + public boolean hasMinorEdit() { + return xwiki.hasMinorEdit(context); + } /** * API to rename a page (experimental) Rights are necessary to edit the source and target page Index: xwiki-platform-core/src/main/java/com/xpn/xwiki/api/Document.java =================================================================== --- xwiki-platform-core/src/main/java/com/xpn/xwiki/api/Document.java (revision 4076) +++ xwiki-platform-core/src/main/java/com/xpn/xwiki/api/Document.java (working copy) @@ -288,6 +288,11 @@ { return doc.getComment(); } + + public boolean isMinorEdit() + { + return doc.isMinorEdit(); + } /** * return the list of possible traduction for this document @@ -362,7 +367,7 @@ */ public String getArchive() throws XWikiException { - return doc.getDocumentArchive(getXWikiContext()).getArchive(); + return doc.getDocumentArchive(getXWikiContext()).getArchive(getXWikiContext()); } /** @@ -688,6 +693,10 @@ { return doc.getRecentRevisions(nb, getXWikiContext()); } + + public RevisionInfo getRevisionInfo(String version) throws XWikiException { + return new RevisionInfo( doc.getRevisionInfo(version, getXWikiContext()), getXWikiContext() ); + } public List getAttachmentList() { @@ -1406,6 +1415,10 @@ { getDoc().setComment(comment); } + + public void setMinorEdit(boolean isMinor) { + getDoc().setMinorEdit(isMinor); + } public void save() throws XWikiException { Index: xwiki-platform-core/src/main/java/com/xpn/xwiki/XWiki.java =================================================================== --- xwiki-platform-core/src/main/java/com/xpn/xwiki/XWiki.java (revision 4076) +++ xwiki-platform-core/src/main/java/com/xpn/xwiki/XWiki.java (working copy) @@ -895,8 +895,14 @@ // If no comment is provided we should use an empty comment saveDocument(doc, "", context); } + + public void saveDocument(XWikiDocument doc, String comment, XWikiContext context) + throws XWikiException + { + saveDocument(doc, comment, false, context); + } - public void saveDocument(XWikiDocument doc, String comment, XWikiContext context) + public void saveDocument(XWikiDocument doc, String comment, boolean isMinorEdit, XWikiContext context) throws XWikiException { String server = null, database = null; @@ -910,6 +916,7 @@ // Setting comment before saving doc.setComment((comment == null) ? "" : comment); + doc.setMinorEdit(isMinorEdit); getStore().saveXWikiDoc(doc, context); @@ -4953,6 +4960,16 @@ return false; return "1".equals(Param("xwiki.editcomment.mandatory", "0")); } + + public boolean hasMinorEdit(XWikiContext context) + { + String bl = getXWikiPreference("minoredit", "", context); + if ("1".equals(bl)) + return true; + if ("0".equals(bl)) + return false; + return "1".equals(Param("xwiki.minoredit", "1")); + } /** * @deprecated use {@link XWikiDocument#rename(String, XWikiContext)} instead Index: xwiki-platform-core/src/main/java/com/xpn/xwiki/web/Utils.java =================================================================== --- xwiki-platform-core/src/main/java/com/xpn/xwiki/web/Utils.java (revision 4076) +++ xwiki-platform-core/src/main/java/com/xpn/xwiki/web/Utils.java (working copy) @@ -136,14 +136,18 @@ return redirect; } - public static String getRedirect(String action, XWikiContext context) { + public static String getRedirect(String action, String params, XWikiContext context) { String redirect; redirect = context.getRequest().getParameter("xredirect"); if ((redirect == null) || (redirect.equals(""))) - redirect = context.getDoc().getURL(action, true, context); + redirect = context.getDoc().getURL(action, params, true, context); return redirect; } + public static String getRedirect(String action, XWikiContext context) { + return getRedirect(action, null, context); + } + public static String getPage(XWikiRequest request, String defaultpage) { String page; page = request.getParameter("xpage"); Index: xwiki-platform-core/src/main/java/com/xpn/xwiki/web/EditForm.java =================================================================== --- xwiki-platform-core/src/main/java/com/xpn/xwiki/web/EditForm.java (revision 4076) +++ xwiki-platform-core/src/main/java/com/xpn/xwiki/web/EditForm.java (working copy) @@ -50,11 +50,13 @@ private String title; private String comment; + + private boolean isMinorEdit = false; private String tags; private boolean lockForce; - + public void readRequest() { XWikiRequest request = getRequest(); @@ -71,6 +73,7 @@ setDefaultLanguage(request.getParameter("default_language")); setTags(request.getParameterValues("tags")); setLockForce("1".equals(request.getParameter("force"))); + setMinorEdit(request.getParameter("minor_edit")!=null); } public void setTags(String[] parameter) @@ -231,6 +234,16 @@ { this.comment = comment; } + + public boolean isMinorEdit() + { + return isMinorEdit; + } + + public void setMinorEdit(boolean isMinorEdit) + { + this.isMinorEdit = isMinorEdit; + } public boolean isLockForce() { Index: xwiki-platform-core/src/main/java/com/xpn/xwiki/web/CommentAddAction.java =================================================================== --- xwiki-platform-core/src/main/java/com/xpn/xwiki/web/CommentAddAction.java (revision 4076) +++ xwiki-platform-core/src/main/java/com/xpn/xwiki/web/CommentAddAction.java (working copy) @@ -55,7 +55,7 @@ newobject.setNumber(oldobject.getNumber()); newobject.setName(doc.getFullName()); doc.setObject(className, nb, newobject); - xwiki.saveDocument(doc, context.getMessageTool().get("core.comment.addComment"), context); + xwiki.saveDocument(doc, context.getMessageTool().get("core.comment.addComment"), true, context); } // forward to edit String redirect = Utils.getRedirect("edit", context); Index: xwiki-platform-core/src/main/java/com/xpn/xwiki/web/ObjectRemoveAction.java =================================================================== --- xwiki-platform-core/src/main/java/com/xpn/xwiki/web/ObjectRemoveAction.java (revision 4076) +++ xwiki-platform-core/src/main/java/com/xpn/xwiki/web/ObjectRemoveAction.java (working copy) @@ -43,7 +43,7 @@ objects.set(classId, null); doc.addObjectsToRemove(object); doc.setAuthor(context.getUser()); - xwiki.saveDocument(doc, context.getMessageTool().get("core.comment.deleteObject"), context); + xwiki.saveDocument(doc, context.getMessageTool().get("core.comment.deleteObject"), true, context); // forward to edit String redirect = Utils.getRedirect("edit", context); Index: xwiki-platform-core/src/main/java/com/xpn/xwiki/web/SaveAction.java =================================================================== --- xwiki-platform-core/src/main/java/com/xpn/xwiki/web/SaveAction.java (revision 4076) +++ xwiki-platform-core/src/main/java/com/xpn/xwiki/web/SaveAction.java (working copy) @@ -95,6 +95,7 @@ tdoc.setContent(content); tdoc.setTitle(title); tdoc.setComment(sectionDoc.getComment()); + tdoc.setMinorEdit(sectionDoc.isMinorEdit()); }else{ tdoc.readFromForm((EditForm) form, context); } @@ -110,7 +111,7 @@ // We get the comment to be used from the document // It was read using readFromForm - xwiki.saveDocument(tdoc, tdoc.getComment(), context); + xwiki.saveDocument(tdoc, tdoc.getComment(), tdoc.isMinorEdit(), context); XWikiLock lock = tdoc.getLock(context); if (lock != null) tdoc.removeLock(context); Index: xwiki-platform-core/src/main/java/com/xpn/xwiki/web/PropAddAction.java =================================================================== --- xwiki-platform-core/src/main/java/com/xpn/xwiki/web/PropAddAction.java (revision 4076) +++ xwiki-platform-core/src/main/java/com/xpn/xwiki/web/PropAddAction.java (working copy) @@ -62,7 +62,7 @@ if (doc.isNew()) { doc.setCreator(username); } - xwiki.saveDocument(doc, context.getMessageTool().get("core.comment.addClassProperty"), context); + xwiki.saveDocument(doc, context.getMessageTool().get("core.comment.addClassProperty"), true, context); } } // forward to edit Index: xwiki-platform-core/src/main/java/com/xpn/xwiki/web/XWikiPortlet.java =================================================================== --- xwiki-platform-core/src/main/java/com/xpn/xwiki/web/XWikiPortlet.java (revision 4076) +++ xwiki-platform-core/src/main/java/com/xpn/xwiki/web/XWikiPortlet.java (working copy) @@ -179,6 +179,8 @@ form = new ObjectRemoveForm(); else if (action.equals("propadd")) form = new PropAddForm(); + else if (action.equals("deleteversions")) + form = new DeleteVersionsForm(); if (form != null) { form.reset(null, request); @@ -217,6 +219,8 @@ (new RegisterAction()).action(context); } else if (action.equals("inline")) { (new InlineAction()).action(context); + } else if (action.equals("deleteversions")) { + (new DeleteVersionsAction()).action(context); } } catch (Throwable e) { handleException(request, response, e, context); Index: xwiki-platform-core/src/main/java/com/xpn/xwiki/web/ObjectAddAction.java =================================================================== --- xwiki-platform-core/src/main/java/com/xpn/xwiki/web/ObjectAddAction.java (revision 4076) +++ xwiki-platform-core/src/main/java/com/xpn/xwiki/web/ObjectAddAction.java (working copy) @@ -63,7 +63,7 @@ if (doc.isNew()) { doc.setCreator(username); } - xwiki.saveDocument(doc, context.getMessageTool().get("core.comment.addObject"), context); + xwiki.saveDocument(doc, context.getMessageTool().get("core.comment.addObject"), true, context); // forward to edit String redirect = Utils.getRedirect("edit", context); Index: xwiki-platform-core/src/main/java/com/xpn/xwiki/web/DeleteVersionsForm.java =================================================================== --- xwiki-platform-core/src/main/java/com/xpn/xwiki/web/DeleteVersionsForm.java (revision 0) +++ xwiki-platform-core/src/main/java/com/xpn/xwiki/web/DeleteVersionsForm.java (revision 0) @@ -0,0 +1,72 @@ +/* + * Copyright 2007, XpertNet SARL, and individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package com.xpn.xwiki.web; + +/** + * Struts form for {@link DeleteVersionsAction}. + * @version $Id: $ + */ +public class DeleteVersionsForm extends XWikiForm +{ + /** from revision. */ + private String rev1; + /** to revision. */ + private String rev2; + /** document language. */ + private String language; + /** is action confirmed. */ + private boolean confirm; + /** {@inheritDoc} */ + public void readRequest() + { + XWikiRequest request = getRequest(); + rev1 = request.getParameter("rev1"); + rev2 = request.getParameter("rev2"); + language = request.getParameter("language"); + confirm = request.getParameter("confirm") != null; + } + /** + * @return from revision + */ + public String getRev1() + { + return rev1; + } + /** + * @return to revision + */ + public String getRev2() + { + return rev2; + } + /** + * @return document language + */ + public String getLanguage() + { + return language; + } + /** + * @return is action confirmed + */ + public boolean isConfirmed() + { + return confirm; + } +} Property changes on: xwiki-platform-core/src/main/java/com/xpn/xwiki/web/DeleteVersionsForm.java ___________________________________________________________________ Name: svn:eol-style + native Index: xwiki-platform-core/src/main/java/com/xpn/xwiki/web/PropUpdateAction.java =================================================================== --- xwiki-platform-core/src/main/java/com/xpn/xwiki/web/PropUpdateAction.java (revision 4076) +++ xwiki-platform-core/src/main/java/com/xpn/xwiki/web/PropUpdateAction.java (working copy) @@ -75,7 +75,7 @@ doc.setxWikiClass(bclass2); doc.renameProperties(bclass.getName(), fieldsToRename); - xwiki.saveDocument(doc, context.getMessageTool().get("core.comment.updateClassProperty"), context); + xwiki.saveDocument(doc, context.getMessageTool().get("core.comment.updateClassProperty"), true, context); // We need to load all documents that use this property and rename it if (fieldsToRename.size() > 0) { @@ -88,7 +88,7 @@ for (int i = 0; i < list.size(); i++) { XWikiDocument doc2 = xwiki.getDocument((String) list.get(i), context); doc2.renameProperties(bclass.getName(), fieldsToRename); - xwiki.saveDocument(doc2, context.getMessageTool().get("core.comment.updateClassPropertyName"), context); + xwiki.saveDocument(doc2, context.getMessageTool().get("core.comment.updateClassPropertyName"), true, context); } } xwiki.flushCache(); Index: xwiki-platform-core/src/main/java/com/xpn/xwiki/web/DeleteVersionsAction.java =================================================================== --- xwiki-platform-core/src/main/java/com/xpn/xwiki/web/DeleteVersionsAction.java (revision 0) +++ xwiki-platform-core/src/main/java/com/xpn/xwiki/web/DeleteVersionsAction.java (revision 0) @@ -0,0 +1,91 @@ +/* + * Copyright 2007, XpertNet SARL, and individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package com.xpn.xwiki.web; + +import org.suigeneris.jrcs.rcs.Version; + +import com.xpn.xwiki.XWikiContext; +import com.xpn.xwiki.XWikiException; +import com.xpn.xwiki.doc.XWikiDocument; +import com.xpn.xwiki.doc.XWikiDocumentArchive; + +/** + * Struts action for delete document versions. + * @version $Id: $ + */ +public class DeleteVersionsAction extends XWikiAction +{ + /** + * {@inheritDoc} + */ + public boolean action(XWikiContext context) throws XWikiException + { + XWikiResponse response = context.getResponse(); + XWikiDocument doc = context.getDoc(); + DeleteVersionsForm form = (DeleteVersionsForm) context.getForm(); + + boolean confirm = form.isConfirmed(); + String rev1 = form.getRev1(); + String rev2 = form.getRev2(); + String language = form.getLanguage(); + XWikiDocument tdoc; + + if (confirm) { + if (language == null || language.equals("") || language.equals("default") + || language.equals(doc.getDefaultLanguage())) { + // Need to save parent and defaultLanguage if they have changed + tdoc = doc; + } else { + tdoc = doc.getTranslatedDocument(language, context); + if (tdoc == doc) { + tdoc = new XWikiDocument(doc.getSpace(), doc.getName()); + tdoc.setLanguage(language); + } + tdoc.setTranslation(1); + } + + XWikiDocumentArchive archive = tdoc.getDocumentArchive(context); + Version v1; + Version v2; + try { + v1 = new Version(rev1); + v2 = new Version(rev2); + } catch (NullPointerException e) { + // incorrect or unselected versions + sendRedirect(context); + return false; + } + archive.removeVersions(v1, v2, tdoc, context); + context.getWiki().getVersioningStore().saveXWikiDocArchive(archive, true, context); + tdoc.setDocumentArchive(archive); + } + sendRedirect(context); + return false; + } + /** + * redirect back to view history. + * @param context used in redirecting + * @throws XWikiException if any error + */ + private void sendRedirect(XWikiContext context) throws XWikiException { + // forward to view + String redirect = Utils.getRedirect("view", "viewer=history", context); + sendRedirect(context.getResponse(), redirect); + } +} Property changes on: xwiki-platform-core/src/main/java/com/xpn/xwiki/web/DeleteVersionsAction.java ___________________________________________________________________ Name: svn:eol-style + native Index: xwiki-platform-core/src/main/resources/ApplicationResources.properties =================================================================== --- xwiki-platform-core/src/main/resources/ApplicationResources.properties (revision 4076) +++ xwiki-platform-core/src/main/resources/ApplicationResources.properties (working copy) @@ -702,6 +702,10 @@ core.comment.rollback=Rollback to version {0} core.comment.updateContent=Update Content +core.minoredit=Is minor edit +core.minoredit.show=Show minor edits +core.minoredit.hide=Hide minor edits + # top menu core.menu.documentation=Documentation core.menu.create=Create @@ -738,6 +742,9 @@ core.delete.confirm=The deletion of a document is not reversible. Are you sure you wish to continue? core.delete.confirmWithBacklinks=In addition, the deletion of a document is not reversible. Are you sure you wish to continue? +core.versions.delete=Delete versions +core.versions.delete.confirm=Do you want to delete selected versions? + core.tagedit.title=Tags: panels.documentInformation.title=Document information Index: xwiki-platform-core/src/main/resources/xwiki.oracle.hbm.xml =================================================================== --- xwiki-platform-core/src/main/resources/xwiki.oracle.hbm.xml (revision 4076) +++ xwiki-platform-core/src/main/resources/xwiki.oracle.hbm.xml (working copy) @@ -101,17 +101,37 @@ + + + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + Index: xwiki-platform-core/src/main/resources/xwiki.derby.hbm.xml =================================================================== --- xwiki-platform-core/src/main/resources/xwiki.derby.hbm.xml (revision 4076) +++ xwiki-platform-core/src/main/resources/xwiki.derby.hbm.xml (working copy) @@ -107,17 +107,37 @@ + + + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + Index: xwiki-platform-core/src/main/resources/xwiki.hbm.xml =================================================================== --- xwiki-platform-core/src/main/resources/xwiki.hbm.xml (revision 4076) +++ xwiki-platform-core/src/main/resources/xwiki.hbm.xml (working copy) @@ -99,17 +99,33 @@ + + + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + Index: xwiki-platform-core/pom.xml =================================================================== --- xwiki-platform-core/pom.xml (revision 4076) +++ xwiki-platform-core/pom.xml (working copy) @@ -83,14 +83,12 @@ org.suigeneris jrcs.diff - 0.3.0 - xwiki + 0.4.2 org.suigeneris jrcs.rcs - 0.3.0 - xwiki + 0.4.2 @@ -560,4 +563,4 @@ - \ No newline at end of file +