API functionality allows remote systems to access your BookyFlow database through \"API Clients\".
You could think of these as sub-accounts of your CMS account, ones which can be given individual rights (or permissions ). This allows third party systems to \"talk\" to the BookyFlow system. It provides a platform agnostic framework, enabling diverse applications such as mobile apps or other websites to pull information from your database in a way that is safe and secure for you.
Why does BookyFlow have its own REST API functionality, and not use the CMS's REST API features?
The first iteration of BookyFlow' REST API was created in early 2016, and has been steadily built upon ever since. Having our own functionality for the feature allows us to have documentation and API endpoints that are common to both CMSs without significant duplication of code. If it works in Joomla, it works in WordPress, and vice versa.
####### API Clients
An API Client is a Client ID/Secret key pair that can be created by all registered users. This key pair is then used by remote sites to generate access tokens. Access tokens are sent with (most) REST API requests.
You can create and manage API key pairs in frontend > BookyFlow > Account Details > API Key management.
When you create an API Client you must give them permission to access certain features on the server. These permissions are called Scopes.
Any registered user of the website can create API Clients, however, normal guest users will not have access to property-related functionality.
The REST API uses the OAuth2 standard for giving Clients permission to access the system. All available REST API Endpoints are available at https://api.bookyflow.net
Users can create REST API keys manually. You can also use the Implicit flow to redirect sites to the BookyFlow site so that remote sites can be given API key access.
####### Endpoints
Not all BookyFlow installations have the same endpoints available. To install an endpoint plugin go to the BookyFlow plugin manager and visit the API Features tab. Each of the plugins in the tab deliver endpoints.
Most endpoints demand that the client accessing them has been given rights to access their scope.
####### Using Postman to access the API
You can use POSTMAN to talk to the BookyFlow API.
Postman is an excellent tool that allows you to send API queries to your BookyFlow installation. If you\'d like to test endpoints that already exists, or if you\'ve added new endpoints, then this is a great way to test the code without having to build an entire app.
NB: The screenshots here are from an older version of Postman. One of the reasons I hate using screenshots from external services is because inevitably the user interface of the third party service changes, immediately rendering my documentation out of date. If you're in any doubt about using Postman, please refer to their documentation.
In a nutshell, what you're going to do here is use your REST API client id and secret to request a token (a long string of characters) from the server. Once you have requested that token, you can then send that token in the headers of your API request to the endpoint.
{width="4.895609142607174in"
height="2.5996872265966755in"}
We\'ll start with a simple example, getting a user\'s favourite properties. GET
Here you can see the basic panel in Postman. The endpoint to get the user\'s favourites is
/favourites/ ( trailing slash is important ), so to call BookyFlow from my localhost, and get the favourites for the user who created this API key set, you would set the method to GET, and the url to http://localhost/quickstart/bookyflow/api/favourites/
{width="6.401865704286964in"
height="2.6866666666666665in"}
Before we can get the favourites, we first need to get an access token, so if you need to click on the Authorization tab, and choose OAuth2 from the dropdown.
{width="5.34375in" height="1.2083333333333333in"}
Click \"Get New Access Token\", you\'ll see a popup like this. Give the token a name, set the url ( trailing slash! ) and enter the Client ID and Client Secret you created earlier. Next set the Grant Type to \"Client Credentials\". Finally, click \"Request Token\".
{width="6.506160323709536in" height="4.1810411198600175in"}
Now you\'ll see that the token has been pulled from the server and is available in the Authorization tab. I\'ll click \"Localhost\" and the token\'s details will appear to the right.
{width="5.645833333333333in" height="3.2083333333333335in"}
These are the token\'s details, complete with the scope that it supports. in \"Add token to\" change the dropdown to \"Header\" and then click \"Use Token\".
{width="5.395833333333333in"
height="3.3645833333333335in"}
Ok, we\'re ready to get the user\'s favourites. Click \"Send\".
{width="6.426268591426072in" height="0.6396872265966754in"}
In the panel down below, you\'ll see the response, which is exactly as we expected.
{width="3.7916666666666665in"
height="2.9166666666666665in"}
POST
Ok, that was a good first step. Let\'s now go ahead and test posting data to our server. We\'ll change the method to POST, so to send a new favourite property uid we\'ll first change the method to POST, and then add the id of the property we want to the end of the url, like this :
{width="6.417540463692038in"
height="0.6890616797900262in"}
Again, you will need a token, so if you haven\'t already done that, get and use your token.
{width="6.502287839020123in" height="2.525207786526684in"}
Ok, here are some of my favourites already. The \"Some Camping Area\" property is the property with id 7.
{width="6.478980752405949in"
height="1.7339577865266842in"}
So now, when I click Send, this is the response I\'ll see in Postman.
{width="3.3541666666666665in"
height="1.4583333333333333in"}
And when I reload the page, here you can see that \"Some Camping Area\" has been added to my favourites.
{width="6.467742782152231in" height="3.27875in"}
####### Creating Clients
{width="3.256793525809274in" height="1.12375in"}
API key management is done via the Account Details menu option. This option is available to any registered user, however a user\'s state will depend on what Permissions they can give to that client.
{width="6.443639545056868in" height="1.4904155730533684in"}
When an API client is created, a Client ID and Secret are automatically created for the client. These are what you will need to use when programming apps to talk to the BookyFlow API.
{width="4.33931539807524in" height="4.485in"}
When you create a Client you must give them certain permissions, for example a simple Guests app would need to be able to read information about the guest, like their bookings information, modify their favourites etc. A more extensive app for property managers would need to be able to Get and Modify booking information.
####### Communicating with the BookyFlow API
Ok, so you\'ve decided that you want to build an App on another website which will then call your BookyFlow installation to pull information out for display elsewhere. You\'ve created an API client id and secret, so how do you use that to talk to BookyFlow?
First a little background.
BookyFlow uses the OAuth2 standard to provide limited access to functionality through a REST API which uses the HTTP protocol to communicate.
Many acronyms, much technical.
I\'ve always found that the best way to learn is to find practical applications for a subject, so let\'s just dive right in and do that. Make sure that the client you\'ve created has the right to Get the user\'s email address. If you\'re already familiar with using REST APIs, then you can probably just skip to the bottom of the example script.
Now, your BookyFlow installation is at https://www.example.com. Assuming that you\'re running BookyFlow 9.8 or later, then the path to the API is through https://www.example.com/bookyflow/api
On another server ( localhost is fine) create a script index.php, and in that put
Next we\'ll create an array which will be posted to the server.
Then we\'ll send our request to the BookyFlow server to get our access token, which we\'ll need if we want to pull any information off the server.
The token will be in the \$response variable, so let\'s check it, and if it\'s set we can go ahead and use it to pull information from the server.
Now we can send our request for the email address
And voila, if we dump the contents of \$result then we\'ll have the email address.
If the client credentials were wrong, then you\'ll have something like
{\"error\":\"invalid_client\",\"error_description\":\"The client credentials are invalid\"} in the response.
The full script, with more error checking
\$data );
\$response = json_decode(\$token_request[\'result\']); if (isset(\$response->access_token))
{
\$token = \$response->access_token;
// Now that we\'ve got the access token, we can request access to the
API
\$result = query_remote_server( \$server , \$token , \"GET\" , \"email\" , array() );
if (\$result[\'result\'] != \"\")
{
var_dump(\$result[\'result\']);exit;
}
else
}
else
{
{
var_dump(\$result[\'status\']);exit;
}
throw new Exception(\"Error, json & token not returned \".\$token_request);
}
}
catch(Exception \$e)
{
echo \'Message: \' .\$e->getMessage();
}
function query_remote_server( \$server , \$token=\"\" , \$method=\"GET\" ,
\$request =\"\" , \$data=array() )
{
\$ch = curl_init(\$server.\$request); switch ( \$method )
{
case \'POST\':
curl_setopt(\$ch, CURLOPT_POST, true); curl_setopt(\$ch, CURLOPT_POSTFIELDS,
http_build_query(\$data));
break; case \'DELETE\':
####### Creating your own API plugins
{width="2.2916666666666665in"
height="1.6979166666666667in"}
I have deliberately made it as simple as possible to create your own API plugins for BookyFlow.
The functionality your plugin supplies can be as simple or as complicated as you need it to be, but there are a few files that are required for it to run :
All BookyFlow plugins need a plugin_info.php file to describe the system to the plugin manager.
A 00005 script ( for example j00005api_feature_favourites.class.php ) to include a language file. Most BookyFlow plugins that require a language file will have one of these, just look at one of them if in any doubt as to how they work.
A scopes.json file, which is a JSON description of the permissions the user needs to give to the Client so that they can access the plugin\'s functionality. These scopes appear in the API key management page when you're editing a client.
Optional : auth_free.json
In the Favourites example I am using prepared statements. This means that the CMS and BookyFlow frameworks are not included by the API, which makes this particular endpoint extremely fast. The huge majority of BookyFlow endpoints however use this line
Which includes both the CMS and the BookyFlow frameworks. It's slower, however it means that we can use existing BookyFlow functionality like this
to perform complex tasks without reinventing the wheel.
scopes.json
Here is an example of what\'s in a scopes.json file.
_OAUTH_SCOPE_CATEGORY_USER refers to the parent category, currently this will either be
_OAUTH_SCOPE_CATEGORY_USER or _OAUTH_SCOPE_CATEGORY_PROPERTIES (
\"User permissions\" & \"Property permissions\" ). This is purely for separating functionality in the Edit Client page.
Scope is important. In this example we\'ve stuck with two simple descriptions \"favourites_get\" and \"favourites_set\", which means that only API clients that have been given permission to \"Get favourites\" can download the property uids that the user has set as a favourite. In theory you could have as many or as few scopes as you want, but we recommend you have at least one \"get\" scope and one \"set\" scope.
Any registered user can give rights to API Clients in the user \"user_type\" category, but only property managers can give API Clients \"manager\" access rights. There is also a user_type \"super\", which allows you to create functionality that\'s only available to Super Property Managers.
_OAUTH_SCOPE_FAVOURITES_GET & _OAUTH_SCOPE_FAVOURITES_GET_DESC refer
to language strings that are stored in the language file that are included when you include a 00005 script with your plugin (see above).
There are several sub-directories, which is where the BookyFlow functionality will look for your API scripts. Ignoring the \"language\" directory where your language file(s) are kept, the directories are DELETE, GET, POST & PUT. If an app is sending a Delete request, then the script that is run will be in the DELETE directory. \"The primary or most-commonly-used HTTP verbs (or methods, as they are properly called) are POST, GET, PUT, and DELETE. These correspond to create, read, update, and delete (or CRUD) operations, respectively.\" See http://www.restapitutorial.com/lessons/httpmethods.html
auth_free.json
In BookyFlow 9.9.6 I added support for Authentication Free access to the REST API. This gives plugins the option to declare their endpoints as \"authentication free\" through an auth_free.json file.
The addition of this functionality opens up the scope of user access to your BookyFlow REST API by allowing anybody to search and retrieve information from your site, if you have the appropriate API Feature endpoint plugins installed.
Clients accessing an authentication free API feature ( such as Api Feature Search ) will not need to supply a Client ID/Secret keypair.
The auth_free.json file will need to contain
Users creating client id/secret pairs will not be able to select this auth free api features through the Client creation page, because it\'s automatically available to everybody.
Note : individual endpoints in api features can still restrict access through the use of
####### Anatomy of an API endpoint plugin
The script in this example would be run when a GET request is sent to the API www.example.com/bookyflow/api/favourites/
This is the endpoint in its entirety
Let\'s look at the pertinent lines in the script to describe what they do :
This ensures that the call to the API has come through the BookyFlow API index.php and doesn\'t get called by itself. It is a security feature.
BookyFlow uses a micro-framework called Flight PHP ( http://flightphp.com/ ) because it\'s very small, fast and offers routing functionality. This example is basically a \"route\" script for Flight. To see the options that can be passed to a route, please see http://flightphp.com/learn#routing
Confirm that the client has the rights to access this scope.
Set up the response object.
This calls the PDO object that\'s already been setup for talking to mysql.
The next few lines set up a prepared query for mysql to pull the user\'s favourites from the database. Describing this is outside of the scope of this article.
These lines set the value of the response, depending on whether or not any data was found. Generally you\'ll probably only want to send two responses, either the requested data or a 204 response code, which confirms that the script ran correctly, but there\'s no data to return. This is the data that\'s returned in the \"Envelope\".
Every response is contained by an envelope. That is, each response has a predictable set of keys with which you can expect to interact. The json response of the envelope typically looks like
So in this example the \"data\" section will have \"ids\" which in turn will be an array of property uids.
{width="4.072916666666667in"
height="2.8229166666666665in"}
####### Envelope
API responses consist of an object with two values, data and meta.
You can disable the BookyFlow envelope by setting X-BOOKYFLOW-NO-ENVELOPE in the header and setting its value to 1. When you do that, only the data section is returned by the BookyFlow REST API.
####### Code reuse
It is possible for BookyFlow scripts to call BookyFlow through the call_self class, like so :
This assumes that the Client has Properties Get rights.
You don\'t want to constantly reinvent the wheel, BookyFlow already does a lot so to re-use the BookyFlow framework\'s code to do something you can call BookyFlow functions directly once you\'ve included the framework code, like so
For example, to use the search functionality we\'ve done this
which allows us to easily search by stars, as it returns a result that\'s easily manipulated in our own off-site code, the j06110ajax_search_composite is already capable of searching by stars.
> BookyFlow 10.7
Before attempting to use the Call Self functionality, you should use the API capability test first to ensure that BookyFlow can actually call local endpoints. Plugins like adminTools can impose very restrictive access to sub-directories, which can prevent BookyFlow from calling its own endpoints (and other sites calling in via the REST API).
In 10.7 a new class was included that allows you to test whether or not BookyFlow is able to perform API calls. If it returns false you should not attempt to call self because errors will likely be thrown.
####### Local tokens
Another new feature added in 10.7 is the ability for plugins to have local tokens.
Local tokens allow plugins to create tokens that can be used by the UI to call local endpoints. One example of a plugin that would use local tokens is the BookyFlow Messaging System.
The messaging system is in fact a suite of different plugins, which use local endpoints Here's an example of how it's used, from the Channel Management Framework plugin.
The token is then added to the Local Tokens showtime array so that the user will not see and/or be able to remove/edit them through the API key management page.
A minicomponent can then use the local token by doing something like
This allows the UI to use REST API endpoints, instead of querying functionality directly.
The purpose for this is that a plugin can be used either for testing local endpoints, or for developing functionality that can be used by both the local UI, and remote callers to the REST API. It negates the need for the UI plugin developer to build two different sets of functionality, instead the plugin's UI can use ajax to perform the same queries as a client accessing the endpoints.
The BookyFlow Messaging System is a good example of how this is done because the messaging system uses REST API endpoints almost exclusively. The local tokens are still restricted in the same way as tokens created in the API key management system, so scopes and token expiry are still respected. It's possible therefore for a mobile app developer to use the same set of endpoints as the JMS plugin uses, to communicate through the JMS with property managers.
####### REST API & Webhook case study
#######
Introduction
Here I will describe how the REST API and Webhooks can be used together to build a web service.
This functionality is very ambitious, to date I\'m not aware of any other self hosted booking portal for either Joomla or Wordpress that includes it. It sets BookyFlow apart from its competitors because it offers features that, until now, could only be enjoyed by the big SaaS booking businesses.
Both features saw considerable development in the second half of 2016, and as is always the way, development outpaces documentation. As a result, I will describe a case study of how these features can be used. First however I need to introduce you to the features, describe what they\'re intended for and any pertinent information I feel you\'ll need.
Webhooks and the REST API should be considered two sides of the same coin.
The REST API is designed to facilitate the ability of remote services to request data from, and update data to, a BookyFlow installation. This is achieved through a secure Oauth2 token based system that\'s tied to individual property managers. This allows individual managers to grant access to their own properties to remote services.
The Webhooks feature is designed to offer a framework that, again, a manager can configure to send notifications to remote services that they determine. Webhook notifications are small snippets of data, the payload normally only contains a handful of IDs of affected database rows. To query the complete change, the remote service would need to send a callback to the REST API on the server.
This all sounds very complicated, so let\'s simplify it somewhat
REST API receives stuff.
Webhooks sends stuff.
Security
I will not go into great detail here about security as it\'s discussed elsewhere in the manual. The important point to note is that Webhook configurations are assigned to property managers, not property uids. This is worth remembering as previous plugins in BookyFlow were tied to specific properties, but these features are instead linked to manager ids. This means that Webhook authentication is checked against the property manager/property uids cross reference table.
In BookyFlow, Super Property Managers are not typically given access to individual properties, instead they\'re automatically given access to all properties. Super Property Managers therefore usually don\'t have a record in the cross reference table for a given property. As a result, if a Super Manager creates a webhook then that webhook might not be triggered.
When working with the Webhooks, make sure that the user creating Webhooks is a \"normal\" property manager.
To clarify this, the current version of the Webhooks feature doesn\'t offer the Webhook configuration option in the menu to Super Property Managers.
REST API features are semi-restricted, it depends on the feature. For example, in this case study we\'ll use the API Features Webhooks plugin to answer callbacks from a remote service that\'s been notified of a change to a property in BookyFlow. That particular plugin checks to ensure that the manager who created the REST API keys has access to the properties being called, however there\'s an API Feature called \"Search\" which is a super property manager only API feature and isn\'t tied to any specific properties. That particular feature, however, doesn\'t offer any POST functionality, it\'s purely a search mechanism.
Discussion
One thing I\'ve learned over the years of working on BookyFlow is that not everybody wants everything on offer. For this feature some people will only want the REST API framework then they\'ll develop their own code to make use of that framework, others want everything handed to them on a plate, and others want to selectively choose what to use.
As a result, the REST API/Webhook framework has been designed to be modular, deferring to related plugins to provide the bulk of their functionality.
Both frameworks require installation of the API Core and Webhooks Core respectively.
REST API Core allows property managers to create API key pairs which will authorise remote services to access their property data. The Oauth2 authentication is bundled in BookyFlow itself, and doesn\'t need to be installed. This was done to ensure that endpoints would always be predictable and clean.
Webhooks Core allows property managers to create Webhook implementations, and actually triggers webhook notifications. Every webhook needs an authentication method, three are currently available : None, Basic and OAuth. These allow basic notification to remote services, however you\'re not limited to these methods. It\'s entirely possible that you might want to create your own implementation method, for example if your site\'s users predominantly use Home Away then you might want to build an integration with the Home Away API. This is why the Webhooks Core is only responsible for creating Webhooks and their configuration options, and triggering webhook integration scripts.
As previously mentioned, many of our users are developers who use BookyFlow as a foundation that they can use to build their own unique sites. They\'re skilled programmers in their own rights and BookyFlow is just a building block on the path to that goal. We encourage this and much of the functionality available should be considered as \"sample\" code on how to do things the right way in BookyFlow. The REST API/Webhook functionality is no different. There are so many different
PMSs and Channel Managers out there, it\'s impossible for us to write integrations for all of them, so it\'s my philosophy that it\'s better to give developers the tools to achieve those integrations in the minimum amount of time possible.
Basic Webhook/REST API setup
In this case study we will show you how to set up an installation of BookyFlow to send Webhook notifications and allow a remote server to respond to those calls.
You will need to install the REST API Core, API Feature Webhooks ( which is designed as a companion to the Webhooks feature, but can be used by any authorised remote service ) and the Webhooks Core plugins.
If you\'re using this feature for the first time and are just experimenting then you\'ll probably benefit from visiting our Public Snippets repository on Github and downloading the Simulation Webhook Responder. This simulation code is designed to receive a notification from BookyFlow, then to call BookyFlow\' REST API back to request the full details of the change. In reality remote services would selectively decide if the API Feature Webhooks endpoints are the relevant endpoints to call in the event of the notification, depending on their own particular setups, but for education purposes this is how we\'ll do it here.
In my installation of WAMP BookyFlow resides in /www/joomla_portal/ so I\'m going to copy the directory contents of simulation_webhook_responder into /www/simulation_webhook_responder This set of scripts will act as a receiver of Webhook calls, and automatically trigger calls back to the BookyFlow REST API to find out the details of the change that it was notified about.
In practice, the webhook responder would in fact be a remote service, such as a Property Management System\'s gateway or a Channel Manager\'s API. As it\'s impossible to predict how the data will actually be used by the far end, we\'re going to just receive calls and send them back to BookyFlow API to confirm that everything\'s working as expected.
All this stuff happens out of sight of a user so it can be tricky to track what\'s happening. To monitor what the webhook responder is doing I use a syslog application for Windows called \"Visual Syslog Server for Windows\". If you have your own tool for monitoring syslogs use that, or Visual Syslog. We\'ll get to configuration for connecting to it in a moment.
BookyFlow Installation
For the purposes of this case study I\'m going to assume that you\'re running the Joomla Portal Quickstart, as that\'s the most likely setup that will be used. You can do this on one of the other flavours of BookyFlow Quickstart, it doesn\'t need to be one of the Quickstarts either, any BookyFlow installation will do. Screenshots here however will be from the Quickstart Portal.
In this article I will assume that you\'re reasonably familiar with BookyFlow. I\'ll show you relevant screenshots so that you can ensure that you\'re following the steps as described, but on the whole you shouldn\'t struggle too much.
First visit the BookyFlow Plugin Manager and install the API Core, Api Feature Webhooks, Webhooks Core and Webhooks Authmethod None plugins.
{width="4.028415354330709in"
height="1.828124453193351in"}
Now go to Site Configuration and configure it so that the webhooks and API menu options are available through the frontend.
{width="3.8817541557305337in"
height="1.9877077865266841in"}
Next, create a new user in the Joomla ( or Wordpress ) user manager. I\'ll create mine and call them \"manager\".
{width="2.8356266404199477in" height="1.9985411198600176in"}
I\'ll use the BookyFlow user manager to make them a property manager, and give them access to Fawlty Towers and Best West Hotel ( property uids 1 & 2 respectively ).
{width="2.5177088801399825in" height="2.15875in"}
Now that you\'ve done this, you should see some new options under the Account menu option in the frontend manager\'s toolbar :
{width="1.9192104111986001in" height="1.755in"}
API key management is the link that you use to access the REST API configuration page where API keys are created. Webhooks is the link for Webhook configuration. We\'ll start off by creating an API key pair.
Click on API key management and click New. The Client ID and Secret are created for you. Make sure that you click the checkbox next to \"Get data as part of a webhook communication\", as this gives the App client rights to use the API Webhook Feature\'s functionality.
{width="4.223211942257218in"
height="2.199374453193351in"}
Save the key pair, and again in the Accounts menu click Webhooks and click New. Set the URL to http://localhost/simulation_webhook_responder/ and click Save.
{width="3.56369094488189in"
height="1.3985411198600175in"}
Responder setup
You\'re almost ready to start testing, but first we need to configure the responder script. Go into the simulation_webhook_responder directory in your WAMP/XAMPP/MAMP/LAMP public_html directory and open config.php.
{width="6.4203926071741035in"
height="0.9157283464566929in"}
Assuming you\'ve installed Visual Syslog, then you can probably leave the settings for that alone. In the Client ID and Secret fields, copy and paste the Client ID and Secret from the API key management -> Edit page into this document. The responder will need to know these details so that it can send requests back to the BookyFlow installation to pull the full details of the changed record.
The trailing slash in the server\'s url is important, don\'t forget it. Save the document and close it.
Testing
At this point we could start testing, but that would mean performing a lot of activities to trigger the webhook code built into BookyFlow. Fortunately, we don\'t need to do this, we can artificially trigger webhooks. Go back to our Github repository for the responder, and there you\'ll see a file called j00005fake_webhook_calls.class.php We can use this file to trigger webhook calls to happen every time a BookyFlow page is loaded.
Copy this file into your /bookyflow/remote_plugins/custom_code directory ( create the directory if it doesn\'t already exist ) and rebuild the registry. From now on, until you delete this file it\'ll run every time a BookyFlow page is loaded.
Open it in your editor and you\'ll see lots of ids. Here you\'ll need to do some of your own legwork, as every system is likely to be different. You\'ll need to identify contract ( booking ) uids, invoice uids etc as they exist on your own installation.
{width="5.214763779527559in"
height="1.378853893263342in"}
Once you\'ve modified this file and added your own Ids, then scroll down further, you\'ll see a variety of webhook triggers. Make sure they\'re all commented out, then uncomment
When you next visit any page in BookyFlow ( e.g. the Dashboard ) the webhook notification should appear in Visual Syslog and look something like this. Double click the last line to see the popup
which will contain the actual details of the response. You do not need to be logged in as a manager for this to happen.
{width="6.212284558180228in"
height="4.105415573053368in"}
What happened?
First the j00005fake_webhook_calls.class.php script created a fake webhook call that would normally be triggered if a contract was modified by a manager.
Next the Webhooks Core plugin used the Webhook integration to send a message to http://localhost/simulation_webhook_responder/ to tell it that the booking has been modified.
The index.php in http://localhost/simulation_webhook_responder/ called the BookyFlow API back, specifically the endpoint http://localhost/joomla_portal/bookyflow/api/webhooks/1/booking_modified/41 to request the updated details of the booking ( contract ).
When it received a response, it sent the contents of the response to Visual Syslog for you to analyse.
You can now go ahead and comment this section, and uncomment others in j00005fake_webhook_calls.class.php to continue testing.
####### Postscript
Why is this a good thing?
This functionality is the foundation of a new way of working with BookyFlow. As the Internet of Things evolves, applications like BookyFlow need to evolve to keep pace. Allowing remote services to interact automatically with BookyFlow without going through a web browser makes BookyFlow accessible to an uncountable variety of outside devices and services, anything from Channel Manager services to mobile apps to Property Management Systems, CRM systems and other things we haven\'t even thought of yet.
BookyFlow and you, forging ahead into the future together.
Do you want to access the BookyFlow functionality through a simple, well documented API? You can, easily, using the REST API.
The API is well documented here, and it provides you with a significant amount of flexibility to manipulate the system, but did you know that you can also access the API from within the same server?
Let\'s say that you\'re building a feature for your client, it\'s in another component/plugin on the same server. You want to access some BookyFlow data without making any changes to BookyFlow Core files. Here\'s how you do it.
The \"system\" user is a special REST API user that allows the system to call any installed REST API feature, see
https://github.com/WoollyinWalesIT/bookyflow/blob/master/libraries/bookyflow/classes/bookyflow_call_api. class.php#L109
If you need to send headers, you can do something like
And to POST data you could do something like
to build the POST data.
When you inspect (or dump) the content of the result you will see something like :
The REST API test tool in Wordpress says that I have to move files before I can use channel management features, why?
The correct location for BookyFlow files is in the /public_html/bookyflow/ directory, and the REST API routing requires that location for it to function correctly.
In recent years many Wordpress hosting services have started making it so that plugins cannot write directly to the /public_html directory. To resolve this BookyFlow can install its core files into the
/wp-content/plugins/bookyflow/bookyflow directory and it will work fine except for the REST API.
If you need to use the REST API, using FTP or a file manager you need to move the files in the
/public_html/wp-content/plugins/bookyflow/bookyflow/ directory to the public_html/bookyflow directory then empty the /public_html/bookyflow/temp/ directory.
Important : do not move any other files from the /public_html/wp-content/plugins/bookyflow/ directory. Directories like \"admin\", \"includes\" , \"public\" and files like \"bookyflow.php\", \"index.php\" etc should remain in the same place, only the files under /bookyflow/bookyflow should be moved.
Once you have made this change, double check the REST API test page. It should confirm that you can use the REST API now.
The BookyFlow Quickstarts are already set up with BookyFlow in the correct location. If you're struggling with any of this, please do consider installing them instead.
There are a number of reasons why the REST API test might fail.
It may well fail with a 500 error. To determine the cause you will need to check your PHP error log.
A number of settings you can try are as follows.
In your .htaccess in the root of your CMS\'s installation (typically /public_html/), try adding the following :
If you are calling your server through Postman or CURL on the command line and you\'re seeing the message \"\'No authentication is provided. Bearer missing from headers?\" then it\'s possible that you will need to modify Apache\'s configuration a little. To resolve this on my own server I logged into WHM as admin, Service configuration > apache configuration > Include editor and created a Pre Virtual Host include with the following :
and then restarted apache.
How do I change the layout in a BookyFlow template file?
First, a really quick description of how the template system in BookyFlow works
Before I answer that, I want to give you a quick rundown on the template system. It's not like other systems you might be used to, so a basic primer is in order.
Populating template elements
Look at top.html. It is called in this script
https://github.com/WoollyinWalesIT/bookyflow/blob/master/core-minicomponents/j00060toptemplat e.class.php
The line that adds the property name is
And in the template file it's used like this :
{PROPERTYNAME}
NB: The array index in the script ( \$output[ \'PROPERTYNAME\' ] ) doesn't have to be capitalised, I use that as a convention to make it immediately obvious to me which array indexes will be used in template files. {PROPERTYNAME} in the template file itself, has to be capitalised.
Note, strings don't always need to be added to the calling script. See the new Importing Strings feature that arrived in BookyFlow 10.6
Displaying multiple rows of repeating content
Look at https://github.com/WoollyinWalesIT/bookyflow/blob/master/assets/templates/bootstrap5/frontend/list
_bookings.html On L25 it says
\<patTemplate:tmpl name=\"rows\" ....
The rows array is populated here
https://github.com/WoollyinWalesIT/bookyflow/blob/master/core-minicomponents/j06005mulistbooki ngs.class.php#L156
Because the "rows" array has (probably) got more than one record in it, the template system loops through that array to show all of the bookings.
Not all variables that populate multiple rows are called \$rows. There is html markup here that I do not recognise
Take a look at this template file
https://github.com/WoollyinWalesIT/bookyflow/blob/master/assets/templates/bootstrap5/frontend/ba sic_module_output.html
This is the template file that is used to generate the property display in most of the plugins that display properties on landing pages etc, for example the isotope and the property grid plugins both use this template.
This BookyFlow template file uses Bootstrap 5 for displaying output. There are several lines that include the class text-truncate
\<div class=\"col-sm-12 fs-6 fw-lighter text-truncate\">
There are times when a property name is too long for the available space, therefore the
text-truncate class from Bootstrap 5 is used so that the name can still be shown but the longer property names don't break the layout of the cards on the page.
It may be that you're including this output on pages where this isn't an issue for you, so you could remove that if it's a problem for you.
col-sm-12 refers to the Bootstrap 5 Grid system
fs-6 refers to Font Sizes
fs-lighter refers to Font Weight
You now know where template files are typically stored (more on overrides later), how dynamic content is added to those template files, and how Bootstrap 5 markup is used to fine tune the layout.
I want to show xxx in yyy.html template file. How do I do it?
The first rule to understand when editing BookyFlow templates is, if the data isn\'t available to the template then you can\'t show it, however there may be times when the template file you\'re using has data available to it but it\'s just not showing it. How do you find out?
If you visit the basic_module_output.html template file you will see a comment which demonstrates that there's some content that could be output by the template file, but isn't currently. Not all template files have this commentary, however.
The easiest way is to go to the Site Configuration -> Debugging tab and set \"Dump Template Vars\" to \"Yes\". This will change how templates are output in the front end, and is a great way of identifying which template file is producing some output. If you hover over the file name, you\'ll see a list of all elements that are available to that template. This approach isn't flawless, however. There are times when templates are so nested within templates that it just isn't shown.
Another way of looking to see what's currently available to a template is to dump an array's contents to see what's in it. For example in the top template script I could add
Before the line that says
Then, when I reload the page that the template file is used in I would see something like
I would then know that I could use {VIDEO_TUTORIALS} somewhere in top.html template file so long it was inside the section that says
\<patTemplate:tmpl name=\"pageoutput\" unusedvars=\"strip\">
If there were another section that said
\<patTemplate:tmpl name=\"another_array_name\" unusedvars=\"strip\">
Then I probably couldn't use {VIDEO_TUTORIALS} inside that because the \"another_array_name\" array has different content.
BookyFlow template files can be found in a number of different places due to template override and template package features, so how do you find which template you need to edit to change the layout?
In Site Configuration, go to the Debugging tab. At the bottom there is an option called Dump Template Vars. When this option is set to Yes, the frontend templates will have their output modified to look something like this. As you can see from the image, the full path to the template file and the template file name itself is displayed. Additionally, if you hover over the path with the mouse then the names of the variables that can be used in that template are shown.
{width="6.401405293088364in" height="4.267603893263342in"}
Common Strings
The patTemplate templating system is an old library which is no longer supported or maintained by its original creators. Normally this is a bad thing for most libraries and I\'d consider moving over to a new library, but with patTemplate I've decided to not do that, and instead I\'ve added some of my own functionality to the library.
The most obvious of these are the \"Common strings\", which are strings that we add to every template that\'s parsed by the patTemplate class, meaning that they don\'t need to be added programatically by the calling script. You can see the strings by visiting the administrator area, BookyFlow Developer Tools area, Common Strings menu option, if the plugin is installed
{width="6.4320986439195105in" height="5.775in"}
If you\'re a developer who\'s modifying templates and finds yourself adding strings to every template, you can programatically have your own common strings. Open the file j00005x_create_misc_common_strings.class.php in the /bookyflow/core-minicomponents directory to see an example of how we do it. You can create your own 00005 script to perform the same function.
Importing strings
You cannot normally include language definitions (such as
_BOOKYFLOW_COM_MR_QUICKRESDESC ) without first declaring it in the calling script. In 10.6 a new feature was introduced that changes that.
Most BookyFlow patTemplate template files have this code for each array it works with
You can now use a different unusedvars definition like so :
When this setting is set to "import" then BookyFlow will attempt to find a language definition when it comes across items like {ABCDXYZ} in the markup but not a corresponding index in the array it's looping through. If it does, it'll attempt to use jr_gettext() to find language definitions. Now if a template is allowed to import, any usage of {_BOOKYFLOW_COM_MR_QUICKRESDESC} would result in the current language's contents to be shown, so if I'm using English, then it would output Quick reservation in the page.
Not all Core templates will be changed to use this as it may result in unexpected behaviour however new templates and revised templates may well use this going forward, as needed, on a case by case basis. You, however, can use this feature.
This is a quick clarification on how BookyFlow itself is licensed.
Previous to December 2009 BookyFlow was licensed under a proprietary licence restricting the number of servers you could install BookyFlow on.
As of the first of December 2009 all versions until further notice are now dual-licensed under the GNU/GPL V2 and MIT licence, choose whichever suits your circumstance best.
Developers, If you\'re considering writing a BookyFlow plugin, be aware that we do not consider software that uses BookyFlow functions or classes to be derivative of BookyFlow. This means that you can licence your plugins in any way that you wish.
For the most uptodate copy of the licence, see https://www.bookyflow.net/license
####### Vince\'s aide mémoire to making gateway plugins for BookyFlow
This is a series of notes to indicate what each of the files in the gateway plugins do.
On Github you can find an example payment gateway for BookyFlow.
The components of a payment module in BookyFlow: j00509GATEWAYNAME\.class.php j00510GATEWAYNAME\.class.php j00510GATEWAYNAME\.gif j00510GATEWAYNAME\.html
j00600GATEWAYNAME\.class.php j00600GATEWAYNAME\.html
j00605GATEWAYNAME\.class.php j00610GATEWAYNAME\.class.php j03108GATEWAYNAME\.class.php
Obviously _GATEWAYNAME_ is substituted with the name of the gateway in question. Gateways are only triggered at the Confirm Booking stage when the user doing the booking is not an authorised manager.
####### Brief explanation of each file and what it does
j00509GATEWAYNAME\.class.php
Purpose: To acknowledge existence of the module to the configuration panel \"gateways\".
Notes: When the gateways config panel is generated this file is called. It generates the link seen in the config panel that is clicked to enable the gateway module configuration popup.
{width="4.9838188976377955in" height="1.1141666666666667in"}
j00510GATEWAYNAME\.class.php
Purpose: Compiles configuration options that the user can edit. Notes: N/A
{width="4.077727471566054in"
height="1.7973950131233596in"}
j00510GATEWAYNAME\.gif
Purpose: The gateway image Notes:N/A
j00510GATEWAYNAME\.html
{width="3.9849409448818895in" height="1.7460411198600174in"}Purpose: patTemplate file used when generating module configuration popup Notes: N/A
j00600GATEWAYNAME\.class.php
Purpose: 00600 plugin interrupt.
Notes: function showBookingConfirmation in bookyflow_bookingroom_functions.php lists active gateway plugins. When a gateway is chosen and the customer clicks submit to proceed to payment the module designer is able to program an \'interrupt\' that will be triggered before the customer is sent off-site to perform payment. This enables data collection that the system has not collected elsewhere that the module specifically requires. This file is optional, if it doesn\'t exist then BookyFlow will skip it and go straight to the 00605 eventTrigger.
j00600GATEWAYNAME\.html
Purpose: patTemplate file for generating input/output for j00600 class file.
Notes: N/A
j00605GATEWAYNAME\.class.php
Called by: bookyflow.php
Purpose: Sends any required postage data to payment gateway, eg paypal. and redirects the user to the payment gateway\'s interface.
Notes: Is triggered after 00605, or if 00605 doesn\'t exist then is called straight away.
j00610GATEWAYNAME\.class.php
Purpose: Is called by payment gateways confirming receipt of payment, and when customers are redirected back to the bookyflow site after payment.
Notes: It is worth noting that the payment data is still only stored in the temporary session data at this point. It is only when the insertInternetBooking function is specifically called, normally by this file but there are other places it can be called, that the booking is transferred from the temporary data to the contracts table. If the process is not completed then the booking is technically lost. Available but liable to be overwritten if the guest should return to the booking form). Also note that in some cases this file may be optional, eg. the cheque (or offline payment) module doesn\'t use it, the insertInternetBooking function is called by 00605cheque.class.php as there\'s no extra processing required.
Because the temporary data is stored in session variables it\'s not unheard of for Paypal\'s IPN to take so long returning it\'s information that the session has expired and the data lost, therefore it\'s advised that you up the Joomla session timeout from the pretty short timescale configured by the CMS\'s to something a little more reasonable. I can\'t suggest what\'s reasonable because that\'s down to you to decide what\'s best, but I wouldn\'t be surprised if many people set their timeouts to an hour or more.
j03108GATEWAYNAME\.class.php
Purpose: Reports file path to the calling function.
Notes: Provides the calling function with the path to the gateway gif.
{width="3.8331233595800525in"
height="1.5318744531933508in"}
####### Final notes to gateway developers
If you do
and do
you\'ll see that you\'ve got access to all the guest\'s details there.
In your 610 file, please make sure, after you\'ve confirmed the payment was successful, use this line :
before you do the insert otherwise BookyFlow will mark the deposit as unpaid.
Finally, don\'t attempt to clean up any data stored in \$tmpBookingHandler unless you specifically put it there yourself (you probably won\'t need to), I had another gateway developer do the same
and it caused BookyFlow to behave in some very weird and wonderful ways until I took it out. BookyFlow will clean up that variable itself in the \"insertInternetBooking\" function.
####### BookyFlow 9 changes
In BookyFlow 9 I introduced functionality that allowed invoices balances to be paid, as well as paying for bookings, which requires some changes to payment gateways if gateway developers wish to support this functionality.
Existing booking payment functionality is not affected, you can treat this as code that is independent of booking payments.
j10509paypal.class.php
Example :
Allows the gateway to be listed in the administrator area List Gateways page.
{width="6.1018099300087485in" height="2.4475in"}
j10510paypal.class.php
Builds the settings that are used to build the administrator area gateway settings page. Example :
\$settingArray[\'client_id\'] = array ( \"default\" => \"\", \"setting_title\" =>
jr_gettext(\'_BOOKYFLOW_CUSTOMTEXT_GATEWAY_CONFIG_PAYPAL_CLIENT_ID\'.\$plugin,\'Cl ient ID\'),
\"setting_description\" => \"\", \"format\" => \"input\"
) ;
\$settingArray[\'pendingok\'] = array ( \"default\" => \"1\", \"setting_title\" =>
jr_gettext(\'_BOOKYFLOW_JR_GATEWAY_CONFIG_PAYPAL_PENDINGOK\'.\$plugin,\'Pending ok?\'),
\"setting_description\" => jr_gettext(\'_BOOKYFLOW_JR_GATEWAY_CONFIG_PAYPAL_PENDINGOK_DESC\'.\$plugin,\'In some instances it is acceptable to receive a payment status of \"Pending\". \'),
\"format\" => \"boolean\"
) ;
\$settingArray[\'currencycode\'] = array ( \"default\" => \"1\",
\"setting_title\" => jr_gettext(\'_BOOKYFLOW_JR_GATEWAY_CONFIG_PAYPAL_CURRENCYCODE\'.\$plugin,\'Currenc y code (eg EUR) \'),
\"setting_description\" => \"\", \"format\" => \"currencycode\"
) ;
\$siteConfig = bookyflow_singleton_abstract::getInstance( \'bookyflow_config_site_singleton\' );
\$jrConfig = \$siteConfig->get();
\$index = \'gatewaysetting\\'.\$plugin.\'_checkbox\';
\$checkbox_value = \$jrConfig[\$index];
\$settingArray[\'checkbox\'] = array ( \"html\" => \'\<input type=\"checkbox\"
name=\\"gateway_setting_paypal_checkbox\\" value=\"\'.\$checkbox_value.\'\"/>\', \"setting_title\" => \"Checkbox\",
{width="4.049879702537183in"
height="2.6898950131233597in"}
There are two more files, which actually send and receive to the remote gateway servers. Their file names should always be as follows :
invoice_payment_send.class.php invoice_payment_receive.class.php
They\'re used to send and receive information from the remote gateway, the file names should be self-explanatory. They are passed an object that contains information about the invoice, (
\$invoice_obj ), the payment reference ( required to allow us to cross reference invoice ids and the processing gateway plugin ) , the value of the invoice plus the invoice line items. It also contains the specific gateway\'s settings as that were saved in j10510paypal.class.php.
If you\'re in any doubt, it\'s always best to look at how the Core Gateway Paypal does things, as it contains all of the code mentioned above.
{width="6.234469597550306in"
height="3.3820395888014in"}
The Media Centre for BookyFlow has been written from the ground up to be completely plugin friendly, meaning that other plugins can use the uploading functionality if they include the appropriate minicomponents.
Here I will add notes as a guide to which minicomponents do what.
You can upload images for Property Main Image, Slideshow images, Room features, Optional Extras and Room images.
This functionality is accessed via the frontend under the Media Centre menu option. In the administrator administrator area look in Settings > Media Centre. For the purpose of this document, I will focus on the files used by the Media Centre in the frontend.
Property centric images are all uploaded to the uploadedimages directory under /bookyflow, then to a subdirectory of the resource type, then finally an identifying ID. Because resources like the main property image and the slideshow don\'t have resource IDs, their ids are always 0. So, for example slideshow images for a property with the id of 15 will be uploaded into
/bookyflow/uploadedimages/15/slideshow/0/ whereas images for the hypothetical room with an id of 34 will be uploaded to /bookyflow/uploadedimages/15/rooms/34/.
It will probably help you to look at the source of the individual files as they\'re mentioned here so that you can see the code in context.
resource_type_gathering_trigger - 03379
The first file required by an external plugin is the 03379 trigger file, for example j03379slideshow
The sole purpose of the 03379 files is to tell BookyFlow that a particular resource type exists, and a few things about that resource type. For example j03379media_centre_resource_type_slideshow returns an indexed array :
The resource_type will be used for validation of the type and to make the subdirectory under
/bookyflow/uploadedimages/15/, to tell BookyFlow if it needs to look for an individual id for the resource, and its translated, friendly name.
upload_root_abs_path
In the properties context, this refers to the root of an individual property\'s images, which here is
The first is a constant that resolves to /xxx/public_html/bookyflow/uploadedimages/
Next we have a built in BookyFlow function that will find the manager\'s active property id.
Finally JRDS is another constant, and stands for JomRes Directory Separator (ie. \ in linux, or / in windows).
upload_root_rel_path
In the properties context, this refers to the url of an individual property\'s images, which here is
The first is a constant that resolves to /bookyflow/uploadedimages/
Next we have a built in BookyFlow function that will find the manager\'s active property id. resource_id_required
This can be either true or false. So if you want images in paths like this :
/uploadedimages/15/rooms/34 or /uploadedimages/15/slideshow/0 , then use true.
If this is set to false, as in the case of property features, then BookyFlow will not create further subdirectories, instead it will just upload images to, for example, /uploadedimages/pfeatures or
/uploadedimages/rmtypes
resource_id_gathering_trigger - 03381
The 03381 is called via ajax when a user changes the resource type that they\'re uploading images for in the Media Centre. The purpose of this is to allow us to show a dropdown list of resource ids if necessary. As already mentioned, slideshows (and the main property image) don\'t use a resource id, but rooms do. So for example j03381slideshow doesn\'t do anything so returns an empty string, but j03381rooms returns a dropdown with a dropdown list of room numbers and names. This allows the manager to upload multiple images for individual rooms.
post_upload_processing_trigger - 03382
This trigger is used for post uploaded processing, so it\'s triggered after an image has been uploaded. For example, j03382slideshow was emptying the /bookyflow/uploadedimages/15/gifs/ directory, then built a new copy of the slideshow gif which was commonly used in the property list (i.e search results) and module popup. Now the gif has been replaced by a slideshow, but the code is still there commented, so you can use it as an example.
get_existing_images_trigger - 03383
This is used to find the existing images for a given context, which are then returned as an array. With this array, BookyFlow can build the existing images column which a user can use to view or delete existing images.
If you\'re a developer and have a new property resource that you\'d like to upload images for, then these triggers are likely all that you\'ll need.
####### Allowed file types
By default, BookyFlow only supports uploading of jpg and png images. Whilst you could probably upload other types of resources, that functionality hasn\'t been tested and we recommend that you only implement uploading of other types at your own risk, and after exhaustive testing. Also, you should read the File Uploader\'s security notes.