mattdickens

Widget-based UI features/enhancements without customisations

Discussion created by mattdickens on Dec 19, 2014
Latest reply on May 26, 2015 by mattdickens

My organisation, like many other Jive customers would like to provide our users with the ability to add custom user interface features/enhancements to their places (e.g. tabbed tables, accordions, table column filters, carousels, image navigation) without sacrificing security and without always having to develop plugins that may impede our upgrade path or introduce recurring re-development costs.

As you know, HTML widgets in groups and projects do not allow non-sys admin users to save JavaScript (OOTB) and where sys admins have taken time to add it, it is usually embedded within the page's content and cannot be used on another group without again requiring the time of a sys admin to edit it. These HTML widgets are also available for editing by group administrators and therefore run the risk of the JavaScript being stripped out by Jive if they try to re-save it as a non-sys admin (again requiring the time of a sys admin to rectify it).

The other issue we are faced with is how best to store this additional JavaScript/CSS for these enhancements without customising the theme or creating an addon/plugin. This is because our Jive instance doesn't currently have the statics/Manage files functionality enabled due to the security issues associated with it.

 

I therefore had the following high-level requirements for a set of UI features/enhancements for overview pages:

 

  • Centrally approved, held and maintained code snippets. Stored once, referenced by many overview pages. Upgrades to the widgets need only be made once to be reflected in all groups using them.
  • Separate the JavaScript from the page content such that the snippets are completely generic and can be used on multiple pages.
  • Cannot be edited by non-admins to avoid unintentional stripping of the JavaScript from the widget.
  • Can be added by non-admins to their overview pages.

 

One possible approach I am proposing and would like your opinion on is to store the JS/CSS assets as attachments on jive documents that are only editable by system admins or other qualified users with the ability to assess and identify any security issues with any submitted snippets before approving them.

Template group for each of the snippets would then be created that group admins could copy the widgets from onto their own overview pages and customise the content to suit their needs.

 

The HTML widget on these groups contains JavaScript that loads the specific snippet code as below:

 

  1. Ensure the parent window's jQuery is loaded before attempting to use it.
  2. Make an api call to the snippet's host jive document to obtain the URLs to the latest version of its attached assets.
  3. Load the assets into the parent window to ensure they are not limited to the iframe in which this code is being executed.
  4. Prevent the HTML widget housing this code from being edited except by a system admin.
  5. Hide the HTML widget in read mode so as not to take up any unnecessary screen estate.


The pic below shows how the code is being loaded by JavaScript in an HTML widget an iframe but injected into the parent page to run on its sibling widget content. It goes without saying that care must be taken in naming our functions and css classes to ensure they do not interfere with the core Jive product.

widgets flow.PNG


The screenshot below shows how the widget is displayed on the page in edit mode. Notice the missing 'Edit this widget' option when a non-system admin edits the overview page.

 

adminonly.png

<script>

    var $;
    injectWhenJQueryIsLoaded("1001"); //this is the DOC ID of the host document with the attached code.
    determineWidgetEditability();

    function injectWhenJQueryIsLoaded(hostDoc) {
        if (parent.$j) {
            $ = parent.$j;
            $.ajaxSetup({cache: true});
            var target = "/api/core/v3/contents?filter=entityDescriptor(102," + hostDoc + ")";
            //Perform an api call to obtain the urls to the latest versions of the host doc's attachments
            var data = JSON.parse($.ajax({type: "GET", dataType: "text", url: target, async: false}).responseText.replace(/^throw [^;]*;/, ''));
            if (data.list[0].attachments){
                    $.each(data.list[0].attachments, function(index, attach) {
                        var attName = attach.name;
                        if (attName.substring(attName.length - 4) == ".css") {
                           $('<link/>', {rel: 'stylesheet', type: 'text/css', href: attach.url}).appendTo('head');
                        } else if (attName.substring(attName.length - 3) == ".js") {
                            $.getScript(attach.url);  //This will load the script into the parent window rather than this iFrame.
                        }
                    });
            }
        } else {
            setTimeout(injectWhenJQueryIsLoaded(hostDoc), 50);
        }
    }

    function determineWidgetEditability() {
        framenum = window.frameElement.id.substring(12);
        if (parent.document.getElementById('jive-widgets-panel')) { // If this panel is available in the dom, this overview page is being edited  
            $('#jive-widgetframe_' + framenum).show(); //Unhide this HTML widget  
            if (parent.containerType != "14") { //Not a Space! This is a Group or Project overview page. Administrators of Spaces can save JavaScript.  
                $('#jive-widgetframe-title_' + framenum).text("THIS HTML WIDGET CONTAINS JAVASCRIPT. IT CAN ONLY BE EDITED BY SYSTEM ADMINSTRATORS.");
                var restEndPoint = "/api/core/v3/admin/properties?count=1"; //This api call is a means to identify if the user is a sys admin.
                var data = JSON.parse($.ajax({type: "GET", url: restEndPoint, async: false}).responseText.replace(/^throw [^;]*;/, ''));
                // if system properties can be accessed, this must be an admin so we can allow the widget to be edited, else we'll hide the edit link.  
                if (data.list){$('#jive-widgetframe-options_' + framenum + ' .jive-widget-edit').show()}else{$('#jive-widgetframe-options_' + framenum + ' .jive-widget-edit').hide()}
            }
        } else {
            $('#jive-widgetframe_' + framenum).hide() //Hide this whole html widget frame when overview page is in read mode ...    
        }
    }
</script>

 

The disadvantages I have identified with this approach are:

  • The entitlement checks required to read the host document and each of the assets.
  • Limited caching of the asset files.
  • Flicker as the DOM is transformed from the original tab (although this could be reduced with some targeted CSS changes to hide the widgets on load and display them once the manipulation has completed).

 

Re: the entitlement checks - although I accept this is a small overhead, I don't see it as much different from a user adding an image to a document/formatted text widget or adding a 'View document' widget to an overview page. In fact, with the image and view document widget, both require an entitlement check as well as a 'view' to be recorded for them each time they are read.

I appreciate however that I know very little about the back-end of Jive and so would very much like the opinions of those of you with deeper knowledge. I'd really appreciate your thoughts on this approach and whether you see any major reason we should not use it as a short term measure until such time that we enable the statics/Manage files functionality or implement a plugin that is able inject the snippets directly onto the page upon load.

 

In anticipation of us enabling the statics/Manage files functionality in our community and for those of you who have it, I have also created an alternative snippet that will load the assets from an open space's statics folder (note that it has to be a space due to the limitation on file types that can be uploaded to the statics folder of a group). This one is a little faster too as it uses plain JavaScript for the initial load instead of having to wait for jQuery to be loaded. The files are also cached which is good if you don't intend to ever upgrade them but a pain if you do as you'll need to rename all the resources and update the references to them in all groups that are using the widget.

The asset files could of course be stored outside of Jive if you have an available web server but I prefer to keep these type of file dependencies to the same environment in which they are being displayed.

 

<script>
    injectAsset('/resources/statics/1010/mytabbedtable.js', 'js');
    injectAsset('/resources/statics/1010/tabbedtable.css', 'css');
    determineWidgetEditability();

    function injectAsset(assetpath, assettype) {
        if (assettype == 'js') {
            var asset = parent.document.createElement('script'); asset.setAttribute('src', assetpath); asset.setAttribute('type', 'text/javascript');
        } else if (assettype == 'css') {
            var asset = parent.document.createElement('link'); asset.setAttribute('href', assetpath); asset.setAttribute('type', 'text/css'); asset.setAttribute('rel', 'stylesheet');
        }
        if (typeof asset != 'undefined') parent.document.getElementsByTagName('head')[0].appendChild(asset)
    }

    function determineWidgetEditability() {
        framenum = window.frameElement.id.substring(12);
        if (parent.document.getElementById('jive-widgets-panel')) { // If this panel is available in the dom, this overview page is being edited 
            parent.document.getElementById('jive-widgetframe_' + framenum).style.display = "block";
            if (parent.containerType != "14") { //Not a Space! This is a Group or Project overview page. Administrators of Spaces can save JavaScript. 
                parent.document.getElementById('jive-widgetframe-title_' + framenum).innerHTML = "THIS HTML WIDGET CONTAINS JAVASCRIPT. IT CAN ONLY BE EDITED BY SYSTEM ADMINSTRATORS.";
                allowEditIfSysAdmin();
            }
        } else {
            parent.document.getElementById('jive-widgetframe_' + framenum).style.display = "none"; //Hide this whole html widget frame when overview page is in read mode ...  
        }
    }

    function allowEditIfSysAdmin() {
        if (parent.$j) {
            var $ = parent.$j;
            var restEndPoint = "/api/core/v3/admin/properties?count=1"; //This api call is a means to identify if the user is a sys admin.
            $.ajaxSetup({cache: true});
            var data = JSON.parse($.ajax({type: "GET", url: restEndPoint, async: false}).responseText.replace(/^throw [^;]*;/, ''));
            // if system properties can be accessed, this must be an admin so we can allow the widget to be edited, else we'll hide the edit link.
            if (data.list) {
                $('#jive-widgetframe-options_' + framenum + ' .jive-widget-edit').show()
            } else {
                $('#jive-widgetframe-options_' + framenum + ' .jive-widget-edit').hide()
            }
        } else {
            setTimeout(allowEditIfSysAdmin(), 50);
        }
    }
</script>

 

I have a couple of snippets in beta so far which appear to run fine under 7.0.1 and 8.0.0.0 8c4 and I'm happy to share if anyone is interested.

 

  • Add filters and/or a filter box to a Jive table
  • Transform a Jive table of content into a tabbed table of content

 

The first is an updated version of the code I shared here-> HTML Widget: How to add column filters and search capability to any jive table on an overview page. The functionality is pretty much the same but the code has been enhanced slightly and made compatible with this loading method.


table filter ani.gif


The tabbed table snippet allows a simple 2 column table to be transformed into a tabbed table as shown below.

Column 1 for the label and Column 2 for the tab's content.

Note the addition of the text '{tabbedtable}' in the table header that the code looks for to identify which tables to work on.


tabbedtable1.PNG

 

and the result once the overview page is viewed:

tabbedtabe2.gif

 

Ryan Rutan, Nils Drews, nilsheuer, mcollinge + anyone who has experience in this area - I'd very much appreciate your views and thoughts on this approach.

The specified item was not found. - FYI

Outcomes