VikRestaurants APIs

API Framework

The API Framework is a collection of tools useful to make a communication between VikRestaurants and all your external applications.

For example, through the API Framework, a CLIENT software, installed on your computer, can communicate with your server to retrieve all the reservations and orders stored in VikRestaurants.

The API Framework can be accessed from the Configuration of VikRestaurants, by visiting the Applications section.

Configuration

Enable APIs
Enable this setting if you want to connect external applications to your website. 
The connection is allowed only if the credentials of the client are valid.

If this option is disabled, any connection attempt will be denied.

Max Failure Attempts
Every time a client fails the login to your system, its failure attempts number is increased by one. When the number of attempts reaches this value, the calling IP Address will be automatically (and permanently) banned.

Register Logs
Choose the type of messages the system should log. It is possible to log only anything or to log only the errors. Otherwise it is possible to completely disable the logs.

Auto-Flush Logs
You can allow the system to remove the stored logs automatically after some time. The possible options are: Every DayEvery WeekEvery MonthNever.

Communication Usage

It is possible to start a communication with your server by reaching the following BASE URL:

.../index.php?option=com_vikrestaurants&task=apis

The ellipsis (...) are meaning the name of your website, such as:
http://yourdomain.com/index.php?option=com_vikrestaurants&task=apis

By reaching this BASE URL you will get the authorisation error below:

{
"errcode": 101,
"error": "Authentication Error! The username is empty or invalid."
}

In order to use the API Framework it is required the authentication of your credentials.
This means that you have to extend the BASE URL to send your username and your password.

.../index.php?option=com_vikrestaurants&task=apis&username=[YOUR_USERNAME]&password=[YOUR_PASSWORD]

Since VikRestaurants 1.8, the system accepts login credentials sent by using the HTTP BASIC_AUTH header.

@see API Users to create some valid API credentials.

Once the BASE URL will contain a valid username and password, it will raise another type of error:

{
"errcode": 201,
"error": "File Not Found! The event requested does not exists."
}

The API Framework is built to execute certain pieces of code and it is mandatory to specify also the name of the EVENT (or PLUGIN) to run.
To complete the BASE URL we have to add an additional parameter called event.

.../index.php?option=com_vikrestaurants&task=apis&username=[YOUR_USERNAME]&password=[YOUR_PASSWORD]&event=[YOUR_EVENT_NAME]

To make sure your BASE URL is correct, you can try to use a pre-installed event called connection_ping.
Your URL should look like the one below:

.../index.php?option=com_vikrestaurants&task=apis&username=my_username&password=my_password&event=connection_ping

which should return the successful message below:

{
"status": 1
}

Event Arguments

Your events (or plugins) may work depending on different values, such as a certain table ID or for a certain date.

For this reason, it is possible to pass certain arguments within the BASE URL. The special &args[] field can be placed in the query string to send all the additional arguments needed for your plugin.

To pass, for example, the fields first name, last name and e-mail, we can use the following query string:

&args[first_name]=John&args[last_name]=Smith&args[email]=This email address is being protected from spambots. You need JavaScript enabled to view it.

which must be concatenated to your BASE URL.

API Users

It is possible to see and manage the API Users by clicking the See Users List button from the Application configuration section.

This is a list of all the applications and their relative credentials that can communicate with your VikRestaurants.

Application Details

An API User can be configured with all the fields below.

  • Application Name - the name of the application. If not specified, the username will be used.
  • Username - the username of the application, used to authenticate the requests.
  • Password - the password of the application, used to authenticate the requests. Press the icon to show the password.
    Press the Generate Password button to insert a random and safe password.
  • Active - enable this option it the application can access to the API Framework.

Allowed IPs

It is possible to specify also one or more IP Addresses to restrict the origin from which the application can connect itself to your VikRestaurants.

Press the Add IP Address button to insert the 4 parts of your IP (min 0.0.0.0 and max 255.255.255.255).

By leaving the list of IPs empty, all the IP Addresses will be accepted.

Plugin Rules

You can also specify the plugins that the application can perform.

If you are not able to deny a certain plugin, it means that it is always enabled for all the active applications.
@see Event::alwaysAllowed() for a better explanation.

Credentials Requirements

The username and password credentials MUST follow the restrictions of the API Framework, otherwise they won't be considered as valid and the authentication will fail.

Username Requirements

  • It accepts only letters (uppercase and lowercase), digits, dots (.) and underscores (_). White spaces ARE NOT accepted.
  • The username length MUST be between 3 and 128 characters.

Password Requirements

  • It accepts only letters (uppercase and lowercase), digits, and all these !?@#$%{}[]()_-. symbols. White spaces ARE NOT accepted.
  • The password length MUST be between 8 and 128 characters.
  • The password MUST contain at least one letter.
  • The password MUST contain at least one number.

Logs

It is possible to see the stored Logs from the API Users page, by clicking the See Logs button.

The logs describe the result of the actions performed by your applications.
In case a log is referring to an authentication error, considering it is not possible to evaluate the application name, it will be used the IP Address of the caller instead.

It is possible to filter the logs by Application Name and/or by Content.

  • ID - the ID of the log. By deleting all the existing logs, the ID will be reset.
  • Created On - the creation date of the log. Hover the mouse above these values to see the detailed date.
  • Content - the description of the log.
  • Status - the status of the log: OK on success, ERROR on failure. Depending on your configuration, you may not be able to see successful logs.
  • Application Name - the name of the application authenticated, otherwise the caller IP Address.

Banned List

It is possible to see the Banned List from the API Users page, by clicking the See Banned List button.

Here you can find the list of the IP Addresses that ARE NOT allowed to use your API Framework.
It is not possible to add manually an IP Address in this blacklist, as the system evaluates automatically if an IP Address should be considered as malevolent.

An IP Address is banned depending on the value specified for the Max Failure Attempts setting. For example, if this parameter is set to 20, an IP Address is pushed in the blacklist if it gets 20 authentication failures in a row.

By removing an IP Address from the blacklist, it will be able to re-try the authentication to your API Framework.

From the dropdown located in this page, you can select the All Records option to see the list of the IP Addresses that have failed at least once the authentication. The records in this list are STILL able to try the authentication to your API Framework.

It is possible to filter the list by IP Address only.

  • ID - the ID of the banned user.
  • IP Address - the IP Address from which the user is attempting to authenticate itself.
  • Last Update - the last update of the record. Hover the mouse above these values to see the detailed date.
  • Failure Attempts - the current number of failures made in a row (failures / max_failures). When the failures count reaches the max_failures setting, the IP Address will be banned. When an IP Address authenticates itself successfully, the failures count will be reset (only if the failures count is still lower than the max_failure amount).

API Plugins

It is possible to see and test the API Plugins by clicking the See Installed Plugins button from the Application configuration section.

This is a list of all the plugins (or events) installed on your system than can be used for the communication between VikRestaurants and all your applications.

It is possible to filter the installed Plugins by name.

Structure & Path

The plugins are simple .PHP files that extend the methods of the Event class of the API Framework library.
All these files are placed in the directory below:

.../components/com_vikrestaurants/helpers/src/libraries/API/Plugins/

Plugin Description

You can click the icon to see a full description of the installed plugins.
The description is generated starting from the string returned by the Event::getDescription() method.

Libraries

Take a look at all the classes and methods that can be used to create your own API Plugins.

Classes

Here is a list of all the classes that can be used in order to develop a custom plugin/event that all your external applications can run.

E4J\VikRestaurants\API\User

abstract

This class is used from the API framework to wrap the details of the connecting users and to handle the authorizations of the events.

Considering this is an abstract class, you have to use a class that inherits it.
If you need to access it, you can use our E4J\VikRestaurants\API\Framework\BasicAuthUser object.

@since 1.9

__construct

Class constructor.

@param string $username The username of the user for login.
@param string $password The password of the user for login.
@param string $ip The IP address from which the user is trying to login (default NULL).

/* fatal error : abstract class */
$user = new \E4J\VikRestaurants\API\User("test", "test_pwd", "127.0.0.1");
/* correct instantiation */
$user = new \E4J\VikRestaurants\API\Framework\BasicAuthUser("test", "test_pwd", "127.0.0.1");

getUsername

Get the username of the user.
The username is not empty only if it is verified from the constructor.

@return string The username of the user.

@see User::getCredentials()   Get always the provided credentials, also in failure cases.

$username = $user->getUsername();

getPassword

Get the password of the user.
The password is not empty only if it is verified from the constructor.

@return string The password of the user.

@see User::getCredentials()   Get always the provided credentials, also in failure cases.

$password = $user->getPassword();

getCredentials

Get the credentials of the user, also in failure cases.

@return object An object containing the credentials of the user.

$credentials = $user->getCredentials();
echo $credentials->username . ", " . $credentials->password;

assign

Set the ID of the user after a successful login.
By setting an ID through this method, the framework assumes that the user is currently connected.

@return User This object to support chaining.

if ($id_user = doLogin()) { // this method does not exist
    $user->assign(id_user);
}

id

Get the ID of the user. Return NULL in case the user is not yet connected.

@return int The ID of the user or NULL.

if ($user->id()) {
    /* user is logged */
}

isConnectable

Return true if the credentials provided match the structure requirements.
When true, it is possible to proceed with the login check, otherwise it means the username and/or password are not correctly filled in (for example they have not enough characters).

 @return bool True if the username and password are not empty (accepted).

if ($user->isConnectable()) {
    /* user can login */
}

getSourceIp

Get the origin IP address from which the user is trying to connect.

@return string The IP address if provided, otherwise NULL.

$ip = $user->getSourceIp();

authorise

Check if the user is able to perform the event provided.

@param Event $event The event to authorise.

@return bool True if the event can be performed, otherwise false.

@see Event

$event = new \E4J\VikRestaurants\API\Plugins\ConnectionPingPluginAPI('connection_ping');

if ($user->authorise($event)) {
    /* the event can be performed */
}

E4J\VikRestaurants\API\Error

This class is a basic representation of an error that can be raised from the API Framework.

The errors are (and must be) raised in case something gone wrong. The external applications are able to see the raised errors.

@since 1.9

__construct

Class constructor.

@param int $errcode The code identifier.
@param string $error The text description.

$err = new \E4J\VikRestaurants\API\Error(404, "Not Found!");
echo $err->errcode . ", " . $err->error;

toJSON

Return this object encoded in JSON format.

@return string This object in JSON.

$json = $err->toJSON();
$obj = json_decode($json);
echo $obj->errcode . ", " . $obj->error;

asException

Creates an exception with the details of this error.

@return Exception The error as exception.

$error = new \E4J\VikRestaurants\API\Error(404, "Not Found!");
throw $error->asException();

Response

This class is used to wrap the details of a valid response for the API Framework.

The responses are used to log something about the performed events, on success or failure. The logs are not accessible from external applications.

@since 1.9

__construct

Class constructor.

@param bool $status True for success response, otherwise false (default false).
@param string $content The text description of the response (default empty string).

$response = new \E4J\VikRestaurants\API\Response(false, "Response Error.");
/* or */
$response = new \E4J\VikRestaurants\API\Response(true);

setStatus

Set the status of the response.

@param bool $status True for success response, otherwise false.

@return Response This object to support chaining.

$response->setStatus(true);

isVerified

Return true if the status of the response is success, otherwise false.

@return bool True on success, otherwise false.

if ($response->isVerified()) {
/* status is TRUE */
}

isError

Return true if the status of the response is failure, otherwise false.

@return bool True on failure, otherwise false.

if ($response->isError()) {
/* status is FALSE */
}

setContent

Set the text description of the response.

@param string $content The content of the response.

@return Response This object to support chaining.

$response->setContent("content [1]");

appendContent

Append some text to the existing description of the response.

@param string $content The content of the response.

@return Response This object to support chaining.

$response->setContent("content [1]")
->appendContent(" - content [2]");
/* content [1] - content [2] */

prependContent

Prepend some text to the existing description of the response.

@param string $content The content of the response.

@return Response This object to support chaining.

$response->setContent("content [1]")
->appendContent(" - content [2]");
->prependContent("content [0] - ");

/* content[0] - content [1] - content [2] */

clearContent

Clear the text description of the response.

@return Response This object to support chaining.

$response->clearContent();

getContent

Get the text description of the response.

@return string The text description of the response.

$content = $response->getContent();

setContentType

Set the Content-Type header for the response. If not specified the default application/json will be used.

@param string $type The content type of the response.

@return Response This object to support chaining.

$response->setContentType("text/xml");

getContentType

Get the Content-Type header set for the response.

@return string The content type of the response.

$type = $response->getContentType();

createdOn

Get the initial unix timestamp of the response.
The initial time is recorded during the creation (instantiation) of the response.

@return int The initial timestamp in seconds.

$created_on = $response->createdOn();
echo date('Y-m-d H:i:d', $created_on);

getElapsedTime

Get the elapsed time (in seconds) between the current time and the initial time.

@return int The elapsed time in seconds.

$elapsed = $response->getElapsedTime();

E4J\VikRestaurants\API\Event

This class is the representation of a callable event in the API Framework.
If you need to implement a new event (also called plugin) to perform a certain request, you have to create a new CLASS that inherits Event.

@since 1.9
@abstract Extend this class in order to perform a specific action.

__construct

Class constructor.

@param string $name The name of the event.
@param mixed $options A configuration array/object.

$event = new \E4J\VikRestaurants\API\Plugins\ConnectionPingPluginAPI('connection_ping');
if ($event instanceof EventAPIs) {
    /* ConnectionPingPluginAPI inherits Event class */
}

getName

Get the name of the event.

@return string The name of the event.

$name = $event->getName(); /* connection_ping */

getTitle

Get the title of the event.

@return string The title of the event.

$title = $event->getTitle(); /* Connection Ping */

getDescription

Get the description (in HTML) of the event.

@return string Returns an empty string. To display a description, override this method from the child class.

$desc = $event->getDescription();

alwaysAllowed

Returns true if the plugin is always authorised, otherwise false.
When this value is false, the system will need to authorise the plugin through the ACL of the user.

@return bool Always false. To always allow this plugin, override this method from the child class.

$allowed = $event->alwaysAllowed();

run

Perform the specified action of the event.

@param array $args Some arguments to pass to the event.
@param Response &$response The response object for admin (passed by reference).

@return Error The error occurred, if any.

@uses Event::execute()   Executes the code written in the child class.

@see Response
@see Error

$response = new \E4J\VikRestaurants\API\Response(false);

$event = new \E4J\VikRestaurants\API\Plugins\ConnectionPingPluginAPI('connection_ping');

$output = $event->run([], $response);

if ($output instanceof \E4J\VikRestaurants\API\Error) {
	echo $err->toJSON();
	exit;
}

echo $output;

execute

protected

The custom action that the event has to perform. This is an abstract method that should be defined from the child class.
This method should not contain any exit or die function, otherwise the event won't be stopped properly.

On success, all the information to return to the applications should be echoed instead.

In case of failure, you can return a new ErrorAPIs instance.

@param array $args Some arguments to pass to the event.
@param Response &$response The response object for admin (passed by reference).

@return mixed The response to output or the error faced.

@protected Use Event::run() instead.
@abstract Implement this method in a child class to perform all the actions you need.

@see Response
@see Error

use E4J\VikRestaurants\API\Error;
use E4J\VikRestaurants\API\Event;
use E4J\VikRestaurants\API\Response;

class HeadsOrTails extends Event
{
    protected function execute(array $args, Response $response)
    {
        /* 0 for tails, 1 for heads */
        $coin = rand(0, 1);

        if (!$coin) {
            /* if tails, raise error */
            $response->setContent('It is Tails!'); // error for admin
            return new Error(501, 'Tails!'); // error for customer
        }

        /* otherwise, display message */
        $response->setStatus(1)
            ->setContent('It is Heads!'); // success message for admin

        $obj = new stdClass;
        $obj->message = 'Heads!'; // success message for customer

        return json_encode($obj);
    }
}

API

abstract

The base API Framework of VikRestaurants.
This class is used to run all the installed events/plugins listed in a given directory.

All the events are runnable only if the user is correctly authenticated.

Considering this is an abstract class, you have to use a class that inherits it.
If you need to access it, you can use our E4J\VikRestaurants\API\Framework\API object.

@since 1.9

__construct

protected

Class constructor.

@param Container $container The container holding the registered event providers.
@param BlockEngine $blockEngine The engine used to look for banned users.
@param array $config The API framework configuration.

// auto-register all the default API plugins
$containerDecorator = (new \E4J\VikRestaurants\DI\ContainerDecorator)
    ->register(VREHELPERS . '/src/libraries/API/Plugins', [
        'suffix'    => 'PluginAPI',
        'namespace' => 'E4J\\VikRestaurants\\API\\Plugins',
    ]);

// create new API Framework
$api = new E4J\VikRestaurants\API\Framework\API(
    $containerDecorator->getContainer(),
    new E4J\VikRestaurants\API\Framework\MaxAttemptsBanner(10)
);

If you want to access the global API framework, you can rather use the code below.

/** @var E4J\VikRestaurants\API\API */
$api = VREFactory::getAPI();

registerEventProvider

Registers a new event provider for lazy initialization.

This method is needed in order to implement third party events/plugins.

@param string $event The event identifier.
@param callable $provider The callback used to instantiate the event.
@param array $args The arguments to pass within the plugin (default empty array).

@return string The response echoed from the plugin on success.

// add support to "custom" event
$api->registerEventProvider('custom', function($id, $args) {
    // autoload the PHP file and create the event instance
    require_once dirname(__FILE__) . '/plugins/custom.php';
    return new CustomPluginAPI($id, $args);
}, [
    // do not use a static instance
    'shared' => false,
    // allow other plugins to override this event
    'protected' => false,
]);

isEnabled

Returns true if the API framework is enabled and accessible.

@return bool True if enabled, otherwise false.

if ($api->isEnabled()) {
    /* $api object can be used */
}

isConnected

Returns true if the user is correctly logged.

@return bool True if logged, otherwise false.

if ($api->isConnected()) {
    /* user is authenticated */
}

getUser

Returns the object of the logged user.

@return User The object of the authenticated user, otherwise NULL.

@see User

if ($user = $api->getUser()) {
    echo $user->getUsername() . ", " . $user->getPassword();
}

disconnect

Disconnect the authenticated user.

@return API This object to support chaining.

$api->disconnect();

getPlugin

Get the object matching the given plugin name.

In case there are not events matching the specified name, an exception will be thrown.

@param string $id The identifier of the plugin to get.

@return Event The plugin found.

/** @var E4J\VikRestaurants\API\Event */
$event = $api->getPlugin('connectionping');

getPluginsList

Returns a list containing all the registered plugins.

@return array A list containing all the supported plugins.

$plugins = $api->getPluginsList();

foreach ($arr as $plugin) {
    echo $plugin->getTitle() . "\n";
}

hasError

Returns true if an error has been raised.

@return bool True in case of error, otherwise false.

if ($api->hasError()) {
/* an error has been thrown */
}

getError

Get the last error caught and clean it.

@return Error The error object if exists, otherwise NULL.

@see Error

if ($api->hasError()) {
$err = $api->getError(); /* returns an error */
$err = $api->getError(); /* returns NULL (error cleaned)*/
}

has

Check if the specified key is set in the configuration.

@param string $key The configuration key to check.

@return bool True if exists, otherwise false.

if ($api->has('apimaxfail')) {
/* the setting exists */
}

get

Get the configuration value of the specified setting.

@param string $key The configuration key to check.
@param mixed $def The default value if not exists (default null).

@return mixed The configuration value if exists, otherwise the default specified value.

$max_fail = $api->get('apimaxfail', 0); /* this setting always exists */ 
var_dump($max_fail); /* an integer higher than 0 */

$something = $api->get('something', null); /* this setting does not exist by default */
var_dump($something); /* null */

set

Set the configuration value for the specified setting.

@param string $key The key of the configuration value to set.
@param mixed $val The configuration value to set.

@return APIs This object to support chaining.

$fake = $api->get('fake', null);
var_dump($fake); /* null */
$api->set('fake', 100); $fake = $api->get('fake', null); var_dump($fake); /* 100 */

connect

Connect the specified user to the API framework.

In case the login fails, here is evaluated a permanent BAN.
Otherwise the MANIFEST of the user is updated and the BAN is reset.

This method can raise the following internal errors:

  • 100 - Authentication Error (Generic)
  • 101 - The username is empty or invalid
  • 102 - The password is empty or invalid
  • 103 - The username and password do not match
  • 104 - The account is blocked
  • 105 - The source IP is not authorised

@param User $user The object to represent the user login.

@return bool True if the user is accepted, otherwise false.

@see User

$input = JFactory::getApplication()->input;

$username = $input->getString('username');
$password = $input->getString('password');

$user = new \E4J\VikRestaurants\API\Framework\BasicAuthUser($username, $password);

if (!$api->connect($user)) {
    /* user is not authenticated */
    echo $api->getError()->toJSON();
    exit;
}
/* user is authenticated */

trigger

Trigger the specified event.
Accessible only in case the user is correctly connected.

This method can raise the following internal errors:

  • 100 - Authentication Error (Generic)
  • 201 - The event requested does not exists
  • 202 - The event requested is not valid
  • 203 - The event requested is not runnable
  • 204 - The event requested is not authorised
  • 500 - Internal error of the plugin executed (Generic)
  • xxx - A custom error raised from the plugin.

The response of the plugin is always echoed.

@param string $event The filename of the plugin to run.
@param array $args The arguments to pass within the plugin (default empty array).
@param bool $register True to register the response, otherwise false to ignore it (default true).

@return bool True if the plugin is executed without errors.

/* only if the user is connected */
$event_name = 'connectionping';
$args = []; if (!$api->trigger($event_name, $args)) { /* plugin error */ echo $api->getError()->toJSON(); exit; } /* successful response automatically echoed from the plugin */

dispatch

Dispatch the specified event to catch the response echoed from the plugin.
Accessible only in case the user is correctly connected.

This method can raise the following internal errors:

  • 100 - Authentication Error (Generic)
  • 201 - The event requested does not exists
  • 202 - The event requested is not valid
  • 203 - The event requested is not runnable
  • 204 - The event requested is not authorised
  • 500 - Internal error of the plugin executed (Generic)
  • xxx - A custom error raised from the plugin.

The response of the plugin is never echoed.
This method is useful to dispatch events during the execution of other plugins, in order to manipulate the response retrieved.

@param string $event The filename of the plugin to run.
@param array $args The arguments to pass within the plugin (default empty array).
@param bool $register True to register the response, otherwise false to ignore it (default true).

@return string The response echoed from the plugin on success.

@throws Exception In case of failure, an exception is thrown.

/* only if the user is connected */
$event_name = 'connectionping';
$args = [];

try {
	$json = $api->dispatch($event_name, $args);
} catch (Exception $e) {
    /* plugin error */
    echo (new \E4J\VikRestaurants\API\Error($e->getCode(), $e->getMessage()))->toJSON();
    exit;
}

/* successful response retrieved from the plugin */
$api->output($json);
exit;

Joomla Core

In case you are not an expert Joomla Developer, here you can find a few tips to use the Joomla Framework at best.

Factory

2.5 3.x

Joomla Platform Factory class.
It is used to instantiate quickly the main Joomla classes.

@abstract This class cannot be instantiated.

getApplication

2.5 3.x

Get an application object.
Returns the global JApplicationCms object, only creating it if it doesn't already exist.

@param mixed $id A client identifier or name (default null).
@param array $config An optional associative array of configuration settings (default empty array).
@param string $prefix Application prefix (default "J").

@return JApplicationCms

@see JApplicationCms

/* do not specify any parameter */
$app = JFactory::getApplication();

getDbo

2.5 3.x

Get a database object.
Returns the global JDatabaseDriver object, only creating it if it doesn't already exist.

@return JDatabaseDriver

@see JDatabaseDriver

$dbo = JFactory::getDbo();

getMailer

2.5 3.x

Get a mailer object.
Returns the global JMail object, only creating it if it doesn't already exist.

@return JMail

@see JMail

$mailer = JFactory::getMailer();

Application

2.5 3.x

Joomla! CMS Application class.

@see JFactory::getApplication()

/* get application */
$app = JFactory::getApplication();

/* get input (request variables handler) */
$input = $app->input;

/* get configuration value */
$list_limit = $app->get('listlimit', 10);

/* enqueue an information message */
$app->enqueueMessage('Information message');

/* enqueue an error message */
$app->enqueueMessage('Error message', 'error');

/* redirect the user to the specified internal URL */
$app->redirect('index.php');
$app->redirect('index.php?option=[COMPONENT_NAME]&task=[TASK_NAME]');

Database

2.5 3.x

Joomla Platform Database Driver class.

@abstract This class cannot be instantiated.

@see JFactory::getDbo()

How to: SELECT

/* get database object */
$dbo = JFactory::getDbo();

/* get list limit for queries */
$list_limit = JFactory::getApplication()->get('listlimit', 10);

/* it is possible to launch a plain query */
$q = "SELECT * FROM `#__users` WHERE `block`=1 ORDER BY `name` ASC";

/* or it is possible to create the query through an apposite builder */
$q = $dbo->getQuery(true)
	->select('*')
	->from($dbo->qn('#__users'))
	->where($dbo->qn('block') . ' = 1')
	->order($dbo->qn('name') . ' ASC');

/* setup the query with limit */
$dbo->setQuery($q, 0, $list_limit);
/* or setup the query with no limit */
$dbo->setQuery($q);

/* execute the query */
$dbo->execute();

/* check query result */
if (!$dbo->getNumRows()) {
	/* no rows */
	exit;
}

/* get rows as associative array */
$assoc_list = $dbo->loadAssocList();

/* get rows as array of objects */
$objects_list = $dbo->loadObjectList();

/* get first row as associative array */
$assoc = $dbo->loadAssoc();

/* get first row as object */
$object = $dbo->loadObject();

/* get first column of first row */
$column = $dbo->loadResult();

How to: UPDATE

/* get database object */
$dbo = JFactory::getDbo();

/* it is possible to launch a plain query */
$q = "UPDATE `#__users` SET `block`=0 WHERE `block`=1";

/* or it is possible to create the query through an apposite builder */
$q = $dbo->getQuery(true)
	->update($dbo->qn('#__users'))
	->set($dbo->qn('block') . ' = 0')
	->where($dbo->qn('block') . ' = 1');

/* setup the query with no limit */
$dbo->setQuery($q);

/* execute the query */
$dbo->execute();

/* check the number of affected rows */
if ($aff = $dbo->getAffectedRows()) {
	echo "Affected rows " . $aff . "\n";
}

How to: INSERT

/* get database object */
$dbo = JFactory::getDbo();

$note = new stdClass;
$note->user_id = 1;
$note->subject = 'Subject';
$note->body = '<p>Notes body.</p>';
$note->state = 1;

/* it is possible to launch a plain query */
$q = "INSERT INTO `#__user_notes` (`user_id`, `subject`, `body`, `state`) VALUES (" .
$note->id . ", " .
$dbo->quote($note->subject) . ", " .
$dbo->quote($note->body) . ", " .
$note->state . ")";

/* or it is possible to create the query through an apposite builder */
$q = $dbo->getQuery(true)
	->insert($dbo->qn('#__users'))
	->columns(array(
		$dbo->qn('user_id'), $dbo->qn('subject'), $dbo->qn('body'), $dbo->qn('state')
	))
	->values(array(
		$note->id . ", " . $dbo->quote($note->subject) . ", " . $dbo->quote($note->body) . ", " . $note->state
	));

/* setup the query with no limit */
$dbo->setQuery($q);

/* execute the query */
$dbo->execute();

/* get last insert ID */
$note->id = $dbo->insertid();
if ($note->id <= 0) {
	/* INSERT failed */
}

It is possible to use also the code below in order to use a simplified INSERT query.

/* get database object */
$dbo = JFactory::getDbo();

/* the properties of the object MUST match the table columns */
$note = new stdClass;
$note->user_id = 1;
$note->subject = 'Subject';
$note->body = '<p>Notes body.</p>';
$note->state = 1;

/* build query and insert */
$res = $dbo->insertObject('#__user_notes', $note, 'id'); // table name, object to insert, primary key

/* check query result */
if (!$res || $note->id <= 0) {
	/* INSERT failed */
}

How to: DELETE

/* get database object */
$dbo = JFactory::getDbo();

$match = 'John';

/* it is possible to launch a plain query */
$q = "DELETE FROM `#__users` WHERE `name` LIKE " . $dbo->quote("%" . $match . "%");

/* or it is possible to create the query through an apposite builder */
$q = $dbo->getQuery(true)
	->delete($dbo->qn('#__users'))
	->where($dbo->qn('name') . ' LIKE ' . $dbo->quote("%" . $match . "%"));

/* setup the query with no limit */
$dbo->setQuery($q);

/* execute the query */
$dbo->execute();

Input

2.5 3.x

Joomla! Input Base class.
This is an abstracted input class used to manage retrieving data from the application environment.

@see JApplicationCms

How to: SUPERGLOBAL Handlers

/* get application input */
$input = JFactory::getApplication()->input;

/* get $_REQUEST object handler */
$request = $input; /* or $input->request */;

/* get $_POST object handler */
$post = $input->post;

/* get $_GET object handler */
$get = $input->get;

/* get $_SERVER object handler */
$server = $input->server;

/* get $_FILES object handler */
$files = $input->files;

/* get $_COOKIE object handler */
$cookie = $input->cookie;

How to: Available Filters

$input = JFactory::getApplication()->input;

/* get string */
$string = $input->getString('name', ''); /* "something" */

/* get integer */
$int = $input->getInt('name', 0); /* 100 or -15 */

/* get unsigned integer */
$uint = $input->getUint('name', 0); /* 30 (not negative numbers) */

/* get float */
$float = $input->getFloat('name', 0.0); /* 70.50 or -100.25 */

/* get bool */
$bool = $input->getBool('name', false); /* true or false */

/* get unfiltered data */
$raw = $input->getRaw('name', null) /* return original value */

/* get all SUPERGLOBAL variables */
$arr = $input->getArray();

The example code below is used to retrieve some fields sent via POST method and to INSERT them in a fake database table.

/* get application */
$app = JFactory::getApplication();

/* get application input */
$input = $app->input;

/* get $_POST handler */
$post = $input->post;

/* get vars */
$obj = new stdClass;

$obj->name = $post->getString('name', '');
$obj->amount = $post->getFloat('amount', 0.0);
$obj->published = $post->getBool('published', false);
$obj->ordering = $post->getUint('ordering', 1);

/* get $_SERVER handler */
$server = $input->server;

/* get user IP ADDRESS */
$obj->ip_addr = $server->getString('REMOTE_ADDR', '');

/* insert object in database */
$dbo = JFactory::getDbo();

$res = $dbo->insertObject('#__fake_table', $obj);

if (!$res || $obj->id <= 0) {
	$app->enqueueMessage('Impossible to insert the object!', 'error');
} else {
	$app->enqueueMessage('Object inserted successfully!');
}

/* redirect the user to the fake_table task */
$app->redirect('index.php?option=com_example&task=fake_table');

Mailer

2.5 3.x

Email class.
Provides a common interface to send email from the Joomla! Platform.

@see JFactory::getMailer()

/* get mailer object */
$mailer = JFactory::getMailer();

/* set FROM [sender name, sender address] */
$mailer->setSender(array('test', 'test@mail.com'));

/* add TO e-mail address(es) */
$mailer->addRecipient('customer.one@mail.com');
$mailer->addRecipient('customer.two@mail.com');

/* add REPLY TO e-mail address */
$mailer->addReplyTo('test@mail.com');

/* set e-mail SUBJECT */
$mailer->setSubject('E-Mail Subject');

/* set TEXT/PLAIN body */
$mailer->isHTML(false);
$mailer->setBody('This body does not contain HTML tags.');

/* set HTML body */
$mailer->isHTML(true);
$mailer->setBody('<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	</head>
	<body>
		This body contains <strong>HTML</strong> tags.
	</body>
</html>');

/* set ENCODING */
$mailer->encoding = 'base64'; /* needed to avoid encoding issues */

/* add ATTACHMENT(s) */
$images = array('img-001.jpg', 'img-002.jpg', 'img-003.jpg'); 
foreach ($images as $img) {
	$img_path = JPATH_SITE . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . $img;
	if (file_exists($img_path)) {
		$mailer->addAttachment($img_path);
	}
}

/* send e-mail to the specified TO address(es) */
$res = $mailer->Send();

if ($res) {
	/* e-mail sent correctly */
}

Integration

Here are the steps to follow to understand how an API Plugin for VikRestaurants can be developed.

In this guide, we will build an API Plugin to retrieve the rooms and all the relative tables from your VikRestaurants.

Problem Description

For example, our CLIENT software needs to retrieve the rooms & tables from VikRestaurants in JSON format.

The JSON structure below seems to be appropriate for our case:

[
    {
        "id": 1,
        "name": "Room A",
        "published": 1,
        "description": "",
        "tables": [
            {
                "id": 1,
                "name": "Table A1",
                "min": 2,
                "max": 4,
                "published": 1
            },
            {
                "id": 2,
                "name": "Table A2",
                "min": 4,
                "max": 6,
                "published": 0
            }
        ]
    },
    {
        "id": 2,
        "name": "Room B",
        "published": 1,
        "description": "",
        "tables": [
            {
                "id": 3,
                "name": "Table B1",
                "min": 4,
                "max": 8,
                "published": 1
            }
        ]
    },
    {
        "id": 3,
        "name": "Room C",
        "published": 0,
        "description": "",
        "tables": []
    }
]

We also may consider to implement a parameter to ignore/accept the unpublished rooms and tables. 

Getting Started

Before to start coding the API Plugin it is required to enable the API Framework and create a new Application for the authentication.

Enable Framework

From the configuration of VikRestaurants in the back-end, under the Applications section, we have to make sure the APIs setting is enabled.

New Application

Once the framework is enabled, the See Users List button should become visible. Click this button to see the list of the users and press the NEW button to create an Application.

Feel free to configure your application as displayed in the image below (you can ignore all the other fields).

@see API Users for further details.

Test Authentication

It is possible to test the authentication of your app by using one of the pre-installed plugins. You can see the list of plugins by clicking the See Installed Plugins button from the Applications section of the configuration.

Find the Connection Ping plugin and press its icon to see the full description.

Insert your credentials in the username and password fields, click outside of the input to refresh the URL properly and finally click it.

 The system should open the clicked URL in a different tab of your browser. Make sure the response you get is equals to the one below.

{
	"status": 1
}

Plugin Skeleton

After a successful authentication, we can proceed with the creation of an empty API Plugin.

File Creation

Create a new file on your local computer and rename it as:

RoomsTablesPluginAPI.php

Paste Base Code

Copy & paste the code below into the file created previously.

<?php

// No direct access to this file
defined('_JEXEC') or die('Restricted access');

use E4J\VikRestaurants\API\Event;
use E4J\VikRestaurants\API\Response;

class RoomsTablesPluginAPI extends Event
{
    // parent override
    protected function execute(array $args, Response $response)
    {

    }

    // parent override
    public function alwaysAllowed()
    {
        return false;
    }

    // parent override
    public function getShortDescription()
    {
        return '';
    }

    // parent override
    public function getDescription()
    {
        return '';
    }
}

FTP Upload

Access to your server via FTP (e.g. by using FileZilla) and upload the RoomsTablesPluginAPI.php file onto the directory below:

.../components/com_vikrestaurants/helpers/src/libraries/API/Plugins/

Visit the Plugins List from the back-end of VikRestaurants and make sure the Rooms Tables plugin is visible.

The  icon is disabled because the Event::getDescription() method that we have initially defined is returning an empty string.

To test our new empty plugin, try to launch this URL (replace the ellipsis with your domain):

.../index.php?option=com_vikrestaurants&task=apis&event=roomstables&username=app&password=ABCD1234

and make sure the result is matching the one below:

{
    "errcode": 500,
    "error": ""
}

We are getting an error (this is normal) because we haven't verified yet the status of the response within the Event::execute() method.

@see Response::setStatus() to understand how a plugin response can be confirmed.

If you are planning to implement a new API event through a Joomla! plugin, please refer to the link below:
https://extensionsforjoomla.com/help/knowledge-base/vik-restaurants/plugin-events/api

Plugin Development

Now, we can proceed with the development of the event that the plugin should handle.

All the blocks of code described in this section have to be included within the Event::execute() method of our plugin.

protected function execute(array $args, Response $response)
{
    /* insert code here */
}

Arguments

As mentioned in the Problem Description section of this tutorial, we should implement the code to retrieve the argument to accept or ignore the unpublished rooms and tables.

@uses JApplicationCms
@uses JInput

/* get application input */
$input = JFactory::getApplication()->input;

/* 0 to load all, 1 to load only published rooms and tables */
$published = $input->getUint('published', 0);

MySQL Query

Before to retrieve the rooms and tables from the database, we can start thinking about the query to use.

SELECT `r`.`id`, `r`.`name`, `r`.`published`, `r`.`description`,
`t`.`id` AS `id_table`, `t`.`name` AS `table_name`, `t`.`min_capacity`, `t`.`max_capacity`, 
`t`.`published` AS `table_published`
FROM `#__vikrestaurants_room` AS `r`
LEFT JOIN `#__vikrestaurants_table` AS `t` ON `r`.`id`=`t`.`id_room`
WHERE `r`.`published`>=[PUBLISHED] AND `t`.`published`>=[PUBLISHED]
ORDER BY `r`.`ordering` ASC, `t`.`name` ASC;

Before to launch the query, we have to replace all the #__ occurrences with the real prefix of the database (e.g. xyz_).

It is also needed to replace the [PUBLISHED] parameter with 0 or 1 values.

The result of the query, launched from your MySQL Panel, should look like the one below.

Run PHP Query

Once the QUERY seems to return the proper result, we can implement its execution via PHP.

@uses JDatabaseDriver

/* get database object */
$dbo = JFactory::getDbo();

/* get empty query */
$q = $dbo->getQuery(true);

/* build query */
$q->select('`r`.`id`, `r`.`name`, `r`.`published`, `r`.`description`')
    ->select('`t`.`id` AS `id_table`, `t`.`name` AS `table_name`, `t`.`min_capacity`')
    ->select('`t`.`max_capacity`, `t`.`published` AS `table_published`')
    ->from($dbo->qn('#__vikrestaurants_room', 'r'))
    ->join('LEFT', $dbo->qn('#__vikrestaurants_table', 't') . ' ON `r`.`id`=`t`.`id_room`')
    ->where('`r`.`published` >= ' . $published . ' AND `t`.`published` >= ' . $published)
    ->order('`r`.`ordering` ASC, `t`.`name` ASC');

/* setup query */
$dbo->setQuery($q);
/* get fetched rows */
$rows = $dbo->loadObjectList();

/* validate result */
if (!$rows) {
    /* verify the response */
    $response->setStatus(1);
    /* return empty result */
    return $rows;
}

Manipulate Response

To complete the plugin, we have to manipulate the result of the query and adapt it to the required JSON structure.

/* manipulate response */
$rooms = [];

$last_room_id = -1;

foreach ($rows as $row) {

    if ($last_room_id != $row['id']) {
        /* push room */
        $rooms[] = array(
            "id" => $row['id'],
            "name" => $row['name'],
            "published" => $row['published'],
            "description" => $row['description'],
            "tables" => [],
        );

        /* store ID last room pushed */
        $last_room_id = $row['id'];
    }

    if (!empty($row['id_table'])) {
        /* push table in last room */
        $rooms[count($rooms) - 1]['tables'][] = array(
            "id" => $row['id_table'],
            "name" => $row['table_name'],
            "min" => $row['min_capacity'],
            "max" => $row['max_capacity'],
            "published" => $row['table_published'],
        );
    }
}

/* verify the response */
$response->setStatus(1);
/* write some logs for the administrator */
$response->setContent('Fetched #' . count($rooms) . ' rooms.');

/* return fetched result */
return $rooms;

Plugin Response

The plugin seems to be ready. We can start testing the response we get.

Client

The client that is calling the event should retrieve a JSON string like the one below.

[
    {
        "id": "1",
        "name": "Main Room",
        "published": "1",
        "description": "",
        "tables": [
            {
                "id": "1",
                "name": "t1",
                "min": "2",
                "max": "2",
                "published": "1"
            },
            {
                "id": "2",
                "name": "t2",
                "min": "2",
                "max": "4",
                "published": "0"
            },
            {
                "id": "3",
                "name": "t3",
                "min": "2",
                "max": "4",
                "published": "1"
            },
            {
                "id": "4",
                "name": "t4",
                "min": "2",
                "max": "4",
                "published": "1"
            },
            {
                "id": "5",
                "name": "t5",
                "min": "2",
                "max": "4",
                "published": "1"
            },
            {
                "id": "6",
                "name": "t6",
                "min": "2",
                "max": "4",
                "published": "1"
            },
            {
                "id": "7",
                "name": "t7",
                "min": "4",
                "max": "8",
                "published": "1"
            }
        ]
    },
    {
        "id": "2",
        "name": "Garden",
        "published": "1",
        "description": "",
        "tables": [
            {
                "id": "9",
                "name": "g1",
                "min": "2",
                "max": "6",
                "published": "1"
            },
            {
                "id": "10",
                "name": "g2",
                "min": "2",
                "max": "10",
                "published": "1"
            }
        ]
    },
    {
        "id": "4",
        "name": "Private",
        "published": "1",
        "description": "",
        "tables": [
            {
                "id": "11",
                "name": "p1",
                "min": "2",
                "max": "20",
                "published": "1"
            }
        ]
    }
]

Admin

From the back-end of VikRestaurants it is possible to check the logs (if any) of the event called by the client.

By clicking the icon, it is possible to see the logs related to the performed event.

Application Usage

How should the plugins be handled from your Applications?

Here is a simple example that uses a cURL connection to retrieve the rooms and tables from a different website/server.

/* define params */
$params = array(
    "event" => "rooms_tables",
    "username" => "app",
    "password" => "ABCD1234",
);

/* specify your domain URL */
$ch = curl_init('http://domain.com/index.php?option=com_vikrestaurants&task=apis&tmpl=component');

/* configure cURL connection */
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

/* connect to the URL and retrieve the JSON response */
$json = curl_exec($ch);

/* evaluate error */
if ($errno = curl_errno($ch)) {
    echo "Error ($errno) : " . curl_error($ch) . "\n";
    exit;
}

/* otherwise decode the response */
$rooms = json_decode($json);

echo print_r($rooms, true) . "\n";

Errors

Here is the list of all the possible errors that can be raised by the API Framework.

Authentication

All the errors raised during the authentication of the application.

Generic Authentication Error

100

Something gone wrong while trying to authenticate the application.
You should see this error only if you are triggering an event (via code) without the authentication, such as the example below.

FrameworkAPIs::getInstance()->trigger('connection_ping');

Username Error

101

The username provided is empty or invalid. Make sure the connection URL contains the &username=[USERNAME] parameter.
@see also Credentials Requirements to check if your username matches the system requirements.

Password Error

102

The password provided is empty or invalid. Make sure the connection URL contains the &password=[PASSWORD] parameter.
@see also Credentials Requirements to check if your password matches the system requirements.

Invalid Credentials

103

The username and password do not match. Your application may not exist or the credentials may not be correct.

Account Blocked

104

The account is blocked. The application you are using is blocked, you need to re-enable it from the back-end of VikRestaurants.
Even if you are seeing this error, the username and password you are using are correct.

Otherwise, the IP Address from which you are trying to connect may be banned. In this case, check the Banned List from the back-end of VikRestaurants.

@see Banned List

Unauthorised Source IP

105

The IP Address from which you are trying to connect is not allowed. Make sure the list of allowed IPs for this application is empty or includes your origin address. Even if you are seeing this error, the credentials are correct.

@see Allowed IPs

Event Triggering

All the errors raised during the triggering of the event. 

File Not Found

201

The event requested does not exist. Make sure the event you are requesting is listed in the plugins folder.

 @see API Plugins Path

Event Not Valid

202

The event requested is not valid. Make sure the classname of the event follows the standard of the EventAPIs class.

 @see Event

Event Not Runnable

203

The event requested is not runnable. Make sure the event you are requesting inherits correctly the Event class.

 @see Event

Unauthorised Event

204

The event requested is not authorised. Make sure your application owns the permissions to run the event you are requesting.

 @see API Users Plugin Rules

Plugin Response

The errors raised during the plugin execution.

Generic Plugin Error

500

If you are seeing this error, it means that you are correctly running the plugin but there is still something wrong.

If the error message is empty, make sure you are verifying the response within the code.

@see Response::setStatus()

Otherwise it means that the plugin is throwing an own error.

Custom Plugin Error

xxx

Any other error code you see should be considered as a custom error thrown by the plugin.
In order to understand the reason of this error, you should read the description of the plugin that you are running.

 @see API Plugins Description