I’m currently looking for a mechanism that would allow to set dynamic rights to newly created pages. More specificially, whenever a page is created whose name follows a pattern (_Homepage):
The Groupname should be extracted from the pagename
An XWikiRights-Object should be created for the page which allows the extracted Group access to the document
As @tmortagne pointed out in the linked post, this can be done via an Event Listener.
I also added the following groovy-code to the onEvent method:
{{groovy}}
import org.xwiki.context.*
import org.xwiki.observation.*
import org.xwiki.observation.event.*
import org.xwiki.bridge.event.*
import org.xwiki.model.reference.*
import org.xwiki.model.*
import com.xpn.xwiki.web.*
import com.xpn.xwiki.*
try {
System.out.println("\n[BEGIN]\nThe document ${xcontext.method.input.get(1)} has been created.")
def crtContext = Utils.getComponent(Execution.class).getContext().getProperty("xwikicontext")
def docSource = xcontext.method.input.get(1)
def oldDoc = docSource.getOriginalDocument()
def existingObj = oldDoc.getObject("XWiki.XWikiRights")
if (existingObj == null) {
System.out.println("XWikiRights object does not exist, creating.")
def rightsObj = oldDoc.getObject("XWiki.XWikiRights", true, crtContext)
// try to read props from obj
def allow = rightsObj.get("allow")
def users = rightsObj.get("users")
def levels = rightsObj.get("levels")
System.out.println("Setting value of 'allow'.")
rightsObj.setIntValue("allow", 1)
System.out.println("Setting value of 'levels'.")
rightsObj.setStringValue("levels", "view,edit")
System.out.println("Setting value of 'users'.")
rightsObj.setLargeStringValue("users", "XWiki.DigitalData")
System.out.print("\nallow:\t")
System.out.println(rightsObj.get("allow").value)
System.out.print("levels:\t")
System.out.println(rightsObj.get("levels").value)
System.out.print("users:\t")
System.out.println(rightsObj.get("users").value)
oldDoc.setObject("XWiki.XWikiRights", 0, rightsObj)
System.out.println("\nXWikiRights object added.\n[END]")
} else {
System.out.println("XWikiRights object exists.")
System.out.println(existingObj)
}
} catch(e) {
System.out.println("ERROR!")
System.out.println(e.getMessage())
}
{{/groovy}}
EDIT
I added the code I’ve written so far.
I can check for an existing XWikiRights object and also create a new one. Setting the property works fine too.
What does not work is actually saving the object back to the page.
Do I have to call the setObject on the document?
Am I using the method correctly?
Is there something else wrong with my code that prevents creating the XWikiRights object?
@Pbas: Thanks for your answer. However, when I call save on oldDoc (or docSource), I get an error saying that the save method does not exist:
No signature of method: com.xpn.xwiki.doc.XWikiDocument.save() is applicable for argument types: () values: []
Possible solutions: wait(), any(), wait(long), sleep(long), any(groovy.lang.Closure), isCase(java.lang.Object)
As you see, that is not an api object, but an internal class
(com.xpn.xwiki.doc.XWikiDocument vs. com.xpn.xwiki.api.Document). This
means that API methods don’t work, unless they have an equivalent in the
other class.
You can either look at the methods of the XWikiDocument class, or get an
API object and work with it (new com.xpn.xwiki.apiDocument(tmpDoc)
When I uncomment the xwiki.saveDocument lin, whenever I save the document (from the wysiwyg editor), I’m stuck in a loop where the document is being saved over and over before giving up eventually.
My main problem seems to be that I want to change the document that created the event. To save my changes I need to call saveDocument which triggers my listener again…
I’ve found and example where saving a document changes another document and appends some text to it. Here the problem is solved by not exiting the listener when the other document (Main.Logs) is saved:
void onEvent(Event event, Object source, Object context)
{
// Prevent infinite recursion since in this example we log to wiki page which
// triggers a document change... :)
if (source.fullName != "Main.Logs") {
def xwiki = context.getWiki()
def document = xwiki.getDocument(logReference, context)
document.setContent("${document.getContent()}\n* ${source.fullName} has been modified!")
xwiki.saveDocument(document, "Logging event", true, context)
}
}
I’m beginning to wonder: Is there even a way to edit the document that was just saved without getting in to that save-loop?
When I create a new XWikiRights Object for document and set properties on it, the Object is added to the document without the need to call saveDocument
if you modify a document you must absolutely save the doc or it won’t be saved. You may think it works because it’s in the doc cache but if you restart your wiki you’ll see it hasn’t been saved.
I’ve just restarted the wiki and the XWikiRights Object is still there.
I’m not actually trying to change the content of the document but add an XObject(?) of type XWikiRights to it.
I do it in the onEvent method of the Listener that listens to the DocumentCreatedEvent.
ah ok. Now, DocumentCreatedEvent means that the doc has been created and saved already… DocumentCreatingEvent is the one before the save… So normally it shouldn’t be saved in your case.
If you use DocumentCreatingEvent then the modifications are saved automatically for you in the same revision. If you use DocumentCreatedEvent then your listener is called after the save and you need to call save() again (also adding a new revision). If you don’t call save() then restarting your wiki should loose the change (it’ll be just in the memory doc cache).
I did not followed that thread in details but when you want to modify a saved document you should do it before it’s saved by listening to DocumentUpdatingEvent and by modifying the XWikiDocument instance you get as parameter. If you do it after you create new versions which is not very nice, impact the performances (save is expensive) and indeed can cause an infinite loop in your listener if you are not careful before the each save will call your listener over and over again.
Unfortunately the problem runs a bit deeper. What I’m trying to achieve is a mechanism for some users to easily and flexible set edit rights on profile pages.
We have the need for restricted access to upload an download documents on user profiles.
Therefore I added some properties on the User Class to easily select a list of users which then shall be taken into account as a variable when setting the RightsObject.
However, I already tried my luck with the DocumentUpdatingEvent and the RightsObject doesn’t get set properly without the saveDocument which then leads to a loop again
It should definitely work just fine and it’s used a lot AFAIK. The XWikiDocument instance sent as second argument (you are modifying that, right ? Not the original document ? since that’s what do do in the last code you pasted and it does not make much sense) with the DocumentUpdatingEvent is definitely the one sent to the store so any modification to it will be applied (unless a following DocumentUpdatingEvent listener remove them from the document but I guess you don’t have that).
Already tried it that way, too. But for some reason the created RightsObject is empty.
Then sometimes after reloading the profile it shows as expected. But then again loading of the profile triggers another DocumentUpdatingEvent probably from the LDAP sync
You are supposed to modify docSource itself which is a XWikiDocument instance already, that’s the XWikiDocument instance which is actually sent to the store. XWiki#getDocument depending on the use case may return you a XWikiDocument which is different from the one actually saved (in short some code directly modify the XWikiDocument from the cache before saving it and other clone it first to be safe).