Table of Contents
This documentation is intended for developers who want start writing their own services or want to extend Amun.
This chapter explains the structure of an service and how it works. It is intended for users who want write a service. Each section describes an file or folder in an service dir. Goto the folder "service" in Amun to see some examples.
The RESTful API of an service. It is accessible via index.php/api/service/[service_name]. In Amun you can only insert, update or delete items via the API. On installation this files are copied into the folder module/api/service/[service_name]
This is the web-fronted of the service. On installation this files are copied to the folder module/application/[service_name]
Gadgets are small php script wich can be displayed in a sidebar on the website. On installation this files are copied to module/gadget/[service_name].
The library files wich are used by the service. On installation this files are copied to library/Amun/Service/[service_name]
This folder contains all templates for the service. On installation this files are copied to template/default/application/[service_name].
Here a sample configuration file wich explains each entry.
<?xml version="1.0" encoding="UTF-8"?> <service> <!-- These fields are required --> <!-- The status of your module "normal" or "system". "system" plugins can --> <!-- not be selected in a page as plugin --> <status>normal</status> <!-- The title of your service should only contain alphanumeric signs --> <name>sample</name> <!-- The type of the service wich is used in the XRDS --> <type>http://ns.amun-project.org/2011/amun/service/news</type> <!-- A url to your website or # --> <link>http://phpsx.org</link> <!-- These fields are optional. --> <author>Christoph Kappestein</author> <license>GPLv3</license> <version>0.0.1</version> <required> <service>comment</service> </required> <!-- Here is the possiblity for the service to make some sql queries. The comment for the tag shows the SQL that will be produced by the parser. Maybe it is a little bit overpowered to allow a service to delete tables but for now i think that offers also many possiblities. Maybe we should add a warning that appears when the service delete or change tables --> <database> <!-- CREATE TABLE [table/@name] ( [table/col], [table/col], ..., ) --> <create> <table name="table"> <col>id INT(10) AUTO_INCREMENT</col> <col>user_id INT(10)</col> <col>title VARCHAR(32)</col> <col>text LONGTEXT</col> <col>date VARCHAR(25)</col> </table> </create> <alter> <!-- ALTER TABLE [table/@name] ADD [table/col] --> <add> <table name="table"> <col>email VARCHAR(32)</col> </table> </add> <!-- ALTER TABLE [table/@name] CHANGE [table/col] --> <change> <table name="table"> <col>email emails VARCHAR(128)</col> </table> </change> <!-- ALTER TABLE [table/@name] MODIFY [table/col] --> <modify> <table name="table"> <col>emails VARCHAR(64)</col> </table> </modify> <!-- ALTER TABLE [table/@name] DROP [table/col] --> <drop> <table name="table"> <col>emails VARCHAR(64)</col> </table> </drop> </alter> <!-- DROP TABLE [table/@name] --> <drop> <table name="table" /> </drop> <!-- TRUNCATE TABLE [table/@name] --> <truncate> <table name="table" /> </truncate> <!-- INSERT INTO [table/@name] ( [table/col], [table/col], ..., ) VALUES ( [table/record/row], [table/record/row], ..., ) --> <insert> <table name="table"> <col>title</col> <col>some</col> <col>foo</col> <col>bar</col> <record> <row>bar</row> <row>bar</row> <row>bar</row> <row>bar</row> </record> </table> </insert> <!-- UPDATE [table/@name] SET [table/col] = [table/record/row], [table/col] = [table/record/row], ..., WHERE [table/@col] = [table/@value] --> <update> <table name="table" col="id" value="2"> <col>title</col> <col>some</col> <col>foo</col> <col>bar</col> <record> <row>bar</row> <row>bar</row> <row>bar</row> <row>bar</row> </record> </table> </update> <!-- DELETE FROM [table/@name] WHERE [table/@col] = [table/@value] --> <delete> <table name="table" col="id" value="2" /> </delete> </database> </service>
Table of Contents
Use leading tabs to ident your code and spaces to format them if necessary. That means you should only use tabs to the first sign after that use only spaces. This has the advantage that your code is good readable in any editor even if the tabs are displayed in different lengths. Here an example how it looks.
<?php function foobar() { $hello = 'foo'; $bar = 'world'; }
We have used one tab to indent each variable. To indent the equal sign of $bar to the same width as above we use only white spaces not tabs. You should setup your editor that trailing spaces are removed on save and that you can see leading tabs and trailing spaces.
The following examples show you howto format control structures correct.
<?php $foo = ($bar > 10) ? true : false; $bar = ($x < 10) ? '<' : (($x > 10) ? '>' : '=');
<?php switch($foo) { case 'foo': echo 'action 1'; break; default: echo 'action 2'; break; }
<?php $i = 1; while(true) { if($i > 10) { break; } else { echo 'action ' . $i . "\n"; $i++; } }
<?php do { echo 'action 1'; } while(false);
The following examples show you howto call functions correct.
<?php $foo = bar($val, $ue); $bar = $this->foo($an, $oth, $er, null);
Here an example howto create a class with a proper document block. Note because Amun needs PHP > 5 you should always define the visibility of the method with "public", "protected" or "private".
<?php /** * [class] * * [description] * * @author [author_name] <[author_email]> * @license [license_url] [license_name] * @link [url] * @category module * @package service * @subpackage [service_name] * @version $Revision: 451 $ */ class index extends Amun_ApplicationAbstract { public function onLoad() { echo 'some content'; } }
If you make an SQL query you should always use the HEREDOC syntax to declare the SQL query. This increases the readability and therefore the code is easier to maintain. The following examples shows howto make an proper SQL select.
<?php class index extends Amun_ApplicationAbstract { public function onLoad() { $foos = array(); $sql = <<<SQL SELECT bar.id, bar.title, bar.date FROM tbl_bar bar WHERE bar.id > 10 AND bar.id < 100 ORDER BY bar.id DESC LIMIT 0, 10 SQL; $result = $this->sql->getAll($sql); foreach($result as $row) { array_push($foos, array( 'title' => $row['title'], 'date' => date('r', $row['date']), )); } } }
Table of Contents
Amun uses other applications to work but all these "3rd party applications" are not included in the SVN repositiory. Because of that you have to setup a little development enviroment to start working on Amun. You need the following things to setup a local development enviroment.
Things you need to setup a local development enviroment
A local HTTP server (i.e. XAMPP more informations at http://www.apachefriends.org.
A SVN client. It is not necessary to have an GUI svn client the command-line client wich comes with SVN is also great. You can get SVN at http://subversion.apache.org.
A texteditor. If you dont have a favorite editor yet I can recommend jEdit (http://www.jedit.org) if you like an GUI editor or VIM (http://www.vim.org) if you like developing on the console.
First you have to create a folder where you want checkout Amun. This folder should be in the htdocs folder of your server where it can be accessed via the browser. Goto your directoy and checkout the latest version of Amun with:
svn checkout http://amun.googlecode.com/svn/trunk/ .
The easiest way to copy all 3rd party applications into Amun is to download the current version of Amun and take them fom there. You have to copy the following files and folders.
Path | Description |
---|---|
library/PSX | The library of the PSX framework (http://phpsx.org) |
library/HTMLPurifier | The library of HTMLPurifier to validate user input (http://htmlpurifier.org) |
library/HTMLPurifier.php | The entry point of the HTMLPurifier library |
library/JSMin.php | The JSMin class to minify the javascript output (http://github.com/rgrove/jsmin-php) |
template/default/js/ace | The code editor wich is used by some services (http://ace.ajax.org/) |
template/default/js/jquery | The javascript library used by the default template (http://jquery.com) |
template/default/js/tinymce | The WYSIWYG editor used by the default template (http://tinymce.moxiecode.com) |
public/css/blueprint | The CSS framework used by the default template (http://blueprintcss.org) |
If you have successful installed Amun you should check the SVN status by using the command
svn status
. You should see at least the following entries.
? library\HTMLPurifier.php ? library\JSMin.php ? library\HTMLPurifier ? library\PSX
These are 3rd party applications. If all works fine you are now ready to start developing with Amun.
This chapter describes step by step howto write a "Hello World" service.
The first thing you have todo is to create a folder in service. The folder must have the name of the service. In our example we take "world". In this folder we create a file called "config.xml" and a folder application containing a file "index.php". You should now have the following structure in your service folder.
service / world / application / index.php config.xml
Open the file config.xml with you favorite editor and insert the following content.
<?xml version="1.0" encoding="UTF-8"?> <service> <status>normal</status> <name>world</name> <type>http://ns.amun-project.org/2011/amun/service/world</type> <link>http://phpsx.org</link> <author>Your Name</author> <license>GPLv3</license> <version>0.0.1</version> <database> <insert> <table name="tbl_user_right"> <col>name</col> <col>description</col> <record> <row>world_view</row> <row>World view</row> </record> </table> </insert> </database> </service>
These are the basic informations about your service wich will be inserted into the service table. Each plugin should have at least one right with the suffix "_view". In our case we insert the right "world_view". If the user has the right the page is shown in the navigation else not. More informations about the options wich a service has see the chapter "How services work".
Now open the file application/index.php and write the following code to it
<?php class index extends Amun_ApplicationAbstract { public function onLoad() { if($this->user->hasRight('world_view')) { echo '<h1>Hello World</h1>'; } else { echo '<p>You have not the right to view hello world xD</p>'; } } }
Each file of a service wich you want access must have a class wich has the same name as the file. The output wich is generated by the class will be later inserted into the template. We first check whether the user has the right to view the template if yes we output "Hello World" else we show an message that the user has not the right to view it.
To install the service goto the backend and select the option content / service. Select the "world" service and install it. If the service was installed successful you can create a new page at content / page wich uses the "world" service. Now you have to set the rights in your user group. Goto user / group and select the user group wich you want give the right to view this application. If anything works you should now see the service at the frontend.
Table of Contents
Amun is an valid opensocial API container so it offers by default the required APIs (people and activites). In addition each service can add API endpoints. The API is accessed via Oauth. In this chapter I explain how the API works.
To connect through the API you need an API key and secret. In the administrator section you can create a new API consumer under system / api. In amun we have the followign endpoints:
OAuth endpoints
Request Token:
[psx_url]/index.php/api/auth/request
Authorize:
[psx_url]/index.php/api/auth/authorization
Access Token:
[psx_url]/index.php/api/auth/access
If you have an consumer key and secret you can connect to the API like defined in the OAuth specification.
If its not other mentioned this implementation follows the specification of OAuth 1.0 Revision. This section describes how you can access protected resource via the API. Note for better reading I have break long lines. If a line is indented with one space it was broken by the line above and should be seen as it where in the same line. If you need a oauth consumer implementation take a look at the PSX framework (http://phpsx.org) and see the oauth library.
The web application requests an request token.
GET /index.php/api/auth/request HTTP/1.1 Host: 127.0.0.1 Authorization: OAuth, oauth_consumer_key="[key]", oauth_signature_method="HMAC-SHA1", oauth_signature="[signature]", oauth_timestamp="[timestamp]", oauth_nonce="[nonce]", oauth_version="1.0"
On success Amun respond with an token and token secret. Note you can request max 5 valid tokens per IP.
HTTP/1.1 200 OK oauth_token=[token]& oauth_token_secret=[token_secret]& oauth_callback_confirmed=true& x_oauth_expire=[expire]
Your application should now redirect the user to Amun to authorize the request token. You must use the obtained request token in your request
GET /index.php/api/auth/authorization?oauth_token=[token] HTTP/1.1 Host: 127.0.0.1
The user must login and "Allow" or "Deny" the request for the web application.
If the user has "Allow" or "Deny" the request Amun redirects the user back to the callback. Note the callback must be from the same host as the url defined in the backend. If the request was denied the GET variable x_oauth_error is set.
Accepted: GET [callback]?oauth_token=[token]&oauth_verifier=[verifier] HTTP/1.1 Denied: GET [callback]?x_oauth_error=[msg] HTTP/1.1
Now we have all informations to get an access token. Note the expire time of your request token must be valid to exchange the token.
GET /index.php/api/auth/access HTTP/1.1 Host: 127.0.0.1 Authorization: OAuth, oauth_consumer_key="[key]", oauth_token="[token]", oauth_signature_method="HMAC-SHA1", oauth_signature="[signature]", oauth_timestamp="[timestamp]", oauth_nonce="[nonce]", oauth_version="1.0", oauth_verifier="[verifier]"
If all parameters are valid Amun respond with an access token and secret. You can use the token [expire] seconds to access the API.
HTTP/1.1 200 OK oauth_token=[token]&oauth_token_secret=[token_secret]&x_oauth_expire=[expire]
Here an example how you can access the API with an access token
GET /index.php/api/news/@me/@all HTTP/1.1 Host: 127.0.0.1 Accept: application/xml Authorization: OAuth, oauth_consumer_key="[key]", oauth_token="[token]", oauth_signature_method="HMAC-SHA1", oauth_signature="[signature]", oauth_timestamp="[timestamp]", oauth_nonce="[nonce]", oauth_version="1.0"
We get the latest entries from the news service
HTTP/1.1 200 OK <resultset> <startIndex>1</startIndex> <itemsPerPage>1</itemsPerPage> <totalResults>1</totalResults> <entry> <id>1</id> <user>foo</user> <title>some headline</title> <text>and here a really great description</text> <date>Mon, 16 Nov 2009 12:56:11 +0100</date> </entry> </resultset>
In the description we use variables like [key] etc. In this section I explain each variable.
This is the public consumer key of the user. You get an consumer key and secret if you register an new application at the backend at system / api.
You get the token and token secret as response from an request to the "request token" endpoint. This is usually http://[host]/index.php/api/auth/request. The value is a string with random hex characters. The string is 40 signs long i.e. 0864dc856a34e4590125836e4151d521
The signature is for verifing the request. Amun follows the specification except that you use as Normalize Request Parameters (http://oauth.net/core/1.0a#sig_norm_param) only the OAuth header parameters. More informations about signing a request at http://oauth.net/core/1.0a#signing_process
The timestamp is expressed in the number of seconds since January 1, 1970 00:00:00 GMT i.e. 1258369873
The nonce is a string with random hex characters. The string must be 16 signs long i.e. d4a35e7181e370e4
The verfier is to get sure that only application can request an access token that have previously gets user authorization. The value is a string with random hex characters. The string is 32 signs long i.e. dc66d622d793e0af0e3bcc639fecbb65
The seconds howlong you can use a token. I.e. if you obtain a request token and x_oauth_expire is 900 you have 15 minutes to exchange your request token for an access token.
When you use the API you should keep in mind that you can never trust an API user. This is because you cant get sure that the key and secret is used only by the trusted user. In example if the user uses the API key and secret in an desktop application someone can sniff the key and secret or if the user uses the API key and secret in an javascript application. There are many possibilities how someone can get the API key and secret of a user. The conclusion should be that the assigend user to the API should only have the rights wich are necessary. If you allow a user to Create Update or Delete data you must get sure that this user is reliable because else someone can create automatic scripts that delete all data or insert spam.
This glossary contains additional specifications wich you are maybe intersted in.
http://activitystrea.ms/specs/
http://tools.ietf.org/html/rfc4287
http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=cmis
http://xmlns.com/foaf/spec/
http://tools.ietf.org/html/draft-hammer-hostmeta-16
http://tools.ietf.org/html/rfc2616
http://www.w3.org/TR/html5/
http://www.ecma-international.org/publications/standards/Ecma-262.htm
http://tools.ietf.org/html/draft-hammer-discovery-05
http://dev.mysql.com/doc/
http://tools.ietf.org/html/rfc5849
http://openid.net/specs/openid-authentication-2_0.html
http://www.opensocial.org/Technical-Resources/
http://www.opensearch.org/Specifications/OpenSearch/1.1/Draft_4
http://www.php.net/manual/en/
http://portablecontacts.net/draft-spec.html
http://phpsx.org
http://www.rssboard.org/rss-specification
http://code.google.com/p/webfinger/
http://www.w3.org/TR/xml/