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.
Q: What skills do I need in order to create RQL applications?
A: If you do have basic programming knowledge and joined the basic training course for the Open Text Management Server then this should be enough ;-)
Q: Is there a specific programming language that I have to use?
A: No, technically spoken every programming language that has the ability to create objects can be used. So C#, ASP, PHP, JSP and many other languages can be used.
Q: Where can I use RQL?
A: You can create plugins that can be called via the normal front end in SmartTree, SmartEdit and in the Server Manager. Further more you can use RQL in the Template code and in external applications.
Q: What cannot be done with RQL?
A: Some functionality in the front end is implemented by using JavaScript and do not result into any communication with the database. As RQL is “just” implemented to communicate with the database, actions that do not require interaction with the database are not handled with RQL.
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.)
Step 1: The Plugin target
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
Step 2: Modifying the plugin definition file
Under CMSASPPlugins 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
Step 3: Importing the plugin
Figure 4: Server Manager: Importing the plugin
Figure 5: Activate the plugin
Step 4: Calling 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
Step 5: Programming
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.
Step 6: Calling the plugin …again
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
Step 7: Rendering the result
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 RQLObject = Server.CreateObject("RDCMSAsp.RdPageData")
to the block where the objects are created we will have an object that is able to render the result (XML).
Step 8: Displaying the result
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.
Session variables
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.
myPage = session(“PageGuid”)
If I then would like to get the extended page information of this page I can easily create the RQL statement.
Q: Are the session variables always the same?
A: No, depending where the current user is (SmartTree, SmartEdit, Server Manager) some values are different. If the user clicks on a page, the session variable “TreeGuid” contains the GUID of the page. If the user clicks on a group in the Server Manager, the same variable contains the GUID of the group.
Q: Is there a way to get a list of all session variables?
A: Yes, just create a plugin based on the code below. If you call the plugin from different locations you will see which session varaibles are available and what content they have.
<% 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.
RQL Statements
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:
Rendering the server response
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 give us some feedback!