Creating a link that updates a user preference

Hey, all,

I am creating a “control panel” for my wiki staff, which is just a menu of links in the left panel that is only visible to users in certain groups. It has been really useful for me so far; I even managed to get it to include links to object and class editors for the current context.

What I really want to do is add links that, respectively, toggle the current user’s “display hidden documents” preference and the current user’s “advanced” status, refreshing the current page after the toggle. (Yes, I do use the “xxxh” keyboard sequence, but I doubt most of my wiki users will be aware of that, so I’d like to have a shortcut in plain sight.) Modifying the values of these properties using Velocity looks trivial, thanks to Object.set(String,Object), but I can’t figure out how to run such a script on demand.

I ended up trying to use the RESTful API via AJAX, but that has gone very poorly so far. All of the examples in the API documentation are for creating pages, not modifying objects, let alone their properties. I am getting HTTP 400 responses no matter what I do, which is probably an issue with the XML I am trying to PUT and/or my ugly mixing of Velocity and JS. But again, there are no examples, so I am flying blind here.

My Velocity+AJAX Test Script
{{velocity}}
#set( $userObj = $xwiki.getDocument($xcontext.user).getObject('XWiki.XWikiUsers') )
#set( $user = $xcontext.getUser())

#set( $hidden = $userObj.get('displayHiddenDocuments') )

#if( !$hidden )
  #set ( $setting = '1' )
#else
  #set( $setting = '0' )
#end

{{html clean="false"}}
<script>
function toggleHidden() {
   var xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" +
    "<property name=\"displayHiddenDocuments\" type=\"Boolean\">" +
    "<link href=\"http://[servletURL]/xwiki/rest/wikis/xwiki/spaces/XWiki/pages/$user/objects/XWiki.XWikiUsers/0/properties/displayHiddenDocuments\" rel=\"self\"/>" +
    "<attribute name=\"name\" value=\"displayHiddenDocuments\"/>" +
    "<attribute name=\"prettyName\" value=\"Display Hidden Documents\"/>" +
    "<attribute name=\"unmodifiable\" value=\"0\"/>" +
    "<attribute name=\"disabled\" value=\"0\"/>" +
    "<attribute name=\"displayFormType\" value=\"select\"/>" +
    "<attribute name=\"customDisplay\" value=\"\"/>" +
    "<attribute name=\"defaultValue\" value=\"\"/>" +
    "<attribute name=\"displayType\" value=\"yesno\"/>" +
    "<attribute name=\"hint\" value=\"\"/>" +
    "<attribute name=\"number\" value=\"16\"/>" +
    "<attribute name=\"validationMessage\" value=\"\"/>" +
    "<attribute name=\"validationRegExp\" value=\"\"/>" +
    "<value>$setting</value>" +
    "</property>";
  var ajax = new XMLHttpRequest();
  console.log("ajax created.");
  ajax.onreadystatechange = function() {
    if (this.readyState == 4) {
      //document.getElementById('xmlresponse').innerHTML = this.responseText;
      alert(this.status);
    }
  };
  console.log("ajax state ready?");
  ajax.open('PUT','http://[servletURL]/xwiki/rest/wikis/xwiki/spaces/XWiki/pages/$user/objects/XWiki.XWikiUsers/0');
  console.log("ajax open.");
  ajax.setRequestHeader("Content-Type", "application/xml");
  ajax.send(xml);
  console.log("ajax sent!");
}
</script>
<a href="javascript:void(0);" onClick="toggleHidden();">Toggle Hidden Page Display</a>
//<p id="xmlresponse"></p>
{{/html}}
{{/velocity}}

(Apologies for the cringeworthiness! I’ve only used AJAX once or twice before.)

How much am I overthinking this?

I probably should have thought of doing so sooner, but I looked at the source code for the keyboard shortcuts for live-toggling the current user’s usertype and displayHiddenDocuments properties, and that gave me exactly the code I needed.

In case you are interested, here’s a snippet of what I came up with (which I may post on the Snippets wiki, too):

Code
{{velocity}}
{{html}}
<!-- Adapted from https://github.com/xwiki/xwiki-platform/blob/73b07f8352d1716b9945b577d6e4a3334f058a77/xwiki-platform-core/xwiki-platform-flamingo/xwiki-platform-flamingo-skin/xwiki-platform-flamingo-skin-resources/src/main/resources/flamingo/shortcuts.vm on 7 March 2022 -->
<script>
   //<![CDATA[
   /**
    * Perform a PUT on the given REST API. If the request is successful, reload the page.
    *
    * We use this function in order to quickly edit the user properties for developer shortcuts.
    * Also note that JQuery is not supported here, so we use a starndard XMLHttpRequest.
    *
    * @param restUrl the URL to use
    * @param errorMessage the message to display if an error ocurred in the request
    */
    var developerShortcutsRestCall = function(restUrl, errorMessage) {
        const req = new XMLHttpRequest();
        var notification = new XWiki.widgets.Notification(
            "$escapetool.javascript($services.localization.render('core.shortcuts.developer.user.ajax.inprogress'))",
            'inprogress');

        req.onreadystatechange = function(event) {
            if (this.readyState === XMLHttpRequest.DONE) {
                if (this.status >= 200 && this.status < 300) {
                    // Reload the page to apply the user modifications
                    notification.replace(new XWiki.widgets.Notification(
                        "$escapetool.javascript($services.localization.render(
                                'core.shortcuts.developer.user.ajax.success'))", 'done'));
                    location.reload()
            } else if (this.status == 500) {
              notification.replace(new XWiki.widgets.Notification(this.data, 'error'));
            } else {
              notification.replace(new XWiki.widgets.Notification(errorMessage, 'error'));
            }
          }
        };

        req.open('PUT', restUrl, true);
        req.send(null);
      };

      var toggleUserType = function() {
        developerShortcutsRestCall("${request.contextPath}/rest/currentuser/properties/usertype/next",
              "$escapetool.javascript($services.localization.render('core.shortcuts.developer.user.type.error'))");
      }

      var toggleDisplayHiddenDocs = function() {
        developerShortcutsRestCall("${request.contextPath}/rest/currentuser/properties/displayHiddenDocuments/next",
               "$escapetool.javascript($services.localization.render('core.shortcuts.developer.user.displayHiddenDocs.error'))");
      };
  //]]>
</script>
{{/html}}

[...]

#enDis()
#set( $toggleAdvUser = '<a style="cursor: pointer;" onClick="toggleUserType();">' + $enableAdv + 'Adv. Mode</a>' )
#set( $toggleHiddens = '<a style="cursor: pointer;" onClick="toggleDisplayHiddenDocs();">' + $enableHiddens + 'View All</a>' )
* {{html}}$toggleHiddens{{/html}}
* {{html}}$toggleAdvUser{{/html}}

[...]

#macro( enDis )
  #if( $isAdvancedUser )
    #set( $enableAdv = 'Disable ' )
  #else
    #set( $enableAdv = 'Enable ' )
  #end
  #if( $crtUserDoc.getObject('XWiki.XWikiUsers').getValue('displayHiddenDocuments') == 1 )
    #set( $enableHiddens = 'Disable ' )
  #else
    #set( $enableHiddens = 'Enable ' )
  #end
#end
{{/velocity}}
1 Like