Recently in the community I’ve been advocating and drumming up support for DS REST API and Jian, Kim, and Manuel have been discussing MS Plugins using AJAX to call RQL. I recently got an opportunity to give them a test drive to see how they will work for rapid prototyping a solution for a Proof of Concept (POC). I was impressed.
The scenario (as originally understood):
To create a set of JSP pages that provide access to WSM
1)DS to display different contents based on date range, resolution, and later an audience category for personalization.
2) Remote access to Asset Manager contents, and on selection return relative paths for where published files would reside.
DS REST API In Depth:
After installing (used the quick start guide for backup) and testing base functionality. I jumped back to Management Server to create contents with metadata to drive the searches. Some assigned by categories and keywords, some by standard fields, and some embedded in the content class templates. After publishing I created a redundant database structure.
I created a new handler by copying the existing Target DynaMent handler. I removed authentication for the handler, as this is a POC it just make things simpler. I developed a query to the specified my parameters.
http://host/api/1/newtarget.html?dbs=wcs&project=demo&include-mode=content,content&chunksize=1&chunk=1&sortedby=cms.date.wcsdatetime&ignore-constraints=completely&sortorder=desc&attributepath=none&constrainttime=20110623&hres=300
I added a couple DynaMents to write constraints for me based on values of ‘constrainttime’ and ‘hres’. And I had a single content returned per date/resolution. I finished it up by adding a simple HTML XSLT so the content of this one item is accessible as if it were called directly. The rest was some JSP to scrape characters from a URL into a StringBuffer. Much more sophisticated option is possible but it is a POC.
RQL via AJAX
var sessionguid=””;$(document).ready(function() {// hide search result area
$(“#searchresult”).hide();
if($.cookie(“assetlogin”) == null || $.cookie(“assetsession”) == null || $.cookie(“assetlogin”) == “” || $.cookie(“assetsession”) == “”)
{
login();
}else{
if(“<%= session(“sessionkey”) %>” ==”” || “<%= session(“loginguid”) %>”==””){
loginguid=$.cookie(“assetlogin”);
sessionguid=$.cookie(“assetsession”);
DisplayAssets(“FE46C4F25F4847F4A983C6F41EDC42A3”);
}else{
//alert(“asp vars”);
loginguid=”<%= session(“loginguid”) %>”;
sessionguid=”<%= session(“sessionkey”) %>”;
DisplayAssets(“FE46C4F25F4847F4A983C6F41EDC42A3”);
}
}
});
function DisplayAssets(FolderGuid)
{
if(FolderGuid == “”)
{
$(“#searchresult .content”).append(“<div class=\”error\”>Error</div>”);
return;
}
$(“#searchresult .content”).empty();
//load simple page info
var strRQLXML = padRQLXML(“<MEDIA><FOLDER guid=\””+FolderGuid+ “\” subdirguid=\””+FolderGuid+”\”><FILES action=\”list\” view=\”list\” sectioncount=\”-1\” maxfilesize=\”0\” attributeguid=\”\” searchtext=\”*\” pattern=\”\” startcount=\”1\” orderby=\”name\”/></FOLDER></MEDIA>”);
$.post(“/CMS/PlugIns/RemoteAssetManager/rqlaction.asp”, { rqlxml: strRQLXML },
function(data){
// add stuff to search results
if($(data).find(“FILE”).attr(“name”) != null)
{
//add on click alert http://host/saq/images/imagename
$(data).find(‘FILE’).each(function(){
$(“#searchresult .content”).append(“<div style=\”border: 2px solid grey; padding 15px;\”><a onclick=\”alert(‘URL for Integration Externally: http://host/url/images/”+$(this).attr(“name”)+”‘)\” href=\”#img=” + $(this).attr(“name”) + “\”><br/><img style=\”border:3px solid black;\” src=\”http://win-kk55dom76sa/cms/”+$(this).attr(“thumbnailpath”)+”\” /><br/>” + $(this).attr(“name”) + “</a><br/> </div>”);
});
}
else
{
$(“#searchresult .content”).append(“<div class=\”error\”>Folder with guid ” + FolderGuid + ” not found.</div>”);
}
$(“#searchresult”).show();
}, “xml”);
}
function login(){
// login
var strLoginRQLXML = “<IODATA> <ADMINISTRATION action=\”login\” name=\”admin\” password=\”dontdothis\”/></IODATA>”;
$.post(“/CMS/PlugIns/RemoteAssetManager/rqlaction.asp”, { rqlxml: strLoginRQLXML },
function(data){
//handle the login request
loginguid=$(data).find(‘LOGIN’).attr(“loginguid”);
userguid=$(data).find(‘LOGIN’).attr(“userguid”);
//sessionguid=$(data).find(‘LOGIN’).attr(“guid”);
$.cookie(“assetlogin”, loginguid);
//alert(“loginguid: “+loginguid);
//”<IODATA loginguid=\”<%= session(“loginguid”) %>\” sessionkey=\”<%= session(“sessionkey”) %>\”>” + innerRQLXML + “</IODATA>”;
var strProjectRQLXML = padRQLXMLNoSession(“<ADMINISTRATION action=\”validate\”><PROJECT guid=\”0B7FE095D7814EE48B95B2E2A41A0BA0\” /></ADMINISTRATION>”);
//alert(strProjectRQLXML);
$.post(“/CMS/PlugIns/RemoteAssetManager/rqlaction.asp”, { rqlxml: strProjectRQLXML },
function(data){
//handle the login request
sessionguid=$(data).find(‘SERVER’).attr(“key”);
$.cookie(“assetsession”, sessionguid);
DisplayAssets(“FE46C4F25F4847F4A973C6F41EDC42A3”);
}, “xml”);
return “”;
}, “xml”);
return “”;
}
function padRQLXML(innerRQLXML)
{
return “<IODATA loginguid=\””+loginguid+”\” sessionkey=\””+sessionguid+”\”>” + innerRQLXML + “</IODATA>”;
}
function padRQLXMLNoSession(innerRQLXML)
{
return “<IODATA loginguid=\””+loginguid+”\”>” + innerRQLXML + “</IODATA>”;
}
This made a quick and easy UI to include via a similar JSP to the ones used for Delivery Server. This could be expanded on by
Result:
Both satisfied the technical requirements as they were understood. Some on the fly reconfiguration during a break was able to more accurately meet the customers requirements.
Time Frame:
Total time <16 hrs
Facts:
This includes download, install, configuration of REST project. A good amount of this time was spent coding the JSP to include the results of my REST services. I will be honest I’m really a novice with limited hands on experience. Only some rusty revision of existing RQL scripting as background and an existing AJAX plugin as guide.
Hey Tim,
Interesting article.
I thought I would mention an alternate solution we've been starting to use at Enthink on projects. That is to do RQL purely via JS. All these solutions require the client to setup the rql.asp file which essentially only creates a call to the RQL interface and takes commands from JS. Instead of doing this we've created a small SOAP client in JS which just creates SOAP envelopes and calls the RQL Web Service in MS directly. This is a small difference but what it allows you to do is extend or create multi-step functionality in CMS to allow authors to manage content more easily.
A JS SOAP client can be downloaded here http://www.codeproject.com/KB/ajax/JavaScriptSOAP… and depending on the server you can directly hit the RQL Service with an RQL call. An example of how we are using this can be seen below.
Enthink.Settings.RQLServiceURL = "/cms/Navigation/Services/RqlService.asmx";
// RQL function to rename a page filename.
Enthink.RQL.Page.FileNameWrite = function( pageGuid, fileName )
{
var sp = new SOAPClientParameters();
sp.add('command', '<IODATA loginguid="<%inf_LoginGUID%>" sessionkey="<%inf_SessionKey%>"><PAGE action="save" guid="' + pageGuid + '" name="' + fileName + '"/></IODATA>');
SOAPClient.invoke(Enthink.Settings.RQLServiceURL, 'ExecuteString', pl, true, Enthink.RQL._callback);
}
Just another perspective on how to do AJAX RQL I thought was worth mentioning. We're working on creating a RQL JS library which would allow us to create better interfaces in SmartEdit without the need to install plugins for client projects, but still have the ability to re-use some code for plugins when needed.
A good point. The only consideration I'd suggest is looking at performance of the SOAP interface vs Direct calls. For lower usage interface either is a good solution. Glad you are on the same path with development techniques. The one ASP file can serve for N endpoints of any language. Thanks for the feedback.
Hi Arek,
Thanks for the feedback. That definitely provides another option in RQL plugin development.
My experience with Webservice VS. ASP Connector so far:
I used ILSpy and found:
RQL Communication order
User -> RQL Web Service (Remoting) -> RQL Windows Service -> COM .NET Interop -> COM (VB unmanaged code).
Also, RQL Web Service uses a semi singleton pattern wrapped mutex for threadsafe execution. My concern with the implementation is that the semi singleton pattern might introduce memory leak after heavy use. Mutex is needed for threadsafe execution, but Management Server also uses this web service, so now the plugin is competing with the system for access, everything slows down.
As Tim mentioned, ASP connector should be faster since it is 2 layers lower than the Web Service. After some general everyday testing and usage, it seems that the ASP connector is much faster during bulk RQL commands, uses less system resources, and has minimal effect on MS performance.