Wednesday, December 5, 2012

Generating Java sources from multiple DTD schemas

found out i could not generate Java source from multiple DTD's using xjc (JAXB), so I had to write this script. This script solves two thing: xjc cannot compile multiple DTDs and xjc will generate the same file ObjectFactory.java for each call (overwriting file from previous call).

#!/bin/sh

while IFS= read -r -u3 -d $'\0' file; do
 filename="${file##*/}"                      # Strip longest match of */ from start
    dir="${file:0:${#file} - ${#filename}}" # Substring from 0 thru pos of filename
    base="${filename%.[^.]*}"                       # Strip shortest match of . plus at least one non-dot char from end
    ext="${filename:${#base} + 1}"

newDir="$dir$base"
echo "Package dir to create $newDir"
DIRECTORY="generatedsrc/$newDir"
if [ ! -d "$DIRECTORY" ]; then
    # Control will enter here if $DIRECTORY doesn't exist.
mkdir -p $DIRECTORY
fi
    PACKAGE=${newDir//.\//};
    PACKAGE=${PACKAGE//\//.};
    cmd="$JAVA_HOME/bin/xjc -dtd -d generatedsrc -p "$PACKAGE" $file"
    echo "Running: $cmd"
    $cmd
done 3< <(find . -iname *.dtd -type f -print0)

Friday, November 30, 2012

... and letting Alfresco Share know about custom permissions

Again this is Spring magic, i didn't know how to get Spring to update an Array, so I replaced it, but then you must check Alfresco context-xml for each version to check if it changes - dependencies :(


 <!-- adds to script-services-context.xml -->
 <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean" depends-on="jsonConversionComponent">
        <property name="targetObject" >
            <ref bean="jsonConversionComponent"/>
        </property>
        <property name="targetMethod" value="setUserPermissions" />
        <property name="arguments">
            <list>
                <array value-type="java.lang.String">
                    <!-- Standard ones -->
                    <value>CancelCheckOut</value>
                    <value>ChangePermissions</value>
                    <value>CreateChildren</value>
                    <value>Delete</value>
                    <value>Write</value>
                    <!-- customised ones -->
                    <value>Finalise</value>
                </array>
            </list>
        </property>
    </bean>


This will let Alfresco Share see the CRUD-permission 'Finalise' defined as 'Permission' in the Alfresco Repository Permission Model (extension). Then you can use this in your share-config-custom.xml where your actions now can be dependant on this permission

            <action id="...
 
                <permissions>
                    <permission allow="true">Finalise</permission>
                </permissions>

              ...
            </action>


About making custom share actions see here http://blogs.alfresco.com/wp/mikeh/2011/09/26/share-document-library-extensions-in-v4-0/ among others

Thursday, November 29, 2012

Adding custom permissions in Alfresco

If your extending the PermssionModel of Alfresco, you might get an Exception when using the new permission groups .. like:

...
Caused by: java.lang.NullPointerException
    at org.alfresco.repo.site.RoleComparatorImpl.compare(RoleComparatorImpl.java:39)
    at org.alfresco.repo.site.RoleComparatorImpl.compare(RoleComparatorImpl.java:24)
    at java.util.TreeMap.put(TreeMap.java:530)
    at java.util.TreeSet.add(TreeSet.java:238)
    at org.alfresco.repo.site.SiteServiceImpl.getMembersRole(SiteServiceImpl.java:1717)

...

This is caused by this RoleComparator not knowing the groups. This can be resolved by some spring magic:

 <!-- adds to script-services-context.xml -->
    <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean" depends-on="siteRoleComparator">
        <property name="targetObject">
            <util:property-path path="siteRoleComparator.RolePrecedence" />
        </property>
        <property name="targetMethod" value="putAll" />
        <property name="arguments">
            <list>
                <map key-type="java.lang.String" value-type="java.lang.Integer">
                   <entry key="User" value="10" />
                </map>
            </list>
        </property>
    </bean>


This is an addition to the guides http://keytocontent.blogspot.dk/2011/04/alfresco-share-permissionsroles-part-ii.html and https://wiki.alfresco.com/wiki/Custom_Permissions_in_Share

Monday, October 15, 2012

.. and posting to action afterwards

in continuation of previous post, you can use the form engine to retrieve your properties for current node and post to your action for special handling by using the share action settings:

<param name="mode">create</param>
<param name="submissionUrl">/api/action/ACTION-ID/formprocessor</param>

Where ACTION-ID is the bean action is in the repo, and mode must be create for html id 'alf_destination' which is needed by ActionFormProcessor :)

Best of both worlds, properties retrieved by form framework and action handling ...

Make Doclib action with a subset of properties for edit

Tested using the 'OnActionFormDialog' for editing properties ...

<action id=".." icon="..." type="javascript" label="...">
                <param name="formId">FORM-ID</param>
                <param name="function">onActionFormDialog</param>
                <param name="itemKind">node</param>
                <param name="itemId">{node.nodeRef}</param>
                <param name="mode">edit</param>
                <param name="destination">{node.nodeRef}</param>
             ...



Now you can specify a new form with id FORM-ID in your node-type form definition! Now this will not call an action anymore thou ... but will persist your property changes.

 

Thursday, October 11, 2012

Ordering forms aspecs and types

The Alfresco Share form can sometimes be tricky, because aspects are configured in one form-config section and the type in another (if you do not duplicate everything for every type). But luckily the order in the form config section matters, and the order in which form-config files are loaded matters. First comes first! So to make an aspect form presentation appear first move in above the type definition and visa versa :)

Wednesday, August 22, 2012

Remember to define title and description for actions in repo/alfresco

If you still use the alfresco web app (JSP repo access), then remember to give your repo actions a property for title and one for description in a bootstraped properties-file/messages.

<action-bean-id>.title=lala
<action-bean-id>.description=lololo

If you don't do this, you will see non-informational exception.

Another version of this issue is provided here: http://stackoverflow.com/questions/21886116/custom-type-not-showing-up-in-rule-config

regarding missing specialise type in content rule config.

Tuesday, August 14, 2012

Note on share node type evaluator

I noted that using the NodeTypeEvaluator for alfresco share 4.0, the subtypes configuration does not work if your bean configuration does not add DictionaryQuery bean ... no logging :( If you do not add it, the allowSubTypes=true does not work.

Snipplet defining a NodeTypeEvaluator (point in bold)

<bean id="myEvaluator" class="org.alfresco.web.evaluator.c">
  <property name="allowSubtypes" value="true" />
  <property name="dictionary" ref="slingshot.scriptprocessor.dictionaryQuery" />
      <property name="types">
         <list>
            <value>x:yz</value>
                ....
         </list>
      </property>
   </bean>

Tuesday, July 31, 2012

Use alfresco forms framework for categories

I wanted to use the forms framework in alfresco for creating new categories ... works fine until you save the changes and the TypeFormProcessor handles the association to the parent (defaults to cm:contains like for folders).

So you need a custom FormProcessor, like:

public class CategoryFormProcessor extends TypeFormProcessor { ...

You need to override the creation of a node:

protected NodeRef createNode(TypeDefinition typeDef, FormData data) {  ...

Take the code from TypeFormProcessor and change the Association saved to ContentModel.ASSOC_SUBCATEGORIES approx. line 107.

Then you must configure you bean in the Repo context (some loaded
*-context.xml file), see alfrescos in form-services-context.xml

<bean id="categoryTypeFormProcessor"     class="com.redpill_linpro.alfresco.noark5.repo.forms.processor.node.CategoryFormProcessor"
      parent="typeFormProcessor">
        <property name="filterRegistry" ref="typeFilterRegistry" />
      <property name="matchPattern">
         <value>category-type</value>
      </property>
   </bean>


NB! the code (class file for the new FormProcessor) must be in the WEB-INF (not share), because of class-loading dependencies.

No you can load a form, example javascript snipplet here with a configured destination.

var templateUrl = YAHOO.lang.substitute(Alfresco.constants.URL_SERVICECONTEXT + "components/form?itemKind={itemKind}&itemId={itemId}&destination={destination}&mode={mode}&submitType={submitType}&formId={formId}&showCancelButton=true",
             {
                itemKind: "category-type",
                itemId: "cm:category",
                destination: destination,
                mode: "create",
                submitType: "json",
                formId: "doclib-common"
             });

             // Using Forms Service, so always create new instance
             var createFolder = new Alfresco.module.SimpleDialog(this.id + "-createCategory");

             createFolder.setOptions(
             {
                width: "33em",
                templateUrl: templateUrl,

...

The itemKind variable trickers the formprocessor! type->TypeFormProcessor, category-type -> CategoryTypeFormProcessor, which I configured in the bean ...

This works for form type 'create' and sub-categories ... I will have to investigate more ... but it is nice to be able to reuse the form-framework/knowledge for categories instead of rolling your own ... this also supports custom categories, children / inherited types of cm:category.

Friday, July 6, 2012

Moving alf_data


Transfering large repositories to new servers (upgrade, backup) - can be time consuming and this can mean downtime for users. Further more often networks can be loaded.So lets do all in one go ....

In remote terminal at source host:

cd /opt/alfresco; tar jcf - alf_data/ | ssh -o 'Compression no' <user>@<host> 'tar jxf -'

Or in terminal at destination host (reverse, pipe back):

ssh -o 'Compression no' <user>@<host> 'cd /opt/alfresco; tar jcf - alf_data/' | tar jxf -

I turn of ssh compresion and above provide reverse method, if you only have acces oneway ....

Thursday, July 5, 2012

Form control Period.ftl

If your into doing the new share 4.0 forms for simple form doc.lib. actions (onActionFormDialog), you might what to use the Period.ftl control setting it up manually.

To do this it needs some specification of what goes into the drop-down etc.

This example adds all possibilities as an control-param containing a json array:

<control template="/org/alfresco/components/form/controls/period.ftl" >
     <control-param name="dataTypeParameters">                   [{"hasExpression":true,"expressionType":"int","expressionMandatory":false,"defaultExpression":"1","label":"End Of Quarter","type":"quarterend"},{"hasExpression":true,"expressionType":"int","expressionMandatory":false,"defaultExpression":"1","label":"Year","type":"year"},{"hasExpression":true,"expressionType":"int","expressionMandatory":false,"defaultExpression":"1","label":"Quarter","type":"quarter"},{"hasExpression":true,"expressionType":"int","expressionMandatory":false,"defaultExpression":"1","label":"Day","type":"day"},{"hasExpression":true,"expressionType":"int","expressionMandatory":false,"defaultExpression":"1","label":"End Of Month","type":"monthend"},{"hasExpression":false,"label":"None","type":"none"},{"hasExpression":false,"label":"Immediately","type":"immediately"},{"hasExpression":true,"expressionType":"int","expressionMandatory":false,"defaultExpression":"1","label":"End Of Financial Month","type":"fmend"},{"hasExpression":true,"expressionType":"int","expressionMandatory":false,"defaultExpression":"1","label":"End Of Year","type":"yearend"},{"hasExpression":true,"expressionType":"int","expressionMandatory":false,"defaultExpression":"1","label":"End Of Financial Year","type":"fyend"},{"hasExpression":true,"expressionType":"int","expressionMandatory":false,"defaultExpression":"1","label":"Week","type":"week"},{"hasExpression":true,"expressionType":"int","expressionMandatory":false,"defaultExpression":"1","label":"End Of Financial Quarter","type":"fqend"},{"hasExpression":true,"expressionType":"int","expressionMandatory":false,"defaultExpression":"1","label":"Month","type":"month"}]
     </control-param> 

</control>

:)

Thursday, June 21, 2012

Type subcomponent evaluator ...

A new Alfresco 4.0.x feature is the customisation of Share by component replacement ... see http://blogs.alfresco.com/wp/ddraper/2011/07/29/sub-component-evaluations/

If you want to show/hide/change components based on types? Look no further. Here is an example that lets choose a type or all others not this type (inverted/negated).

The extension configuration holdes the reference to the Java-bean id and the parameters you define:

<evaluator type="share.evaluation.type" >
   <params>
      <nodetype>cm:folder</nodetype>
      <negate/>
   </params>
</evaluator>

Bean Id should be some thing like (replace <java-package>):

<bean id="share.evaluation.type"  class="<java-package>.TypeSubComponentEvaluator"/> 

 The Share java code needs to resolve the parameters, find the current nodeRef and perform a CMIS call to the repository to retrieve the type info.

public class TypeSubComponentEvaluator extends DefaultSubComponentEvaluator {

    @Override
    public boolean evaluate(RequestContext context, Map<String, String> arg1) {
        String requestedType = arg1.get("nodetype");
        boolean resultSuccess = arg1.get("negate") != null ? false : true;

        if (null == requestedType)
            return !resultSuccess;

        Map<String, String> uriTokens = context.getUriTokens();
        String nodeRef = uriTokens.get("nodeRef");
        if (nodeRef == null) {
            nodeRef = context.getParameter("nodeRef");
        }

        try {

            final Connector conn = context
                    .getServiceRegistry()
                    .getConnectorService()
                    .getConnector("alfresco", context.getUserId(),
                            ServletUtil.getSession());

            final Response response = conn.call("/api/node/"
                    + nodeRef.replace(":/", ""));
            if (response.getStatus().getCode() == Status.STATUS_OK) {

                String type = parseReponse(response);

                if (requestedType.equals(type))
                    return resultSuccess;
            } else {
                return !resultSuccess;
            }
        } catch (ConnectorServiceException cse) {
            cse.printStackTrace();
            return !resultSuccess;
        }

        return !resultSuccess;
    }

    private String parseReponse(Response response) {
        try {
            Document dom = DocumentBuilderFactory.newInstance()
                    .newDocumentBuilder().parse(response.getResponseStream());
            NodeList list = dom.getElementsByTagName("cmis:propertyId");
            int len = list.getLength();

            for (int i = 0; i < len; i++) {
                Element element = (Element) list.item(i);
                String propertyName = element
                        .getAttribute("propertyDefinitionId");
                String objectTypeId = null;
                if (propertyName.equals("cmis:objectTypeId")) {
                    objectTypeId = element.getElementsByTagName("cmis:value")
                            .item(0).getTextContent();
                    objectTypeId = objectTypeId.replaceAll("F:", "");
                }
                if (objectTypeId == null) {
                    continue;
                }
                return objectTypeId;
            }
        } catch (Exception exc) {
            exc.printStackTrace();
        }
        return null;
    }
}


I use this functionality to hide all comments for types not of a certain type :)

Tuesday, June 19, 2012

Custom admin console groups and sorting

In continuation of previous post, anybody trying this, will notice, that the sorting is out-of-control. For a quick Alfresco Share customisation, you could be tempted to hard-code some sorting into the freemaker templates. Well we could of cause also take the long road (some times better) and make a more general solution. So here goes. I propose a mechanism controlled by a group index configured in the properties along side the group label etc. The index can be configured for each locale/language thus alphabetical sorting is possible. The index sorting shall allow for multiple groups on same index and holes in the index sequence, and still handle the default (no group) tools.

This is implemented by customising the page-template controller ('console.js' in 'templates/org/alfresco/'), applying a property key for each group. The key is formatted like tools.group.<group>.index

Change group key generating loop in console.js:
            var group = "",
                groupLabelId = null,
                groupIndexId = null,
                paths = tool.scriptPath.split('/');
            if (paths.length > 1 && paths[paths.length - 2] == "console")
            {
               // found webscript package grouping
               group = paths[paths.length - 1];
               groupLabelId = "tool.group." + group;
               groupIndexId = "tool.group." + group + ".index";
            }
           
            var info =
            {
               id: scriptName,
               url: toolUrl,
               label: labelId,
               group: group,
               groupLabel: groupLabelId,
               groupIndex: groupIndexId,
               description: descId,
               selected: (currentToolId == scriptName)
            };


Then the component controller ('consol-tools.js' in 'site-webscripts/org/alfresco/components/console') will read the index (if present) and attempt sorting. The sorting rules are simple, precedence to groups with configured index, rest are places if index is 'free'. Group zero(0) is still for default group (no group name).

The component controller (console-tools.get.js) has large parts added:

/**
 * Admin Console Tools list component
 */

function main()
{
   // get the tool info from the request context - as supplied by the console template script
   var toolInfo = context.properties["console-tools"];
  
   // resolve the message labels
   for (var g = 0, group; g < toolInfo.length; g++)
   {
      group = toolInfo[g];
      for (var i = 0, info; i < group.length; i++)
      {
         info = group[i];
         info.label = msg.get(info.label);
         info.description = msg.get(info.description);
         if (info.group != "")
         {
            info.groupLabel = msg.get(info.groupLabel);
            info.groupIndex = msg.get(info.groupIndex);
         } else {
            info.groupIndex = 0;
         }
        
      }
   }

   var index = 0;
   var addedAtIndex = false;
   var indexArray = new Array();
   // Sort
   while (index < toolInfo.length) {
       for (var g = 0, group; g < toolInfo.length; g++)
       {
          group = toolInfo[g];
          for (var i = 0, info; i < group.length; i++)
          {
             info = group[i];
            
             if (info.groupIndex == index)
             {
                indexArray[indexArray.length] = index;
                 addedAtIndex=true;
                 break;
             }
          }
          if (addedAtIndex==true) break;
       }
      
       if (!addedAtIndex) {
           //find one with
           for (var g = 0, group; g < toolInfo.length; g++)
           {
              group = toolInfo[g];
              for (var i = 0, info; i < group.length; i++)
              {
                 info = group[i];
                
                 if (isNaN(info.groupIndex))
                 {
                    if (indexArray.length == index) indexArray[indexArray.length] = index;
                    info.groupIndex=index;
                     addedAtIndex=true;
                    
                 }
              }
              if (addedAtIndex==true) break;
           }
       }
      
       addedAtIndex = false;
       index++;
   }
  
   model.tools = toolInfo;
   model.indeces = indexArray;
}

main();


The sorting might still result in holes in the indexes range, so the freemarker template could be messy trying to figure out the ordering (group are not sorted in the transferred data (model.tools)). So to make it easy for the template, a new array is transferred with the actual used indexes. So the freemaker template can just go through the indexes finding the group with each index.

The component template (console-tools.get.html.ftl) is changed to:

<div id="${args.htmlid?html}-body" class="tool tools-link">
   <h2>${msg("header.tools")}</h2>
   <ul class="toolLink">
  
    <#list indeces as index>
          
        <#list tools as group>
               <#list group as tool>
                 <#if (tool.groupIndex)?number == index>
                     <#if tool_index=0 && tool.group != ""></ul><h3>${tool.groupLabel}</h3><ul class="toolLink"></#if>
                    <li class="<#if tool_index=0>first-link</#if><#if tool.selected> selected</#if>"><span><a href="${tool.id}" class="tool-link" title="${tool.description?html}">${tool.label?html}</a></span></li>
                 </#if>
             </#list>
        </#list>
           
    </#list>
   </ul>
</div>


Friday, June 15, 2012

Custom Admin Console groups

If you add alfresco share - admin console components, it is super easy: just provide a webscript-backed component, where the webscript family is set to 'admin-console'. This is the page-id.

See this excelent guide: http://blogs.alfresco.com/wp/wabson/2011/08/12/custom-admin-console-components-in-share/

Now what if you want it, grouped together with other custom components in a section like 'File Management' or 'Search' ... Well First you have to find the hard-code configuration-by-definition in the template controller 'console.js'. Paths are relative to site-webscripts.

It states in a comment, followed by code:


            // identify console tool grouping if any
            // simple convention is used to resolve group - last element of the webscript package path after 'console'
            // for example: org.alfresco.components.console.repository = repository
            //              org.yourcompany.console.mygroup = mygroup
            // package paths not matching the convention will be placed in the default root group
            // the I18N label should be named: tool.group.<yourgroupid>

            var group = "",
                groupLabelId = null,
                paths = tool.scriptPath.split('/');
            if (paths.length > 4 && paths[3] == "console")
            {
               // found webscript package grouping
               group = paths[4];
               groupLabelId = "tool.group." + group;
            }


This means component path 'com/mycomp/project/console/projectcomponents/component' will not be picked up, but 'com/mycomp/console/projectcomponents/component' will!

Side note: A more flexible approach could be to change it to:


            var group = "",
                groupLabelId = null,
                paths = tool.scriptPath.split('/');
            if (paths.length > 1 && paths[paths.length - 2] == "console")
            {
               // found webscript package grouping
               group = paths[paths.length - 1];
               groupLabelId = "tool.group." + group;
            }


This way both examples are valid and group is extracted as 'projectcomponents'.

Any way this gives you a new group after you configure you properties as well, for each group configure a property tool.group.<group>=<Your Group Title>

What you might notice now is a little to hard-coded assumption, that the first group is the default without a group is the first to be rendered in 'console-tools.get.html.ftl'. How ever the new group might be first, so the default group is placed wrong.


This can be fixed by customising the code to find the default group first:
<div id="${args.htmlid?html}-body" class="tool tools-link">
   <h2>${msg("header.tools")}</h2>
   <ul class="toolLink">
      <#list tools as group>
         <#list group as tool>
             <#if tool.group == "">
         <li class="<#if tool_index=0>first-link</#if><#if tool.selected> selected</#if>"><span><a href="${tool.id}" class="tool-link" title="${tool.description?html}">${tool.label?html}</a></span></li>
             </#if>
         </#list>
      </#list>
      <#list tools as group>
         <#list group as tool>
             <#if tool.group != "">
         <#if tool_index = 0></ul><h3>${tool.groupLabel}</h3><ul class="toolLink"></#if>
         <li class="<#if tool_index=0>first-link</#if><#if tool.selected> selected</#if>"><span><a href="${tool.id}" class="tool-link" title="${tool.description?html}">${tool.label?html}</a></span></li>
             </#if>
         </#list>
      </#list>
   </ul>
</div>

Tuesday, June 12, 2012

Default workflow message

Setting the default workflow message in share, based on the bpm package items

Two approaches possible. Override the java webscript FormUIGet or 'steal' the values from the start-workflow component (combobox).
see below form.control for a solution.

- Creating default messages ala: 'Review of myDoc.pdf'

I know the 'Review of ' is hardcoded, it could be pushed to a form.control.param :-)

Below is a freemaker template, which you can use in share-config-custom.xml ...




share-config-custom.xml 


<config evaluator="string-compare" condition="jbpm$wfl:customwfl">
        <forms>
            <form>
                <field-visibility>
                    <show id="bpm:workflowDescription" />
...
                </field-visibility>
                <appearance>
  
                    <field id="bpm:workflowDescription" label-id="workflow.field.message">
                        <control template="/com_redpill_linpro/components/form/controls/wf_preset_textarea.ftl">
               
                            <control-param name="style">width: 95%</control-param>
                        </control>
                    </field>
                    ...

wf_preset_textarea.ftl 


<#include "/org/alfresco/components/form/controls/textarea.ftl" />
 
 <#assign controlId = fieldHtmlId + "-cntrl">
    
 <#-- Below customization for default message -->

<script type="text/javascript">//<![CDATA[
            
   (function() {

            var onSuccess = function Wf_Preset_textArea_onSuccess(response)
             {
                var items = response.json.data.items,
                   item;
                if (0 < items.length) {
                    textArea = Dom.get("${fieldHtmlId}");
                    var message = "Review of ";
                    for (var i = 0, il = items.length; i < il; i++)
                    {
                      message += items[i].name;
                    }
                    textArea.innerHTML= message;
                }
    
             };
             
             var onFailure = function Wf_Preset_textArea_onFailure(response)
             {
                //empty
             };
      
             var startWorkflowComp = Alfresco.util.ComponentManager.get("${fieldHtmlId}".substring(0, "${fieldHtmlId}".indexOf("default") + 7));
             if (startWorkflowComp !== undefined) {
                Alfresco.util.Ajax.jsonRequest(
                {
                   url: Alfresco.constants.PROXY_URI + "api/forms/picker/items",
                   method: "POST",
                   dataObj:
                   {
                        items: startWorkflowComp.options.selectedItems.split(","),
                        itemValueType: "nodeRef"
                      
                   },
                   successCallback:
                   {
                      fn: onSuccess,
                      scope: this
                   },
                   failureCallback:
                   {
                      fn: onFailure,
                      scope: this
                   }
                });
            }
         })();
   
   //]]></script>

Monday, June 11, 2012

... little things matter

if you tried one of these error 40s (self-inflicted), this is hopefully a quick resolve for you :)

If you have a javascript webscript controller which uses JSON to communicate, remember to have 'json' in the webscript name, otherwise you do not get the 'magic' JSON object.

'testscript.post.js' (does not have a root object called 'json')
'testscript.post.json.js' (does)


You could also use the following start of the webscript (parsing the input data manually)
      // Check we have a json request
    if (typeof json === "undefined") {
        json = jsonUtils.toObject(requestbody.content);

        if (typeof json === "undefined") {

            if (logger.isWarnLoggingEnabled() == true) {
                logger.warn("Could not run webscript, because json object was undefined.");
            }

            status.setCode(501, "Could not run webscript, because json object was undefined.");
            return;
        }
    }