$Revision: 619 $
$Date: 2012-04-01 23:10:13 +0200 (So, 01. Apr 2012) $
Abstract
This documentation explains howto start developing on Amun.
Table of Contents
This documentation is intended for developers who want start writing their own services or want to extend Amun.
Table of Contents
This chapter describes some general conecpts wich are implemented in Amun.
All dynamic data is stored in an database. Each table has the following associated classes.
Vendor/ News/ Table.php Handler.php News.php
In this example we have the class Vendor_News wich represents a record of the news table. The class Vendor_News_Table wich represents the table and the class Vendor_News_Handler wich is used to create, update and delete entries. The following sections explain more details what each class does.
A record represents an row of an table. It must extend the class Amun_Data_RecordAbstract. The record is a data object wich is used to create or receive entries from a database. Here an example howto create a new record.
$news = Amun_Sql_Table_Registry::get('Service_News')->getRecord();
$news->setPageId(1);
$news->setTitle('Foobar');
$news->setText('<p>lorem ipsum</p>');
In this example you can be sure that the page id exists and that the title and text contains valid values.
The Table class represents an database table. It must extend the class Amun_Sql_TableAbstract. It contains all informations about the table what columns are available and how they are connected to other tables. The table class can be used to CRUD (Create, Receive, Update and Delete) records on the table. Here an example howto receive a list of Vendor_News records.
$result = Amun_Sql_Table_Registry::get('Vendor_News')
->select(array('id', 'title', 'url', 'text', 'date'))
->join(PSX_Sql_Join::INNER, Amun_Sql_Table_Registry::get('User_Account')
->select(array('name', 'profileUrl'), 'author')
)
->where('pageId', '=', $this->page->id)
->orderBy('date', PSX_Sql::SORT_DESC)
->getAll(PSX_Sql::FETCH_OBJECT);We use the Amun_Sql_Table_Registry class to get the table class. By default the getAll() method returns an associative array.
The handler class implements the business logic when create, update or delete a record. It contains the depending table class. If an action occurs the notify method is called wich invokes all classes wich match the table pattern in the table "amun_system_notify". If you want hook a specific action if an record is created you have to add a class to the notify table. Here an example howto insert a new record.
$handler = new Amun_Service_News_Handler($user);
$news = Amun_Sql_Table_Registry::get('Service_News')->getRecord();
$news->setPageId(1);
$news->setTitle('Foobar');
$news->setText('<p>lorem ipsum</p>');
$handler->create($news);
Amun was designed to work with an messaging queue like active mq. If the Stomp notify class was added to the "amun_system_notify" table everytime a record is inserted the notifier sends a message to the broker containing the table, type, userId and the complete record data. In this way you can implement listeners to the broker wich can do time consuming tasks so that the page load is not influenced.
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]. Each class should extend Amun_Api_RestAbstract to provide a basic REST api for your table.
This is the web-fronted of the service. On installation the files are copied to the folder module/application/[service_name]. An application must extend Amun_ApplicationAbstract.
Gadgets are normally displayed in a sidebar on the website. On installation the files are copied to module/gadget/[service_name]. An gadget must extends Amun_GadgetAbstract.
The library files wich are used by the service. On installation the files are copied to library/Amun/Service/[service_name]
This folder contains all templates for the service. On installation the files are copied to template/default/application/[service_name].
The config.xml contains all informations about the service. If the service is installed each md5 sum of the file wich is defined in the config is checked. Also the signature must match in order to install an service. The signature is simply an SHA1 HMAC hash of the configuration with the following secret:
d6b0c93c6f9b0a7917fdb402298ac692bf25fab8
You can use the php script generate-service-config.php to generate all md5 hashes and the signature for the service. The signature provides a basic security to ensure the integrity of all files in the service. If someone simply modifies an file in the service the installation will not succeed.
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: 619 $
*/
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 with PHP.
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 /var/www 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/Zend | The library of Zend (http://framework.zend.com/). At the moment we use it only to send mails because of that not the complete library is included. |
| 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) |
| 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\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 xmlns="http://amun-project.org" signature="">
<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>
<application>
<dir name="world">
<file name="index.php" md5=""/>
</dir>
</application>
<database>
<query><![CDATA[INSERT INTO `{table.user_right}` (`name`, `description`) VALUES
('service_world_view', 'Service World view');]]></query>
</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 "service_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('service_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/