Amun development

$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

1. Introduction
2. General concepts
2.1. Data handling
2.1.1. Record
2.1.2. Table
2.1.3. Handler
3. How services work
3.1. api
3.2. application
3.3. gadget
3.4. library
3.5. template
3.6. config.xml
4. Coding standard
4.1. PHP
4.1.1. Indenting and line length
4.1.2. Control Structures
4.1.3. Function calls
4.1.4. Class definitions
4.1.5. SQL queries
4.1.6. E_STRICT-compatible code
4.2. Javascript
4.3. Template
5. How setup a development enviroment
5.1. Checkout the repository
5.2. Include the 3rd party applications
5.3. Install amun
5.4. Check status
6. Hello world service
6.1. File structure
6.2. Build config.xml
6.3. Start coding
6.4. Installation
7. API
7.1. Overview
7.2. Workflow
7.2.1. Request Token
7.2.2. Responds Unauthorized Request Token
7.2.3. Redirect user to Amun
7.2.4. Logs in and grant / deny request
7.2.5. Redirect with authorized request token
7.2.6. Request an access token
7.2.7. Responds Access Token
7.2.8. Request data with Access Token
7.2.9. Response with data
7.2.10. Defined types
7.3. Security
Glossary

Chapter 1. Introduction

This documentation is intended for developers who want start writing their own services or want to extend Amun.

Chapter 2. General concepts

This chapter describes some general conecpts wich are implemented in Amun.

2.1. Data handling

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.

2.1.1. Record

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.

2.1.2. Table

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.

2.1.3. Handler

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.

Chapter 3. How services work

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.

3.1. api

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.

3.2. application

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.

3.3. gadget

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.

3.4. library

The library files wich are used by the service. On installation the files are copied to library/Amun/Service/[service_name]

3.5. template

This folder contains all templates for the service. On installation the files are copied to template/default/application/[service_name].

3.6. config.xml

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.

Chapter 4. Coding standard

4.1. PHP

4.1.1. Indenting and line length

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.

4.1.2. Control Structures

The following examples show you howto format control structures correct.

4.1.2.1. If

<?php
if($x !== false || $y >= 0)
{
	echo 'action 1';
}
else
{
	echo 'action 2';
}

4.1.2.2. Ternary operation

<?php
$foo = ($bar > 10) ? true : false;

$bar = ($x < 10) ? '&lt;' : (($x > 10) ? '&gt;' : '=');

4.1.2.3. Switch

<?php
switch($foo)
{
	case 'foo':

		echo 'action 1';
		break;

	default:

		echo 'action 2';
		break;
}

4.1.2.4. While

<?php
$i = 1;

while(true)
{
	if($i > 10)
	{
		break;
	}
	else
	{
		echo 'action ' . $i . "\n";

		$i++;
	}
}
<?php
do
{
	echo 'action 1';
}
while(false);

4.1.2.5. For

<?php
for($i = 0; $i < 10; $i++)
{
	echo 'action ' . $i;
}

4.1.3. Function calls

The following examples show you howto call functions correct.

<?php
$foo = bar($val, $ue);

$bar = $this->foo($an, $oth, $er, null);

4.1.4. Class definitions

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';
	}
}

4.1.5. SQL queries

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']),

			));
		}
	}
}

4.1.6. E_STRICT-compatible code

All code should be developed under E_ALL | E_STRICT wich is default if you are in debug mode. That means that you code must not produce any error or warning on this error level.

4.2. Javascript

@todo

4.3. Template

@todo

Chapter 5. How setup a development enviroment

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

  1. A local HTTP server with PHP.

  2. 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.

  3. 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.

5.1. Checkout the repository

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/ .

5.2. Include the 3rd party applications

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.

PathDescription
library/PSXThe library of the PSX framework (http://phpsx.org)
library/HTMLPurifierThe library of HTMLPurifier to validate user input (http://htmlpurifier.org)
library/HTMLPurifier.phpThe entry point of the HTMLPurifier library
library/ZendThe 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/aceThe code editor wich is used by some services (http://ace.ajax.org/)
template/default/js/jqueryThe javascript library used by the default template (http://jquery.com)
public/css/blueprintThe CSS framework used by the default template (http://blueprintcss.org)

5.3. Install amun

Now you should install Amun. More informations howto install Amun at the manual.

5.4. Check status

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.

Chapter 6. Hello world service

This chapter describes step by step howto write a "Hello World" service.

6.1. File structure

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

6.2. Build 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>&lt;![CDATA[INSERT INTO `{table.user_right}` (`name`, `description`) VALUES
('service_world_view', 'Service World view');]]&gt;</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".

6.3. Start coding

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.

6.4. Installation

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.

Chapter 7. API

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.

7.1. Overview

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

  1. Request Token:

    [psx_url]/index.php/api/auth/request

  2. Authorize:

    [psx_url]/index.php/api/auth/authorization

  3. 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.

7.2. Workflow

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.

7.2.1. Request Token

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"

7.2.2. Responds Unauthorized Request Token

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]

7.2.3. Redirect user to Amun

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

7.2.4. Logs in and grant / deny request

The user must login and "Allow" or "Deny" the request for the web application.

7.2.5. Redirect with authorized request token

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

7.2.6. Request an access token

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]"

7.2.7. Responds Access Token

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]

7.2.8. Request data with Access Token

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"

7.2.9. Response with data

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>

7.2.10. Defined types

In the description we use variables like [key] etc. In this section I explain each variable.

7.2.10.1. Key

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.

7.2.10.2. Token / Token secret

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

7.2.10.3. Signature

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

7.2.10.4. Timestamp

The timestamp is expressed in the number of seconds since January 1, 1970 00:00:00 GMT i.e. 1258369873

7.2.10.5. Nonce

The nonce is a string with random hex characters. The string must be 16 signs long i.e. d4a35e7181e370e4

7.2.10.6. Verifier

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

7.2.10.7. Expire

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.

7.2.10.8. Callback

The callback is defined either at registration or when you obtain an request token (with the oauth_callback parameter). If both are present amun uses the callback from the request token. Note the callback must have the same host as the web application url defined on registration.

7.3. Security

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.

Glossary

This glossary contains additional specifications wich you are maybe intersted in.

A

Activity Streams

http://activitystrea.ms/specs/

ATOM

http://tools.ietf.org/html/rfc4287

C

CMIS

http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=cmis

F

FOAF

http://xmlns.com/foaf/spec/

H

Host Metadata

http://tools.ietf.org/html/draft-hammer-hostmeta-16

HTTP

http://tools.ietf.org/html/rfc2616

HTML5

http://www.w3.org/TR/html5/

J

Javascript

http://www.ecma-international.org/publications/standards/Ecma-262.htm

L

LRDD

http://tools.ietf.org/html/draft-hammer-discovery-05

M

MySQL

http://dev.mysql.com/doc/

O

OAuth

http://tools.ietf.org/html/rfc5849

OpenID

http://openid.net/specs/openid-authentication-2_0.html

OpenSocial

http://www.opensocial.org/Technical-Resources/

OpenSearch

http://www.opensearch.org/Specifications/OpenSearch/1.1/Draft_4

P

PHP

http://www.php.net/manual/en/

Portable Contacts

http://portablecontacts.net/draft-spec.html

PSX

http://phpsx.org

R

RSS

http://www.rssboard.org/rss-specification

W

WebFinger

http://code.google.com/p/webfinger/

X

XML

http://www.w3.org/TR/xml/