Amun development

GPLv3

Abstract

This documentation explains howto start developing on Amun.


Table of Contents

1. Introduction
2. How services work
2.1. api
2.2. application
2.3. gadget
2.4. library
2.5. template
2.6. config.xml
3. Coding standard
3.1. PHP
3.1.1. Indenting and line length
3.1.2. Control Structures
3.1.3. Function calls
3.1.4. Class definitions
3.1.5. SQL queries
3.1.6. E_STRICT-compatible code
3.2. Javascript
3.3. Template
4. How setup a development enviroment
4.1. Checkout the repository
4.2. Include the 3rd party applications
4.3. Install amun
4.4. Check status
5. Hello world service
5.1. File structure
5.2. Build config.xml
5.3. Start coding
5.4. Installation
6. API
6.1. Overview
6.2. Workflow
6.2.1. Request Token
6.2.2. Responds Unauthorized Request Token
6.2.3. Redirect user to Amun
6.2.4. Logs in and grant / deny request
6.2.5. Redirect with authorized request token
6.2.6. Request an access token
6.2.7. Responds Access Token
6.2.8. Request data with Access Token
6.2.9. Response with data
6.2.10. Defined types
6.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. 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.

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

2.2. application

This is the web-fronted of the service. On installation this files are copied to the folder module/application/[service_name]

2.3. gadget

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

2.4. library

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

2.5. template

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

2.6. config.xml

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>
			

Chapter 3. Coding standard

3.1. PHP

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

3.1.2. Control Structures

The following examples show you howto format control structures correct.

3.1.2.1. If

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

3.1.2.2. Ternary operation

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

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

3.1.2.3. Switch

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

		echo 'action 1';

		break;

	default:

		echo 'action 2';

		break;
}

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

3.1.2.5. For

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

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

3.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: 451 $
 */
class index extends Amun_ApplicationAbstract
{
	public function onLoad()
	{
		echo 'some content';
	}
}

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

			));
		}
	}
}

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

3.2. Javascript

3.3. Template

Chapter 4. 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 (i.e. XAMPP more informations at http://www.apachefriends.org.

  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.

4.1. Checkout the repository

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

4.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/JSMin.phpThe JSMin class to minify the javascript output (http://github.com/rgrove/jsmin-php)
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)
template/default/js/tinymceThe WYSIWYG editor used by the default template (http://tinymce.moxiecode.com)
public/css/blueprintThe CSS framework used by the default template (http://blueprintcss.org)

4.3. Install amun

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

4.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\JSMin.php
?      library\HTMLPurifier
?      library\PSX

These are 3rd party applications. If all works fine you are now ready to start developing with Amun.

Chapter 5. Hello world service

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

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

5.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>
	<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".

5.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('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.

5.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 6. 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.

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

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

6.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"

6.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]

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

6.2.4. Logs in and grant / deny request

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

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

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

6.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]

6.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"

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

6.2.10. Defined types

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

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

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

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

6.2.10.4. Timestamp

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

6.2.10.5. Nonce

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

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

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

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

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