SVNGroovy
import java.util.Collection;
import java.util.Iterator;
import org.apache.commons.lang.StringUtils;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.io.*;
import org.tmatesoft.svn.core.wc.*;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
import org.tmatesoft.svn.core.io.diff.SVNDeltaGenerator;
import com.xpn.xwiki.doc.XWikiDocument;
public class SVNLogReader {
def xwiki;
def context;
def repurl = "";
def defaultspace = "";
def configdoc;
def savedlist = "";
def SVNRepository repository = null;
def username = "";
def password = "";
def defaultDate = "";
def defaultUser = "";
def sdebug = "";
def status = new HashMap();
public setXWiki(xwiki, context) {
this.xwiki = xwiki;
this.context = context;
}
public hasProgrammingRights() {
return xwiki.hasProgrammingRights();
}
public setSVNConfig(page) {
this.configdoc = xwiki.getDocument(page)
this.username = configdoc.getValue("username")
this.password = configdoc.getObject("SVNCode.SVNConfigClass").getProperty("password").property.value
this.repurl = configdoc.getValue("repository");
this.defaultspace = configdoc.getValue("defaultspace");
this.savedlist = configdoc.getValue("savedlist");
this.defaultDate = configdoc.getValue("defaultdate");
this.defaultUser = configdoc.getValue("defaultuser");
this.status = getStatus(configdoc.getValue("status"));
debug("Date:" + this.defaultDate);
}
public getStatus(status) {
def smap = new HashMap();
for (sline in StringUtils.split(status, "\r\n")) {
def items = sline.split(";");
smap.put(items[0], [ "xwikiversion" : (items.length>1) ? items[1] : "", "xwikihash" : (items.length>2) ? items[2] : "", "svnversion" : (items.length>3) ? items[3] : "" ])
}
return smap;
}
public getPageStatus(filePath) {
return status.get(filePath);
}
public saveStatus() {
def sstatus = "";
for (key in status.keySet()) {
def stat = status.get(key);
sstatus += "${key};${stat.xwikiversion};${stat.xwikihash};${stat.svnversion}\n";
}
only save if changed
if (status!=configdoc.getValue("status")) {
configdoc.set("status", sstatus);
configdoc.save();
}
}
public debug(message) {
sdebug += message + "\n";
def cdate = new Date();
System.out.println("${cdate}: ${message}");
}
public getDebug() {
return this.sdebug;
}
public setRepository(rep) {
this.repurl = rep;
}
public getDefaultSpace() {
return this.defaultspace;
}
public getSavedList() {
return (this.savedlist==null) ? "" : this.savedlist;
}
public getFilePath(pagedoc) {
get page
def filePath = pagedoc.getSpace() + "/" + pagedoc.getName();
def language = pagedoc.getLanguage();
if (language!=null&&language!="")
filePath += "." + language;
filePath += ".xml"
return filePath;
}
public getStatusPath(pagedoc) {
get page
def filePath = pagedoc.getSpace() + "/" + pagedoc.getName();
def language = pagedoc.getLanguage();
if (language!=null&&language!="")
filePath += "." + language;
return filePath;
}
public boolean initRepository() {
/*
- initializes the library (it must be done before ever using the
- library itself)
*/
setupLibrary();
try {
/*
- Creates an instance of SVNRepository to work with the repository.
- All user's requests to the repository are relative to the
- repository location used to create this SVNRepository.
- SVNURL is a wrapper for URL strings that refer to repository locations.
*/
repository = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(repurl));
} catch (SVNException svne) {
/* - Perhaps a malformed URL is the cause of this exception
*/
debug("Error while creating an SVNRepository for location '"
+ repurl + "': " + svne.getMessage());
return false;
}
/* - User's authentication information (name/password) is provided via an
- ISVNAuthenticationManager instance. SVNWCUtil creates a default
- authentication manager given user's name and password.
- Default authentication manager first attempts to use provided user name
- and password and then falls back to the credentials stored in the
- default Subversion credentials storage that is located in Subversion
- configuration area. If you'd like to use provided user name and password
- only you may use BasicAuthenticationManager class instead of default
- authentication manager:
- authManager = new BasicAuthenticationsManager(userName, userPassword);
- You may also skip this point - anonymous access will be used.
*/
ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager(username, password);
repository.setAuthenticationManager(authManager);
try {
/*
- Checks up if the specified path/to/repository part of the URL
- really corresponds to a directory. If doesn't the program exits.
- SVNNodeKind is that one who says what is located at a path in a
- revision. -1 means the latest revision.
*/
SVNNodeKind nodeKind = repository.checkPath("", -1);
if (nodeKind == SVNNodeKind.NONE) {
debug("There is no entry at '" + repurl + "'.");
return false;
} else if (nodeKind == SVNNodeKind.FILE) {
debug("The entry at '" + repurl + "' is a file while a directory was expected.");
return false;
}
} catch (SVNException svne) {
debug("error while listing entries: " + svne.getMessage());
return false;
}
return true;
}
public listFiles() {
def str = ""
try {
/*
- getRepositoryRoot() returns the actual root directory where the
- repository was created. 'true' forces to connect to the repository
- if the root url is not cached yet.
*/
str += "Repository Root: " + repository.getRepositoryRoot(true).toString() + "\n";
/* - getRepositoryUUID() returns Universal Unique IDentifier (UUID) of the
- repository. 'true' forces to connect to the repository
- if the UUID is not cached yet.
*/
str += "Repository UUID: " + repository.getRepositoryUUID(true).toString() + "\n";
/*
- Displays the repository tree at the current path - "" (what means
- the path/to/repository directory)
*/
str += listEntries(repository, "");
return str;
} catch (SVNException svne) {
debug("error while listing entries: " + svne.getMessage());
return str;
}
}
/*
- Initializes the library to work with a repository via
- different protocols.
*/
private static void setupLibrary() {
/* - For using over http:// and https://
*/
DAVRepositoryFactory.setup();
/* - For using over svn:// and svn+xxx://
*/
SVNRepositoryFactoryImpl.setup();
/* - For using over file:///
*/
FSRepositoryFactory.setup();
}
/*
- Called recursively to obtain all entries that make up the repository tree
- repository - an SVNRepository which interface is used to carry out the
- request, in this case it's a request to get all entries in the directory
- located at the path parameter;
- path is a directory path relative to the repository location path (that
- is a part of the URL used to create an SVNRepository instance);
-
*/
public listEntries(SVNRepository repository, String path) throws SVNException {
/* - Gets the contents of the directory specified by path at the latest
- revision (for this purpose -1 is used here as the revision number to
- mean HEAD-revision) getDir returns a Collection of SVNDirEntry
- elements. SVNDirEntry represents information about the directory
- entry. Here this information is used to get the entry name, the name
- of the person who last changed this entry, the number of the revision
- when it was last changed and the entry type to determine whether it's
- a directory or a file. If it's a directory listEntries steps into a
- next recursion to display the contents of this directory. The third
- parameter of getDir is null and means that a user is not interested
- in directory properties. The fourth one is null, too - the user
- doesn't provide its own Collection instance and uses the one returned
- by getDir.
*/
def str = "";
Collection entries = repository.getDir(path, -1, null,
(Collection) null);
Iterator iterator = entries.iterator();
while (iterator.hasNext()) {
SVNDirEntry entry = (SVNDirEntry) iterator.next();
str += entry.getDate().toString() + ": " + path + "/" + entry.getName().toString() + " " + entry.getRevision().toString() + " \n";
/* str += "/" + (path.equals("") ? "" : path + "/")
+ entry.getName().toString() + " (author: '" + entry.getAuthor().toString()
+ "'; revision: " + entry.getRevision().toString() + "; date: " + entry.getDate().toString() + ")" + "\n";
*/
/* - Checking up if the entry is a directory.
*/
if (entry.getKind() == SVNNodeKind.DIR) {
str += listEntries(repository, (path.equals("")) ? entry.getName().toString()
- path + "/" + entry.getName().toString());
}
}
return str;
}
public String getFileContentAsString(String space, String page, String language) {
def c = getFileContent(space, page, language);
if (c==null)
return null;
def str = new String(c);
return str.trim();
}
public byte[] getFileContent(String space, String page, String language) {
get page
def filePath = space + "/" + page;
if (language!=null&&language!="")
filePath += "." + language;
filePath += ".xml"
/*
- This Map will be used to get the file properties. Each Map key is a
- property name and the value associated with the key is the property
- value.
*/
SVNProperties fileProperties = new SVNProperties();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
/*
- Checks up if the specified path really corresponds to a file. If
- doesn't the program exits. SVNNodeKind is that one who says what is
- located at a path in a revision. -1 means the latest revision.
*/
SVNNodeKind nodeKind = repository.checkPath(filePath, -1);
if (nodeKind == SVNNodeKind.NONE) {
debug("There is no entry at '" + filePath + "'.");
return null;
} else if (nodeKind == SVNNodeKind.DIR) {
debug("The entry at '" + repurl
+ "' is a directory while a file was expected.");
return null;
}
/* - Gets the contents and properties of the file located at filePath
- in the repository at the latest revision (which is meant by a
- negative revision number).
*/
repository.getFile(filePath, -1, fileProperties, baos);
} catch (SVNException svne) {
System.err.println("error while fetching the file contents and properties: " + svne.getMessage());
System.exit(1);
}
return baos.toByteArray();
}
public getXML(pagedoc) {
def clonedDoc = pagedoc.document.clone();
remove Tag object
if (clonedDoc.getObject("XWiki.TagClass")) {
clonedDoc.removeObject(clonedDoc.getObject("XWiki.TagClass"));
}
if (defaultUser && defaultUser!="") {
clonedDoc.setCreator(defaultUser);
clonedDoc.setContentAuthor(defaultUser);
clonedDoc.setAuthor(defaultUser);
} else {
clonedDoc.setCreator(clonedDoc.getAuthor());
clonedDoc.setContentAuthor(clonedDoc.getAuthor());
}
if (defaultDate && defaultDate!="") {
clonedDoc.setCreationDate(defaultDate);
clonedDoc.setContentUpdateDate(defaultDate);
clonedDoc.setDate(defaultDate);
clonedDoc.setVersion("1.1");
} else {
clonedDoc.setContentUpdateDate(clonedDoc.getDate())
clonedDoc.setCreationDate(clonedDoc.getDate())
clonedDoc.setVersion("1.1");
}
clonedDoc.setComment("");
clonedDoc.setMinorEdit(false);
def c = clonedDoc.toXML(true, false, true, false, context.getContext())
return c.trim().replaceAll("[\r]","");
}
public commitFile(pagedoc, message) {
def c = getXML(pagedoc);
return setFileContent(pagedoc.getSpace(), pagedoc.getName(), pagedoc.getLanguage(), c, message)
}
public commitFiles(pagelist, message) {
def newSpaces = new ArrayList();
def newPages = new ArrayList();
def currentPageData = new HashMap();
def pagesBySpace = new HashMap();
def pagename2 = "";
for (pagename in pagelist) {
def pagedoc = xwiki.getDocument(pagename);
make sure we get the right translations
def lang = context.getRequest().get("${pagename}_language")
if (lang!=null && lang!="")
pagedoc = pagedoc.getTranslatedDocument(lang);
def space = pagedoc.getSpace();
def page = pagedoc.getName();
def language = pagedoc.getLanguage();
if (!newSpaces.contains(space)) {
if (repository.checkPath(space, -1) == SVNNodeKind.NONE) {
newSpaces.add(space);
debug("Adding to new spaces: ${space}")
}
}
get page
def filePath = space + "/" + page;
if (language!=null&&language!="")
filePath += "." + language;
filePath += ".xml"
if (repository.checkPath(filePath, -1) == SVNNodeKind.NONE) {
newPages.add(filePath);
debug("Adding to new pages: ${filePath}")
} else {
debug("Adding to updated pages: ${filePath}")
currentPageData.put(filePath, getFileContent(space, page, language))
}
def pageList = pagesBySpace.get(space);
if (pageList==null) {
pageList = new ArrayList();
pagesBySpace.put(space, pageList);
}
pageList.add(pagedoc);
}
def editor = repository.getCommitEditor(message, null);
def committedPages = new ArrayList();
def commitInfo = null;
try {
/*
- Always called first. Opens the current root directory. It means all
- modifications will be applied to this directory until a next entry
- (located inside the root) is opened/added.
- -1 - revision is HEAD (actually, for a comit editor this number is
- irrelevant)
*/
editor.openRoot(-1);
for (space in pagesBySpace.keySet()) {
start by creating the space if necessary and open it if not
/*
- Adds a new directory (in this case - to the root directory for
- which the SVNRepository was created).
- Since this moment all changes will be applied to this new directory.
- dirPath is relative to the root directory.
- copyFromPath (the 2nd parameter) is set to null and copyFromRevision
- (the 3rd) parameter is set to -1 since the directory is not added
- with history (is not copied, in other words).
*/
if (newSpaces.contains(space)) {
editor.addDir(space, null, -1);
} else {
editor.openDir(space, -1);
}
loop on each page
for (pagedoc in pagesBySpace.get(space)) {
pagename2 = pagedoc.getFullName();
def page = pagedoc.getName();
def language = pagedoc.getLanguage();
get page path
def filePath = space + "/" + page;
if (language!=null&&language!="")
filePath += "." + language;
filePath += ".xml"
def newXML = getXML(pagedoc);
def newData = newXML.getBytes();
if (newPages.contains(filePath)) {
/*
- Adds a new file to the just added directory. The file path is also
- defined as relative to the root directory.
* - copyFromPath (the 2nd parameter) is set to null and copyFromRevision
- (the 3rd parameter) is set to -1 since the file is not added with
- history.
*/
editor.addFile(filePath, null, -1);
/*
- The next steps are directed to applying delta to the file (that is
- the full contents of the file in this case).
*/
editor.applyTextDelta(filePath, null);
/* - Use delta generator utility class to generate and send delta
- Note that you may use only 'target' data to generate delta when there is no
- access to the 'base' (previous) version of the file. However, using 'base'
- data will result in smaller network overhead.
- SVNDeltaGenerator will call editor.textDeltaChunk(...) method for each generated
- "diff window" and then editor.textDeltaEnd(...) in the end of delta transmission.
- Number of diff windows depends on the file size.
-
*/
SVNDeltaGenerator deltaGenerator = new SVNDeltaGenerator();
String checksum = deltaGenerator.sendDelta(filePath, new ByteArrayInputStream(newData), editor, true);
/*
- Closes the new added file.
*/
editor.closeFile(filePath, checksum);
} else {
def oldData = currentPageData.get(filePath);
/*
- Opens the file added in the previous commit.
- filePath is also defined as a relative path to the root directory.
*/
editor.openFile(filePath, -1);
/* - The next steps are directed to applying and writing the file delta.
*/
editor.applyTextDelta(filePath, null);
/* - Use delta generator utility class to generate and send delta
- Note that you may use only 'target' data to generate delta when there is no
- access to the 'base' (previous) version of the file. However, here we've got 'base'
- data, what in case of larger files results in smaller network overhead.
- SVNDeltaGenerator will call editor.textDeltaChunk(...) method for each generated
- "diff window" and then editor.textDeltaEnd(...) in the end of delta transmission.
- Number of diff windows depends on the file size.
-
*/
SVNDeltaGenerator deltaGenerator = new SVNDeltaGenerator();
String checksum = deltaGenerator.sendDelta(filePath, new ByteArrayInputStream(oldData), 0, new ByteArrayInputStream(newData), editor, true);
/*
- Closes the file.
*/
editor.closeFile(filePath, checksum);
updating status to check for changes
committedPages.add(getStatusPath(pagedoc));
def pageStatus = status.get(getStatusPath(pagedoc));
if (pageStatus==null)
pageStatus = [ "xwikiversion" : "", "xwikihash" : "", "svnversion" : ""];
status.put(getStatusPath(pagedoc), pageStatus);
pageStatus.xwikiversion = "${pagedoc.getVersion()}";
pageStatus.xwikihash = "${newXML.hashCode()}";
}
}
close the space
editor.closeDir();
}
close root dir
editor.closeDir();
} catch (Throwable e) {
e.printStackTrace();
debug("error preparing commit on page ${pagename2}: " + e.getMessage());
return null;
} finally {
/*
- This is the final point in all editor handling. Only now all that new
- information previously described with the editor's methods is sent to
- the server for committing. As a result the server sends the new
- commit information.
*/
try {
commitInfo = editor.closeEdit();
} catch (Throwable e) {
e.printStackTrace();
debug("error calling closeEdit: " + e.getMessage());
return null;
}
}
we need to save the status
def rev = commitInfo.getNewRevision();
for (def pagepath in committedPages) {
status.get(pagepath).svnversion = "${rev}";
}
saveStatus();
return commitInfo;
}
public setFileContent(String space, String page, String language, String xml, String message) {
def createDir = (repository.checkPath(space, -1) != SVNNodeKind.DIR);
setFileContent(space, page, language, xml, message, createDir, true);
}
public setFileContent(String space, String page, String language, String xml, String message, boolean createDir) {
get page
def filePath = space + "/" + page;
if (language!=null&&language!="")
filePath += "." + language;
filePath += ".xml"
def c = getFileContentAsString(space, page, language);
def changed = false;
if (c==null || !c.equals(xml))
changed = true;
if (changed) {
def editor = repository.getCommitEditor(message, null);
def commitInfo = null;
try {
if (c==null) {
page does not exist we need to add it
debug("Ready to add file ${filePath} with createDir ${createDir}")
addFile(editor, space, filePath, xml.getBytes(), createDir, true, true)
} else {
content is different we can commit
page does not exist we need to add it
debug("Ready to update file ${filePath}")
modifyFile(editor, space, filePath, c.getBytes(), xml.getBytes())
}
} catch (Throwable e) {
e.printStackTrace();
debug("error preparing commit: " + e.getMessage());
return null;
} finally {
/*
- This is the final point in all editor handling. Only now all that new
- information previously described with the editor's methods is sent to
- the server for committing. As a result the server sends the new
- commit information.
*/
try {
commitInfo = editor.closeEdit();
} catch (Throwable e) {
e.printStackTrace();
debug("error calling closeEdit: " + e.getMessage());
return null;
}
}
return commitInfo;
} else {
content is the same. do not commit
debug("content is the same. do not commit");
return null;
}
}
/*
- This method performs commiting an addition of a directory containing a
- file.
*/
private void addFile(ISVNEditor editor, String dirPath,
String filePath, byte[] data, boolean createDir, boolean closeDir, boolean closeRootDir) throws SVNException {
/* - Always called first. Opens the current root directory. It means all
- modifications will be applied to this directory until a next entry
- (located inside the root) is opened/added.
- -1 - revision is HEAD (actually, for a comit editor this number is
- irrelevant)
*/
editor.openRoot(-1);
/* - Adds a new directory (in this case - to the root directory for
- which the SVNRepository was created).
- Since this moment all changes will be applied to this new directory.
- dirPath is relative to the root directory.
- copyFromPath (the 2nd parameter) is set to null and copyFromRevision
- (the 3rd) parameter is set to -1 since the directory is not added
- with history (is not copied, in other words).
*/
if (createDir) {
editor.addDir(dirPath, null, -1);
} else {
editor.openDir(dirPath, -1);
}
/* - Adds a new file to the just added directory. The file path is also
- defined as relative to the root directory.
* - copyFromPath (the 2nd parameter) is set to null and copyFromRevision
- (the 3rd parameter) is set to -1 since the file is not added with
- history.
*/
editor.addFile(filePath, null, -1);
/* - The next steps are directed to applying delta to the file (that is
- the full contents of the file in this case).
*/
editor.applyTextDelta(filePath, null);
/* - Use delta generator utility class to generate and send delta
- Note that you may use only 'target' data to generate delta when there is no
- access to the 'base' (previous) version of the file. However, using 'base'
- data will result in smaller network overhead.
- SVNDeltaGenerator will call editor.textDeltaChunk(...) method for each generated
- "diff window" and then editor.textDeltaEnd(...) in the end of delta transmission.
- Number of diff windows depends on the file size.
-
*/
SVNDeltaGenerator deltaGenerator = new SVNDeltaGenerator();
String checksum = deltaGenerator.sendDelta(filePath, new ByteArrayInputStream(data), editor, true);
/*
- Closes the new added file.
*/
editor.closeFile(filePath, checksum);
/* - Closes the new added directory.
*/
if (closeDir)
editor.closeDir();
/*
- Closes the root directory.
*/
if (closeRootDir)
editor.closeDir();
}
/*
- This method performs committing file modifications.
*/
private void modifyFile(ISVNEditor editor, String dirPath,
String filePath, byte[] oldData, byte[] newData) throws SVNException {
/* - Always called first. Opens the current root directory. It means all
- modifications will be applied to this directory until a next entry
- (located inside the root) is opened/added.
- -1 - revision is HEAD
*/
editor.openRoot(-1);
/* - Opens a next subdirectory (in this example program it's the directory
- added in the last commit). Since this moment all changes will be
- applied to this directory.
- dirPath is relative to the root directory.
- -1 - revision is HEAD
*/
editor.openDir(dirPath, -1);
/* - Opens the file added in the previous commit.
- filePath is also defined as a relative path to the root directory.
*/
editor.openFile(filePath, -1);
/* - The next steps are directed to applying and writing the file delta.
*/
editor.applyTextDelta(filePath, null);
/* - Use delta generator utility class to generate and send delta
- Note that you may use only 'target' data to generate delta when there is no
- access to the 'base' (previous) version of the file. However, here we've got 'base'
- data, what in case of larger files results in smaller network overhead.
- SVNDeltaGenerator will call editor.textDeltaChunk(...) method for each generated
- "diff window" and then editor.textDeltaEnd(...) in the end of delta transmission.
- Number of diff windows depends on the file size.
-
*/
SVNDeltaGenerator deltaGenerator = new SVNDeltaGenerator();
String checksum = deltaGenerator.sendDelta(filePath, new ByteArrayInputStream(oldData), 0, new ByteArrayInputStream(newData), editor, true);
/*
- Closes the file.
*/
editor.closeFile(filePath, checksum);
/*
- Closes the directory.
*/
editor.closeDir();
/*
- Closes the root directory.
*/
editor.closeDir();
}
/*
- This method performs committing a deletion of a directory.
*/
private void deleteDir(ISVNEditor editor, String dirPath) throws SVNException {
/* - Always called first. Opens the current root directory. It means all
- modifications will be applied to this directory until a next entry
- (located inside the root) is opened/added.
- -1 - revision is HEAD
*/
editor.openRoot(-1);
/* - Deletes the subdirectory with all its contents.
- dirPath is relative to the root directory.
*/
editor.deleteEntry(dirPath, -1);
/* - Closes the root directory.
*/
editor.closeDir();
}
public getChangedPages(String spaces) {
return getChangedPages(spaces, "");
}
protected checkPage(page, pagedoc, changedMap, samePages, svnEntries) {
def wikicontent = getXML(pagedoc);
def svncontent = getFileContentAsString(pagedoc.getSpace(), pagedoc.getName(), pagedoc.getLanguage())
svncontent = (svncontent == null) ? null : svncontent.trim();
if (!wikicontent.equals(svncontent)) {
def filePath = getFilePath(pagedoc);
def pageStatus = getPageStatus(getStatusPath(pagedoc));
if (pageStatus==null) {
pageStatus = [ "xwikiversion" : pagedoc.getVersion(), "xwikihash" : "", "svnversion" : "" ];
status.put(getStatusPath(pagedoc), pageStatus);
}
pageStatus.filePath = filePath;
pageStatus.page = page;
pageStatus.fullname = pagedoc.fullName;
pageStatus.language = pagedoc.language
pageStatus.status = "";
debug("Checking page ${page} version ${pagedoc.getVersion()} pageStatus ${pageStatus}")
if (pageStatus.xwikihash=="") {
def entry = svnEntries.get(filePath);
def svnversion = (entry==null) ? "" : entry.svnversion;
if (svnversion=="")
pageStatus.status = "A";
else
pageStatus.status = "?";
} else if (pagedoc.getVersion().equals(pageStatus.xwikiversion)) {
version has not changed in the wiki
def whash = "${wikicontent.hashCode()}";
if the recorded hash is the same then we have a modified version in SVN
otherwise it's a bad state so it's a conflict
if (whash == pageStatus.xwikihash)
pageStatus.status = "U";
else
pageStatus.status = "C";
} else {
def entry = svnEntries.get(filePath);
def svnversion = (entry==null) ? "" : entry.svnversion;
if (svnversion=="")
pageStatus.status = "A";
else if(pageStatus.svnversion==svnversion)
pageStatus.status = "M";
else
pageStatus.status = "C";
}
changedMap.put(page, pageStatus)
} else {
def filePath = getFilePath(pagedoc);
samePages.add(filePath);
def pageStatus = getPageStatus(getStatusPath(pagedoc));
if (pageStatus==null) {
pageStatus = [ "xwikiversion" : "", "xwikihash" : "", "svnversion" : "" ];
status.put(getStatusPath(pagedoc), pageStatus);
}
pageStatus.xwikiversion = pagedoc.getVersion();
pageStatus.xwikihash = wikicontent.hashCode();
pageStatus.svnversion = svnEntries.get(filePath).svnversion;
}
}
public getChangedPages(String spaces, String savedlist) {
def changedMap = new TreeMap();
def svnEntries = getEntries();
def spaceList = null;
def samePages = new ArrayList();
def list;
if (!savedlist || savedlist=="") {
spaceList = Arrays.asList(StringUtils.split(spaces," ,"));
def sspaces = StringUtils.join(spaceList, "','");
def sql = "select doc.fullName from XWikiDocument as doc where doc.space in ('${sspaces}')";
debug("Searching for ${sql}");
list = xwiki.search(sql)
} else {
list = StringUtils.split(xwiki.getDocument(savedlist).getValue("list"), "|");
}
for (page in list) {
def pagedoc = xwiki.getDocument(page);
checkPage(page, pagedoc, changedMap, samePages, svnEntries);
for (trans in pagedoc.getTranslationList()) {
def tpagedoc = pagedoc.getTranslatedDocument(trans);
checkPage(page + ".fr", tpagedoc, changedMap, samePages, svnEntries);
}
}
if (spaceList!=null) {
for (entry in svnEntries.keySet()) {
def i1 = entry.indexOf("/");
def i2 = entry.indexOf(".xml");
def spaceName = (i1==-1) ? entry : entry.substring(0, i1);
def pageName = (i2==-1) ? entry : entry.substring(i1+1, i2);
if (spaceList.contains(spaceName)&&!samePages.contains(entry)&&!changedMap.keySet().contains(spaceName + "." + pageName)) {
changedMap.put(spaceName + "." + pageName, [ "status" : "N", "xwikiversion" : "", "svnversion" : svnEntries.get(entry).svnversion ])
}
}
}
saveStatus if changed
saveStatus();
return changedMap;
}
public updatePages(pageList) {
def changedMap = new TreeMap();
def svnEntries = getEntries();
for (page in pageList) {
def pagedoc = xwiki.getDocument(page);
make sure we are getting the right translation
def lang = context.getRequest().get("${page}_language")
if (lang!=null && lang!="")
pagedoc = pagedoc.getTranslatedDocument(lang);
def wikicontent = getXML(pagedoc);
def svncontent = getFileContentAsString(pagedoc.getSpace(), pagedoc.getName(), pagedoc.getLanguage())
def svnversion = "";
if (!wikicontent.equals(svncontent)) {
def filePath = getFilePath(pagedoc);
def pageStatus = getPageStatus(getStatusPath(pagedoc));
if (pageStatus==null) {
pageStatus = [ "xwikiversion" : pagedoc.getVersion(), "xwikihash" : "", "svnversion" : "" ];
}
pageStatus.filePath = filePath;
pageStatus.page = page;
pageStatus.status = "";
debug("Checking page ${page} version ${pagedoc.getVersion()} pageStatus ${pageStatus}")
def needUpdate = false
def entry = svnEntries.get(filePath);
svnversion = (entry==null) ? "" : entry.svnversion;
if (pageStatus.xwikihash=="") {
if (svnversion!="")
needUpdate = true;
} else if (pagedoc.getVersion().equals(pageStatus.xwikiversion)) {
needUpdate = true;
} else {
if (svnversion!="")
needUpdate = true
else if(pageStatus.svnversion!=svnversion)
needUpdate = true;
}
if (needUpdate) {
updating XWiki document from SVN
changedMap.put(page, pageStatus);
def archive = pagedoc.document.getDocumentArchive(context.getContext());
def version = pagedoc.document.getRCSVersion();
def newdoc = new XWikiDocument();
newdoc.fromXML(svncontent);
check attachments that do not exist in updated pages and delete them to recycle bin
for (xa in pagedoc.getAttachmentList()) {
if (!newdoc.getAttachment(xa.getFilename())) {
pagedoc.document.deleteAttachment(xa.attachment, true, context.getContext());
}
}
Make sure they are not marked dirty
for (xa in newdoc.getAttachmentList()) {
xa.setMetaDataDirty(false);
xa.getAttachment_content().setContentDirty(false);
}
we need to make sure previous history is kept
newdoc.setDocumentArchive(archive);
we need to keep the creator if there was already a document
if (pagedoc.getCreator()!=null)
newdoc.setCreator(pagedoc.getCreator());
set user and author to current user
newdoc.setContentAuthor(context.getUser());
newdoc.setAuthor(context.getUser());
we need to make sure no version is added
if (pagedoc.isNew()) {
newdoc.setMetaDataDirty(true);
newdoc.setContentDirty(true);
newdoc.setRCSVersion(null);
} else {
newdoc.setMetaDataDirty(true);
newdoc.setContentDirty(true);
}
saving document
xwiki.getXWiki().saveDocument(newdoc, "Updated from SVN", context.getContext());
saving attachments
newdoc.saveAllAttachments(false, true, context.getContext());
we need to force the saving the document archive.
if (newdoc.getDocumentArchive() != null) {
xwiki.getXWiki().getVersioningStore().saveXWikiDocArchive(newdoc.getDocumentArchive(context.getContext()), true, context.getContext());
}
reading the information to set the status
def newpagedoc = xwiki.getDocument(pagedoc.getFullName());
def newwikicontent = getXML(pagedoc);
pageStatus.xwikiversion = pagedoc.getVersion();
pageStatus.xwikihash = "${newwikicontent.hashCode()}";
pageStatus.svnversion = svnversion;
}
}
}
saveStatus if changed
saveStatus();
return changedMap;
}
public exportPages(docname, pageList) {
def export = xwiki.package
export.setWithVersions(true)
export.setWithVersions(false)
export.setName(docname)
for (page in pageList) {
export.add(page, 0);
}
export.export();
}
public getModifiedFiles(rev) {
return (getModifiedFiles("", rev, "10"));
}
public getModifiedFiles2(date, hour) {
return (getModifiedFiles("", date, hour));
}
public getModifiedFiles2(dir, date, hour) {
}
public getModifiedFiles(dir, rev, max) {
}
public getRevisions(dir) {
}
public listFiles(dir, recursive) {
}
public getCommitStatus(prefix, sep, updatedonly) {
def str = "";
Collection spaceEntries = repository.getDir("", -1, null, (Collection) null);
Iterator spaceIterator = spaceEntries.iterator();
str += "${prefix}page${sep}language${sep}version${sep}isnew${sep}hash${sep}svnpath${sep}svnversion${sep}svnhash${sep}isdiff\n"
while (spaceIterator.hasNext()) {
SVNDirEntry entry = (SVNDirEntry) spaceIterator.next();
if (entry.getKind() == SVNNodeKind.DIR) {
def space = entry.getName().toString();
Collection pageEntries = repository.getDir(space, -1, null, (Collection) null);
Iterator pageIterator = pageEntries.iterator();
while (pageIterator.hasNext()) {
def pageEntry = pageIterator.next();
def fileName = pageEntry.getName().toString();
def i1 = fileName.indexOf(".xml");
def pageName = (i1==-1) ? fileName : fileName.substring(0, i1);
pageName = space + "." + pageName;
def pagedoc = xwiki.getDocument(pageName);
def pagexml = getXML(pagedoc);
def fileProperties = new SVNProperties();
def baos = new ByteArrayOutputStream();
debug("reading file: ${space}/${fileName}")
repository.getFile(space + "/" + fileName, -1, fileProperties, baos);
def svncontent = new String(baos.toByteArray())
def version = (pagedoc==null) ? "" : pagedoc.getVersion();
def hash = (pagexml==null) ? "" : pagexml.hashCode();
def svnversion = pageEntry.getRevision().toString();
def svnhash = (svncontent==null) ? "" : svncontent.hashCode();
def isdiff = !pagexml.equals(svncontent)
if (!updatedonly || !isdiff)
str += "${prefix}${pageName}${sep}${pagedoc.getLanguage()}${sep}${version}${sep}${pagedoc.isNew()}${sep}${hash}${sep}${space}/${fileName}${sep}${svnversion}${sep}${svnhash}${sep}${isdiff}\n"
}
}
}
return str;
}
public getEntries() {
def entries = new HashMap();
Collection spaceEntries = repository.getDir("", -1, null, (Collection) null);
Iterator spaceIterator = spaceEntries.iterator();
while (spaceIterator.hasNext()) {
SVNDirEntry entry = (SVNDirEntry) spaceIterator.next();
if (entry.getKind() == SVNNodeKind.DIR) {
def space = entry.getName().toString();
Collection pageEntries = repository.getDir(space, -1, null, (Collection) null);
Iterator pageIterator = pageEntries.iterator();
while (pageIterator.hasNext()) {
def pageEntry = pageIterator.next();
def fileName = pageEntry.getName().toString();
def filePath = space + "/" + fileName;
def fileProperties = new SVNProperties();
def baos = new ByteArrayOutputStream();
debug("reading file: ${space}/${fileName}")
repository.getFile(filePath, -1, fileProperties, baos);
def svncontent = new String(baos.toByteArray())
def svnversion = pageEntry.getRevision().toString();
def svnhash = (svncontent==null) ? "" : svncontent.hashCode();
entries.put(filePath, [ "svnversion" : svnversion, "svnhash" : svnhash ]);
}
}
}
return entries;
}
public showXMLDiff(pagedoc) {
def svnxml = getFileContentAsString(pagedoc.getSpace(), pagedoc.getName(), pagedoc.getLanguage());
def xml = getXML(pagedoc);
if (pagedoc.isNew() && svnxml == null)
return "Document does not exist";
if (pagedoc.isNew())
return "Document does not exist in the wiki"
if (svnxml==null)
return "Document does not exist in SVN"
remove attachment content from xml
svnxml = svnxml.replaceAll("(?s)<attachment>(.*?)<content>(.*?)</content>(.*?)</attachment>", "<attachment>\$1<content></content>\$3</attachment>")
xml = xml.replaceAll("(?s)<attachment>(.*?)<content>(.*?)</content>(.*?)</attachment>",
"<attachment>\$1<content></content>\$3</attachment>")
return xwiki.diff.getDifferencesAsHTML(svnxml, xml, false);
}
}