Index: core/xwiki-core/src/test/java/com/xpn/xwiki/doc/XWikiDocumentTest.java =================================================================== --- core/xwiki-core/src/test/java/com/xpn/xwiki/doc/XWikiDocumentTest.java (revision 23274) +++ core/xwiki-core/src/test/java/com/xpn/xwiki/doc/XWikiDocumentTest.java (working copy) @@ -304,6 +304,32 @@ new HashSet(linkedPages)); } + public void testGetUniqueWikiLinksFromObjects() throws XWikiException + { + BaseObject theObj = this.document.getObject(CLASSNAME); + theObj.setStringValue("string", "[["+DOCWIKI+":"+DOCSPACE+".Page1]]"); + theObj.setStringListValue("stringlist", Arrays.asList("[["+DOCSPACE+".Page2]]", "VALUE2")); + theObj.setLargeStringValue("area", "[[Page3]]"); + theObj.setLargeStringValue("puretextarea", "[[ShouldNotParse]]"); + this.document.setSyntaxId("xwiki/2.0"); + + String links = ""; + for(XWikiLink link : this.document.getUniqueWikiLinksFromObjects(getContext())){ + links += link.getLink(); + } + assertEquals(links.indexOf(DOCSPACE+".Page1")!=-1, true); + assertEquals(links.indexOf(DOCSPACE+".Page2")!=-1, true); + assertEquals(links.indexOf(".Page3")!=-1, true);//returns XWiki.Page3 in test but seems to work in practice + assertEquals(links.indexOf("ShouldNotParse")!=-1, false); + + //set our vars back to origional states + this.document.setSyntaxId("xwiki/1.0"); + theObj.setStringValue("string","string"); + theObj.setStringListValue("stringlist", Arrays.asList("VALUE1", "VALUE2")); + theObj.setLargeStringValue("area", "area"); + theObj.setLargeStringValue("puretextarea", ""); + } + public void testGetUniqueLinkedPages() { XWikiDocument contextDocument = new XWikiDocument("contextdocspace", "contextdocpage"); @@ -751,6 +777,47 @@ assertEquals("[[newspace.newpage]]", doc3.getContent()); } + public void testRefactorObjectLinks() throws XWikiException + { + BaseObject theObj = this.document.getObject(CLASSNAME); + theObj.setStringValue("string", "[["+DOCWIKI+":"+DOCSPACE+".TestPage]]"); + theObj.setStringListValue("stringlist", Arrays.asList("[["+DOCSPACE+".TestPage2]]", "VALUE2")); + theObj.setLargeStringValue("area", "[["+DOCSPACE+".TestPage3]]"); + theObj.setLargeStringValue("puretextarea", "[["+DOCSPACE+"ShouldNotParse]]"); + this.document.setSyntaxId("xwiki/2.0"); + + org.xwiki.bridge.DocumentName oldName = new org.xwiki.bridge.DocumentName(DOCWIKI,DOCSPACE,"TestPage"); + org.xwiki.bridge.DocumentName newName = new org.xwiki.bridge.DocumentName("newwiki","NewSpace","NewTestPage"); + org.xwiki.bridge.DocumentName oldName2 = new org.xwiki.bridge.DocumentName(DOCWIKI,DOCSPACE,"TestPage2"); + org.xwiki.bridge.DocumentName newName2 = new org.xwiki.bridge.DocumentName("newwiki","NewSpace","NewTestPage2"); + org.xwiki.bridge.DocumentName oldName3 = new org.xwiki.bridge.DocumentName(DOCWIKI,DOCSPACE,"TestPage3"); + org.xwiki.bridge.DocumentName newName3 = new org.xwiki.bridge.DocumentName("newwiki","NewSpace","NewTestPage3"); + org.xwiki.bridge.DocumentName oldName4 = new org.xwiki.bridge.DocumentName(DOCWIKI,DOCSPACE,"ShouldNotParse"); + org.xwiki.bridge.DocumentName newName4 = new org.xwiki.bridge.DocumentName("newwiki","NewSpace","Failure"); + + this.document.refactorObjectLinks(oldName,newName,getContext()); + this.document.refactorObjectLinks(oldName2,newName2,getContext()); + this.document.refactorObjectLinks(oldName3,newName3,getContext()); + this.document.refactorObjectLinks(oldName4,newName4,getContext()); + + String links = ""; + for(XWikiLink link : this.document.getUniqueWikiLinksFromObjects(getContext())){ + links += link.getLink(); + } + + assertEquals(links.indexOf("newwiki:NewSpace.NewTestPage")!=-1, true); + assertEquals(links.indexOf("newwiki:NewSpace.NewTestPage2")!=-1, true); + assertEquals(links.indexOf("newwiki:NewSpace.NewTestPage3")!=-1, true); + assertEquals(theObj.getLargeStringValue("puretextarea"), "[["+DOCSPACE+"ShouldNotParse]]"); + + //set our vars back to origional states + theObj.setStringValue("string","string"); + theObj.setStringListValue("stringlist", Arrays.asList("VALUE1", "VALUE2")); + theObj.setLargeStringValue("area", "area"); + theObj.setLargeStringValue("puretextarea", ""); + this.document.setSyntaxId("xwiki/1.0"); + } + /** * Normally the xobject vector has the Nth object on the Nth position, but in case an object gets misplaced, trying * to remove it should indeed remove that object, and no other. Index: core/xwiki-core/src/main/java/com/xpn/xwiki/doc/XWikiDocument.java =================================================================== --- core/xwiki-core/src/main/java/com/xpn/xwiki/doc/XWikiDocument.java (revision 23274) +++ core/xwiki-core/src/main/java/com/xpn/xwiki/doc/XWikiDocument.java (working copy) @@ -1340,6 +1340,38 @@ } } + /** + * Gets all of the properties of all of the objects attached to this document. + * + * @returns a {@link java.util.Vector} of BaseProperty + * @throws XWikiException if theres an exception getting the property. + */ + public Vector getProperties() throws XWikiException + { + Vector allProperties = new Vector(); + Vector allObjects = getObjects(); + for(BaseObject thisObject : allObjects){ + for(String propertyName : thisObject.getPropertyNames()){ + allProperties.add((BaseProperty) thisObject.get(propertyName)); + } + } + return allProperties; + } + + /** + * Gets all objects connected to this document. + * + * @returns a {@link java.util.Vector} of BaseObjects + */ + public Vector getObjects() + { + Vector allObjects = new Vector(); + for(Vector objectsThisClass : getxWikiObjects().values()){ + allObjects.addAll(objectsThisClass); + } + return allObjects; + } + public Vector getObjects(String classname) { if (classname == null) { @@ -3037,6 +3069,115 @@ } /** + * Get a list of unique links from the objects in this document to others documents. + *

+ *

    + *
  • 1.0 content: returns null (not supported)
  • + *
  • Other content: finds all properties of attached objects which are text + * meaning they are either a String, a TextArea, or a list of Strings + * (DB list or otherwise) takes the content of all text properties and + looks for links. + *
  • . + *
+ * + * @param context the XWiki context + * @return the found wiki links (a Set of XWikiLink) + * @throws exception if there is an error parsing content or (unlikely) an error casting list classes. + */ + public Set getUniqueWikiLinksFromObjects(XWikiContext context) throws XWikiException + { + if (is10Syntax()) {return null;}//should 1.0 be supported? + //first we get the contents of all object properties containing text, collect + //them in a StringBuffer so they can be treated as a glob of content. + StringBuffer objectPropertyValues = new StringBuffer(); + + for(BaseProperty thisProperty : this.getProperties()){ + String classType = thisProperty.getClassType(); + if(classType.indexOf("String")==-1){ + //it's not a String, it can't possibly contain any links + }else{ + if(classType.indexOf("List")!=-1){ + //it's a list of Strings + List theList = (List) thisProperty.getValue(); + for(String stringFromList : theList){ + objectPropertyValues.append(stringFromList).append("\n"); + } + }else{ + //it's a String (or LargeString) and it's parsable content. + String contentType = ""; + try{ + contentType = ((TextAreaClass) + thisProperty + .getObject() + .getxWikiClass(context) + .getField(thisProperty.getName()) + ) + .getContentType(); + }catch(Exception e){} + if(!contentType.equals("puretext") && !contentType.equals("velocitycode")){ + objectPropertyValues.append(thisProperty.getValue()).append("\n"); + } + } + } + } + //then we get all links from that content and return them. + return getUniqueWikiLinks(objectPropertyValues.toString()); + } + + /** + * If syntax is xwiki/1.0 return null (not supported) + * Parse given text in document's native syntax, call {@link #getUniqueWikiLinks(XDOM)} + * return output. + * + * @return a Set of XWikiLink objects. + * @throws XWikiException if there is an error while parsing. + */ + private Set getUniqueWikiLinks(String input) + throws XWikiException + { + if (is10Syntax()) {return null;} + return(getUniqueWikiLinks(this.parseContent(input))); + } + + /** + * Get all links to other documents from supplied document object model. + * + * @param input the document object model to search for links in. + * @return the found wiki links (a Set of XWikiLink) + */ + private Set getUniqueWikiLinks(XDOM input){ + Set links = new LinkedHashSet(); + List linkBlocks = input.getChildrenByType(LinkBlock.class, true); + + for (LinkBlock linkBlock : linkBlocks) { + org.xwiki.rendering.listener.Link link = linkBlock.getLink(); + if (link.getType() == LinkType.DOCUMENT) { + // If the reference is empty, the link is an autolink + if (!StringUtils.isEmpty(link.getReference()) + || (StringUtils.isEmpty(link.getAnchor()) && StringUtils.isEmpty(link.getQueryString()))) { + // The reference may not have the space or even document specified (in case of an empty + // string) + // Thus we need to find the fully qualified document name + DocumentName documentName = + documentNameFactory.createDocumentName(link.getReference()); + + // Verify that the link is not an autolink (i.e. a link to the current document) + if (!documentName.equals(this.getDocumentName())) { + //generate an XWikiLink + XWikiLink xlink = new XWikiLink(); + xlink.setDocId(this.getId()); + xlink.setFullName(this.getFullName()); + xlink.setLink(this.compactDocumentNameSerializer.serialize(documentName)); + links.add(xlink); + if(LOG.isDebugEnabled()) {LOG.debug("link found in text from document: "+xlink.getFullName()+" pointing to "+xlink.getLink());} + } + } + } + } + return links; + } + + /** * Extract all the unique static (i.e. not generated by macros) wiki links (pointing to wiki page) from this 1.0 * document's content to others documents. * @@ -4125,6 +4266,96 @@ } } + /** + * Looks through text properties (Strings, TextAreas, Lists of Strings...) of all objects attached to + * this document, looks for links to oldDocumentName, replaces with links to newDocumentName + * Incompatible with xwiki/1.0 syntax, does nothing. + * + * @param oldDocumentName the name of the document to search for links to. + * @param newDocumentName the name of the document to change the links to. + * @param context the XWiki context + * @throws XWikiException if parsing of the object property content fails. + */ + public void refactorObjectLinks(DocumentName oldDocumentName, DocumentName newDocumentName, XWikiContext context) + throws XWikiException + { + if (is10Syntax()){return;}//should 1.0 be supported? + + for(BaseProperty thisProperty : this.getProperties()){ + String classType = thisProperty.getClassType(); + if(classType.indexOf("String")==-1){ + //it's not a String, it can't possibly contain any links + }else{ + if(classType.indexOf("List")!=-1){ + //it's a list of Strings + List theList = (List) thisProperty.getValue(); + List theNewList = new ArrayList(); + for(String stringFromList : theList){ + theNewList.add(getTextWithRefactoredLinks(stringFromList, oldDocumentName, newDocumentName)); + if(LOG.isDebugEnabled()) { + if(!theNewList.get(theNewList.size()-1).equals(theList.get(theList.size()-1))){ + LOG.debug("A link inside of a list property in an object was refactored"+ + " old link: "+theList.get(theList.size()-1)+ + " new link: "+theNewList.get(theNewList.size()-1)); + } + } + } + thisProperty.setValue(theNewList); + }else{ + //it's a String (or LargeString) and it's parsable content + String contentType = ""; + try{ + contentType = ((TextAreaClass) + thisProperty + .getObject() + .getxWikiClass(context) + .getField(thisProperty.getName()) + ) + .getContentType(); + }catch(Exception e){} + if(!contentType.equals("puretext") && !contentType.equals("velocitycode")){ + thisProperty.setValue(getTextWithRefactoredLinks(thisProperty.getValue().toString(), oldDocumentName, newDocumentName)); + } + } + } + } + } + + /** + * Looks through input String for links to oldDocumentName, replaces with links to newDocumentName. + * {@link #getTextWithRefactoredLinks(XDOM, DocumentName, DocumentName)} + */ + private String getTextWithRefactoredLinks(String input, DocumentName oldDocumentName, DocumentName newDocumentName) + throws XWikiException + { + return renderXDOM(getTextWithRefactoredLinks(parseContent(input), oldDocumentName, newDocumentName), getSyntax()); + } + + /** + * Looks through input document object model for links to oldDocumentName, replaces with links to newDocumentName. + * + * @param oldDocumentName the name of the document to search for links to. + * @param newDocumentName the name of the document to change the links to. + * @throws XWikiException if parsing of the input text fails. + */ + private XDOM getTextWithRefactoredLinks(XDOM input, DocumentName oldDocumentName, DocumentName newDocumentName) + throws XWikiException + { + List linkBlockList = input.getChildrenByType(LinkBlock.class, true); + + for (LinkBlock linkBlock : linkBlockList) { + org.xwiki.rendering.listener.Link link = linkBlock.getLink(); + if (link.getType() == LinkType.DOCUMENT) { + DocumentName documentName = this.documentNameFactory.createDocumentName(link.getReference()); + + if (documentName.equals(oldDocumentName)) { + link.setReference(this.compactDocumentNameSerializer.serialize(newDocumentName)); + } + } + } + return input; + } + public XWikiDocument copyDocument(String newDocumentName, XWikiContext context) throws XWikiException { String oldname = getFullName();