written by Manuel Schnitger, 10. January 2011
RQL is the abbreviation of “RedDot Query Language”. RQL is the programming interface for the Open Text Management Server. Almost all actions that can be executed via the normal frontend (SmartTree, SmartEdit, Server Manager) can also be executed via the interface RQL. This result into the ability to write small (or even bigger) applications that does a specific job consisting of several steps automatically. Technically spoken it is a simple to understand request-response model.
Figure 1: CD cover of an old RQL workshop
History: Before RQL has been implemented as the interface for the application, all actions that lead to any communication with the project database were implemented as SQL statements. As the number of functions increased the number of different SQL statements increased accordingly. When RedDot CMS 4.0 has been released in April 2001 RQL was now part of software and supported efficient development. The guys from R&D didn’t have to know each SQL statement any longer but could just use short RQL statements which were “translated” into SQL statements by the RQL object.
As RQL is not just used within the development department but can also be used by partners and customers, probably hundreds or thousands of applications have been created in the last ten years.
In this first simple sample we will create a plugin that just lists all pages of the project. (I could have chosen another sample but as (almost) all projects have pages this plugin should work in most cases.)
As plugins do specific things which make sense in a specific context (maybe when someone clicks on a page) we first of all have to decide where the link that calls the plugin shall be displayed. Therefore we click on the item (page, user, whatever) in those context we’d like to provide the plugin and then we click on the info icon above the action menu. In the dialogue that is opened the plugin target is displayed. Highlight the plugin target and paste it into notepad (or any other editor). In SmartEdit the plugin targets can be retrieved via the info icons of all dialogues that provide the ability to insert plugins.
Figure 2: The plugin target for the project start node is displayed
Under CMS\ASP\Plugins\ there is a file called sample.xml. This file will contain information (link name, path and name of the plugin, etc.) regarding our plugin.
What we do is:
Figure 3: Plugin definition file
Figure 4: Server Manager: Importing the plugin
Figure 5: Activate the plugin
After we’ve imported the plugin we can enter the SmartTree mode of the project, click on the project start node and call the plugin. The dialogue below displays the result….an empty page.
Figure 6: First result
Just copy the following code into the file “listpages.asp” and call the plugin again….description follows:
<% sLoginGuid = session("LoginGuid") set RQLObject = Server.CreateObject("RDCMSAsp.RdPageData") RQLObject.XmlServerClassName = XmlServerClassName const XmlServerClassName = "RDCMSServer.XmlServer" const DhtmlClassName = "RDCMSAsp.RdPageData" RQLStatement = "<IODATA loginguid=""" & sLoginGUID & """ sessionkey=""" & sLoginGUID & """>"&_ "<PROJECT>"&_ "<PAGES action=""list""/>"&_ "</PROJECT>"&_ "</IODATA>" RQLRequest = RQLObject.ServerExecuteXml(RQLStatement, sError) if sError > "" then Response.write "Error -> List pages:</BR></BR>"+sError response.write server.htmlencode(RQLRequest) %>
In the first line of the code the session variable “LoginGuid” is retrieved. Every RQL statement need this variable as it indicates that the one who sends the statement to the server is logged on to the system.
In the block below the RQL object is created and some classes are assigned. As this block is just required in two different versions you don’t have to care about the classes.
The RQL statement (<PAGES action=””list””/>) is the really important part of the code. When you spend some time playing around with RQL you’ll figure out that the statements look very similar independently from what they do. The reason is very simple. RQL statements are being used to communicate with the project database. And as databases just know the commands insert, delete and modify (and maybe some more ;-)) RQL statements also just reflect the abilities of the database.
In this line “RQLRequest = RQLObject.ServerExecuteXml(RQLStatement, sError)” the content of the variable RQLStatement is being executed by the method ServerExecuteXml of the RQLObject and the answer of the server is written in the variable RQLRequest.
Call the plugin again or refresh the page. The window should then look like the window below.
Figure 7: Server result: List of all pages
As we can see in figure 7 the server responded but what we retrieved doesn’t look that fancy. Therefore we need one more object.
If we add the line “set XMLDOMPages = Server.CreateObject(“RDCMSAspObj.RdObject”)” to the block where the objects are created we will have an object that is able to render the result (XML).
In order to loop trough the result we…
Call XMLDOMPages.LoadXML (RQLRequest) Set Pagelist = XMLDOMPages.ObjectByQuery ("//PAGES") for iCounter = 1 to Pagelist.objects.count response.write Pagelist.objects(iCounter)("guid") & "<br>" response.write Pagelist.objects(iCounter)("id") & "<br>" response.write Pagelist.objects(iCounter)("headline") & "<hr>" next
Figure 8: Result of the complete code
Complete code of the Hello World! plugin.
<% sLoginGuid = session("LoginGuid") set RQLObject = Server.CreateObject("RDCMSAsp.RdPageData") RQLObject.XmlServerClassName = XmlServerClassName const XmlServerClassName = "RDCMSServer.XmlServer" const DhtmlClassName = "RDCMSAsp.RdPageData" set XMLDOMPages = Server.CreateObject("RDCMSAspObj.RdObject") RQLStatement = "<IODATA loginguid=""" & sLoginGUID & """ sessionkey=""" & sLoginGUID & """>"&_ "<PROJECT>"&_ "<PAGES action=""list""/>"&_ "</PROJECT>"&_ "</IODATA>" RQLRequest = RQLObject.ServerExecuteXml(RQLStatement, sError) if sError>"" then Response.write "Error -> List pages:</BR></BR>"+sError Call XMLDOMPages.LoadXML (RQLRequest) Set Pagelist = XMLDOMPages.ObjectByQuery ("//PAGES") for iCounter = 1 to Pagelist.objects.count response.write Pagelist.objects(iCounter)("guid") & "<br>" response.write Pagelist.objects(iCounter)("id") & "<br>" response.write Pagelist.objects(iCounter)("headline") & "<hr>" next %>
As you can see this RQL plugin uses very few commands such as set, if, for, next.
In order to work correctly most RQL statements require information such as the current language id or the name of the current user or the GUID of the project. Most information can be somehow retrieved by RQL but it’s much easier just to check if the required value is already available as a session variable.
Sample: When a user opens a page in SmartEdit (or clicks on the page in SmartTree), the GUID of the page can be retrieved by the following line:
myPage = session(“PageGuid”)
If I then would like to get the extended page information of this page I can easily create the RQL statement.
<% Response.Buffer = true if Request("sessionitem") <> "" then _ Session(Request("sessionitem")) = Request("sessionitemvalue") %> <HTML> <HEAD> <TITLE>Session Variables</TITLE> </HEAD> <BODY> <% Set oIO = Server.CreateObject ("RDCMSAsp.RdPageData") oIO.XmlServerClassName = "RDCMSServer.XmlServer" %> <table> <tr><td><b>Session Variable</b></td><td><b>Value</b></td></tr> <% on error resume next for each oSession in Session.Contents Response.write("<tr><td>" & oSession & "</td><td>") sSessionString = Session(oSession) if UCase(Left(sSessionString, 7)) = "<IODATA" then sSessionString = oIO.EncodeXmlData (sSessionString) end if Response.Write sSessionString & "</td></tr>" Next %> </table> </BODY> </HTML>
Please be aware that plugins might decrease the performance of the editorial server. The editorial server architecture has been designed in order to provide an environment for editors and authors. If you write plugins with mass operations (listing all pages > listing all elements of each page > …) then the number of RQL statements which are sent to the server is much higher than usual.
First of all you should have a look at the RQL documentation that is part of the software package of the Open Text Management Server. Only statements which are part of the official documentation are supported. So if you use anything else…it will probably work but without any guarantee especially for upcoming versions.
If you don’t have any clue which statement is used to perform a specific action then you can have a look at the RDCMS.log file. Every action via the front end results into an RQL statement that can be found in the RDCMS.log. As there are normally a lot of log files I normally do the following:
In Step 5 I mentioned that the “block” where the objects are created is required in two different versions. The reason for that is that you either get a (big) list of objects (pages, users, groups, language variants,…) as the answer of your request and therefore have to loop through the results or you just have one item and have a slightly different approach to read out the desired values.
Sample for an XML object that have the ability to read out information of just one item:
const XmlDocumentClassName = “RDCMSXmlDom.RDDocument” set XmlDomPageGuid = Server.CreateObject(XmlDocumentClassName) [RQL statement that creates a page] sPageGUID = XmlDomPageGuid.ChildNodes(“LINK”).ChildNodes(“PAGE”).GetNodeAttribute(“guid”).AttributeValue
For lots of applications (free & commercial) visit www.solutionexchange.info. This is the best place to get ideas. Create plugins and afterwards post them in order to share them with the community ;)
If something is wrong, missing or cannot be understood, please leave a comment!
Source: RQL in a nutshell
© copyright 2011 by Manuel Schnitger