jBackend for Joomla 3.0
The Release System Module is implemented with the plg_jbackend_release plugin. It provides functions to implement a full Joomla Update Server and/or a generic Package Distribution System based on Joomla. It supports both free and paid packages, and it is seamlessly integrated with Joomla user's groups for authorization and license key generation. Here is the list of supported methods.
Sends an email to the user with the license key for the specified package group.
action=post module=release resource=keybyemail id=<package group> email=<user email>
<end-point>?action=post&module=release&resource=keybyemail&id=redj_ent&email=This email address is being protected from spambots. You need JavaScript enabled to view it.
<end-point>/post/release/keybyemail/redj_ent?email=This email address is being protected from spambots. You need JavaScript enabled to view it.
{ "status": "ok" }
email is the user's account email on Joomla.
Returns the license key for the specified package group.
action=post module=release resource=keybyauth id=<package group> username=<username> password=<password>
<end-point>?action=post&module=release&resource=keybyauth&id=redj_ent&username=user123&password=pass123
<end-point>/post/release/keybyauth/redj_ent?username=user123&password=pass123
{ "status": "ok", "email": "user email", "key": "license key" }
username and password are the credentials used for the user's account on Joomla.
Checks if a license key is valid for the specified package group.
action=post module=release resource=validate id=<package group> email=<user email> key=<license key>
<end-point>?action=post&module=release&resource=validate&id=redj_ent&email=This email address is being protected from spambots. You need JavaScript enabled to view it.&key=fd8s809f89ds8f90sd81231sdsds323
<end-point>/post/release/validate/redj_ent?email=This email address is being protected from spambots. You need JavaScript enabled to view it.&key=fd8s809f89ds8f90sd81231sdsds323
{ "status": "ok" }
Checks if a license key is valid for the specified package id.
action=post module=release resource=verify id=<package id> email=<user email> key=<license key>
<end-point>?action=post&module=release&resource=verify&id=redj_ent_190&email=This email address is being protected from spambots. You need JavaScript enabled to view it.&key=fd8s809f89ds8f90sd81231sdsds323
<end-point>/post/release/verify/redj_ent_190?email=This email address is being protected from spambots. You need JavaScript enabled to view it.&key=fd8s809f89ds8f90sd81231sdsds323
{ "status": "ok" }
This method is intended to verify a license key over a specific package id before to download it.
Download the package for the specified package id.
action=post module=release resource=download id=<package id> email=<user email> key=<license key>
<end-point>?action=post&module=release&resource=download&id=redj_ent_190&email=This email address is being protected from spambots. You need JavaScript enabled to view it.&key=fd8s809f89ds8f90sd81231sdsds323
<end-point>/post/release/verify/redj_ent_190?email=This email address is being protected from spambots. You need JavaScript enabled to view it.&key=fd8s809f89ds8f90sd81231sdsds323
The binary package (as application/octet-stream)
The following options are available for release system plugin:
Option | Description |
Folder | Packages distribution folder path relative to Joomla root folder (JPATH_ROOT). |
Package groups | List of package groups. Packages in the same group share the same license key. Format is 'package group;groups[;hash;package name]' where package group is an unique identifier (e.g. redj_enterprise), groups is a list of user groups allowed to download packages in this group separated by commas (e.g. 1,3,5), hash is an optional random string used as seed for the generation of group's license keys, and package name is the human readable name of the package (e.g. ReDJ Enterprise). |
Packages | List of packages to distribute. Format is 'package id;package path[;package group]' where package id is an unique identifier (e.g. redj_ent_190), package path is the path of the package file relative to the packages distribution folder (e.g. private/joomla/redj/ReDJ1.9.0.zip), and package group is the group identifier (optional, not required for free downloads). |
The Community Builder Module is implemented with the plg_jbackend_comprofiler plugin. It provides functions related to Community Builder extension. Here is the list of supported methods.
action=get module=comprofiler resource=users limit=<L> (optional) offset=<O> (optional)
<end-point>?action=get&module=comprofiler&resource=users
<end-point>/get/comprofiler/users?limit=10&offset=2
{ "status": "ok", "total": <total>, "limit": <limit>, "offset": <offset>, "users": [ { "id": "<userid>", ... }, ... { "id": "<userid>", ... } ] }
limit is the number of entries to return.
offset is the cursor starting point (0 based, start on record 16 means offset = 15).
If limit is not specified, offset is ignored.
Properties limit and offset in the response are present only when specified in the request.
action=get module=comprofiler resource=profile id=<id>
<end-point>?action=get&module=comprofiler&resource=profile&id=482
<end-point>/get/comprofiler/profile/482
{ "status": "ok", ... }
action=put module=comprofiler resource=profile
{ "id": "<userid>", "name": "<name>", "username": "<username>", "email": "<email>", "password": "<password>", ... }
<end-point>?action=put&module=comprofiler&resource=profile
<end-point>/put/comprofiler/profile
{ "status": "ok" }
The id parameter in the request payload is mandatory to identify the user profile to update.
Other mandatory fields are username, email, and name.
It is possible to specify any supported field in the payload.
The Search Module is implemented with the plg_jbackend_search plugin. It provides functions related to Joomla search module. Here is the list of supported methods.
action=get module=search resource=site searchword=<term> searchphrase=<all> (optional) limitstart=<offset> (optional) limit=<limit> (optional) ordering=<orderby> (optional) areas[]=<area> (optional)
<end-point>?action=get&module=search&resource=site?searchword=joomla&searchphrase=all&limitstart=10&limit=10&ordering=newest&areas[]=content
<end-point>/get/search/site?searchword=<term>
{ "status": "ok", "total": <T>, "limit": <L>, "offset": <O>, "pages_current": <C>, "pages_total": <P>, "results": [ { "title": "<title>", "metadesc": "<metadesc>", "metakey": "<metakey>", "created": "<date created>", "language": "<language>", "catid": "<cid>", "text": "<text>", "section": "<section>", "slug": "<id:slug>", "catslug": "<id:slug>", "browsernav": "<browsernav>", "href": "<href>", "jcfields": "<jcfields>" }, ... ] }
searchphrase:
any, exact, all (default)
ordering:
newest (default), oldest, popular, alpha, category
areas:
an array of search areas (e.g. content)
Created date format is YYYY-MM-DD HH:MM:SS
Language can be "*" if any.
The Kunena Module is implemented with the plg_jbackend_kunena plugin. It provides functions related to Kunena forum for Joomla (topics and categories). Here is the list of supported methods.
action=get module=kunena resource=categories parentid=<P> (optional) default to the root levels=<L> (optional) 0=all levels, default is 1 unpublished=<0|1> (optional) 0=Hide (default), 1=Show
<end-point>?action=get&module=kunena&resource=categories&parentid=<P>&levels=<L>
<end-point>/get/kunena/categories?parentid=<P>
{ "status": "ok", "categories": [ { "id": "<category id>", "name": "<category name>", "alias": "<category alias>", "description": "<category description>", "parent_id": "<parent category id>", "level": "<category level>", "numTopics": "<number of category topics>", "numPosts": "<number of category posts>", "hits": "<number of hits>" } ] }
action=get module=kunena resource=categories id=<N>
<end-point>?action=get&module=kunena&resource=categories&id=<N>
<end-point>/get/kunena/categories/<N>
{ "status": "ok", "id": "<category id>", "name": "<category name>", "alias": "<category alias>", "description": "<category description>", "parent_id": "<category parent id>", "level": "<category language>", "numTopics": "<number of category topics>", "numPosts": "<number of category posts>", "hits": "<number of hits>", "icon": "<category icon>", "icon_id": "<category icon id>", "locked": "<locked status>", "pub_access": "<public access>", "pub_recurse": "<public recurse>", "review": "<review>", "allow_anonymous": "<allow anonymous>", "post_anonymous": "<post anonymous>", "allow_polls": "<allow polls>", "topic_ordering": "<topic ordering>", "last_topic_id": "<last topic id>", "last_post_id": "<last post id>", "last_post_time": "<last post time>" }
action=get module=kunena resource=topics catid=<C> limit=<L> (optional) offset=<O> (optional) orderdir=<dir> (optional) asc, desc
<end-point>?action=get&module=kunena&resource=topics&catid=<C>
<end-point>/get/kunena/topics?catid=<C>
{ "status": "ok", "total": "<total items>", "limit": <pagination limit>, "offset": <pagination offset>, "topics": [ { "id": "<topic id>", "category_id": "<category id>", "subject": "<topic subject>", "locked": "<locked>", "hold": "<hold>", "posts": "<posts>", "hits": "<number of hits>", "attachments": "<attachments>", "first_post_id": "<first post id>", "first_post_time": "<first post time>", "first_post_userid": "<first post user id>", "first_post_message": "<first post message text>", "first_post_guest_name": "<first post guest name>", "last_post_id": "<last post id>", "last_post_time": "<last post time>", "last_post_userid": "<last post user id>", "last_post_message": "<last post message text>", "last_post_guest_name": "<last post guest name>" }, ... { } ] }
action=get module=kunena resource=topics id=<N>
<end-point>?action=get&module=kunena&resource=topics&id=<N>
<end-point>/get/kunena/topics/<N>
{ "status": "ok", "id": <topic id>, "category_id": "<category id>", "subject": "<subject>", "locked": "<locked>", "hold": "<hold>", "posts": "<posts>", "hits": "<number of hits>", "attachments": "<attachments>", "first_post_id": "<first post id>", "first_post_time": "<first post time>", "first_post_userid": "<first post user id>", "first_post_message": "<first post text message>", "first_post_guest_name": "<first post guest name>", "last_post_id": "<last post id>", "last_post_time": "<last post time>", "last_post_userid": "<last post user id>", "last_post_message": "<last post text message>", "last_post_guest_name": "<last post guest name>", "messages": [ { "id": "<message id>", "parent": "<parent id>", "thread": "<thread id>", "catid": "<category id>", "name": "<user name>", "userid": "<user id>", "email": "<email>", "subject": "<message subject>", "time": "<message time>", "ip": "<ip address>", "topic_emoticon": "<topic emoticon>", "locked": "<locked>", "hold": "<hold>", "hits": "<number of hits>", "message": "<message>", "replynum": <number of replies>, "thankyou": [ ... ] }, ... { ... } ] }
The ZOO Module is implemented with the plg_jbackend_zoo plugin. It provides functions related to ZOO extension (applications, categories and items). Here is the list of supported methods.
action=get module=zoo resource=categories app=<app id>
<end-point>?action=get&module=zoo&resource=categories&app=<A>
<end-point>/get/zoo/categories?app=<A>
{ "status": "ok", "total": <T>, "categories": [ { "id": "<category id>", "name": "<category name>", "alias": "<category alias>", "description": "<description>", "parent": "<parent id>" }, ... { "id": "<category id>", "name": "<category name>", "alias": "<category alias>", "description": "<description>", "parent": "<parent id>" } ]}
action=get module=zoo resource=categories id=<N> app=<app id>
<end-point>?action=get&module=zoo&resource=categories&id=<N>&app=<A>
<end-point>/get/zoo/categories/<N>?app=<A>
{ "status": "ok", "id": "<category id>", "name": "<category name>", "alias": "<category alias>", "description": "<category description>", "application_id": "<application id>", "parent": "<parent id>", "ordering": "<ordering>", "content_subtitle": "<content subtitle>", "content_image": "<content image>", "content_image_width": "<content image width>", "content_image_height": "<content image height>", "metadata_title": "<meta title>", "metadata_description": "<meta description>", "metadata_keywords": "<meta keywords>", "metadata_robots": "<meta robots>", "metadata_author": "<meta author>" }
action=get module=zoo resource=items category=<C> limit=<L> (optional) page=<P> (optional) app=<app id>
<end-point>?action=get&module=zoo&resource=items&category=<C>&app=<A>
<end-point>/get/zoo/items?category=<C>&app=<A>
{ "status": "ok", "total": <T>, "limit": <L>, "page": <P>, "items": [ { "id": "<item id>", "name": "<item name>", "alias": "<item alias>", "created": "<created date>", "created_by": "<created by id>", "modified": "<modified date>", "modified_by": "<modified by id>", "publish_up": "<publish up date>", "publish_down": "<publish down date>" }, ... { "id": "<item id>", "name": "<item name>", "alias": "<item alias>", "created": "<created date>", "created_by": "<created by id>", "modified": "<modified date>", "modified_by": "<modified by id>", "publish_up": "<publish up date>", "publish_down": "<publish down date>" }
]
}
Created and modified date format is YYYY-MM-DD HH:MM:SS
action=get module=zoo resource=items id=<N> app=<app id>
<end-point>?action=get&module=zoo&resource=items&id=<N>&app=<A>
<end-point>/get/zoo/items/<N>=?app=<A>
{ "status": "ok", "id": "<item id>", "name": "<item name>", "alias": "<item alias>", "application_id": "<application id>", "type": "<item type>", "created": "<created date>", "created_by": "<created by id>", "modified": "<modified date>", "modified_by": "<modified by id>", "publish_up": "<publish up date>", "publish_down": "<publish down date>", "priority": "<item priority>", "state": "<item state>", "searchable": "<is searchable>", "access": "<item access>", "hits": <item hits>, "metadata_title": "<meta title>", "metadata_description": "<meta description>", "metadata_keywords": "<meta keywords>", "metadata_robots": "<meta robots>", "metadata_author": "<meta author>", "enable_comments": "<enable comments>", "primary_category": "<primary category id>", "elements": { "<element 1 id>": { "identifier": "<element 1 id>", "type": "<element type>", "data": { "<field 1 name>": "<field 1 value>", ... "<field M name>": "<field M value>" } }, ... "<element N id>": { "identifier": "<element N id>", "type": "<element type>", "data": { "<field 1 name>": "<field 1 value>", ... "<field L name>": "<field L value>" } }, } }
Created and modified date format is YYYY-MM-DD HH:MM:SS
When option Identify elements is set to No, the elements structure is different:
"elements": [ { "identifier": "<element 1 id>", "type": "image", "data": { "<field 1 name>": "<field 1 value>", ... "<field M name>": "<field M value>" } }, { "identifier": "<element N id>", "type": "textarea", "data": { "<field 1 name>": "<field 1 value>", ... "<field L name>": "<field L value>" } } ]
The following options are available for user plugin:
Option | Description |
Update hits | Update hits counter for each request. |
Identify elements | Use field identifier as key to build the elements in the response. |
Include elements | Include elements data in the list of items. |
Name elements | Use a slug name for elements (in lowercase without blanks). |
To manage API Keys used to grant access to end-points go to menu Components > jBackend > Keys. A list of jBackend Keys will be shown:
Each key has the following properties:
Field: | Description: |
---|---|
Key | The enabling key |
User | The user associated to this key (to restrict access with key and to a single user) |
Enabled endpoints | If this key is enable on ALL endpoints or ONLY on selected endpoints |
Selected endpoints | The endpoints for which the key is enabled, when not enabled for ALL |
Enabled on default | If the key should also work on the default requests (i.e. /index.php?option=com_jbackend&view=request&...) |
Daily requests | Maximum number of daily requests allowed for the key (0 means unlimited) |
Expiration date | Key expiration date (0000-00-00 00:00:00 means never expires) |
Comment | Just a reminder for the key |
For each key the following statistics fields are available:
Field: | Description: |
---|---|
Hits | Number of times the key was used |
Last visit | Last time the key was used |
Current day | Current day for daily requests limit |
Current hits | Hits of current day for daily requests limit |
The API key, when required by the endpoint, must be included in any request using one of the following methods:
To access to configured notifications go to menu Components > jBackend > Notifications. A list of notifications will be shown:
Each notification has the following information:
Field: | Description: |
---|---|
Title | Notification title (used only on Android) |
Message | Notification message |
Payload | Payload of push notification for app (JSON format) |
App | App code |
Platform | OS platform |
Target | Target registered devices for the push notification (All, Selected users, Selected groups, Selected devices) |
Target Users | Users whose registered devices are target for the push notification |
Target Groups | Groups whose registered devices are target for the push notification |
Target Devices | Registerd devices target of the push notification |
Context | Application specific notification context. Not used yet (it will be used to filter recipients for push notifications) |
Scheduled time | Time scheduled to start sending out this notification on devices |
Status | Current sending status for this notification |
Last run | Last time this notification queue has been executed |
Last device ID | Device ID of the last notification sent in the last run |
The Status of a notification can be:
Status: | Description: |
---|---|
aborted (-2) | The notification is "logically" deleted (in this state the notification is always skipped) |
paused (-1) | The notification is suspended (in this state the notification is always skipped) |
scheduled (0) | The notification is ready to be sent (sending will start at the scheduled time) |
running (1) | The notification is currently being processed |
completed (2) | The notification has been sent to all devices (job completed) |
Using the buttons on the toolbar it is possible to change the status of selected notifications. It is also possible to "unlock" locked notifications. It could be useful, as example, when a notification is locked for too long, which could mean there was a problem on the scheduler and the resource was not released properly (see How notification sending works)
To start processing the pending notifications (with status scheduled and running) it is necessary to call a "trigger" function. The Push Module has a scheduler function that must be called on a regular basis (e.g. from a crontab) to trigger the sending of scheduled push notifications.
Each time the scheduler function is called, it select all pending notifications ordered by the scheduler time ascending (first the notification scheduled before). To be "pending" a notification must have a scheduled time less then current time, must be in status scheduled or running, and must be no locked (locked means the notification is currently in charge of another scheduler). If the pending list is not empty, the scheduler starts to process notifications once a time, until the total number of push notifications sent to devices reach the Batch size (max configured for the scheduler).
For the notification currently being processed, the scheduler first locks it, then start to send the notifications to all matching devices (sorted by device ID, so all devices will be checked only once, including new devices added while the notification is running) until the Batch size is reached or the devices are finished. In the first case the notification is unlocked and the scheduler ends. In the second case the notification state is set to completed, it is unlocked and the scheduler starts to process the next notification (if one) with the same rules.
With this processing logic it is possible to schedule and execute concurrent schedulers to increase volume of push notifications sent.
It is possible to send a custom payload with each push notification. The payload must be a valid JSON, example:
{ "alert": { "alertId": "1404999843555", "alertStatus": false } }
Or:
{ "id": "25" }
How to get the JSON payload in the mobile app depends on the framework used. The following is an example using Ionic Framework with this Cordova Push notification plugin:
https://github.com/phonegap-build/PushPlugin
In the jBackend push plugin the payload hasthe following structure:
$payload = array( 'title' => $notification['title'], 'message' => $notification['message'], 'icon' => 'icon', 'data' => $notification['payload'] );
On the mobile app the push callback has the following code:
window.onNotificationGCM = function(e) { switch (e.event) { case 'registered': if (e.regid.length > 0) { $rootScope.$emit('push.registered', { 'token': e.regid }); } break; case 'message': // Notification in foreground if (e.foreground) { $log.debug('push notification in foreground'); } else { $log.debug('push notification in background'); } $log.debug('push message payload ' + JSON.stringify(e.payload)); $rootScope.$emit('push.received', { 'payload': e.payload.data }); // e.payload.data is { "id": "25" } break; case 'error': $log.debug('push GCM error ' + e.msg); break; default: $log.debug('push GCM unknown event'); break; } };
In the jBackend push plugin the payload hasthe following structure:
$body = array( 'aps' => array( 'alert' => array( 'title' => $notification['title'], 'body' => $notification['message'] ), 'sound' => 'default' ), 'data' => $notification['payload'] );
On the mobile app the push callback has the following code:
window.onNotificationAPN = function(e) { if (e.alert) { $log.debug('push APN alert ' + e.alert); } if (e.sound) { $log.debug('push APN sound'); } if (e.badge) { $log.debug('push APN badge ' + e.badge); pushNotification.setApplicationIconBadgeNumber(successBadgeHandler, errorBadgeHandler, e.badge); } $log.debug('push APN full event ' + JSON.stringify(e)); $rootScope.$emit('push.received', JSON.parse(e.data)); // e.data is { "id": "25" } };
To access to the list of registered devices go to menu Components > jBackend > Devices. A list of mobile devices will be shown:
It is not possible to add or edit device manually. Device registration is performed through a request to the Push Module.
Each registered device has the following information:
Field: | Description: |
---|---|
Token | Device Token |
App | App code |
Platform | OS platform |
IOS Alert | App setting desired about alert on iOS (an optional registration param for the app) |
IOS Badge | App setting desired about badge on iOS (an optional registration param for the app) |
IOS Sound | App setting desired about sound on iOS (an optional registration param for the app) |
In the current version alert, badge and sound fields are not used yet. They will be used in future, as example, to filter target devices for push notification sending.
For each device the following statistics fields are available:
Field: | Description: |
---|---|
Hits | Number of times the device has registered |
Last visit | Last time the device has registered |
Push hits | Number of push notifications sent to device |
Last push | Last time a push notification has been sent to device |
To access to configured apps go to menu Components > jBackend > Apps. A list of mobile apps will be shown:
Each registered app has the following information:
Field: | Description: |
---|---|
Code | The application unique code (it will be converted in lowercase) |
Description | An arbitrary description for the application |
Project ID | Google Project ID |
Project number | Google Project number (it will be used as Sender ID) |
API key | Google API key |
SSL certificate | Apple SSL certificate |
Password | Password for Apple SSL certificate |
The SSL certificate for APNs must be in PEM format, and must include the certificate and the private key. If the private key is not protected by password, the password field must be empty.
Here is an example of the PEM certificate:
-----BEGIN CERTIFICATE----- MIIFjDCCBHSgAwIBAgIIAYymZoETLVYwDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNV BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3 aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw ... Nw2VqrNGWt42Jwt66015Ww+Toh5KaTisMJnR/UQsDOWErVUkmZBbrVkbIy/hhTYZ b/MJRYBvnAQEtNU08eac3aqOEUFBt/Uu/HUigfvkxXOUdwMW4+wEB3uR2cLghrcA XvVMho17NIY9rXy3bEIuzwDTUFoCOfBEBYqW00BJVTGkOa6EWDI7SZbuULtxlByD 4edJl4ukCdZrJaTfHWMPeGqnv+MIrxz6LNSeiulTyQw= -----END CERTIFICATE----- -----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: DES-EDE3-CBC,D20C57B1444E286C uWXgF5I7fBBv2vY4N0yXiF9Uar+BRilhLUEuAcwDJJhWCFvnxVuBerck68Z5Zcbf JNJlESBp7exD5sTgNfok12bRw9cXvhMibY70riCukfhJveSWXCYIgN9BYtUjZgdn u8FZLtuRQH+HqY9OEjfQ9bcAiRX2a4uL1rKGCnyZShyWMRAtjLrOvT6Lzm8D59l6 7u8JcGlRAw+bdpDntwmB+17ep0zYd19uNRR5x9YKQfn9b55z1FiS+cBvwqqxnEtK ... jNqc19lOabQjkmCPzJjUVeaR/7pco2pGhNoA9UpK9kgL82yuj4Vo/+VYbSw1a+/d NkNKwOQTLttvKihEWDIZchoNP4vB/QnBo1DOAf+LRhtS3ja/FnLvsIouVeHo4OTL NqJKpw2FLJUY2cxxfu0rVID6motRsSwR4vZ3RYoFf7lA9nQpWtaCDgxoohuFF4Jp q+j5ZfY1MI0zQV56BnowR/0ocgzoibd6x3hTl076MGikjI6++Go6ng== -----END RSA PRIVATE KEY-----
To access to end-points access logs go to menu Components > jBackend > Logs. A list of traced requests will be shown:
Each logged request has the following information:
Field: | Description: |
---|---|
Endpoint | The ID of the end-point that has received the request |
Access type | The type of access used on the end-point (free, user, API key) |
Request time | The date and time of the request |
Duration | Time required to serve the request (in seconds) |
Error code | The error code in case of request error |
User ID | The ID of the user when available |
Key | The API key used when available |
Action | The action of the request |
Module | The module of the request |
Resource | The resource of the request |
It is possible to enable log trace for each end-point from the menu item options:
jBackend provides a robust and extensible solution to expose content and services through a Joomla website. The component provides the basic structure that has in charge of managing requests, controlling accesses, and dispatching incoming requests to the corresponding module. The jBackend architecture is extensible because its service modules are implemented as Joomla plugins, so adding new plugins allows to add new services and expose the contents of new extensions.
In this section we will describe how to create custom modules, and what is the structure of a jBackend plugin. For this purpose it was created an "Hello World" plugin available for download here:
http://www.selfget.com/downloads/file/31-plg-jbackend-helloworld-j30.html
Each HTTP request to an jBackend endpoint MUST include at least three parameters:
action=<action_type> module=<module_name> resource=<service_request>
The action would specify one of the HTTP methods used in a RESTful service (e.g. GET, PUT, POST, or DELETE). This is because all Joomla requests are made using GET, so this FIRST parameter introduces the missing information.
The module parameter indicates the name of the module that has in charge of managing this request (e.g. content). This can be implemented as a single jBackend plugin, or even a pool of plugins where each one implements a subset of module services. As example, for com_content core extension a content plugin already exists, which provides read-only access to content and categories. It could be possible to create a new plugin (e.g. plg_jbackend_content_admin) registered also as content module, which takes in charge editor functions like create content and categories.
The resource parameter specifies the service we are requesting to the module (e.g. articles of the content module, login service of the user module, etc.). This parameter must be validated and managed by the plugin itself, no checks will be made on this by jBackend (it cannot know about services provided by each plugin).
Considering all the required parameters (action, module, and resource), each request has this structure:
<end-point>?action=<action_type>&module=<module_name>&resource=<service_request>
If Joomla SEF is enabled, the request can assume this REST-like structure:
<end-point>/<action_type>/<module_name>/<service_request>
A request can also take additional parameters needed by the resource itself for the required service (e.g. the id of the article). This parameters can be added as a standard URL query string, so the request structure assumes this aspect:
<end-point>?action=<action_type>&module=<module_name>&resource=<service_request>&var1=<value1>&...&varN=<valueN>
Or can be (when SEF is enabled):
<end-point>/<action_type>/<module_name>/<service_request>?var1=<value1>&...&varN=<valueN>
An event onBeforeCheckModule is triggered just before to check the module name to dispatch. This permits to override the module variable (as well as action and resource variables) before the request dispatching process and can be useful, as example, when a plugin must deal with existing client requests that cannot be customized as required by jBackend.
Using onBeforeCheckModule it is possible, as example, to match a request like the following:
<endpoint>?var=catchme
This request doesn't include any of the required variables (action, module, resource), but we can "catch" the var and set the required variables:
public function onBeforeCheckModule() { $app = JFactory::getApplication(); $var = $app->input->getString('var'); if ($var === 'catchme') { $app->input->set('action', 'get'); $app->input->set('module', 'content'); $app->input->set('resource', 'articles'); $app->input->set('catid', '64'); $app->input->set('id', '71'); } }
As a standard Joomla plugin, any jBackend plugin needs an XML installation file with the following structure:
<?xml version="1.0" encoding="utf-8"?> <extension version="3.0" type="plugin" group="jbackend" method="upgrade"> <name>plg_jbackend_helloworld</name> ... </extension>
The plugin group must be "jbackend". All other XML elements are the same of a standard Joomla plugin. The PHP plugin code should have a generateError() function which builds and return all plugin specific errors:
/** * This is the function to generate plugin specific errors * The error response is an array with the following structure: * array( * 'status' => 'ko', * 'error_code' => $errorCode, * 'error_description' => <short error description> * ) * * @param string $errorCode The error code to generate * * @return array The response of type error to return */ public static function generateError($errorCode) { $error = array(); $error['status'] = 'ko'; switch($errorCode) { case 'REQ_ANS': $error['error_code'] = 'REQ_ANS'; $error['error_description'] = 'Action not specified'; break; case 'HWD_GEN': $error['error_code'] = 'HWD_GEN'; $error['error_description'] = 'Generic hello world error'; break; } return $error; }
It is not mandatory, but a good practice for the format of the error code is to use a pattern like XXX_YYY, where XXX is a code associated to the plugin (e.g. HWD for the helloworld plugin), and YYY for the specific error (e.g. GEN for generic error).
The other function that any jBackend plugin should have is the onRequest<Module>() function. This is called by jBackend during requests dispatching process. It has three parameters, an input parameter $module that is the module name and is used by the plugin to understand if it should handle the request, an output parameter $response that is the response object, and a $status parameter that can return additional information. The function returns true if there are no problems (status = ok), false in case of errors (status = ko):
public function onRequestHelloWorld($module, &$response, &$status = null)
The first thing the onRequest function should do is to check if it must handle the request or not. If not, it should return to improve the performance of the dispatching process:
if ($module !== 'helloworld') return true; // Check if this is the triggered module or exit
The second thing is to add the plugin to the module call stack:
// Add to module call stack jBackendHelper::moduleStack($status, 'helloworld'); // Add this request to the module stack register
After these tasks, it is possible to get request vars and process the response:
$app = JFactory::getApplication(); $action = $app->input->getString('action'); $resource = $app->input->getString('resource'); // Check if the action is specified if (is_null($action)) { $response = plgJBackendHelloWorld::generateError('REQ_ANS'); // Action not specified return false; } switch ($resource) { case 'greeting': if ($action == 'get') { return $this->actionGreeting($response, $status); } break; case 'about': if ($action == 'get') { return $this->actionAbout($response, $status); } break; } return true;
Each function like actionGreeting() and actionAbout() is specific to manage the resource requested by the action. To maintain the code clean it is a good practice to create a different function for each action.
The following is an example of an action() function that shows the code structure and how to manage the errors:
public function actionGreeting(&$response, &$status = null) { $app = JFactory::getApplication(); // Get additional request parameters $id = $app->input->getInt('id'); if (!is_null($id)) { // Example of how to generate and return an error inside an action function if ($id == '101') { $response = plgJBackendHelloWorld::generateError('HWD_GEN'); // Generic hello world error return false; } } // Get plugin params $option_name = $this->params->get('option_name', 0); $response['status'] = 'ok'; $response['message'] = $this->_greeting_msg; if ($option_name) { $response['option'] = 'true'; } return true; }
The K2 Module is implemented with the plg_jbackend_k2 plugin. It provides functions related to K2 content construction kit for Joomla (items and categories). Here is the list of supported methods.
action=get module=k2 resource=categories rootid=<R> (optional) endlevel=<E> (optional)
<end-point>?action=get&module=k2&resource=categories&rootid=<R>&endlevel=<E>
<end-point>/get/k2/categories?rootid=<R>
{ "status": "ok", "total": <T>, "categories": [ { "id": "<category id>", "name": "<category name>", "alias": "<category alias>", "description": "<category description>", "parent": "<parent category id>" }, ... { "id": "<category id>", "name": "<category name>", "alias": "<category alias>", "description": "<category description>", "parent": "<parent category id>" } ] }
If rootid is specified, starts to get subcategories from the specified category id instead of the root category. If endlevel is specified, get categories and all subcategories up to the specified number of level, otherwise get all levels (default).
action=get module=k2 resource=categories id=<N>
<end-point>?action=get&module=k2&resource=categories&id=<N>
<end-point>/get/k2/categories/<N>
{ "status": "ok", "id": "<category id>", "name": "<category name>", "alias": "<category alias>", "description": "<category description>", "robots": "<meta robots>", "author": "<meta author>", "language": "<language code>", "parent": "<category id>" }
Language can be "*" if any.
action=get module=k2 resource=items cats=<C1,C2,...,CN> (optional) subs=<0|1> (optional) featured=<0|1|2> (optional) limit=<L> (optional) page=<P> (optional) ordering=<field> (optional)
<end-point>?action=get&module=k2&resource=items&cats=<C>&subs=1
<end-point>/get/k2/items?cats=<C>&limit=<L>&page=<P>
{ "status": "ok", "total": "<total items>", "limit": <pagination limit>, "offset": <pagination offset>, items: [ { "id": "<item id>", "title": "<item title>", "alias": "<item alias>", "content": "<item content>", "featured": "<featured>", "images": { "image": "<image URL>", "imageWidth": "<image width>", "image_caption": "<image caption>", "image_credits": "<image credits>", "imageXSmall": "<image extra small URL>", "imageSmall": "<image small URL>", "imageMedium": "<image medium URL>", "imageLarge": "<image large URL>", "imageXLarge": "<image extra large URL>" }, "video": { "video": "<video URL>", "video_caption": "<video caption>", "video_credits": "<video credits>" }, "language": "<item language>", "category_id": "<category id>", "category_name": "<category name>", "author": "<item author>", "created": "<item creation date>", "modified": "<item modification date>" }, ... { ... } ] }
Cats:
List of categories id to include
Subs:
0=Do not include sub categories (default), 1=Include sub categories
Featured:
0=Hide, 1=Show (default), 2=Show only featured items
Ordering:
date, rdate, alpha, ralpha, order, rorder, featured, hits, rand, best, modified, publishUp, id (default)
Created and modified date format is YYYY-MM-DD HH:MM:SS
action=get module=k2 resource=items id=<N>
<end-point>?action=get&module=k2&resource=items&id=<N>
<end-point>/get/k2/items/<N>
{ "status": "ok", "id": "<item id>", "title": "<item title>", "alias": "<item alias>", "content": "<item content>", "featured": "<featured>", "images": { "image": "<image URL>", "imageWidth": "<image width>", "image_caption": "<image caption>", "image_credits": "<image credits>", "imageXSmall": "<image extra small URL>", "imageSmall": "<image small URL>", "imageMedium": "<image medium URL>", "imageLarge": "<image large URL>", "imageXLarge": "<image extra large URL>" }, "video": { "video": "<video URL>", "video_caption": "<video caption>", "video_credits": "<video credits>" }, "metadesc": "<meta description>", "metakey": "<meta keywords>", "metadata": { "robots": "<meta robots>", "author": "<meta author>", "rights": "<meta content rights>", "xreference": "<meta external reference>" }, "language": "<language code>", "category_id": "<category id>", "category_name": "<category name>", "author": "<item author>", "created": "<item creation date>", "modified": "<item modification date>" }
Created and modified date format is YYYY-MM-DD HH:MM:SS
Language can be "*" if any.
The following options are available for user plugin:
Option | Description |
Full image URL | Generate absolute URL (i.e. with domain) for images fields. |
Full video URL | Generate absolute URL (i.e. with domain) for video fields. |
Full img tag URL | Force absolute URL (i.e. with domain) in any HTML img tag. |
Content in items | Include or exclude full content in the items response. |
The SobiPro Module is implemented with the plg_jbackend_sobipro plugin. It provides functions related to SobiPro directory extension (sections, categories and entries). Here is the list of supported methods.
action=get module=sobipro resource=sections
<end-point>?action=get&module=sobipro&resource=sections
<end-point>/get/sobipro/sections
{ "status": "ok", "total": <T>, "sections": [ { "id": "<section id>", "nid": "<section nid>", "name": "<section name>" }, ... { "id": "<section id>", "nid": "<section nid>", "name": "<section name>" } ]}
action=get module=sobipro resource=sections id=<N>
<end-point>?action=get&module=sobipro&resource=sections&id=<N>
<end-point>/get/sobipro/sections/<N>
{ "status": "ok", "id": "<section id>", "nid": "<section nid>", "name": "<section name>", "description": "<section description>", "owner": "<section owner id>", "counter": "<counter>", "createdTime": "<created time>", "updatedTime": "<updated time>", "metaDesc": "<meta description>", "metaKeys": "<meta keywords>", "metaAuthor": "<meta author>", "metaRobots": "<meta robots>", "version": "<version number>" }
Created and updated date format is YYYY-MM-DD HH:MM:SS
action=get module=sobipro resource=categories parent=<N>
<end-point>?action=get&module=sobipro&resource=categories&parent=<N>
<end-point>/get/sobipro/categories?parent=<N>
{ "status": "ok", "total": <T>, "categories": [ { "id": "<category id>", "nid": "<category nid>", "name": "<category name>", "owner": "<owner id>", "counter": "<counter>", "createdTime": "<created time>", "updatedTime": "<updated time>", "metaDesc": "<meta description>", "metaKeys": "<meta keywords>", "metaAuthor": "<meta author>", "metaRobots": "<meta robots>", "version": "<version number>" }, ... { "id": "<category id>", "nid": "<category nid>", "name": "<category name>", "owner": "<owner id>", "counter": "<counter>", "createdTime": "<created time>", "updatedTime": "<updated time>", "metaDesc": "<meta description>", "metaKeys": "<meta keywords>", "metaAuthor": "<meta author>", "metaRobots": "<meta robots>", "version": "<version number>" } ]}
Created and updated date format is YYYY-MM-DD HH:MM:SS
action=get module=sobipro resource=categories id=<N>
<end-point>?action=get&module=sobipro&resource=categories&id=<N>
<end-point>/get/sobipro/categories/<N>
{ "status": "ok", "id": "<category id>", "nid": "<category nid>", "name": "<category name>", "description": "<category description>", "owner": "<category owner id>", "counter": "<counter>", "createdTime": "<created time>", "updatedTime": "<updated time>", "metaDesc": "<meta description>", "metaKeys": "<meta keywords>", "metaAuthor": "<meta author>", "metaRobots": "<meta robots>", "version": "<version number>", "parent": "<parent id>" }
Created and updated date format is YYYY-MM-DD HH:MM:SS
action=get module=sobipro resource=entries category=<C>
<end-point>?action=get&module=sobipro&resource=entries&category=<C>
<end-point>/get/sobipro/entries?category=<C>
{ "status": "ok", "total": <T>, "entries": [ { "id": "<entry id>", "nid": "<entry nid>", "name": "<entry name>", "owner": "<owner id>", "counter": "<counter>", "createdTime": "<created time>", "updatedTime": "<updated time>", "metaDesc": "<meta description>", "metaKeys": "<meta keywords>", "metaAuthor": "<meta author>", "metaRobots": "<meta robots>", "version": "<version number>" }, ... { "id": "<entry id>", "nid": "<entry nid>", "name": "<entry name>", "owner": "<owner id>", "counter": "<counter>", "createdTime": "<created time>", "updatedTime": "<updated time>", "metaDesc": "<meta description>", "metaKeys": "<meta keywords>", "metaAuthor": "<meta author>", "metaRobots": "<meta robots>", "version": "<version number>" } ]}
Created and updated date format is YYYY-MM-DD HH:MM:SS
action=get module=sobipro resource=entries id=<N>
<end-point>?action=get&module=sobipro&resource=entries&id=<N>
<end-point>/get/sobipro/entries/<N>
{ "status": "ok", "id": "<entry id>", "nid": "<entry nid>", "name": "<entry name>", "owner": "<entry owner id>", "section": "<entry section id>", "counter": "<counter>", "createdTime": "<created time>", "updatedTime": "<updated time>", "metaDesc": "<meta description>", "metaKeys": "<meta keywords>", "metaAuthor": "<meta author>", "metaRobots": "<meta robots>", "version": "<version number>", "fields": [ { "name": "<field name>", "fid": "<field id>", "nid": "<field nid>", "sid": "<field sid>", "section": "<section id>", "lang": "<language code>", "_data": "<field data>", "_rawData": "<field raw data>", "fieldType": "<field type>", "isFree": "<is free>", "position": "<position>" }, ... { "name": "<field name>", "fid": "<field id>", "nid": "<field nid>", "sid": "<field sid>", "section": "<section id>", "lang": "<language code>", "_data": "<field data>", "_rawData": "<field raw data>", "fieldType": "<field type>", "isFree": "<is free>", "position": "<position>" } ]}
Created and updated date format is YYYY-MM-DD HH:MM:SS
The following options are available for user plugin:
Option | Description |
JSON Entries | Enable JSON payload on entries action. |