>> Table of Contents

General introduction

Prelude

Dear reader

Welcome and thank you for choosing this product.

This program took years of hard development work and many hours that numerous helping hands invested by providing notices, suggestions and tests to steadily improve the quality of the program.

All these hours have been lots of joy to me and all those who have been working on this projects.

We hope that the result of our work will be useful and give you the same pleasure that we had programing it. Please provide us with your experiences and comments. If you got suggestions on how to improve this work, I will appreciate your e-mail.

Thomas Meyer

Description

Yana supports the development of applications by providing ready-to-use components. Examples: user management, authentication, database API, or templates. In addition it helps developers to write clean code by enforcing the use of a 3 layer architecture. (Compare: "Model-View-Controler").

More features are in the following list:

system requirements

The following minimum conditions have to be met so the program can run properly:

The following configuration is recommended:

Notes on compatibility

In this section you will find references to peculiarities of certain system configurations, as well as information on typical configuration errors and advice that can help you, if you encounter problems during the installation.

included files

Note: If you created new skins, plug-ins or translations and believe these may also be of interest to other users, please let us know. We would be pleased to provide interesting solutions to other users for free.

included files (see legend)

Files

1 index.php index site
2 library.php loads system libraries
3 cli.php command-line program to run cron-jobs

common directories

4 cache/ directory for temporary data and log files
5 common_files/smilies/ new emot-icons may be placed here
6 config/ configuration files
7 engine/ template engine

directories, where your extensions and applications are stored

8 languages/ translators may add their language packs here
9 plugins/ PHP programmers may put their extensions and applications here
10 skins/ web designers may create and edit additional skins and layouts here
11 config/db/ databases and configuration files
12 config/db/*/ uploaded files (binary large objects) are stored in .blob/. Configuration files use the extension "config". All data resides in its own respective directories.

configuration files

13 config/profiles/ settings made in the administration menus
14 config/_drive.config path setting for system files
15 config/dbconfig.php parameters for your database connection
16 config/system.config system configuration

Copyright and legal issues

Licences

Note on licences:
The installation package contains software components by different authors.

"Yana Framework for PHP" by Thomas Meyer
This software is published under the GNU GPL. See the license here ( gpl.txt ).

"Manual to Yana Framework" by Thomas Meyer
This manual is published under a Creative Commons Attribution License 3.0. See the license here ( creativecommons.org/licenses/by/3.0 ).

"Smarty Template-Engine" by New Digital Group
This software is published under the GNU LGPL. See the license here( lgpl.txt ).

"PhpConcept Library - Zip Module" by Vincent Blavet
This software is published under the GNU LGPL. See the license here( lgpl.txt ).

"The DHTML Calendar" by Mihai Bazon
This software is published under the GNU LGPL. See the license here( lgpl.txt ).

"CKEditor" by Frederico Knabben
This software is published under the GNU LGPL. See the license here( lgpl.txt ).

"JQuery" by John Resig
This software is published under the MIT-Licence. See the licence here( mit.txt ).

Summary

German translation of the GNU GPL v3 (only the English original version is obligatory)

The place of jurisdiction is the home town of the author.

You are using the software and the included documentation on your own risk. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

Participating in this project

Every user is able to take an active part in the development of this software. A wishlist for future program features and a page to announce discovered bugs can be found at the forum of the project's web site.

In addition you are invited to contribute by writing translations, add your own plug-ins or skins. This manual provides help on completing the first steps. For further questions and discussions with other users you may use the internet forum at any time.

If you don't have the possibility to take part in the project directly, please consider donating to the project. Server and traffic are expensive, as well as literature and hardware - not the mention the effort put in the work on this software. The project's web site explains several ways how to make your donation.

If you would like to link to our web site from your home page, we would be pleased if you do so.

Author: Thomas Meyer
Internet: www.yanaframework.net
Mail:

Beginner's Guide

Installation

automatic installation

Notice to PHP newbies: This software requires PHP and thus will possibly not run on your local hard disk, but only on the internet. To be able to run this software on your web site your provider needs to support PHP. If in doubt, ask your provider.

Please remember to check the system requirements and notes on compatibility.

The Yana Framework has a tool, which can accomplish the installation for you.

Step-by-step instructions

To run the installation program do as follows:

  1. Open a connection to your account via your FTP program.
  2. Copy the files "install.php", "install.pak" and "yana.zip" to a directory on your server
  3. Set the access rights for this directory via your ftp program, so that the script may read and write files. For Unix use CHMOD 777. (see figure)
  4. close FTP connection.
  5. Open the file "install.php" in your browser

When starting the installation program you should see the following page:

Figure: front page of the installation program

Click on "next" to continue

Figure: License conditions

Read the license conditions carefully. In order to continue with the installation, you must accept the license conditions. If you do not want to use the program, you can abort the installation by clicking the button below.

Figure: perform installation

To accomplish the installation click the button "start installation now". When you are finished, click "next".

Figure: choose password

On this side you specify a password. With this password you can enter the administration menu of the application. To do so, use the name "administrator". First please enter the password in the left field. Second please retype the password in the right field, just to ensure you didn't mistype it. Following this please click on "OK".

A message will be shown that indicates whether or not the operation has been successful. When you are finished, click "next".

Figure: self diagnosis

In order to make sure that the installation was successful, you may want to run the self diagnosis of the program. To do so click on "OK". Consider the viewed operating instructions.

If you wish additional details to the be printed, mark "more details" and click "OK" again. If you are content with the installation, continue by clicking on "next".

Figure: Finish installation

After completion of the installation the program automatically deletes all temporary files. If you check the option "run the application now", the YANA Framework is started automatically.

To end the installation program click on "finish".

manual installation

Please remember to check the system requirements and notes on compatibility.

Step-by-step instructions
  1. the program files are stored in the archive "yana.zip". Unpack the files using a ZIP program of your choice.
  2. Open a connection to your account via your FTP program.
  3. Upload all files and directories, belonging to the program, to a folder on your web page and link to the start page "index.php" from some page of your choice on your web site.
  4. Set the access rights for the directories "config/", "cache/", the included files AND subdirectories to read and writable for group "public" using your FTP program. For Unix use CHMOD 777. (see figure)
  5. close FTP connection.

Example: Setting access rights

Screenshot
Figure: WS_FTP LE, Win 2000

In WS-FTP (for example) you can change access rights by right-clicking on the directory names and selecting the option "file attributes" from the context menu. Then check all shown options and click "OK".

Screenshot
Figure: FireFTP, Win XP

In FireFTP you can change access rights by right-clicking on the directory names and selecting the option "Properties (incl. contents)" from the context menu. Then check all shown options and click "OK".

If you are using another FTP program, set the rights via the command CHMOD 777 . If you experience problems finding the "right button", search the documentation of your program for the keyword "CHMOD".

Configuration

open online menu

Open in your browser the file "index.php?action=index", which is stored in the program's main directory.
You will be asked to log on to the system with your user name and associated password. (see figure)

Login: Administrator Password:
Figure: login - Screenshots: Firefox browser

Enter the text "administrator" as "Login".

  1. If you used the "automatic installation", you must now enter the password, which you did choose at the time of installation.
  2. Otherwise, leave the "Password" empty this time.
  3. If you have forgotten your password continue reading at the section "frequently asked questions".

Confirm your entry by clicking on "OK". Then you may access the administration panel. Please note that JavaScript should be activated in your browser, so that the next page can be displayed correctly. If JavaScript is disabled you may experience that some features are not available. Personally I recommend you use Firefox 1.5 or later to view this page.

Choose password

The following section describes how to change the password for your login.

old password: new password: ********
Figure: Change Password

? Tip: To avoid, that someone may guess your password, it should be a minimum of 8 characters in length and contain at least 2 special characters. For example: birthdays, names of persons or animals and terms used in standard dictionaries are easy to guess.

Step-by-step instructions
  1. Scroll down to section "change password".
  2. If you haven't set up a password during installation, you may leave the input filed "old password" empty.
    Otherwise, enter your old password here.
  3. Enter the text, you want to use as password, in the field "new password". (see figure)
  4. To make sure you didn't mistype your password, enter the text again at the field labeled "confirm password".
  5. Click on "save changes".
  6. Finally, you will be asked to use your new password to login to the system.

Write down your new password and keep it safe.

? Note: if the access rights were not set, as described in the "installation" chapter, you will get an error message at this point. In that case read again, what you need to do and execute the instructions step by step. If required contact your operator.

Edit settings

? Note: Please understand that not all the options and settings are explained in detail. The following text will therefore only comment on the most important settings. Where needed follow the references in the program.

If you now want to set up your own settings for your application, please select "page layout Setup" from the options table. On this page you may define the layout of the program. E.g. font size, font color or date format. Colors must be specified as hexadecimal values. If you do not know how to do this, click on the square, colorful button next to the field. This will open a pop-up window, which let's you choose a color. Please also read the help text on this page for more information.

Menu to set up layout options
Figure: layout Options

Tips:
  1. You may click the button "expert mode" In the administration menu, in order to view advanced options
  2. Click on the small icon with the red / white "i" to get additional information on the current menu
  3. To be able to open or close menus JavaScript has to be enabled
  4. To improve security please remember to click the "Logout" button on the main menu, when you have finished your settings
  5. On the main menu click on the name of a plug-in, a language pack or skin pack to get a description

Advanced User's Guide:

Note: This guide is meant for advanced users.
If you have problems, please check the section for beginners.

Installation (summary)

The installation is done in several steps:

  1. Decompression of the archive file. It is important to note that filenames are case-sensitive, and the saved file paths are also be restored.
  2. Transfer the extracted data, taking into account the path structure, to the HTDOCS directory of the Apache Web server Using an appropriate software of your choice. If you can work on the server directly, this is usually either the command line or a file manager. Otherwise, as a rule, it is an FTP client. To get the required settings for the FTP connection, please contact your competent administrator.
  3. Note under Unix or Linux operating systems that access rights for the software are configured correctly. This software requires all included files and directories to be readable. All files with the extension "php" must be executable. In addition, the software requires write permissions for the directories "config /", "cache /" and all the files and subdirectories.
  4. The following steps are required only if a database plug-in is to be used.
    1. Setting up a new database, unless an already existing database is to be used. For example this can be done using an appropriate graphical user interface like PHPMyAdmin. If this is not available, you may also use the command line of MySQL with the following line: "CREATE DATABASE yana". The database may have a different name than those used here. Note that the name is case-sensitive. For further details on MySQL syntax, I recommend reading the free MySQL manual. (see recommended literature)
    2. If no suitable user is available for the database, please add a new user for this purpose. This user needs access to the database is created and the following rights: SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER. Use the following SQL statement: "GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER ON '<DatabaseName>'. * TO" <UserName> "@" <Host> "IDENTIFIED BY '<password>'; ". Adjust this instruction if necessary. Replace the name of the particular database user name, password and the address of the database server ( "Host"), if necessary in accordance with your current configuration. Write down this information for later use. Note: If you want, you may revoke the user rights "CREATE", "ALTER" after successful installation of the database.
Note: This guide aims at users who already have experience in dealing with PHP and HTML, and the creation of scripts on Unix systems. If you find yourself not ready for this, please start with the chapter for beginners.

Configuration

The most important steps to configure the application:

  1. Open the index page "index.php?action=index" in a JavaScript enabled browser. We recommend the use of Firefox 3 or later. This program is available on various platforms. You may, of course, use a different browser of your choice. This should, however, at least meet with the following requirements: support for HTML 4.1, JavaScript 1.5, DOM Level 2 and CSS 2.1 or later.
  2. Log on to the system. Click the button labeled "Login" and log in using the user name "administrator". Leave the field "Password" empty. (no password is set yet)
  3. The main menu has two settings: beginner and expert mode. You should switch to the expert mode. Use the switch at the head of the table.
  4. Above the main menu, you will find a menu bar for profile management. This allows the creation of new profiles, and the selection of existing profiles. Moreover, you will find a switch to empty the template cache. This causes all stored files in the directory "cache /" to be deleted. This switch can occasionally be useful if the program does not behaving as expected, after changing settings. The main menu itself is divided into 2 columns. In the right column, you find a list of all installed components. These are divided in 3 categories: Plug-ins, skins and language files. In the left column, you will find all the configuration settings for the installed plug-ins. The content of this column will change dynamically depending on what plug-ins or application components have been installed.
  5. The following steps are required only if a database plug-in is to be used.
    1. Select "Basic Settings" from the profiles bar, then click on "database setup" in the left column of the menu.
    2. Enter the data necessary for the connection to your database server. You will get this data from your competent administrator, in case you don't already got it. Note the information from the online help. (This is the grey text printed above the entry form.) Then save the changes. Detailed instructions and how to handle errors can be found in chapter "plug-in: database administration".
    3. The required tables are created automatically, and synchronized with existing tables stored in the file system. If you would like to complete the process by hand, please use these SQL source files, which are prepared for your DBMS.
  6. Finally, it is advisable to set a password for the user account "administrator". For this purpose select "change Password" from the options menu. Enter a new password and then save the changes. Then you have to log in to the system with the new password.
  7. Optionally, it is recommended to set individual settings for the layout of the software. This influences the appearance and behavior of the GUI. The options for editing these settings can be found in the left column of the administration menus. Make sure to consult the provided online help for the software.
  8. If you are happy with your settings, you should for security reasons log out by clicking on the button "Logout". This will terminate your active session. This prevents potential attacks by a third party using your session ID. ("Session Riding")

SQL source files for manual installation:

General information

The program has a profile administration, which allows, several sets of settings (profiles) to be stored and operated at the same time. First of all, you should choose the default profile and adapt it to your needs. Choose one after the other the "setup" entries from the options menu to edit the settings, which are related to the functions of the program. The administration menu offers a number of different options.

Tip for web communities: For example, if you have several guest books and want one of your books to use very special settings, which differ from the default values, use the field "new profile" in order to create an own profile for this guest book. Enter the ID of the guest book and click "GO". The options are the same as for the basic settings. Once the settings are saved, the name of the new profile appears in the selection menu of available profiles. This way you can access all saved profiles at any time and edit them.

This program can have any number of users simultaneously using separate instances of the application. It is a multiuser system. For example, if you use the guestbook application, you could assign each user to a separate guest book.
To be able to distinguish between all these guest books, each has a unique ID which clearly identifies this book (like giving it a "name"). The id is also available in the URL of the browser as the optional parameter "id".
You can create multiple profiles. A profile is a set of properties that apply to the guest book. There is always 1 profile for exactly 1 guestbook.
If a guestbook has no own profile, then default values are used. The default values are stored in a separate profile with the name " default settings ".

At the administration menu of your application, you will find a list of all profiles, which have been created until now. You can use this option to edit your different profiles, if more than 1 profile exists. A profile is only for the guest book with the appropriate ID.
You may create additional profiles using the option " create new profile ". To create new profiles, you need to have administrator privileges.

The user administration for communities is disabled by default. To enable this option (requires administrator priviliges), select the option "on" next to "user administration, configuration" from the menu "Plugins" in the administration menu. ("on" is the left of the two switches) then click "Save Changes". In the left column of the main menu a new menu labeled "user administration" will automatically appear, which allows you to create new users, set passwords and grant or revoke access rights.

for further reading

  1. MySQL online documentation, http://dev.mysql.com/doc/
  2. Self-PHP, http://www.selfphp.info
  3. Official PHP website, http://www.php.net
  4. PHP Security - Manual http://www.php.net/manual/en/security.php

Frequently Asked Questions

I got a problem with the automated installation. How do I install the software manually?

Please remember to check the system requirements and read chapter "manual installation".

What should I do if I run into a problem during configuration of the database connection?

Please check first whether your connection parameters are correct and the database server is available. You may also want to check the compatibility notes, as these contain guides and solutions to known problems with some DBMS.

If the connection parameters are correct and there is no problem with the database driver or a version conflict, you may circumvent the problem by trying a manual installation. To do so, read the chapter trouble shooting.

If I call the file "index.php" in my web browser, only the source code is shown. Why?

This script requires PHP. If only source code is shown in the browser, PHP might not be supported by your hosting service provider. When in doubt ask your provider.

Forgotten passwords

If you, as a standard user, have forgotten your password, write an e-mail to your administrator. He can set up a new password for you.

Administrators do as follows to set a new password:

  1. Open the administration menu.
  2. Click the option "user management" / "user overview" to see a list of all users.

    user overview: click here
    Figure: menu "user overview"

  3. You should now see a list of users. Find the name of the user you want to edit and click on the icon in column "user name".

    edit user: click here to view details on this user
    Figure: list of all users

  4. Below these settings you will find the link "lost password". Click to generate a new password. The new password will be sent to the user via e-mail. So ensure the mail address is correct.

    Edit user: generate new password
    Figure: generate new password

If you have forgotten your own password, please contact another administrator. If you ARE the ONLY administrator, then no new password can be generated. However it is also possible to reset the old password manually.

If the installation works with text files, please connect via FTP to your account, and download the file "config/db/user/user.sml". Edit this file and replace the password with the text "UNINITIALIZED". Upload the file again, and set the access rights via CHMOD to 777.

If the installation works with a database, open a connection to the database by using an appropriate program (for example PHPmyAdmin for MySQL) and edit the table "user" and set password of user "Administrator" to "UNINITIALIZED".
Alternatively, you can simply execute the following SQL statement.
UPDATE `user` SET user_pwd = 'UNINITIALIZED' WHERE user_id = 'ADMINISTRATOR';
(please adapt the quoting for the corresponding DBMS)

Remember to set a new password afterwards.

Example Note: This script never stores the password as real text Instead it is encrypted after input. This is an one-way encryption which cannot be reversed. For this reason, it is not possible to retrieve a forgotten password on demand - instead it is always required to create a new one.

Adding own smilies and icons

Choose the smilies you want to insert. Please note that these graphics are to be stored in GIF format.

Copy the files to the directory " common_files/smilies " of the framework.

If you want to use smilies from a directory other than the prescribed, then proceed as follows.

  1. Open the administration menu.
  2. Switch to the expert mode.
  3. In the left column of the main menu "options" click on "program setup".
  4. search for the entry "load smilies from". (see figure)
  5. Type in the desired directory.
  6. When you are done, click "Save changes".

If you have multiple profiles, you may need to copy the settings for all other profiles as well.


Figure: Load icons from another directory

Problems with the installation

Refer to the section " Installation for Beginners ". Since version 2.2 the program has a self-diagnosis mode. If you have finished the installation as described in the manual, you can use it to verify that all files are installed correctly.

For the self-diagnosis to start, open the URL "index.php?action=test" in your browser program. The program will then begin to create a diagnostic protocol. The self-diagnosis tests important directories and files and reports errors. These are shown as red lines in the protocol. If an error is found, read the error message and follow the instructions.

The self-diagnosis finds some of the most typical installation errors, but may not discover all kinds of possible problems. If you have installed the program according to instructions and still encounter problems during operation, send an email to. Don't forget to add the URL and a description of your problem. This offer is available to registered users free of charge.

Using the database prefix

The database setup offers an option labeled "prefix for database" (see figure ). This option is EXPERIMENTAL and serves the only purpose, of running two installations of the YANA Framework on the same server and the same database without having them affect each other. In any situation where this is not needed it is strongly recommend that this option is NOT used, due to it's experimental status.


Figure: experimental option to set a prefix on a database connection

A typical application for this option would be if someone is switching from an older to a new version, and therefore wants to temporarily run tow installations for testing purposes, to test the new version in a real environment before making a permanent change.

If you use a prefix, this also means that the SQL installation files in the directory "config/db/.install" need to be corrected by hand, according to the new table names including the chosen prefix. Only then the database may be installed as usual. The is currently no automatic process available to apply these changes. The PHP source code, however, does not need to be changed.

If you use your own plug-ins with handwritten SQL statements, you must of course also change the source code there to use the reflect names of the tables. Should you, however, use the database API of the framework, and have your SQL statements generated automatically, then editing these files is not necessary. The framework will correct your SQL at runtime automatically.

You should schedule 5 to 15 minutes for the conversion of the SQL installation files.

If this option is used accidentally and you experience problems accessing your data, then you can may reset this option manually. To do this edit the file "config/dbconfig.php" and change the entry at line 9 to: define('YANA_DATABASE_PREFIX',"");

Note: in some versions of the application, this feature is intentionally disabled.

Disable site map

If you don't want to use the site map as the front page for the application, it is possible to replace it with a different page - for example, the start page of a plug-in of your choice.

To do this open the file "config/system.config" in any text editor of your choice and change the entry "DEFAULT.HOMEPAGE" as you see fit (see figure).


Figure: Changing the front page (default homepage)

The following is a list of suggestions of possible settings:

Formatting entries with EmbTags

Why not use HTML in web forms?

It is not allowed to insert HTML codes via a publicly available form. The risk that a person abuses this offer to insert unwanted content or manipulate the website in an unacceptable way would be too large Among other things, a malicious attacker could, for example insert scripts, which, for example, might be used to commence a "phishing attack" to view visitors a different site content in order to steal passwords of users.

Basically to ban HTML completely is also not useful, because some elements are totally harmless and the readability of text may benefit from their use. This requires a compromise.

There are two possibilities:
  1. "Black Listing" prohibits certain elements, which are considered dangerous, but allows all other elements.
  2. "White listing," allows certain elements, which are considered harmless, but prohibits all other elements.
Both methods have exactly opposite advantages and disadvantages:Since the security of the applications of this framework have a greater relevance than the freedom to use HTML, the "whitelist" approach is used.

This program works, as just described, as well as most other programs on the WWW with a "Whitelist". That is, some harmless tags are allowed. All other tags are not allowed and will be automatically removed.

For example: [b]this is bold text[/b] This code is easily controllable and may be converted to clean HTML later: <b>this is bold text</b> An abuse is no longer possible..

As a side effect, these simple tags are much easier to understand than pure HTML. This makes it easier for beginners to write new entries.

What formatting options are there?

Icon Tags Description Example Output
[b] [b] bold text format [b]bold text[/b] bold text
[i] [i] italic text format [i]italic text[/i] italic text
[u] [u] underline text [u]underline text[/u] underline text
[code] [code] insert source code
(code is not executed, but formatted as text)
[code]Text[/code] Text
[php] [php] insert PHP code
(code is not executed, but formatted as text)
[php]print 'Hello World';[/php]
<?php
print 'Hello World';
?>

[color] [color] set a text color [color=red]Text[/color] Text
[mark] [mark] set background color [mark=yellow]Text[/mark] Text
[url] [url] inset hyperlink [url]www.yanaframework.net[/url] www.yanaframework.net
[mail] [mail] insert mail address [mail]mail@domain.tld[/mail] mail@domain.tld
[img] [img] insert graphic * [img]domain.tld/img/image.gif[/img] [img]
[emp] [emp] emphasize text [emp]Text[/emp] Text
[header] [h] insert header [h]header[/h]
header
[small] [small] reduce font size [small]small text[/small] small text
[big] [big] increase font size [big]big text[/big] big text
[c] [c] insert comment
(e.g. for writing signatures)
[c]comment[/c]
comment
[hide] [hide] hide a text, till the users moves the mouse over it [hide]this is a secret[/hide]
this is a secret
[wbr] [wbr] allow line break within a word ship[wbr]yard-[wbr]employee shipyard-
employee
[br] [br] force line break ship-[br]yard-[br]employee ship-
yard-
employee

* YANA prevents attacks on your site through cross-site scripting (XSS), because it only the inclusion of graphics from the local server within the directory of the framework is permitted. The file must also have the file extensions "jpeg", "jpg, png", or "gif". Special characters are not allowed, which will prevent an attacker to conceal the true URL of a file by the skillful use of escape sequences, to try to sneak by the protection of the framework.

The tag [img] is useful for community sites, where users can upload their own graphics files on the server.

Using your own tags

Example Warning: Incorrect settings may by chance prevent the program from running properly. Therefore never perform such changes in a productive environment. Instead check your settings first in a test environment.

It is possible to set up individual tags for each website profile. Proceed as follows.

  1. Open the directory "config/profiles"
  2. Select the profile that you want to edit (for example, "default.config"), and open this file in a text editor of your choice
  3. Fill the container "PROFILES" as follows.
    Explanation of the syntax:
    <PROFILE>
    ...
    <
    EMBTAG>
    <tagname>
    <
    TITLE>value of attribute "title"</TITLE>
    <
    TEXT>label</TEXT>
    <
    IMAGE>image of the button, that inserts the tag/IMAGE>
    <
    1>optional regular expression to transform to HTML</1>
    (default value = /\[$tagname\](.*)(?:\[\/$tagname\]|$)/Us)
    <
    2>optional replacement string to convert to HTML</2>
    (default = &lt;span class="embtag_tag_{tagname}"&gt;$1&lt;/span&gt;)
    </tagname>
    </
    EMBTAG>
    </
    PROFILE>
    See the following example for the tag "bold":
    <PROFILE>
    ...
    <
    EMBTAG>
    <bold>
    <
    TITLE>insert bold text</TITLE>
    <
    TEXT>bold text</TEXT>
    <
    IMAGE>%SKINDIR%/default/styles/tags/b.gif</IMAGE>
    <
    1>/\[bold\](.*)\[\/bold\]/Us</1>
    <
    2>&lt;b&gt;$1&lt;/b&gt;</2>
    </bold>
    </
    EMBTAG>
    </
    PROFILE>
  4. Save your changes.
  5. Try to use your new tags in a form

Example Note: If you remove a tag, it is no longer viewed as HTML as well. This includes such data, that has had this tag earlier. Also be sure not to remove or replace the original tags.

My question is still non answered - what am I to do?

Take your question to the internet forum at yanaframework.net/forum. This offer is free of charge. Please understand, that there is no guarantee that unsolicited e-mail is handled. Please respect, that requests by known donators, who engage in the further development of this project, may be answered faster than normal mail.

Extensions

How to use the administration panel

To open the administration panel, do as follows:

  1. Click the link "administration panel". I can be found at the bottom of every page.

    Link: administration panel
    Figure: position of link "administration panel"

  2. You will be asked to log on to the system with your user name and associated password.

    Login: Administrator Password:
    Figure: login - Screenshots: Firefox browser

    Enter the text "administrator" as "Login".

    If you have forgotten your password continue reading at the section "frequently asked questions".

    Confirm your entry by clicking on "OK". Then you may access the administration panel. Please note that JavaScript should be activated in your browser, so that the next page can be displayed correctly. If JavaScript is disabled you may experience that some features are not available. Personally I recommend you use Firefox 1.5 or later to view this page.

The administration panel comes in two flavors: basic and expert mode. Some options are only visible in expert mode. To switch from basic to expert, click the link "switch to expert mode".

Link: switch to expert mode
Figure: Position of link to switch between configuration modes

Settings in basic mode

administration panel in basic mode
Figure: administration panel's options in basic mode

Note: Installed plug-ins may only be activated or deactivated in expert mode.

Settings in expert mode

administration panel in expert mode
Figure: administration panel's options in expert mode

Install skins

Instructions

A "Skin" is a package of template pages and graphics. This package changes the appearance of the user interface, the "look & feel" of your application.

Attention! Packages may not include executable files or PHP files. Take care of this before you proceed with the installation.

Following files and directories may be included in a package.

To install an archive you got from the internet, do as follows:

  1. Unpack the archive, which contains the plug-in to the skin directory of the application. (Default: "skins/")
  2. Then, upload your changes to the Internet with your FTP program.
  3. Open your administration menu. Make sure you are logged in as the administrator and the expert mode is enabled.
  4. In the right column, you should be able to read the name of the package in the section "installed skins".
  5. Select the desired package by clicking the radio button (round button), next to the name.  (see figure)
  6. When you are done, click "Save changes".

Choose a skin
Figure: Choosing a skin (administrator's menu / expert-mode)

The changes will take effect, the next time you start the program. If the settings do not apply as expected, flush the server's cache by clicking the button in the administrator's menu and try again.

Troubleshooting

  1. If you are not able to see a menu "installed skins", the probably the expert mode is not activated
  2. If you can't activate the expert mode, check whether the access rights have correctly been set during the installation of the program. Maybe a profile file in the directory "config/profiles" is write-protected.
  3. If you see the menu, but not the radio button, you do not have sufficient rights for the installation. Ask an administrator to install it for you.

Install plug-ins

Instructions

Plug-ins extend your application to include new functions.

To install an archive you got from the internet, do as follows:

  1. Unpack the archive, which contains the plug-in to the directory "plugins/" of the application. Be careful not to overwrite files, unless you are in the process of updating a plug-in to a newer version.
  2. Then, upload your changes to the Internet with your FTP program.
  3. Open your administration menu. Make sure you are logged in as the administrator and the expert mode is enabled.
  4. In the right column, section "plugins", click on "refresh list." (see figure)
  5. The new plug-in will now appear in the list Activate it by clicking the check box next to it's name, so a little check mark appears.
  6. When you are done, click "Save changes".
  7. Some plug-ins have additional options. These options will appear automatically after activation.

To the deactivate the plug-in at a later stage, repeat the procedure and remove the check mark next to the name of the plug-in.

List of available plug-ins
Figure: install new plug-in

The changes will take effect, the next time you start the program.

Troubleshooting

  1. If you are not able to see a menu "plugins", then probably the expert mode is not activated
  2. If you can't activate the expert mode, check whether the access rights have correctly been set during the installation of the program. Maybe a profile file in the directory "config/profiles" is write-protected.
  3. If you see the menu, but not the check boxes, you do not have sufficient rights for the installation. Ask an administrator to install it for you.

Install language packs

Instructions

Language packs contain translated text messages of the application for various languages. For example German or English.

After installation of a new plug-in you might need to update an installed language pack. For example, if new text messages were added with the release.

Attention! Packages may not include executable files or PHP files. Take care of this before you proceed with the installation.

Following files and directories may be included in a package.

To install an archive you got from the internet, do as follows:

  1. Unpack the archive, which contains the language pack in the directory "languages/" of the application. Be careful not to overwrite files, unless you are in the process of updating a package to a newer version.
  2. Then, upload your changes to the Internet with your FTP program.
  3. A list of all installed language packs can be found in the administration menu (see figure).

choosing a language package
Figure: install new language file

The changes will take effect, the next time you start the program.

Troubleshooting

  1. If you are not able to see a menu "installed skins", the probably the expert mode is not activated
  2. If you can't activate the expert mode, check whether the access rights have correctly been set during the installation of the program. Maybe a profile file in the directory "config/profiles" is write-protected.
  3. If you see the menu, but not the radio button, you do not have sufficient rights for the installation. Ask an administrator to install it for you.

Plug-in: Guestbook

General introduction

This plug-in enables you to add a guestbook to your web site. Using this software, your visitors may leave messages for you. These messages are visible to you and other visitors. The software automatically informs you on new entries by sending you an e-mail. In addition the latest messages are also available as RSS-feed.

Notice to PHP newbies: This software requires PHP and thus will possibly not run on your local hard disk, but only on the internet. To be able to run this software on your web site your provider needs to support PHP. If in doubt, ask your provider.

install plugin

Guestbook entries may be stored optionally as text files or in a database.

To be able to run this script you need webspace with support for PHP and FTP access. In addition a FTP client is needed to upload files. To use a database to save entries you need to have the PEAR-DB library installed.

Step-by-step guide:

  1. Open the index page "index.php?action=index" in a JavaScript enabled browser.
  2. Log on to the system. After installation the login data is: user name="Administrator", password="". If you already specified a password during the installation, please use this.
  3. The main menu has two settings: beginner and expert mode. The main menu has two settings: Beginner and expert mode. In the right column, you find a list of all installed components. These are divided in 3 categories: Plug-ins, skins and language files. In the left column, you will find all the configuration settings for the installed plug-ins.
  4. Click the button "reload list" to reload the list of installed plug-ins. (see figure)
  5. Enable (if available) the plug-ins "Database Administration", "Guestbook" and "Guestbook setup." Then save your changes.
  6. After finishing the installation successfully an entry labeled "Guestbook" will appear on the application's site map.


Figure: Installing new plug-ins

install database (optional)

Note:  The following steps are required only if, entries are to be saved in a database, rather than a text file.
For a guide to create a new database, see " Instructions for advanced users: Installation." ".
  1. The option "database" offers application specific settings, which apply to all profiles simultaneously and can't be set for each profile separately. Therefore, this option is only available with the pseudo profile "basic settings"
    Select "Basic Settings" from the profiles bar, then click on "database setup" in the left column of the menu.
  2. Enter the data necessary for the connection to your database server. You will get this data from your competent administrator, in case you don't already got it. Note the information from the online help. (This is the grey text printed above the entry form.) Then save the changes. The required tables are created automatically, and synchronized with existing tables stored in the file system. Attention! If this process should fail because of an early timeout, repeat the process until it is completed. This is important, because otherwise you may have no access to the database or the stored data may be inconsistent.
  3. If you would like to complete the process by hand, please use these SQL source files, which are prepared for your DBMS.

SQL source files for manual installation:

Basic settings

Basic settings
Figure: Choosing the profile "Basic settings"

These settings automatically apply to all profiles, unless a profile overwrites these basic settings. In addition they apply to all newly created profiles. Caution: Some plug-ins may have options, either which are only visible in the basic settings, or which are visible everywhere EXCEPT in the basic settings.

? Note: Basic settings are meant to be "Default values", that automatically apply to all books, unless overwritten.

E. mail notifications

In the administrator's menu click "Guestbook Setup". In the section "E. mail notifications" you can choose whether you are to be informed on new entries automatically by e-mail. This option is disabled by default. To activate it, click on the button with the text "activate" and enter your e-mail address in the box "send mail to". (see figure)

In order to save your changes, click on "Save changes", or click on "Abort" to return to the administration menu without changes.

edit e-mail notification settings
Figure: edit basic settings

Multiple guestbooks

The program usually sets up new guests books when needed. When doing this, basic settings are used, which apply to all books equally (see above) If you want a guest book to use very special profile of settings, which differ from the basic settings of all the other guestbooks, then you can use this option.

  1. Open the administration menu.
  2. In the profile administration section select "new profile". Choose a suitable name for the profile, enter it in the input field and then save your entry. The profile is then selected automatically.
  3. To change the settings of the just created profile, select in the left column "Options" the menu "Guestbook Setup".
  4. You will now see a page with various settings that you can change. Once you click the "save changes" button, the settings of your new profile will be saved.

Link to your new guestbook like this:

index.php?id=FOO&action=guestbook_read_read

Where instead of FOO you enter your guestbook's name.

Multiple layouts for the same guestbook

  1. Open the administration menu. Ensure you are logged in as administrator and you are using the expert mode.
  2. Open the option "program setup".
  3. Within the section "data source" there is an entry labeled "get data from". There enter the name of the profile, which the layout refers to. For example: If you want to use the layout for the guestbook with the id "default", then write: " default ".  (see figure)

Using data from another profile
Figure: Using data from another profile

Do the same for any other layouts you want to create.

Link to a layout as you would do with a real guest book:

index.php?id=FOO&action=guestbook_read_read

Where instead of FOO you enter your layout's name.

Protection from unwanted advertisements

To protect you guestbook from being flooded with unwanted advertisement there are several security options included. Via the menu "Guestbook Setup" you may restrict the number of entries per person to a number of your choice.

Spam protection
Figure: Spam protection

  1. Open the administration menu. Ensure you are logged in as administrator and you are using the expert mode.
  2. Open the option "Guestbook Setup".
  3. Scroll down to section "Spam protection". Here you can restrict the number of consecutive entries a person may write to a certain number.

Additional security settings can be made by using the plug-in "Anti-Spam". These can be edited by using the menu option "Anti-Spam Setup".

List of important data fields for the interested developer

To see the full list, insert new fields, or delete old fields, open the appropriate structure file "config/db/guestbook.config".

Column Type Mandatory Default value Description
GUESTBOOK_ID integer yes <auto increment> Primary key
PROFILE_ID string no default Foreign key to the appropriate profile.
GUESTBOOK_IP string no <remote address> IP of the author of the current entry
GUESTBOOK_NAME string yes - Name of the author
GUESTBOOK_MESSAGE string yes - message text
GUESTBOOK_MAIL string no null Mail address of the author of an entry.
GUESTBOOK_DATE integer yes <current timestamp> Date of creation.
GUESTBOOK_COMMENT string no null Commentary by the webmaster (if any).

Plugin: Anti-Spam

General introduction

This plug-in provides protection from unwanted advertisements This protection automatically applies to all installed plug-ins. For configuration an additional "setup"-plug-in is required. This adds the option "Anti-Spam Setup" to the administrator's menu. This menu enables you to set up the configuration for this plug-in.

Configurations


The edit the configuration, do as follows.

  1. Open the administration menu.
  2. Open the option "Anti-Spam Setup".
  3. Here you may activate a so-called "CAPTCHA". This option display a graphic with a text, which the user must retype in order to be allowed to submit a new entry. (see figure) To be able to use the "CAPTCHA", the PHP GD library needs to be installed and support for PNG needs to be activated. This should already been done for any current standard installation of PHP. If for some reason this is not the case for your copy, please request your administrator to install the missing library. For most providers this shouldn't be much of a problem.

    Anti-Spam Setup
    Figure: Anti-Spam Setup

  4. Choose at minimum security level "medium" if you want to use the CAPTCHA.
    The following figure shows a CAPTCHA on the example of the guestbook plug-in.
  5. You may choose level "user defined" to if you wish to set up the configuration yourself.

Display of the CAPTCHA in an application
Display of the CAPTCHA in an application Here: guestbook plug-in

  1. CAPTCHA is an abbreviation for "Completely Automated Public Turing test to tell Computers and Humans Apart"

Plug-in: DB administration

General introduction

This plug-in enables you to edit the connection settings for your database. Furthermore it allows you to install table, transfer required data and create backups.

Setting a database connection

form
Figure: database connection settings

Set the option "use database" to the value "yes", to use the database connection with the given values. The installation of the database is automatically implemented. Only in the event of an error, you need to manually intervene.

Upon activation of the database the following actions are carried out automatically:
  1. test of connection parameters
  2. open a connection to the database
  3. install all required tables
  4. copy all data from file system to database

Should any of these steps result in an error, the connection is not activated and an error message is shown. A detailed description of the error can be found in the error log. Please note that logging must have been activated at this point (see chapter "logging and error messages").

Troubleshooting

In case of an error, you may also complete the steps manually, which usually would be carried out automatically during activation of the database connection. To do so, proceed as follows.

  1. Check you connection settings for errors. Continue, if you are sure, that they are correct.
  2. Enter the connection parameters for your database and save your changes. Leave the option "use database" unchanged to the value "no".
  3. Select the option "install databases". Try to execute this action be clicking on "submit" (see figure). If this is successful, continue to the next step.
    In case of an error, please use another program to check, whether the tables already exist. If so, please drop those tables and try again.
    If the tables don't exist, use another program and try to install the tables. The sources can be found in chapter "Advanced User's Guide" / "Configuration". If this is successful, check your connection settings again.
  4. Select the option "synchronize databases". At first, only try to synchronize the database "user". Deactivate all other options. Then try to synchronize the other databases (see figure). If any of these steps result in an error, the reason might be a data error. You may then decide to waive this data. If this can't be done, you have the opportunity to whether manually copy the data to the target database, or fix the data in the source database.
    The source data can be found in text files within the directory "config/db/name", where "name" stands for the name of the database. This option is only recommended to experienced users.
  5. If the above steps have been successful, then select the option "setup connection to your database". Make sure that you enabled the expert mode at the administration panel. Set the option "use database" to the value "yes". Deactivate the options "create tables automatically" and "copy table rows". These options are only visible in expert mode. Then, click on "submit" (see figure).

Please also note the compatibility notes!

install databases

? Note: This step is executed automatically, when a database connection is activated. The form shown here is meant to be used for manual intervention in case of an error.

form
Figure: form to install tables

This step will try to create the tables belonging to the chosen databases on the database server. Therefore it will try to import sql files, appropriate to chosen type of database. These requires prior proper set up of a connection to the database. You should however not yet activate this connection before you have installed the required tables.

In some uncommon cases this process may fail. For example, if: connecting to the database server fails, there is no SQL file available for the chosen DBMS, the generated SQL contains errors, another table with the same name already exists, the user does not have the appropriate rights to create the tables. In those cases the installation needs to be done by hand.

The required SQL files to create the tables can be found in chapter "Advanced User's Guide" / "Configuration".

Synchronize databases

? Note: This step is executed automatically, when a database connection is activated. The form shown here is meant to be used for manual intervention in case of an error.

form
Figure: form to copy table contents to your database

This synchronizes the entries in your text files with those in your database. The process does NOT delete or update any existing entries, but only creates new ones. When operating on very big amounts of data you may experience a timeout before the process is completed. If this happens, redo this procedure multiple times until all data has been transfered.

Creating a backup

? Note: This option requires an active database connection.

form
Figure: form to create a backup of a database

Use this option to create a backup copy of your database.

SQL syntax
the SQL dialect, which the output should use
export structure
sets, whether table structures should be stored ("create table ...")
export data
sets, whether data sets in tables should be stored
compress as GZip
since backups may be very large, this gives you the option to compress the file in GZip format prior to downloading it. This will cause the file to be smaller.

Plug-in: Search Engine

General introduction

This scripts lets your visitors search the content of your web site. You don't need programming knowledge to be able to use this software.

The Sun Java VM is required for installation of this package. This is a free product that you can get at http://java.sun.com.

Notice to PHP newbies: This software requires PHP and thus will possibly not run on your local hard disk, but only on the internet. To be able to run this software on your web site your provider needs to support PHP. If in doubt, ask your provider.

Features

 

included files

tools for creation of search indexes
tools/index.jar containts program code
tools/start.bat start file
tools/suchIndexErstellen.bat Command line interface (German)
tools/searchIndex.bat Command line interface (English)
misc. files
plugins/search directory for program files of the search engine
plugins/search_admin administrative application
skins/default/search template files

Installation


Tip: If you prefer to work offline on your local machine, create the search index first.. To do this use the file " start.bat " in directory " tools/ ". For further details on the usage of this tools see the next section. The search index itself will be automatically created and saved to the files keywords.dat and documents.dat . Once these files are created, your search engine is ready for use.

Install search engine:

  1. Open a connection to your account via your FTP program.
  2. Copy your changes to a directory on your web site.
  3. close FTP connection.

Tip: The files in directory "tools/" do not need to be uploaded to the internet.

Creating the search index

For better performance while searching the search engine uses an index of all searchable documents. This directory is called a "search index". Before you are able to use the search engine, this index needs to be created. For the search index you may either: 1) create it offline on your local machine and upload it (either by FTP or via the administration menu of the program) to your server, or 2) create it online via the administrator's menu of the program. Both alternatives are available, so just choose the one that serves you best. The upcoming chapter will first explain how to create the search index offline on your own computer. Afterwards a second chapter will explain how to create the search index online on a web server.

Creating the search index offline

You don't have to enter all pages of your web site by hand. The package contains a small Java tool, that can do the work for you.


Figure: graph. user interface to create search index
Installation of Java

It is not relevant whether you are running this software under Winows, Linux, Unix or Mac. OS. However what you do need is a working runtime environment. Get the most up to date version for your system for free at Java.Sun.com. You need at least Java 2 version 1.4.2 or later with the Swing- and Beans-libraries installed. For Linux, Java is already included in many distributions. For Windows this is not necessarily the case.

To find out whether Java is already installed on your system or not, just search for the file "java.exe". On Windows choose "Start" > "Search" > "Files and Folders". If the file is not found, Java still needs to be installed.

After installation, the file needs to be added to the system's path variable. You may check this in Windows by opening the "DOS command line" from the menu "Start" and typing "java". A list of options should then be displayed to you. If not, the file still needs to be added to the system's path.

In Windows XP you do this via the system's setting calling "System" > "Extra" > "Environment variables" > "System variables". Restart your computer afterwards and try again, by entering "java" at the command line. If you still do not succeed at this point, you have no other choice then to install Java again.

For previous versions of Windows please edit the file "autoexec.bat". Add the following entry (don't forget to make yourself a copy of this file first):
SET PATH="JAVA-folder\bin\java.exe;%PATH%"

You can get the installed version of Java under Windows by typing "java -version" at the DOS command line.

Start indexing software

Just start the file start.bat by double-clicking it. The program has a graphical interface with colorful icons which should be self-descriptive. This program declares all necessary input and will create the search index on a click.

Alternatively: you may use the command line interface

Tip: you do NOT have to use the command line, if you don't want to. There is a graphical user interface, which you can use by running the file "start.bat". If this does not work, or you don't like it, you still can run the program from the command line.

You can use the DOS command line, or utilize the file "suchIndexErstellen.bat" to run it.

By default this file is set to search the current directory plus all subdirectories. If you want to change this, edit the file or run the program manually from the command line. Otherwise it is enough to simply execute the file by double-clicking it.

Using the command line interface:

java -classpath index.jar suchindexErstellen FOLDER FOLLOW META_TAGS

FOLDER: is the directory that is to be searched E.g. the current directory is .\ the parent directory is ..\ if the directory would be "home", use home\ aso.
Please use relative path names only (e.g. not C:\\...) otherwise files would be linked to your local hard disk.
FOLLOW: lets you choose whether subdirectories should be searched as well, or not. Choose true if you want this to be done or false if not.
META_TAGS: here you may choose, whether keywords included in meta tags should be included in the search results or not. Select true if you want this, or false if not.

  Examples:

1) If your front page is at "C:\Homepage" and your search engine is installed at "C:\Homepage\search", the Java files need to be copied to "C:\Homepage\search". Here state the following call (at the command line):

java -classpath index.jar suchindexErstellen ..\ true true

2) Given your web pages are stored in "C:\Homepage", again your search engine is stored at "C:\Homepage\search", but you just want to search the directory "C:\Homepage\test" without subdirectories. Then enter the following:

java -classpath index.jar suchindexErstellen ..\test\ false true

3) Given your web is (again) stored in "C:\Homepage", but this time your search engine is in the same directory and you just want to search the directory "C:\Homepage\test" and it's subdirectories, then the call would be:

java -classpath index.jar suchindexErstellen .\test\ true true

Batch file:

Open the file searchIndex.bat in a text editor of your choice (e.g. Notepad). Here you will find the same call as stated above, including the description. Change the settings as you see fit, save and start the file.

Using the administrator's menu to upload the search index
  1. Open the front page of the Yana Framework installed on your server in your web browser.
  2. Log in using your user name and password. Please note that you require administrator privileges to be able to save the search index on the server.
  3. At the "Sitemap" click on "show administration menu".
  4. Make sure, the setup plug-in of the search engine and the search engine itself are activated.
  5. Choose "Search engine Setup" from the options menu (see following figure).
  6. In the menu "upload search index" click the button "choose" to select the files of the search index stored on your local computer
  7. Click on "Go" to start the upload

Figure: Form to upload the search index at the administrator's menu

Creating the search index online

As an alternative to creating the search engine offline at your local computer, you may also have it created for you online on your web server. This way you don't have to upload the index files to the web server. On the other hand, all files, that are to be searched, need to be available on the same web server at the same time. In addition you can't check the files by hand, if you want to, before putting them online.

Using the administrator's menu
  1. Open the front page of the Yana Framework installed on your server in your web browser.
  2. Log in using your user name and password. Please note that you require administrator privileges to be able to create the search index on the server.
  3. At the "Sitemap" click on "show administration menu".
  4. Make sure, the setup plug-in of the search engine and the search engine itself are activated.
  5. Choose "Search engine Setup" from the options menu (see following figure).
  6. Scroll to section "create search index online".
  7. Click on "Go" to start building the index.

Figure: Administrator's menu to create a search index

Frequently Asked Questions

What informations are stored?

The database contains all words found in a document, "key words" and the title of the page.
The search is restricted to HTML documents (*.htm, *.html, *.xml, *.shtml). Beside the found words, the page's URL and first 80 characters as a description are stored too.

All directories whose names begin with an underscore: "_" are ignored. The reason is that some web site making programs (e.g. Frontpage) use this to mark system directory that, of course, are not to be searched.

Some documents have changed. What am I to do to have the new files found as well?

Create and upload a new search index using either " searchIndex.bat " or " start.bat ". Both files " keywords.dat " and " documents.dat " need to be replaced. Also flush the cache of the search engine (if any) stored in directory " cache/ " by deleting the file(s) " *.cache ".

I have created a new search index, but the old results are still viewed.

This may occur while old data is still in the cache of the search engine. To do this open directory " cache/ ", delete all files with the extension " *.cache " and try again.

Nothing or 0 hits are shown.

Displaying the hit list requires JavaScript. This makes the search engine extremly fast and relieves the server load, since no data has to be requested from the server while going through the pages of results. However: to be able to do this JavaScript has to be activated in your web browser. If the page stays empty, check if JavaScript is deactivated or blocked by your firewall software.

What is the difference between "start.bat", "suchIndexErstellen.bat" and "searchIndex.bat".

In directory "tools" there are 3 files "start.bat", "suchIndexErstellen.bat" and "searchIndex.bat". They all do the same thing. Just that "start.bat" offers a neat graphical interface, which asks all necessary data. The other two start the program directly at the command line. Here you need to adjust the arguments by hand. Here "searchIndex.bat" will show an explanation of the arguments in English, while "suchIndexErstellen.bat" will show the same text in German. Choose the solution, which suits you best.

I'm getting an error: "Error 500 search index not found".

Probably you don't have created the search index yet, or the files are stored in the wrong directory. To find out how to create a search index see the section "Creating the search index".

I'm getting an error: "Error 404 page not found".

Probably you have forgotten to copy some important files to the internet. Please check if all files are present.

When searching I'm getting an error: "Error 403 access denied".

This may occur if insufficient access rights are set. Use your FTP program to check whether the following folders: " cache/ " and " config/ ", plus all subdirectories and files have sufficient access rights set (should be: readable AND writable). For UNIX you can set this by using the command CHMOD 777. A detailed explanation of access restrictions can be found in the installation guide for beginners.

I tried to run the file "start.bat", but it didn't work.

Please check if you have at least version 1.4.2 of the Java Virtual Machine installed. To check this enter "java -version" at the command line. If you have an older version, download a more current version of the Java Virtual Machine for free from the internet at java.sun.com. Or try to use the file "searchIndex.bat" to run the program from the command line. While this is not as comfortable, it should even work with version 1.3 of the Java Virtual Machine.

Plug-in: RSS-to-HTML Factory

General introduction

This Plug-in loads a RSS file of your choice and produces a HTML page, which you can adapt according to your desires. To do so you can edit the template "skins/default/rss/template.html".

Who should use this plug-in?

The Plug-in is suitable for people, who already have or want to create a RSS feed on their web site and simultaneously want to offer a news page (for people that don't have or want a RSS reader software). With this Plug-in one spares the doubled maintenance of news page and RSS feed. Instead of separately writing new messages in HTML and XML, you only need to update the feed. The HTML page will be created automatically.

Installation

Make sure that you have the correct version, before you install this plug-in! You need at least version 2.7; this plug-in will probably not work with an older software.
  1. Unpack the archive and copy it's contents to the plug-in directory of the program
  2. Upload the files to the internet
  3. Open the administration menu in your web browser.
  4. You need to have "Administrator" privileges to install a new plug-in!
    ? all new plug-ins are deactivated by default, to make it more complicated for someone to smuggle in a foreign plug-in. You as administrator must activate the plug-in before it starts working.
  5. Next to the entry "Plugins" is a button labeled "reload list". Click this button.
    ? if this does not work as expected, please check, whether access restriction on file "config/plugins" are set to 777. If not, do it now.
  6. Look for the entry "RSS-Feed to HTML Factory" in the list of plug-ins. Next to the entry is a checkbox. Activate the checbox by clicking it.
  7. Below the entry "RSS-Feed to HTML Factory" is an entry labeled "Setup". Activate this also.
  8. When you are done, click "Save changes". A message will appear - accept notice by clicking on "OK".
  1. You can view a test page by calling index.php?action=get_news in your web browser
  2. If you want to bind in your own RSS feed, go to the administrator's menu and click on "RSS-to-HTML Setup" in the option's menu.
    ? You need to have "Administrator" privileges

Plug-in: Software Development Kit (SDK)

General introduction

The SDK supports the developer with a graphic user interface. The graphical interface leads through four steps, in which appearance, behavior and general information about the application and copyright will be gathered.

Installation

It is strongly recommended, that you install the SDK only on web servers, which are not accessible by the public. For example, on a local workstation, which is not accessible over the network. The SDK is used exclusively for the purpose of developing new applications for the Framework. To make best use of it, it is necessary that the user can work locally at the respective computers and has read and write permissions for the directories and files of the framework, which has the SDK installed. Or at least he should be in a position to have a remote connection, that allows to work with the same permissions like a local user.

Improper use could delete or overwrite existing applications of the Framework. It is therefore strongly recommended that backup copies of stored files are created regularly.

In order to install the SDK proceed as follows:

  1. Install and configure the YANA Framework, as described in the instructions.
  2. Make sure that the program has write privileges for the directories "plug-ins" and "skins/default".
  3. Log on as administrator, and go to the administration menu of the application
  4. Look for the "Software Development Kit" in the "plugins" menu and activate this plug-in. Save your changes.
  5. To start the SDK, select "Software Development Kit" from the application's sitemap.

If you wish an introduction on how to use the SDK, see chapter "Creating plug-ins and applications". For a practical example, see the introductory tutorial, at chapter 3 "Creating a new plug-in".

Preparation

Form 1

general information

Name of the software:
any text
Description:
any text HTML is NOT allowed.
Image:
choose an icon from the list, which represents your application

Form 2

Some information on you, the author of the software:

Author:
Your name
E-mail address:
Address for questions about your software. You may leave this field blank if you want.
Web site for updates:
a valid URL. If you have a web site on which you provide updates for this application, please enter the address here.

Form 3

Priority of execution:
This value determines the order in which plug-ins are executed. Security-relevant plug-ins (password queries, etc.) should have a higher priority, thus they are executed before other plug-ins on the series. Other plug-ins (log files, etc.) should have a lower priority.
Type of Application:
Valid values for type of application are:
"default", "primary", "config", "read", "write",  "security".
  • Members of group "security" will always have a higher priority than all the other plug-ins. This group is used for plug-ins that implement user authentication.
  • The group "config" receives only events of type "default", "security" oder "config". It is meant to be used for plug-ins, which provide functionality to edit configuration files and settings. Usually, these plug-ins serve as extensions to other plug-ins. So there is a dependency. This dependence can be expressed, among other things, by adding a definition, that both plug-ins are part of the same package.
  • The group "write" receives events of the type "write", "security" or "default". It is designed for plug-ins, which only write to a file or a database. This group is however seldom used in practice. It is mentioned here for reasons of completeness.
  • The group "read" receives events of type ?read?, ?security? or ?default?. It is meant to be used for plug-ins, which only read from a file or a database. This group is seldom used.
  • The group "primary" treats all events except "config". It is designed for plug-ins, which act as main programs.
  • The group "default" receives all events except those of type "security", "config" and "primary". It is intended for "other" plug-ins and as a solution for developers, who are uncertain about which group they should take for their plug-in.
requires the plug-in:
Id of another plug-in, which is required for your software to run. Use this field if you extend an existing application with new capabilities. The selection menu is used only as guidance. You don't necessarily need to use one of the Ids listed in this menu.
belongs to package:
use this field to show that two plug-ins belong together. Enter into this field a common "package", that both belong to.

Interface

If you want to set up other actions, you can define their interface here. To do so you may use one of the templates, which the SDK suggests. A manual creation is also possible. The interfaces can also be manually changed.

Form 4

For some actions their are standard templates, which can be used. Have a look at the select box. If an option is selected, a list of all generated actions is shown for comparison. You can review the entire interface by selecting the tab "edit interface manually".

Form 5

An interface contains the following information:

Name of action:
Name of the function (any text). May only contain characters A-Z, 0-9 and "_". Look at the examples for comparison.
Mode:
usually "standard". Safe mode is meant to be used in case you want or need to ensure, only default values are used.
Templates:
ID of the templates to be used. This id is defined in the skin. Look at the skin files in the directory "skins/default/*.config", or use the menu for an example! 
Access restriction:
selects the minimum privileges a user must have to be able to trigger this action.
Form 6

This option may be used to control the results, or as an alternative input interface for experienced users. You see all defined actions and can make changes.

Beware! Errors may cause, that the plug-in cannot be generated correctly.

Database

For database application a GUI can be generated automatically. To do so a structure file is required, which describes the database. From the information in this file, for each table the SDK creates matching template files, the appropriate actions and also generates the necessary additions to the interface of the application.

If you don't want to create such a database application, you may skip this step.

This will automatically create for each table: templates to view, edit, create and search database contents, plus the PHP-code for all required functions. In addition functions for downloading images and files from the database. Also a JavaScript file is created, which can be used to trigger actions on the server via Ajax. Last the structure file itself will be stored in the system.

Form 7

If you have not created a structure file yet, you may now create such a file. For this use the plug-in "DB-Tools". On the administration panel open the menu "DB-Tools" / "Import". Choose one of the options to import an already existing database. If you don't have any, please read the beginner's tutorial for a start. Also note the examples in the Developer's Cookbook.

Form 8

If you now got a structure file, input the path to this file here. Click on "search", if you are not sure where this file is stored.

Installation (SQL)

This step is optional. The framework (since version 2.8.6) is able to generate the required SQL statements for the installation automatically when needed by using the file structure defined in step 3. This works for MySQL, MS-SQL, PostgreSQL, Oracle databases, IBM DB2 and MS Access. Handiwork is (except in exceptional cases) unnecessary.

If no automatic generation is possible for your desired DBMS, you should write a SQL installation file yourself.

Form 9

Select the source file in SQL format, that contains the information to install the database.

You will find your SQL installation files in the folder "config/db/.install/" after finishing the process. This folder contains subdirectories for all supported DBMS. You may also copy your SQL-files to this location at a later stage. The file must have the same name as the structure file and use the extension ".sql".

Complete

To end the configuration, click "complete".

The SDK produces a skeleton of the application, all the necessary templates, as well as all the necessary configuration files. These include in particular: meta information, e.g. description of the plug-in's interface, meta information of required files, meta information of used templates.

The newly generated plug-in can be used immediately. You can find all files in the directories "plugins", "skins/default" under the ID code of the plug-in.

Developer's Manual

Before you start...

  1. Please always activate the debug mode first.
    1. Open the file "index.php".
    2. Change this text: 
      error_reporting(0);
      to this text: 
      error_reporting(E_ALL);
      ErrorUtility::setErrorReporting(YANA_ERROR_ON);
    3. Save and close the file.
    4. Please deactivate the debug mode before uploading the software to a public server.
  2. Make sure you have the plugin "SDK" installed and activated.
  3. Please read the tutorial first. 
  4. Please note the API documentation. 
  5. Code examples may be found in the Developer's Cookbook. 

Editors for the Yana Framework

General introduction

Many editors may make working with the Yana Framework easier. To aid you with translating, developing new plug-ins, or writing skins, syntax Highlighter and code templates are available for different editors.

The following section is to show you examples, how these tools can facilitate your work.

Integration in ConTEXT

Installation

For editing the configuration files, templates and plug-ins of the Yana Framework in "ConTEXT", syntax Highlighter and code templates are available ConTEXT is a programmer's editor. You can get it from the internet at http://context.cx. The program is free and offers support for far more than 50 programming languages and file formats. Among them the complete Yana Framework.

For the installation of the highlighter and template files for the Yana Framework, install ConTEXT first. Then open the installation directory of the editor and copy the "ConTEXT Highlighter files" (* chl) to the directory "Highlighters" and "ConTEXT Template files" (* ctpl) to the "Template" directory (see figure). If you have ConTEXT open already, please restart the editor now.


Figure: ConTEXT installation directory

ConTEXT offers the possibility of using the manual of the Yana Framework directly in the editor. To use this feature, do as follows:

1. Open the menu "Options" > "Environment Options"


Figure: Open environment options

2. Switch to the menu "Miscellaneous" and choose the entry "Yana Framework" and / or "Yana Framework Templates" from the list.

3. Click the button "edit".


Figure: Associate documentation

4. Select the file type "all files (*.*)", search your local hard disk for the file "index.html" of this manual and mark this file with your mouse.

5. Click on "Open"


Figure: Choosing file "index.html" as front page

5. Save your changes by clicking "OK".

You can get all the required files at the following web sites:

Use

1. Example: editing a database schema file

Open a configuration filei (e.g. a database schema at folder yana/config/db/) using ConTEXT and choose the highlighter "Yana Framework".


Figure: Working with database schema files in ConTEXT

2. Example: Editing a template

Open a template file with ConTEXT and choose the highlighter setting "Yana Framework Templates".


Figure: Working with a template file in ConTEXT

3. Example: Using text blocks / code snippets

To insert a code snippets use the shortcut <CTRL> + <j>, or choose "Insert Code from Template" in menu "Format". A list will be displayed. It consists of two parts: left you will find the keyboard shortcuts and on the right a description. Select a code template using your mouse or the cursor keys and press <ENTER> to insert it.

You may narrow the selection, by typing the first character of the template. If only 1 templates fits for insertion, the list will not be shown and the template will be inserted directly.

E.g.: fast creation of a database schema file

  1. Open a new file in ConTEXT
  2. Select the highlighter "Yana Framework"
  3. Enter the shortcut "db" and press <CTRL> + <j> to create the file body
  4. Enter "tbl" and press <CTRL> + <j> to create a new table
  5. Enter the name of the table (repeat it at the closing tag)
  6. In the section "Primary_Key" enter "id" as the name of the primary key
  7. Go to the section "CONTENT", enter "id" and press <CTRL> + <j> again to have the primary key column created automatically
  8. After the column "id" enter a new line, then enter the text "string" and press <CTRL> + <j> to create a new column of type "string"
  9. Name the column (repeat name at closing tag)
  10. In the section "Description" you can enter a description for the current column

The result is demonstrated in the following figure.


Figure: Creating a new table the easy way

See the list for more shortcuts.

Integration in PSPad

Installation

For editing the configuration files, templates and plug-ins of the Yana Framework in "PSPad", syntax Highlighter and code templates are available ConTEXT is an editor for web developers. You can get it from the internet at http://www.pspad.com. The program is free ware.

PSPad can also be used on an USB device without installation.

To install highlighter and template files for the Yana Framework please install PSPad first. Then open the installation directory of the editor and copy the "Highlighter files" (*.ini) to the directory "Syntax" and "Template files" (*.def) to the "Context" directory (see figure). If you have PSPad open already, please restart the editor now.


Figure: PSPad installation directory

To finish the installation you have to activate the new files in PSPad.

  1. To do so, open the menu "Settings" > "Highlighter Settings" in PSPad.
  2. Click on a free slot (these are marked by the text "not assigned")
  3. Go to menu "Specification" (see figure)
  4. From the menu "User Highlighters" choose the entry "Yana Framework" and / or "Yana Framework Templates"
  5. To activate the highlighter by clicking the checkbox next to the name
  6. Please click on "OK"


Figure: Activating the highlighter

You can get all the required files at the following web sites:

Use

1. Example: editing a plug-in

Open a plug-in definition file (plugins/*.config) in PSPad.

To select the highlighter click the button "Change document syntax highlight" (see figure).


Figure: a configuration file for the Yana Framework opened in PSPad, on the right an active code browser

Select the entry "Yana Framework" or (when editing templates choose "Yana Framework Templates") and click "OK".


Figure: Choosing syntax highlighting

2. Example: using text blocks (Clips)

As in ConTEXT you can use prepared blocks of text in PSPad. These are called "Clips" in PSPad and can be viewed by using the keyboard shortcut <CTRL> + <SPACE>.

Use of this feature is identical to that in ConTEXT. A menu will be shown. It consists of two parts: left you will find the keyboard shortcuts and on the right a description. Select a code template using your mouse or the cursor keys and press <ENTER> to insert it.

You may narrow the selection, by typing the first character of the template.


Figure: using "Clips" in PSPad

Creating templates and skins

Editing HTML-templates

This program uses so called "templates". What ever you change on these pages will also change the look and feel of your application. These files are stored in the "skins" directory. You can open and edit these files like any normal HTML page in your web editor. For changing the files no knowledge in HTML is necessary, however: it will come in quite handy, if you plan on implementing a larger amount of changes.

In any case you should make copies of the original files, before you apply any changes.

Step-by-step instructions

  1. In the home directory of your application, you will find a directory with the name "skins/", in this directory you will find all the currently installed skins.
  2. To add a new skin theme, first copy the contents of the directory "default/" including its subdirectories to a new directory with the name of your skin. For example, "skins/mytheme/".
  3. Open the file "index.html" in a web editor of your choice. You can also use Notepad or, under Unix / Linux, Emacs.
  4. Adjust the file as you see fit, and save your changes.
  5. Finally, you must create a configuration file for your skin. This file contains the names of your skin and a description of the author. You can use the file "skins/default.config" as a sample. Copy this file to another name. For example, "skins/mytheme.config".
  6. This skin file can by edited in any text editor. Open the file, modify the data as required. Make sure that the file paths are correct. Then save your changes.

Tutorials on creating your own templates can be found on the at Smarty's web site:http://smarty.net

Configure templates and skins

If you are not just editing an existing template, but want to create a new one - or perhaps even a new skin -, then it is not enough to just edit template files. You must also understand and be able to apply the configuration of templates and skins.

However, this is very easy and limited to two configuration files. One for your new templates and the other for the skin.

Configuration of Skins

Here is a simple example.

<SKIN_INFO>
    <NAME>my theme</NAME>
    <DESCRIPTION>description of my theme</DESCRIPTION>
    <AUTHOR>my name</AUTHOR>
    <CONTACT>my web site</CONTACT>
    <LOGO>%SKINDIR%mytheme/data/preview.gif</LOGO>
    <DIRECTORY>mytheme/</DIRECTORY>
</SKIN_INFO>

The syntax is largely self-explanatory. The fields "Name", "Description", "Author" and "Contact" are free text fields. For the "name" you can have any name, which describes your theme. The same applies to the field "Description". Make sure that the only allowed whitespace characters are simple spaces. You can add a line break, if necessary, by writing the text "[br]". The field "Author" should be your name and "Contact" usefully an e-mail or Internet address, where the user may view your web site or download updates.

The field "Logo" is a preview image. The place holder %SKINDIR% refers to the directory in which the skins are saved and should always be used. The graphic should preferably be in GIF, PNG or JPEG format, and about 300x300 pixels in size.

The "Directory" must always be specified. It names the directory in which you theme is stored.

Configuration of templates

Here is a simple example.

<MY_TEMPLATE>
    <FILE>template.html</FILE>
    <STYLE>
        <0>style/default.css</0>
        <1>foo1/foo2.css</1>
        <MY_CSS>foo3/foo4.css</MY_CSS>
    </STYLE>
    <SCRIPT>
        <0>foo5/foo6.js</0>
        <MY_SCRIPT>foo7/foo8.js</MY_SCRIPT>
    </SCRIPT>
    <LANGUAGE>
        <0>guestbook</0>
        <1>admin</1>
    </LANGUAGE>
</MY_TEMPLATE>

Note that "MY_TEMPLATE" acts as an identifier, which identifies the template. You can use this Id in the configuration files for your plug-ins to refer to the template. The text can be chosen arbitrarily. However, be careful that the Id contains no special characters or spaces, it must begin with a letter and must by unique throughout the entire application. The Id is not case-sensitive.

There are 4 fields: "File", "Style", "Script" and "Language". The field "File" names the file name. The other fields are optional and serve automation.

The field "Language" contains references to one or more files, which provide text strings in various languages for this template. For example, the value "guestbook" refers to the file "languages/XX/guestbook.config", where the text "XX" is automatically replaced by the system with the name of the user's language of choice. E.g. "de" for German or "en" for English.

The field "Style" can contain multiple references to stylesheets. Similarly, the field "script" contains links to one or more JavaScript files. These files are automatically included when the template is called.

Notice: If in a skin a required template is not defined, or properties of this template ( "Styles", "script", "Languages") are not declared, the program automatically falls back to the "Default"-skin settings.

This is done as follows.

  1. If in the current skin a template is undefined, take over the default settings for the missing template completely.
  2. If the template exists, but a property, e.g. "Style" is undefined, then it is taken from the "Style"-definition of this template in the default-skin settings. For "Language" and "Script" this is done in the same way.
  3. If the template exists and the property is defined, then the defined properties are merged with those of the default-skin.
    The program does this as follows:
    • All numeric entries are always appended. For example: the entries STYLE.0, STYLE.1 aso. of the default skin are taken, nexte all entries STYLE.0, STYLE.1, STYLE.2 aso. from the current skin are appended to this list.
    • Named entries, for example STYLE.MY_CSS in the default-skin, are overwritten by entries of the same name in the current skin.

This behavior may seem illogical at first, but has a quite a deeper meaning. It allows to exchange individual style sheets, or scripts in a newly created skin. At the same time it also allows individual style sheets, or scripts to be excluded from this mechanism. Both variants are frequently used and have their own application areas.

Another example to illustrate this behavior. For the Template "MY_TEMPLATE", the following definition exchanges only the CSS file "MY_CSS" defined int the default skin with another file. All other settings remain unchanged.

<MY_TEMPLATE>
    <STYLE>
        <MY_CSS>foo/bar.css</MY_CSS>
    </STYLE>
</MY_TEMPLATE>

As you can see in this example, no new HTML template is defined, but the setting of the default skin is used. This means that there is no need for the HTML template from the default-skin to be copied to the new skin, or even edited, to change the layout. This avoids unnecessary redundancy and makes it easier for you to maintain your HTML code.

List of permitted principal wildcards

* "required" means in this case "unequal NULL"

Wildcard Type Required * Default value Origin Description
PHP_SELF String yes - PHP Name of the executed script file
REMOTE_ADDR String yes - PHP IP address of client.
SETTINGS.WEBSITE_URL String no - calling URL complete URL of the script (incl. http://...)
REQUEST_URI String no - calling URL called path of the script, including parameters (/yana/index.php?...)
QUERY_STRING String no - calling URL list of parameters passed to the script
ID String yes default GET/POST ID-Code of the current profile.
ACTION String no <empty> GET/POST Name of the currently (to be) executed action.
TARGET String no <empty> GET/POST Target of the currently executed action. (e.g. the ID of a data set)
SESSION_NAME, SESSION_ID String no <empty> GET/POST Name and ID-Code of the current session (should be added to all hyperlinks)
PAGE integer no 0 GET/POST The number of the data set from which the show started. (If required)
ATIME string yes - File property Insert date and time when the HTML-template was last accessed at this place:
[%$ATIME|date%]
MTIME string yes - File property Inserts at this place the date, when the HTML-template was last changed:
[%$MTIME|date%]
CTIME string yes - File property Inserts at this place the date, when the HTML-template was created:
[%$CTIME|date%]
LANGUAGE Array yes - Language file(s) List of all text strings defined in the currently selected translation.
PROFILE Array yes - Profile file List of all settings in the currently used profile.
The following are some examples. Note that all fields mentioned here may also contain a NULL value.
PROFILE.BGCOLOR,
PROFILE.BGIMAGE
String no - Profile file Background color and image of the web site
PROFILE.PFONT,
PROFILE.PSIZE,
PROFILE.PCOLOR
String no - Profile file font family, size, color of paragraphs (P-tags)
PROFILE.HFONT,
PROFILE.HSIZE,
PROFILE.HCOLOR
String no - Profile file font family, size, color of headings (H1-,H2-,H3-tags)
SKIN_INFO Array no - Skin file Informations on the selected Skin
LANGUAGE_INFO Array no - Language file Informations on the selected language
LANGUAGE Array no - Language file(s) Translations (texts)
SETTINGS.WEBSITE_URL String no - Language file(s) complete URL of the script (incl. http://...)

Reference manual of the template engine

General introduction

This Framework uses the "Smarty Template-Engine". This engine has it's own documentation, which you can download from the internet for free at http://smarty.net/docs.php.

You are permitted to expand the engine with your own functions. In this way, you can extend the syntax of the templates used You will find the engine in the directory "engine/". A guide can be found in the reference manual of the engine. Changes to the engine, are only recommended for experts.

About licenses: Before you make any changes, please note the license information.

Modifications of the syntax

The syntax of the used version of "Smarty template engine" differs in some aspects from that described in the reference manual. The following section describes this modified syntax.

Token delimiters

Unlike in the original version of Smarty, this version does not use the characters'{' and '}' as the opening and closing delimiters of Smarty codes, but the strings: '[%' and '%]'. For example, NOT "{$variable}", but "[%$variable%]". The reason is that the original characters are frequently used in many HTML documents, as they may contain scripts or stylesheets, which may cause problems when processing the templates. This is why the delimiters have been changed. (This is also recommended by the manual of the engine as a standard solution for any such cases.)

Unlike in the original, this version also allows the use of an abbreviated syntax for inserting simple variables. Instead of "[%$VARIABLE%]" this version allows the more comfortable syntax: "%VARIABLE%". However, this only applies to variables. This can NOT be used with any other Smarty-codes.

Naming conventions

Because Smarty codes are case-sensitive, it has been decided to write all variable names in capital letters for simplicity. While the names of functions and other codes are to be written in small letters. For the sake of clarity, is strongly recommended, to keep this convention.

Paths

In this version references to files (links, graphics, CSS, scripts, etc.) can be given as relative path information. File paths are automatically corrected when the page loads. It is therefore no longer necessary, to use absolute file paths only.

Configuration

Smarty is set to run in Safe Mode (security=true). The embedding of PHP code into templates is not permitted. In addition, the following features are disabled: include, include_php, php.

New functions

Note: The following section describes additional features, which are available to you in the Yana Framework and are not part of to the basic functionality of the Smarty template engine. The basic functions of the Smarty template engine are described in the documentation at http://smarty.net/docs.

captcha

[%captcha [ id="<string>" ] %]

Attribute Type Required Default value Description
id string no - Value of the attribute "id" of the tag "input"

The function "captcha" is used to view a graphic, which contains text, that the user must read and enter to the input field next to it. This is a common process to avoid spam.

The parameter "id" is optional. For example, it can be used, when a description is to be shown in a "label".

1. Example:

[%captcha%]


Figure: Display in browser

2. Example, using parameter "id":

<label for="myCaptcha">Please enter the text on the image</label>
[%captcha id="myCaptcha"%]

For further details see also the Developer's Cookbook, chapter "Using a CAPTCHA".

create

[%create template="<string>" file="<string>" table="<string>" [ where="<string>" ] [ desc="<boolean>" ] [ sort="<string>" ] [ page="<integer>" ] [ entries="<integer>" ] [ titles="<boolean>" ] [ on_new="<string>" ] [ on_edit="<string>" ] [ on_delete="<string>" ] [ on_search="<string>" ] %]

Attribute Type Required Default value Description
template string yes - Name of template to be used when creating the GUI. Allowed values are:
view_seperated, view_details, view, new, edit, search
file string yes - Path of the structure file which is to be used.
table string yes - Name of the table which is to be used.
show string no - Comma-separated list of columns of the table, which should be displayed (use small letters)
hide string no - Comma-separated list of columns of the table, which should be NOT displayed (use small letters)
where string no <empty> Used to generate the WHERE-clause for the SQL-statement.
Syntax: <COLUMN1>=<VALUE1>[,<COLUMN2>=<VALUE2>[,...]]
desc boolean no false Selects whether entries are to be sorted in ascending (false) or descending (true) order.
sort string no <primary key> Name of column to sort entries by.
page integer no 0 Number of first entry to be shown.
entries integer no 5 Total of shown entries.
titles boolean no true Selects whether attribute names are to be shown or not.
on_new string no - Name of action to be triggered, when a new entry is to be saved.
on_edit string no - Name of action to be triggered, when changed entries are to be saved.
on_delete string no - Name of action to be triggered, when chosen entries are to be deleted.
on_search string no - Name of action to be triggered, when a search request is to be handled to create a hit list.
on_download string no download_file Name of action to be triggered, if the user wants to download a file or image.
layout int no 0 If a form has multiple ways to be displayed, you can use this parameter to switch between them.

The function "create" automatically generates a GUI for the chosen database. Condition is the existence of a valid structure file, which describes the database.

Example:

[%create template="edit" file="database.config" table="bookstore"%]

An example of a function to download a file (argument on_download "):

<?php
function myDownload($ARGS)
{
   if ($source = DbBlob::getFileId() === false) {
       return false;
   }
   // Downloading a file:
   if (preg_match('/\.gz$/', $source)) {
       $dbBlob = new DbBlob($source);
       $dbBlob->read();
       header('Content-Disposition: attachment; filename=' . $dbBlob->getPath());
       header('Content-Length: ' . $dbBlob->getFilesize());
       header('Content-type: ' . $dbBlob->getMimeType());
       print $dbBlob->get();
       // Downloading an image:
   } else {
       $image = new Image($source);
       $image->outputToScreen();
   }
   exit;
}
?>

Attention! This function does not automatically check, whether the user is "allowed" to download the selected file in the sense of your program. This is unfortunately not possible without knowing the criteria that you want to implement in your program. Therefore, it remains at this point to you to verify that the user has the necessary permissions to view the file.

embeddedTags

[%embeddedTags [ show="<string>" ]  [ hide="<string>" ] %]

Attribute Type Required Default value Description
show string no * comma-separated list of tags,
that are to be shown
hide string no - comma-separated list of tags,
that are to be hidden

The function "embeddedTags" automatically generates buttons, that allow users to use text formatting (bold, italic, etc.) when writing a new entry.

The list "show" can be used to select which tags should be displayed, while the list "hide" can be used if you want to hide certain tags.

In the list "show", you can use the characters "-" to insert line breaks and the characters "|" to create a vertical separator in the output.

Example:

<textarea id="text"></textarea>


Figure: Displaying a tag bar (wih all tags)

<!-- Include all tags -->
[%embeddedTags%]
<!-- Display tags "u", "i" and "b" only -->
[%embeddedTags show="b,u,i"%]
<!-- All tags except "url" and "mail" -->
[%embeddedTags hide="url,mail"%]
<!-- Tags "u","i","b", a seperator, then the tag "url" -->
[%embeddedTags show="u,i,b,|,url"%]

import

[%import [preparser="true"] file="<string>"%]

[%import [preparser="true"] id="<string>"%]

Attribute Type Required Default value Description
file string yes - Path of the file to be included
preparser boolean no false selects whether the file should be imported before (true) or while (false) parsing the template

Or alternatively with the following parameters:

Attribute Type Required Default value Description
id string yes - Id of the template to be included
preparser boolean no false selects whether the file should be imported before (true) or while (false) parsing the template

The function "import" replaces the various file-insertion-functions of the original by one single function. It also differs in the way the files are treated. The function "import" treats each imported file as a separate template. This has several advantages: the inserted template uses e.g. security restrictions as the base template. In addition (and more importantly:) this approach ensures that any given relative paths can be corrected / changes properly.

The Parameter "file" is the name of the file to be imported.

Alternatively, the parameter "id" can be used, so instead of hardwiring the path and file name directly in the template, you may use the Id of the template. The framework resolves the Id to get the appropriate file name automatically at runtime. This has the advantage, that if the file is renamed, or moved to a different path, you don't need to check all templates by hand to correct all references to this file.

Application example: assume you have a template "a.html" defined in skin "default", which imports another template "b.html". Now you want to add another skin theme. In this skin theme you want to change the content of file "b.html", but want to leave the file "a.html" unchanged.
1st Case: If in skin "default" you had file "b.html" imported using the parameter "file", then you cannot change "b.html" in your new skin theme, without changing the path value in "a.html". This means, you must change the file "a.html" within your new skin theme as well, as both files have a direct dependency.
2nd Case: If in skin "default" you had file "b.html" associated with an Id (say "b") and imported the template using the parameter "id", then you can overwrite the id "b" in your new skin theme with the path of another template, which is then only used in this skin. This means, you don't need to change "a.html" and may keep the template unchanged.
Note: Assignment of IDs to files is done via the configuration files (*. config) in the directory of the templates.

The flag "preparser" determines when a file is to be imported: BEFORE or DURING the parser handles the template. If it set, the file is imported BEFORE the actual parsing. The flag "preparser" for example, should always be used if the imported templates need to have access to variables that are set in the base template. A classic example are templates, that are imported inside the body of a loop. ( "Foreach", "section) These require the flag "preparser", if you need to access loop variables. Alternatively, you can use the statement "import" with a list of all the variables you want to access within the template.

Attention! If the parameter "preparser" is used, the imported template is not parsed, translated and cached on it's own, but copied in the source code of the template that contains the import statement. This means, it is not checked at runtime, if the content of the imported template has changed since when the templates was last compiled. If you change the imported template, you must empty the template cache for the changes to take effect.

1. Example, with parameter "file":

File "hello_world.html"
Hello World!

File "template.html"
<p>[%import file="hello_world.html"%]</p>

2. Example, using parameter "id":

File "default.config"
<hello>
  <file>hello_world.html</file>
</hello>

File "template.html"
<p>[%import id="hello"%]</p>

3. Example, additional parameters:

File "hello_world.html"

[%if $foo%]
[%$bar%]
[%/if%]

File "template.html"
<p>[%import file="hello_world.html" foo=true bar="Hello World!"%]</p>

Output (for all 3 examples):

<p>Hello World!</p>

preview

[%preview [ width="<integer>" ] [ height="<integer>" ] %]

Attribute Type Required Default value Description
width integer no 380px Width in pixel
height integer no 50px Height in pixel

The function "preview" creates a preview window on an entry, that the visitor wrote.

Smilies and tags contained in the text are graphically displayed. This gives the visitor the opportunity to check how his entry would be displayed, before he submits the form.

When you call the function all required JavaScript files will be loaded automatically. In addition a button labeled "Preview" will be created. When this button is clicked a preview window will be displayed above the button.

Example:

<textarea id="text"></textarea>
[%preview%]


Figure: The preview will be loaded by clicking the button
"Preview" via an "AJAX HTTP-Request" to the server


Figure: Preview window as it is shown in a browser

The attributes "width" and "height" set width and height of the preview window. These values are interpreted as CSS instructions. So if you want to use this, please note that it is required to add the unit of measurement (for example, "px" for pixels). Percentage values, like "70%" are allowed as well. Numbers without a unit, like "70" may however be displayed incorrectly be the browser.

These values do not refer to the entire size of the preview window, but to the text displayed inside it. The following figures demonstrates this:


Figure: Margins of box

The purple colored area here is the space defined by the attributes "width" and "height". The light green area is the total size of the field. The space required for the heading is shown in light blue. The border is indicated in violet. So the total size of the window depends on the values of the attributes "width" and "height", plus the heading, the border and the peripheral area (margin) of the window.

printArray

[%printArray value="<array>" %]

Attribute Type Required Default value Description
value array yes - an array, that is to be printed as a string

This functions converts a (possibly multidimensional) array to a string and prints it. The array is displayed in SML-format. This can, inter alia, be of interest for the debugging of templates, to find out the name of a variable.

? Note: Since version 2.9.3 of the Yana Frameworks this function supports syntax highlighting.

Example:

[%printArray value=$LANGUAGE%]


Figure: a short excerpt from the output

printUnorderedList

[%printUnorderedList value="<array>" [ keys_as_href="<bool>" ] [ layout="<int>" ] %]

Attribute Type Required Default value Description
value array yes - an array, that is to be printed as a list
keys_as_href bool no false selects, whether links are to be created
layout int no 1 lets you chose between 3 alternative layouts (1, 2 and 3) for your menu

This functions converts a (possibly multidimensional) array to an unsorted list in HTML-format and prints it. It uses the tag "ul".

You can use this function to create tree menus. You may chose the create links within this menu. To do so, use the URLs as a key of the array and the caption as values. Moreover set the parameter "keys_as_href" to "true".

1. Example ? an array presented as a tree menu:

[%printUnorderedList value=$LANGUAGE%]

 


Figure: a short excerpt from the output

 

2. Example ? output a tree menu with links:

<?php
$A = array(
    '1.html' => 'Link',
    'Menu 1' => array(
        '2_1.html' => '1. Item',
        '2_2.html' => '2. Item',
        'Menu 2' => array(
            '2_3_1.html' => '1. Item',
            '2_3_2.html' => '2. Item'
        ),
    ),
    'Menu 3' => array(
        '3_1.html' => '1. Item',
        '3_2.html' => '2. Item',
        '3_2.html' => '3. Item'
    ),
);
$YANA->setVar('A', $A);
?>

[%printUnorderedList value=$A keys_as_href="true"%]

 


Figure: Displaying a tree menu with links

3. Example ? additional layouts:

[%printUnorderedList value=$A layout=2 keys_as_href="true"%]


Figure: A vertical tree menu

[%printUnorderedList value=$A layout=3 keys_as_href="true"%]


Figure: a horizontal tree menu

The keys are printed in bold and values in italics - however, this may be changed via CSS. The following CSS classes are available:

rss

[%rss [ image="<string>" ] %]

Attribute Type Required Default value Description
image string no - The icon, which the visitor can click to view the RSS feed.

This function creates hyperlinks to view the RSS feed of a web site.

The argument "image" can be used to select another icon. If the user clicks on this link, the file is opened. The list of the RSS feeds will be inserted in the source code of the application. See also the Developer's Cookbook, Chapter: "Misc. functions".

? Tip: Even if you do not create hyperlinks, your visitors are still able to read the RSS feeds of your web site, because the links are also provided in the header of the file. This information allows the browser of the visitor, if it is able to, to find and read the feeds. However, this variant is more cumbersome for your guests and therefore should be avoided.

Example:

[%rss%]

RSS
Figure: Output of this function

Example ? adding a feed to the output:

<?php
// The value 'my_rss' is the name of the function, that outputs the RSS feed
RSS::publish('my_rss');
?>

? Note: In the skins provided with this software, the creation of hyperlinks to RSS-feeds works automatically, so that there is generally no need to include this feature by hand, unless you decide to write your own skin theme. In this case you should include the hyperlinks in the head area of the site, where they can be noticed by visitors.

sizeOf

[%sizeOf value="<mixed>" [ assign="<string>" ] %]

Attribute Type Required Default value Description
value mixed yes - scalar value or array, whose length is to be counted
assign string no - Name of the template var, that result should be saved to

The function "sizeOf" returns the length of an array or a string as a number. If the parameter "assign" is specified, then the return value is stored in the named variable and the function does not return a result.

Example:

<td colspan="[%sizeOf value=$cols%]"> ... </td>

Example using "assign":

[%sizeOf value=$cols assign="foo"%] <td colspan="[%$foo%]"> ... </td>

smilies

[%smilies [ width="<integer>" ] %]

Attribute Type Required Default value Description
width integer no 1 Number of columns in a table

The function "smilies" automatically generates a table of all available emot-icons / smilies, with a link to paste them into a specified input field.

Example:

<textarea id="text"></textarea>
[%smilies width="5"%]


Figure: Table of emot-icons (5 per row)

smlLoad

[%smlLoad file="<string>" [ section="string" ] [ scope="string" ] %]

Attribute Type Required Default value Description
file string yes - Number of columns in a table
section string no - ID of an input- or text area field
scope string no local Valid values: "local", "global", "parent", "template_vars"
global boolean no false deprecated

This function provides the same interface as the Smarty function "config_load". However, this function works with SML files with the file extensions "*.sml", "*.cfg" or "*.config".

Unlike the conventional configuration files of the Smarty engine, SML files may use multidimensional array definitions.

Moreover, the argument "scope" may have the value "template_vars", which means that the variables of the SML file can be used as template variables - this allows to access multidimensional arrays in standard smarty syntax, like any other variable. This feature only applies to smlLoad() and not to config_load().

Example:

The directory "skins/.config" contains a file "foo.sml". It has the following contents:

<TEST>
    <A>1</A>
    <B>
        <C>2</C>
    </B>
</TEST>

The template contains the following:

[%smlLoad file="foo.sml" section="TEST.B" scope="template_vars"%]
[%$C%]

Output: 2

[%smlLoad file="foo.sml" section="TEST.B"%]
[%#C#%]

Output: 2

[%smlLoad file="foo.sml" scope="template_vars"%]
[%$TEST.B.C%]

Output: 2

sml_load

The function name "sml_load" is an alias for the function "smlLoad". You can use both names alternative.

Description of this function.

varDump

[%varDump var="<mixed>" %]

? Note: This function is only meant to be used for debugging purposes. It is only available, if the framework is run in debug mode. If this mode is deactivated, any try to call this function will result in an error. This serves the security of your application.

Attribute Type Required Default value Description
var mixed yes - Value to be output

The function "varDump" outputs the value of a variable.

Example:

[%varDump value=$INSTALLED_LANGUAGES%]

Output:

array ( 'de' => 1, 'en' => 1 )

New modifiers

Note: The following section describes additional modifiers, which are available to you in the Yana Framework and are not part of to the basic functionality of the Smarty template engine. The basic functions of the Smarty template engine are described in the documentation at http://smarty.net/docs.

css

[%$foo|css%]

Creates a link to a given path to a CSS-Datei.

Example:

[%"default.css"|css%]

Output:

<link rel="stylesheet" type="text/css" href="default.css">

date

[%$foo|date%]

From a variable containing an UTC, this modifier creates a JavaScript statement, that outputs the current time using the correct time zone of the client browser.

Example:
Template created: [%$CTIME|date%]<br>
Template modified: [%$MTIME|date%]<br>
Template last accessed: [%$ATIME|date%]<br>

Output:

Template created: 1/12/2006
Template modified: 19/1/2007
Template last accessed 21/1/2007

embeddedTags

[%$foo|embeddedTags%]

This modifier replaces all embedded tags in a variable by the appropriate tags, as prescribed by YANA's syntax.

Example:

$foo = "Text [b]Text[/b] Text"

Output:

Text <span style="font-weight: bold;">Text</span> Text

href

[%$foo|href%]

Generates a link to the file index.php including all required parameters. The string $foo is appended to the end of the URL The URL will automatically be embraced by double quotation marks (").

Example:

<a href=[%"action=myplugin_new_entry"|href%]>new entry</a>

Output:

<a href="index.php?sessid=foo&amp;id=example&amp;action=myplugin_new_entry">new entry</a>

smilies

[%$foo|smilies%]

Replaces all icon codes in a variable by the appropriate icons.

Example:

$foo = "Text :example: Text"

[%$foo|smilies%]

Output:

Text
<img border="0" hspace="2" src="common_files/smilies/example.gif">
Text

url

[%$foo|url%]

Generates a link, just as the modifier "href", but outputs an absolute path without quotation marks.

Example:

<meta http-equiv="Refresh" content="2; URL=[%"action=myplugin_new_entry"|url%]">

Output:

<meta http-equiv="Refresh" content="2;
URL=http://.../index.php?sessid=foo&amp;id=example&amp;action=myplugin_new_entry">

Attention!  The following will not work: <a href="[%"action=foo"|url%]">link</a>
Result: <a href="skins/default/http://.../index.php?...">link</a>

urlEncode

[%$foo|urlEncode%]

Encodes a text so that can safely be used as parameters in a URL. Therefore it will use the PHP function urlencode().

Example:

<a href="?bar1=foo1&amp;bar2=[%$foo|urlEncode%]">

Output:

<a href="?bar1=foo1&amp;bar2=foo%20bar">

Creating plug-ins and applications

General introduction

For the Yana Framework all web applications are called "plugins". More complex applications may consist of several sub-systems. These subsystems may have several plug-ins, which work hand in hand. Some of these "subsystems" are already pre-implemented and only need to be used.

In this way, programmer's may save a lot of work. For example, you don't need to worry about checking passwords. There are already plug-ins, which will do this for you. Also the administration menu does not need the be rewritten, instead it just needs to be used. If you don't like a component, just replace or remove it. If a feature is missing, it can be added without changing to other plug-ins.

The creation of a new plug-ins will become simpler, if you are using YANA's SDK. It is a wizard, that gathers all necessary information and then automatically creates the new application for you. You simply have to adapt the result to your needs.

Software Development Kit (YANA-SDK)

The SDK supports the developer with a graphic user interface. The graphical interface leads through four steps, in which appearance, behavior and general information about the application and copyright will be gathered.
It is strongly recommended, that you install the SDK only on web servers, which are not accessible by the public. If you need installation instructions, see chapter "Software Development Kit / Installation".

Step 1: General information

Form 1

First some general information has to be given. See figure above.

Name of the software:
any text
Description:
any text HTML is NOT allowed.
Image:
choose an icon from the list, which represents your application

Form 2

Some information on you, the author of the software:

Author:
Your name
E-mail address:
Address for questions about your software. You may leave this field blank if you want.
Web site for updates:
a valid URL. If you have a web site on which you provide updates for this application, please enter the address here.

Form 3

Priority of execution:
This value determines the order in which plug-ins are executed. Security-relevant plug-ins (password queries, etc.) should have a higher priority, thus they are executed before other plug-ins on the series. Other plug-ins (log files, etc.) should have a lower priority.
Type of Application:
Valid values for type of application are:
"default", "primary", "config", "read", "write",  "security".
  • Members of group "security" will always have a higher priority than all the other plug-ins. This group is used for plug-ins that implement user authentication.
  • The group "config" receives only events of type "default", "security" oder "config". It is meant to be used for plug-ins, which provide functionality to edit configuration files and settings. Usually, these plug-ins serve as extensions to other plug-ins. So there is a dependency. This dependence can be expressed, among other things, by adding a definition, that both plug-ins are part of the same package.
  • The group "write" receives events of the type "write", "security" or "default". It is designed for plug-ins, which only write to a file or a database. This group is however seldom used in practice. It is mentioned here for reasons of completeness.
  • The group "read" receives events of type ?read?, ?security? or ?default?. It is meant to be used for plug-ins, which only read from a file or a database. This group is seldom used.
  • The group "primary" treats all events except "config". It is designed for plug-ins, which act as main programs.
  • The group "default" receives all events except those of type "security", "config" and "primary". It is intended for "other" plug-ins and as a solution for developers, who are uncertain about which group they should take for their plug-in.
requires the plug-in:
Id of another plug-in, which is required for your software to run. Use this field if you extend an existing application with new capabilities. The selection menu is used only as guidance. You don't necessarily need to use one of the Ids listed in this menu.
belongs to package:
use this field to show that two plug-ins belong together. Enter into this field a common "package", that both belong to.

Step 2: edit interface

The second step allows developers to add their own actions to the plug-in's interface.

If you just want to create a simple database application, you may skip this step. Most standard actions, such as the viewing of database entries, search, edit, delete or insert don't need to be written by hand. These actions are created by the SDK automatically.

If you want to set up other actions, you can define their interface here. To do so you may use one of the templates, which the SDK suggests. A manual creation is also possible. The interfaces can also be manually changed.

Form 4

For some actions their are standard templates, which can be used. Have a look at the select box. If an option is selected, a list of all generated actions is shown for comparison. You can review the entire interface by selecting the tab "edit interface manually".

Form 5

An interface contains the following information:

Name of action:
Name of the function (any text). May only contain characters A-Z, 0-9 and "_". Look at the examples for comparison.
Mode:
usually "standard". Safe mode is meant to be used in case you want or need to ensure, only default values are used.
Templates:
ID of the templates to be used. This id is defined in the skin. Look at the skin files in the directory "skins/default/*.config", or use the menu for an example! 
Access restriction:
selects the minimum privileges a user must have to be able to trigger this action.
Form 6

This option may be used to control the results, or as an alternative input interface for experienced users. You see all defined actions and can make changes.

Beware! Errors may cause, that the plug-in cannot be generated correctly.

Step 3: Generate the GUI

For database application a GUI can be generated automatically. To do so a structure file is required, which describes the database. From the information in this file, for each table the SDK creates matching template files, the appropriate actions and also generates the necessary additions to the interface of the application.

If you don't want to create such a database application, you may skip this step.

This will automatically create for each table: templates to view, edit, create and search database contents, plus the PHP-code for all required functions. In addition functions for downloading images and files from the database. Also a JavaScript file is created, which can be used to trigger actions on the server via Ajax. Last the structure file itself will be stored in the system.

Form 7

If you have not created a structure file yet, you may now create such a file. For this use the plug-in "DB-Tools". On the administration panel open the menu "DB-Tools" / "Import". Choose one of the options to import an already existing database. If you don't have any, please read the beginner's tutorial for a start. Also note the examples in the Developer's Cookbook.

Form 8

If you now got a structure file, input the path to this file here. Click on "search", if you are not sure where this file is stored.

Step 4: Create an installation routine

In step 4 your may define your own SQL-files to install your database. This step is optional. The framework (since version 2.8.6) is able to generate the required SQL statements for the installation automatically when needed by using the file structure defined in step 3. This works for MySQL, MS-SQL, PostgreSQL, Oracle databases, IBM DB2 and MS Access. Handiwork is (except in exceptional cases) unnecessary.

If no automatic generation is possible for your desired DBMS, you should write a SQL installation file yourself.

Form 9

Select the source file in SQL format, that contains the information to install the database.

You will find your SQL installation files in the folder "config/db/.install/" after finishing the process. This folder contains subdirectories for all supported DBMS. You may also copy your SQL-files to this location at a later stage. The file must have the same name as the structure file and use the extension ".sql".

Creating the application

To end the configuration, click "complete".

The SDK produces a skeleton of the application, all the necessary templates, as well as all the necessary configuration files. These include in particular: meta information, e.g. description of the plug-in's interface, meta information of required files, meta information of used templates.

The newly generated plug-in can be used immediately. You can find all files in the directories "plugins", "skins/default" under the ID code of the plug-in.

install plugin

To test the just-created plug-in:

  1. Open the file "index.php".
  2. Open the administration menu. You need to have administrator priviliges. Make sure you are using the expert mode.
  3. In the right column, section "plugins", click on "refresh list."
  4. Look for the name of your plug-in in the list and activate it, by clicking the checkbox next to the plugin's name.
  5. Save your changes.
  6. Log out.
  7. The application's name will appear on the site map. Click it to run the application. (the entry on the site map will appear only if you have defined at least 1 action in the interface, which has type "read" or "default" and a minimum access permission of "0" (=public). If not, you have to enter the name of the desired action at the address bar of your browser: index.php?action=my_action. This mechanism is meant to avoid, that plug-ins that are not able to be run as a stand-alone, or that need administrator priviliges appear on the public site map)

Creating translations

General introduction

This application uses files to store content, which can be translated to several languages. These files are grouped together as packages. One package fits to one translation. For example, this might be a German or an English package. Each package contains texts and graphics. In the text files, IDs are assigned texts. The IDs are identical to the corresponding tokens in the skin packages.

Editing translations

If you have found an error in a translation package and want to correct it, you will find the appropriate files in the directory "languages/". One subdirectory per package. Each of the packages contains files which match the names of the plug-ins which they are intended to be used with.

Editing these files is possible easy. Open the file in a text editor of your choice. The files use a XML-like format. Avoid the use of HTML tags in these documents. Using embedded tags is permitted. Note that no line breaks are permitted within the text flow. To insert a line break, use the embedded tag [br].

If you edit a translation, it is recommended to overwrite the original data. Instead make a copy of the file as a new language pack.

Generating new language packs

To translate the text messages of the application to a new language, save a copy of the directory "languages/de" and the related configuration file "deutsch.config" under a new name. You should use the name of the language for which you want to provide the translation. For example "languages/es".

You can add your personal information, such as name of the author, or a brief description. Edit these settings in the configuration file of the language package. You can also insert an appropriate graphic. Usually a national flag. To do so replace the file "flagge.gif". The name of the file is specified in the configuration file under the id "LANGUAGE_INFO.LOGO".

Coding Principles

General introduction

"Coding principles" are general rules on how to write and format PHP-code. These rules are meant to help, to keep your source code easy to read and understand.

If several authors are working together on the same source code, it is quite common, that each author may have it's own idea on how clean code should look like. There are dozens of possibilities to express the same thing with PHP. To ensure the source code remains well readable in the long term, it is necessary that all authors agree on a common standard on how to write statements.

The "Coding principles" stated here provide such a definition. These rules are not obligating, however, I would like to recommend them to you.

Indention and comments

The source code should be indented with 4 blanks. Do not use tabulators, because these can be viewed differently on different systems.

For comments use exclusively the syntax /* comment */. This has a deeper meaning! Later, when you write or use scripts, that analyse or automatically document your source code, this will make your task a lot easier. Unlike the syntax // comment or # comment, you have a fixed start and end for each comment, that you can rely on. Lines of comments can be fetched easily with simple regular expressions, saving you much effort when creating these scripts.

Here is a example.

<?php
$output = array();
/* iterate through input */
foreach ($input as $key => $element)
{
    $element = strip_tags( (string) $element);
    if (preg_match("/\//", $key)) {
        $key = preg_replace("/\//", ".", $key);
        Hashtable::set($output, $key, $element);
    } else {
        $output[$key] = $element;
    }
} /* end foreach */
?>

Declarations and lifetime of variables

In PHP has no variable declaration in the sense of other languages. Nevertheless variables have a certain life span. Before and after a variable's life span it does not exist, what means no value is assigned.

The reason for many errors in PHP is, that programmers assume a variable to be unused, of a certain type, or that is no longer used after a certain code line. However PHP does NOT ensure that and also will not warn you if you are wrong, unless you "please" it to do so. This is exactly what you should do!

If you do it, PHP will warn you, if you use a variable outside it's life span and by doing so will possibly save you many hours of debugging!

Variables should be named in CamelCase - for example, $debugSourcePath instead of $debug_source_path. The first letter should always be written small - in difference to class names. There (and only there) the first letter should be large. This definition for the way of writing is similar to that in many other languages like Java.

<?php
/* Start of life span */
/* The following line will warn you if the variable already exists */
assert('!isset($variable); // Cannot redeclare $variable');

/* always initialize variables before use */
$variable = 0;

/* ... additional statements ... */

/* If a variable is no longer needed it should always explicitly be unset */
unset($variable);
/* End of life span */
?>

If and Else statements

When working with if and else, please always use brackets { } for each block. While PHP permits to spare these brackets, if a block consists of only one statement, this style of coding is error-prone, since you may always come to the point where you decide to add another line of code and in this situation it is easy to oversee the missing brackets.

If an If-block is very long (more than 10 lines), you should add a comment /* end if */  at the closing bracket for better readability. For long code blocks this helps to identify what statement a bracket belongs to.

<?php
if ($test1) {
    /* ... */
} elseif ($test2) {
    /* ... */
} else {
    /* ... */
} /* end if */
?>

Switch statement

When using Switch-statements mind the text indentation and line breaks, which serve readability.

Avoid using "fall trough", for this may lead to code which is difficult to read. If you somehow cannot avoid it, you should mark this special case with the comment /* fall through */ instead of the Break-statement for documentation.

The Default-case should always be included. This enhances the clarity of your code.

If two different cases use the same code, this can be expressed by writing case 1: case 2: ... break;. This is a useful abbreviation and you are recommended to use it, where you see fit.

<?php
switch ($variable)
{
    case 1:
        /* ... */
    break;
    case 2: case 3:
        /* ... */
    break;
    case 4:
        /* ... */
    break;
    case 5:
        /* ... */
    break;
    case 6:
        /* ... */
    /* fall through */
    default:
        /* ... */
    break;
} /* end switch */
?>

For, ForEach and While statements

Again mind the line breaks for better readability.

PHP has a rather uncommon "feature". The life span of a block / loop -variable does NOT end, as in other languages, when exiting the block / loop, but will continue to have it's last assigned value. This also means that loop variables in PHP may overwrite the value of another, already existing variable. PHP will not warn you on this issue. To address this behavior, and the potential risk, which is associated´with it, you should treat the begin of any For- or ForEach-loop like a variable declaration and check whether any used loop variables already exist. When exiting a loop, you should always explicitly unset variables to avoid side effects.

Note that a ForEach statement necessarily expects an array to work on. Before entering the loop check the type of the input variable by using the function is_array () and add appropriate error handling if the variable has not the expected type.

For While-loops ensure that exit points, introduced by Break- or Continue-statements, are always sufficiently documented.

Take a look at the following examples:

<?php
/* Example: FOR-loop */
assert('!isset($i); // Cannot redeclare $i');
for ($i = 0; $i < sizeOf($input); $i++)
{
    /* ... */
} /* end for */
unset($i);

/* Example: FOREACH-loop */
assert('!isset($key); // Cannot redeclare $key');
assert('!isset($element); // Cannot redeclare $element');
foreach ($input as $key => $element)
{
    /* ... */
} /* end foreach */
unset($key);
unset($element);

/* Example: WHILE-loop */
while ($test)
{
    /* ... */
} /* end while */
?>

Classes and functions

For the names of classes and functions be advised to uniformly use CamelCase. For function names, the first letter is always small.. For class names the first letter is always capitalized. This rule is similar to Java and other C-type languages.

In PHP it is common that the names of private functions and methods start with an underscore to improve readability. If you decide to use this notation, please use it only for identification of private functions and methods.

To improve readability it is recommended, that the name of a function starts with a verb in imperative and a noun in singular, for example: "getBeer()" or "setContent()". If the return value is a list object or an array, use a noun in plural, for example: "getLines()".

Use PHPDoc comments (http://www.phpdoc.org/) for documentation of classes and functions. These should be mandatory. Make it a habit to always immediately document any new function you write, while the idea and details are still fresh in your memory. By writing documentation you are forced to review the content of your work and thus will recognize mistakes, false assumptions or defective pre-conditions earlier.

The documentation of a function should always contain: the input data the function expects (for example: "an integer between 0 and 5"), the purpose of the function - that is what this does. Moreover, the return values of the function (for example: "an integer between 1 and 6, or false, if an error occurs").

It is general practice to document source codes in English. If you are able to write your documentation in English, then do so.

If you want to use UML-style stereotypes, then write them in front of the name of the function or class and use parentheses, which is common for writing stereotypes.

It is common to keep variables "private" and the restrict all access to set - and get-functions. For example, access to a private variable "$foo" should be restricted to functions with the name "setFo ()" and "getFoo()". Note that the class variables must always be initialized - if necessary, initialize them with the constant "null"!

<?php
/**
 * «Stereotype» name
 * 
 * The purpose of this class is ...
 * 
 * @access      public
 * @package     package
 * @subpackage  subpackage
 */
class MyClass extends BaseClass
{

    /**#@+
     * @access  private
     */

    /** @var array */  private $var1 = array();
    /** @var int   */  private $var2 = 0;
    /** @var View  */  private $var3 = null;
    /** @var array */  private $var4 = array();
    /** @var array */  private $var5 = array();

    /**#@-*/

    /**
     * «Stereotyp» Constructor
     * 
     * This function accepts the following input ...
     * 
     * This function does ...
     * 
     * It returns the following values ...
     * 
     * @param  string  $filename  path to file.ext
     */
    public function __construct ($filename)
    {
        /* ... */
    }

    /**
     * «Stereotype» name
     * 
     * This function accepts the following input ...
     * 
     * This function does ...
     * 
     * It returns the following values ...
     * 
     * @access  public
     * @param   string  $id  alphanumeric, unique identifier
     * @return  int
     */
    public function getVar ($filename)
    {
        /* ... */
    }

}
?>

API documentation

The API documentation contains information for developers who want to write their own extensions. Here are descriptions of interfaces and examples to all classes and functions of the software.

Click here to open the API documentation.

Cookbook

Working with databases

General introduction

Which features are supported?

The Yana Framework offers a API for working with databases, which is based on PEAR-DB. This API extends the abilities of PEAR by the following features:

Which features are not supported?

There are several features which the flat file database currently does not provide, but are planned for future versions.

There are several features which database schemata currently do not provide, but are planned for future versions.

How do I open a connection to a database?

This requires a schema file. The schema files has to be present in the folder "config/db/" and must have the file extension ".config".

Open a database connection with the current connecting data
<?php
global $YANA;
$structure = 'guestbook';
$db_connection = $YANA->connect($structure);
?>

The connecting data for the database (like host address, user name and password) is entered by the user in the administration menu. So you DON'T have to include this in your code.

If you don't want to use a structure file, you may leave it blank. However: not that this is not recommended within a productive environment. In this case the framework will try to determine the missing information itself. If this fails the database connection will not be usable.

Open a connection to a database with individual connecting data

If you want to open a connection to a database, but want to set the connection information yourself in the code, proceed as follows:

<?php
/* connection information is provided as associative array: */
$connection = array(
    'DBMS' => 'mysql',
    'HOST' => 'localhost',
    'PORT' => 0,
    'USERNAME' => 'user',
    'PASSWORD' => 'pass',
    'DATABASE' => 'database_name'
);

/* To use the YANA-API to communicate with the database, write: */

$db_server = new DbServer($connection);
$yana_api = new DbStream($db_server);

/* To use the PEAR-API to communicate with the database, write: */

$db_server = new DbServer($connection);
$pear_api = $db_server->get();

/*To use the PEAR-API with the default connection data, write: */

$db_server = new DbServer();
$pear_api = $db_server->get();
?>

How do I know if the database has been activated through the administrator's menu?

<?php
if (YANA_DATABASE_ACTIVE === true) {
    print "Database is active";
} else if (YANA_DATABASE_ACTIVE === false) {
    print "Database is NOT active";
}
?>

How do I create a database query?

Therefor the API offers the function $db->get(string $key). This executes a SELECT query on the database and returns the value indicated by the argument $key.

<?php
global $YANA;
$db = $YANA->connect('guestbook');

/*
   The following syntax may be used:

   $db->get(
            string "$table.$row.$column",
            string $where,
            string $order_by,
            int $offset,
            int $limit
           );

   Example:
   $value = $db->get("table.1.field","row1=val1,row2=val2","row1",0,1);

   will create the following SQL statement:
   SELECT field from table where primary_key = "1" and row1 like '%val1%' and row2 like '%val2%' order by row1 limit 1;
*/

/* output field */
$value = $db->get("table.1.field");
/*
   will create the following SQL statement:
   SELECT field from table where primary_key = "1";
*/

/* output column: */
$column = $db->get("table.*.field");
foreach ($column as $row => $value)
{
    print "<p>Value of 'field' in row '$row' = $value</p>";
}
/*
   will create the following SQL statement:
   SELECT field from table;
*/

/* output row: */
$row = $db->get("table.2");
foreach ($row as $column => $value)
{
	echo "<p>Value of column '$column' in row '2' = $value</p>";
}
/*
   will create the following SQL statement:
   SELECT * from table where primary_key = "2";
*/

/* output table: */
$table = $db->get("table");
foreach ($table as $index => $row)
{
	foreach ($row as $column => $value)
	{
		echo "<p>Value at 'table.$index.$column' = $value</p>";
	}
}
/*
   will create the following SQL statement:
   SELECT * from table;
*/
?>

How do I execute INSERT- / UPDATE- statements?

Use the function $db->insert($key,$value). This will insert the value "value" at position "key". This may either be a row or a cell. The insertion of whole tables or columns is not supported.

With the first call of this function a transaction is created automatically. Use the function $db->write() to send a COMMIT. If any of the statements fail a CALLBACK is send automatically.

If the row does not exist, a INSERT statement will be created, otherwise a UPDATE statement.

The function returns "true" on success and "false" otherwise.

Please note: the SQL-statement is not executed unless you call $db->write().

Take a look at the following examples:

<?php
global $YANA;
$db = $YANA->connect("guestbook");

/* insert new row: */
$db->insert("table.*",array("row1"=>"val1","row2"=>"val2"));
$db->write();

/* update row: */
$db->update("table.2",array("row1"=>"val1","row2"=>"val2"));
$db->write();

/* update cell: */
$db->update("table.2.row1","val1");
$db->write();

/* execute transaction: */
$db->insert("table.*",array("row1"=>"wert1","row2"=>"wert2"));
$db->insert("table.*",array("row1"=>"wert3","row2"=>"wert4"));
$db->update("table.1.row3","wert1");
$db->write();
?>

How do I execute a DELETE statement?

Use the function $db->remove($key). This will remove the data set "key" from the table. The function returns "true" on success and "false" otherwise. Only rows can be deleted. No tables, cells or columns.

Note the following restriction: for security reasons only 1 row is deleted with each call. To delete more rows, call the function repeatedly. This restriction is to prevent that someone can delete an entire table by inadvertence or by mistake.

Please note: the SQL-statement is not executed unless you call $db->write().

<?php
global $YANA;
$db = $YANA->connect('guestbook'); /* Remove 2nd row: */ $db->remove("table.2"); $db->write(); /** * will create the following SQL statement: * DELETE FROM table WHERE primary_key = "2" LIMIT 1; */ /* remove whole table: */ for ($i=0; $i < $db->length($table); $i++) { $db->remove("table.*"); } $db->write(); /** * will create the following SQL statement: * DELETE FROM table WHERE primary_key = "2" LIMIT 1; */ ?>

How do I know how many rows a table has?

<?php
global $YANA;
$db = $YANA->connect('guestbook'); if ($db->length('table') === 0) { print "The table is empty."; } else { print "The table contains ".$db->length('table')." rows."; } ?>

How do I know if a table / row exists?

<?php
global $YANA;
$db = $YANA->connect('guestbook'); /* check connection: */ if ($db->exists() === true) { print "Database connection available."; } else if ($db->exists() === false) { print "Database connection NOT available"; } /* check if table exists: */ if ($db->exists('table') === true) { print "Table exists."; } else if ($db->exists('table') === false) { print "No such table"; } /* check if row exists: */ if ($db->exists("table.2") === true) { print "The row '2' exists."; } else if ($db->exists("table.2") === false) { print "No row '2' in table."; } /* check if cell exists and is not null: */ if ($db->exists("table.2.field") === true) { print "There is a cell 'field' in row '2' which has a value."; } else if ($db->exists("table.2.field") === false) { print "The cell does not exist or is NULL."; } /* Check if there is at least one field that is not NULL: */ if ($db->exists("table.*.field") === true) { print "There is a value in column 'field'."; } else if ($db->exists("table.*.field") === false) { print "The column 'field' does not exist or contains no values."; } ?>

How do I create an installation routine for my tables?

Manual providing of installation routines for the Yana Framework is not necessary.

The Yana Framework has a generic installation routine for databases, which you will find in the administrator's menu, at the "database setup". Using this menu a user can install all tables or synchronize contents between the DBMS and the flat-file database.

Screenshot
Figure: Opening a connection and installing databases

The Framework generates the necessary SQL instructions automatically from the structure file of your database. The following source code shows, how you can see the code generated by the Framework.

<?php
$db = $YANA->connect('guestbook');
$dbe = new DbExtractor($db);

// Generates "Create Table ..."-statements for MySQL
$sql = $dbe->createMySQL();

// there are more functions for other DBMS
$sql = $dbe->createPostgreSQL();
$sql = $dbe->createMSSQL();
$sql = $dbe->createMSAccess();
$sql = $dbe->createDB2();
$sql = $dbe->createOracleDB();

// show result
print implode("\n", $sql);
?>

If the generated code does not correspond to your expectations, you can replace it by your own SQL file. Please copy your files to the directory "config/db/.install/{DBMS}", whereby you select the subdirectory, which corresponds to the desired DBMS instead of {DBMS}. The file "config/db/.install/readme.txt", contains a list of the supported DBMS and the names for the intended subdirectories to be used. They don't have to provide own files for all supported DBMS. If a required file does not exist, the Framework (like before) will produce the necessary SQL instructions itself automatically.

For further details see the API documentation of class: "DbExtractor".

How do I import a SQL file containing DDL instructions?

<?php
global $YANA;
$db = $YANA->connect('guestbook');
$db->importSQL('data.sql');
?>

How do I import data from a CSV-file?

<?php
global $YANA;
$db = $YANA->connect('guestbook');
$csv = $db->toString('table');
file_put_contents("table.csv", $csv);
?>

Working with the schema of a database

How do I export a database schema to a file?

<?php
global $YANA;
$db = $YANA->connect('guestbook');
$db->exportStructure("guestbook.config");
?>

Contents of a structure file

Structure files are stored in the XML-like SML-format (see also the article: "The SML file format"). The following is an informal rough overview on the different elements.

The following informal text explains the structure *

DATABASE {
    use_strict    (true | false)
    readonly?     (true | false)
    description?  #string
    include?      (#string | #array)

    TABLES {
        readonly?      (true | false)
        description?   #string
        primary_key    #string
        profile_key?   #string
        foreign_keys?  #array

        CONSTRAINT?    { ... }
        TRIGGER?       { ... }

        CONTENT {
            readonly?     (true | false)
            description?  #string
            type          #string
            length?       #numeric
            precision?    #numeric
            required?     (true | false | AUTO)
            default?      #string
            unique?       (true | false)
            index?        (true | false)

            CONSTRAINT?   { ... }
            TRIGGER?      { ... }

            /* for numeric data types only */
            unsigned?     (true | false)
            zerofill?     (true | false)

            DISPLAY? {
                HIDDEN {
                    new?     (true | false)
                    edit?    (true | false)
                    select?  (true | false)
                    search?  (true | false)
                }
                READONLY {
                    new?   (true | false)
                    edit?  (true | false)
                }
                /* for type "array" only */
                numeric  (true | false)
            }

            /* for type "image" only */
            width?       #numeric
            height?      #numeric
            ratio?       (true | false)
            background?  #array
        }
    }
}

CONSTRAINT {
    select?  #string
    insert?  #string
    update?  #string
    delete?  #string
}

TRIGGER {
	before_insert?  #string
	before_update?  #string
	before_delete?  #string
	after_insert?   #string
	after_update?   #string
	after_delete?   #string
}

* Help: Syntactic elements of the file are printed blue. The possible values and / or data types, which these elements may use are listed behind the elements. Data types are indicated by a rhomb symbol (#). The following data types are used: #string = text, #numeric = number, #array = list type. If multiple values or data types are allowed, they are listed in parentheses, with values separated by a pipe symbol (|). A question mark (?) Indicates that the element is optional.

The following section describes the various elements in detail and provides examples.

How do I write my own database schema?

Schema files have the extension "*.config" and will be stored in the directory "config/db/". A connection on the basis of a schema file is created by this PHP code: $YANA->connect ("name of the file without extension");

Note that Code templates are available for the editors "ConTEXT" and "PSPad", which may save you a lot of work when creating database schemata. If you use one of these two editors, install these templates first before proceeding.

The following listing shows a scheme with all the elements and all valid values. If there are several possible settings, then these various possibilities are listed, separated by a pipe '|'. Identifier, which can be freely chosen, are highlighted by bold text.

/* The field "USE_STRICT" indicates whether queries
 * are to be checked against the schema at runtime or not.
 */
<USE_STRICT>true|false</USE_STRICT>

/* The field "READONLY" is optional. Default = false
 */
<READONLY>true|false</READONLY>

/* The field "DESCRIPTION" is optional and may contain a description, 
 * or the database's name
 */
<DESCRIPTION>Text</DESCRIPTION>

/* The field "INCLUDE" has the same meaning as the PHP-command
 * It imports one or more database schemata in the current file. 
 *
 * The file name is given without the file extension.
 * For example: <INCLUDE>user</INCLUDE> to import the database "user"
 * which is described in the file "config/db/user.config".
 */
<INCLUDE>Database schema</INCLUDE>

/* To select multiple files to import, use the following notation:
 */
<INCLUDE>
	<0>1st schema</0>
	<1>2nd schema</1>
	<2>3rd schema</2>
</INCLUDE>

/* Constraints are boolean expressions in PHP notation.
 * They can be linked to a specific SQL action.
 * If the expression evaluates to "false", the query will not be send
 * and an entry will be written to the logs. Otherwise the action will be carried out
 * without intervention.
 * 
 * In Constraints you can't call any functions, with one
 * exception: preg_match();
 * In addition, you have access to the following constants: 
 * $VALUE      = (for INSERT, UPDATE only) the inserted value
 * $PERMISSION = the security level of the user who triggered the action
 * $OPERATION  = SQL-command that is currently being executed (SELECT, INSERT, ...)
 * $TABLE      = name of target table (in most cases this is the current table)
 * $FIELD      = name of destination column (if any)
 * $ID         = id of current profile (since version 2.8.9)
 * 
 * Examples:
 * <SELECT>true</SELECT>
 * <UPDATE>false</UPDATE>
 * <UPDATE>$VALUE > 0 && $VALUE < 500</UPDATE>
 * <INSERT>$PERMISSION > 50</INSERT>
 * <INSERT>preg_match('/^[\w\d-_]*$/i', $VALUE)</INSERT>
 * 
 * Constraints are available in YANA since version 2.8 .
 */
<CONSTRAINT>
	<SELECT>PHP-Code</SELECT>
	<INSERT>PHP-Code</INSERT>
	<UPDATE>PHP-Code</UPDATE>
	<DELETE>PHP-Code</DELETE>
</CONSTRAINT>

/* Trigger are miniature programs in PHP syntax.
 * They are bound to a particular SQL action and are automatically executed
 *before o r after the SQL statement is executed. 
 * 
 * THESE triggers are different from those triggers in a database.
 * 1) PHP can be used,
 * 2) will work with the same code regardless of the DBMS, but:
 * 3) no direct access to the database.
 * 
 * These triggers are particularly suitable for logging and modify input data.
 * 
 * In addition, you have access to the following constants: 
 * $VALUE      = (for INSERT, UPDATE only) the inserted value
 * $PERMISSION = the security level of the user who triggered the action
 * $OPERATION  = SQL command (BEFORE_INSERT, AFTER_UPDATE, ...)
 * $TABLE      = name of target table (in most cases this is the current table)
 * $FIELD      = name of destination column (if any)
 * $ID         = id of current profile (since version 2.8.9)
 * 
 * Examples:
 * <BEFORE_INSERT>$VALUE = md5($VALUE);true</BEFORE_INSERT>
 * <AFTER_DELETE>if($VALUE){print 'Successfully deleted.';}else{print 'Error.';}</AFTER_DELETE>
 */
<TRIGGER>
	<BEFORE_INSERT>PHP-Code</BEFORE_INSERT>
	<BEFORE_UPDATE>PHP-Code</BEFORE_UPDATE>
	<BEFORE_DELETE>PHP-Code</BEFORE_DELETE>
	<AFTER_INSERT>PHP-Code</AFTER_INSERT>
	<AFTER_UPDATE>PHP-Code</AFTER_UPDATE>
	<AFTER_DELETE>PHP-Code</AFTER_DELETE>
</TRIGGER>

/* As you may have already suspected: the option READONLY=true
 * and the constraint UPDATE=false both have the same effect. 
 */

/* Here is the definition of the tables.
 */
<TABLES>
	<Name of table>
		<READONLY>true|false</READONLY>
		<DESCRIPTION>Label of table</DESCRIPTION>

		<CONSTRAINT>
			<SELECT>PHP-Code</SELECT>
			<INSERT>PHP-Code</INSERT>
			<UPDATE>PHP-Code</UPDATE>
			<DELETE>PHP-Code</DELETE>
		</CONSTRAINT>
		<TRIGGER>
			<BEFORE_INSERT>PHP-Code</BEFORE_INSERT>
			<BEFORE_UPDATE>PHP-Code</BEFORE_UPDATE>
			<BEFORE_DELETE>PHP-Code</BEFORE_DELETE>
			<AFTER_INSERT>PHP-Code</AFTER_INSERT>
			<AFTER_UPDATE>PHP-Code</AFTER_UPDATE>
			<AFTER_DELETE>PHP-Code</AFTER_DELETE>
		</TRIGGER>

/* The field "PRIMARY_KEY" holds the name of the column, which is the primary key.
   (See literature under the keyword: "primary key constraint")
 */
		<PRIMARY_KEY>Name of column</PRIMARY_KEY>

/* The field "PROFILE_KEY" is the name of a column (if any), which contains the profile key.

   The purpose:
   You can assign the records of a table to a profile.
   If you do so, a user sees only the records of the selected profile.

   Creating a "profile key constraint":
   1)  Add a column of the type "profile" to the table
   2) set the property "required" for this column to "AUTO" 
   3) Set the table's property "profile_key " to the name of the column

   (This is a special case of a "compound primary key" - this is a "primary key constraint".
   A "special case" because the key is a virtual key.
   This means that the technical handling for the user is done completely transparent.
   You can remove this constraint afterwards and the primary key will remain
   valid. That also means that you may subsequently add or remove this constraint,
   without having to change the source code of your plug-in. Checking and resolving of
   profile keys is done on the level of the database layer. This also means, when looking at the security
   of your application it is practically impossible to circumvent a "profile key" constraint.
     This feature was introduced in version 2.9.)
 */
		<PROFILE_KEY>Name of column</PROFILE_KEY>

/* The field "FOREIGN_KEYS" may hold a list of foreign keys.
   (See literature under the keyword: "foreign key constraint")
 */
		<FOREIGN_KEYS>
			<name of column>name of target table</name of column>
			<another column>another target table</another column>
		</FOREIGN_KEYS>

/* Here is the definition of table columns.
 */
		<CONTENT>
			<Name of column>
				<READONLY>true|false</READONLY>

				<CONSTRAINT>
					<SELECT>PHP-Code</SELECT>
					<UPDATE>PHP-Code</UPDATE>
				</CONSTRAINT>
				<TRIGGER>
					<BEFORE_UPDATE>PHP-Code</BEFORE_UPDATE>
					<AFTER_UPDATE>PHP-Code</AFTER_UPDATE>
				</TRIGGER>

/* The field "DESCRIPTION" is a label for this column.
 * You can also use %TOKEN% that you define in your program,
 * or in a language file, to be able to provide the label in multiple
 * different languages.
 */
				<DESCRIPTION>Label of column</DESCRIPTION>

/* The primitive, scalar data types integer, float and string correspond to their
 * equivalent in PHP. In addition, other useful data types have been introduced.
 * 
 * mail    = checks automatically, whether the value is a valid email address.
 * ip      = checks automatically, whether the value is a valid IP address.
 * text    = for input from text area fields, provides additional tests
 *           to protect against flooding.
 * select  = enumeration data type, which elements are defined in the "DEFAULT",
 *           see below.
 * array   = can be used to store PHP arrays. When reading the value
 *           from the database, they are automatically converted to an array.
 * image   = is used to store graphics. Handles file uploads, 
 *           testing and conversion of the graphic and automatically generates thumbnails.
 * file    = this type is used to save files ("binary large objects").
 *           For reasons of better performance the files remain in the file system
 *           after the upload. In order to save disk space, they will automatically be GZip-compressed.
 *           The database will only store the file name. When you read the column
 *           the name and location of the compressed file will be returned as a result.
 */
				<TYPE>integer|float|string|text|url|ip|mail|time|select|array</TYPE>

				<LENGTH>positive integer</LENGTH>

/* For data type "float" you may use "PRECISION" to determine the number of decimal places.
 */
				<PRECISION>positive integer</PRECISION>

/* The field "REQUIRED" selects, whether a field is NULLABLE or not.
 */
				<REQUIRED>true|false</REQUIRED>

/* The field "DEFAULT" sets a value that is automatically used when,
 * a data set is created and no other value is given.
 */
				<DEFAULT>a Default value</DEFAULT>

/* The field "UNIQUE" is either "true" or "false", where "false"
 * is the default value. If UNIQUE=true each value in this column may not occur more than
 * 1 time. This means, each value is unique ( thus the name).
 * Use this setting to define more keys in addition to the
 * key primary key.
 */
				<UNIQUE>true|false</UNIQUE>

/* The field "INDEX" is either "true" or "false", where "false"
 * is the default value. If INDEX=true a sorted list of all values of this column
 * are stored, what may increase the speed of sorting and searching for values 
 * in the column.
 */
				<INDEX>true|false</INDEX>

/*
 * Instructions GUI and SDK can be filled in the field "DISPLAY".
 * There are two settings: "HIDDEN" and "READONLY".
 * Where "READONLY" means that this column is not displayed for editing.
 * As the name implies, "HIDDEN" means the columns is not to be displayed
 * at all.
 * There are separate settings for each of the queries: "NEW", "EDIT", "VIEW" and "SEARCH".
 * The properties can be set globally or individually for each action.
 */

/* First, the variant with global settings
 */
				<DISPLAY>
					<HIDDEN>true|false</HIDDEN>
					<READONLY>true|false</READONLY>
				</DISPLAY>

/* Nun die Variante mit lokalen Einstellungen für jede Option
 */ 
				<DISPLAY>
					<HIDDEN>
						<NEW>true|false</NEW>
						<EDIT>true|false</EDIT>
						<SELECT>true|false</SELECT>
						<SEARCH>true|false</SEARCH>
					</HIDDEN>
					<READONLY>
						<NEW>true|false</NEW>
						<EDIT>true|false</EDIT>
					</READONLY>
				</DISPLAY>

/*
 * For type "array" the property "display" may also carry the flag  "NUMERIC"
 * to express that this is a numeric array.
 * This will hide the array keys in the graphical user interface of the program
 * and assign numeric keys to new entries automatically.
 *
 */
				<DISPLAY>
					<NUMERIC>true|false</NUMERIC>
				</DISPLAY>

			</Name of column>


/*
 * For the data types integer, ip and timethe field "REQUIRED" may have the value "AUTO"
 * to be set. This means that the value is automatically generated.
 * For time    = current date as the Unix Time-Stamp
 * For ip      = the IP of the visitor
 * For integer = auto-increment or the value of a sequence
 */
			<name of column>
				<TYPE>integer|ip|time</TYPE>
				<REQUIRED>AUTO</REQUIRED>
			</name of column>

/*
 * For the data type select a enumerated list of valid values can be given.
 * 
 * The semantics can be relatively easy to remembered:
 * + The GUI displays columns of type "select" in forms as select-fields.
 * + The presentation in the scheme also recalls a select field in HTML.
 */
			<name of column>
				<TYPE>select</TYPE>
				<DEFAULT>
					<default value>label</default value>
					<option 1>label 1</option 1>
					<option 2>label 2</option 2>
				</DEFAULT>
			</name of column>

/*
 * For the data type image the maximum file size, width and height of the graphic
 * can be specified. If the image is too small or too large, it will automatically
 * be resized. The value "ratio" is to indicate, whether
 * the ratio of width to height is to sustain (true) or not (false).
 * If "ratio" is set to "true", resizing the image might cause a border around the image.
 * Therefore, a background color can be specified.
 * This background color fills the unused area around the graphics.
 * 
 * The form generator can (if it is used) automatically create an upload field
 * to transfer graphic files to the server. 
 * For each upload, a thumbnail of size 75x75px is created automatically.
 * The form generator automatically displays the thumbnail when calling the record
 * and a link to show the full graphic.
 * 
 * For reasons of performance graphics are not stored as "blobs" in the database,
 * but in a non-public directory in the file system.
 * The database stores only the path to the graphic file.
 * When deleting the record (using the database API of the framework),
 * the graphic and thumbnail associated with the data set are deleted as well.
 */
			<name of column>
				<TYPE>image</TYPE>
				<LENGTH>max. upload size in byte</LENGTH>
				<WIDTH>width in pixel</WIDTH>
				<HEIGHT>height in pixel</HEIGHT>
				<RATIO>true|false</RATIO>
				<BACKGROUND>
					<0>number 0-255 (red)</0>
					<1>number 0-255 (green)</1>
					<2>number 0-255 (blue)</2>
				</BACKGROUND>
			</name of column>

/*
 * For numeric data types integer and float two
 * additional properties may be defined.
 * 
 * unsigned: the number is stored unsigned
 * zerofill: when displaying the number free places are filled by '0'
 * 
 * Note: The property "zerofill" works correctly only if the property
 * "length" has a fixed value.
 */
			<name of column>
				<TYPE>integer|float</TYPE>
				<UNSIGNED>true|false</UNSIGNED>
				<ZEROFILL>true|false</ZEROFILL>
				<LENGTH>positive integer</LENGTH>
			</name of column>


		</CONTENT>
	</name of table>

/* Further tables may follow here.
 */ 

</TABLES>

Now see this very simple example:

/* The head is usually always the same: */

<USE_STRICT>true</USE_STRICT>
<READONLY>false</READONLY>

/* This is followed by the definition of the tables in the database */

<TABLES>
	/* The table 'foo' */
	<foo>
		/* Each table must have a primary key */
		<PRIMARY_KEY>foo_id</PRIMARY_KEY>
		/* Now the columns */
		<CONTENT>
			/* First, the primary key */
			<foo_id>
				<TYPE>integer</TYPE>
				<LENGTH>8</LENGTH>
			/* REQUIRED=auto generates an "autoincrement" column */
				<REQUIRED>auto</REQUIRED>
			/* HIDDEN=true makes the field invisible to visitors */
				<DISPLAY>
					<HIDDEN>true</HIDDEN>
				</DISPLAY>
			</foo_id>
			/* Now the remaining fields */
			<foo_title>
				<TYPE>string</TYPE>
				<LENGTH>128</LENGTH>
				<REQUIRED>true</REQUIRED>
				<DESCRIPTION>Title</DESCRIPTION>
			</foo_title>
			<foo_text>
				<TYPE>text</TYPE>
				<REQUIRED>false</REQUIRED>
				<DESCRIPTION>Text</DESCRIPTION>
			</foo_text>
		</CONTENT>
	</foo>
	/* a second table */
	<bar>
		<PRIMARY_KEY>bar_id</PRIMARY_KEY>
		<CONTENT>
			<bar_id>
				<TYPE>string</TYPE>
				<LENGTH>32</LENGTH>
				<REQUIRED>AUTO</REQUIRED>
				<DISPLAY>
					<HIDDEN>true</HIDDEN>
				</DISPLAY>
			</bar_id>
			<bar_value>
				<TYPE>string</TYPE>
				<LENGTH>128</LENGTH>
				<REQUIRED>true</REQUIRED>
				<DESCRIPTION>Value</DESCRIPTION>
			</bar_value>
			<bar_time>
				<TYPE>time</TYPE>
				<REQUIRED>auto</REQUIRED>
				<DESCRIPTION>Time</DESCRIPTION>
			</bar_time>
		</CONTENT>
	</bar>
</TABLES>

The following is an example of a more complex database. Portrayed is the data structure of the guestbook application:

<USE_STRICT>true</USE_STRICT>
<READONLY>false</READONLY>
<TABLES>
	<guestbook>
		<PRIMARY_KEY>guestbook_id</PRIMARY_KEY>
		<CONTENT>
			<guestbook_id>
				<TYPE>integer</TYPE>
				<LENGTH>5</LENGTH>
				<DESCRIPTION>Id (PK)</DESCRIPTION>
				<DISPLAY>
					<HIDDEN>true</HIDDEN>
				</DISPLAY>
			</guestbook_id>
			<profile_id>
				<TYPE>string</TYPE>
				<LENGTH>128</LENGTH>
				<REQUIRED>AUTO</REQUIRED>
				<DESCRIPTION>Id (FK)</DESCRIPTION>
				<DISPLAY>
					<HIDDEN>true</HIDDEN>
				</DISPLAY>
			</profile_id>
			<guestbook_ip>
				<TYPE>ip</TYPE>
				<LENGTH>15</LENGTH>
				<REQUIRED>AUTO</REQUIRED>
				<DISPLAY>
					<HIDDEN>true</HIDDEN>
				</DISPLAY>
			</guestbook_ip>
			<guestbook_name>
				<TYPE>string</TYPE>
				<LENGTH>128</LENGTH>
				<REQUIRED>true</REQUIRED>
				<DESCRIPTION>Name</DESCRIPTION>
			</guestbook_name>
			<guestbook_message>
				<TYPE>text</TYPE>
				<LENGTH>3000</LENGTH>
				<REQUIRED>true</REQUIRED>
				<DESCRIPTION>Text</DESCRIPTION>
			</guestbook_message>
			<guestbook_mail>
				<TYPE>mail</TYPE>
				<LENGTH>255</LENGTH>
				<DESCRIPTION>Mail</DESCRIPTION>
			</guestbook_mail>
			<guestbook_homepage>
				<TYPE>string</TYPE>
				<LENGTH>512</LENGTH>
				<DESCRIPTION>Homepage</DESCRIPTION>
			</guestbook_homepage>
			<guestbook_messenger>
				<DESCRIPTION>Messenger</DESCRIPTION>
				<TYPE>string</TYPE>
				<LENGTH>255</LENGTH>
			</guestbook_messenger>
			<guestbook_msgtyp>
				<DESCRIPTION>Typ</DESCRIPTION>
				<TYPE>select</TYPE>
				<LENGTH>5</LENGTH>
				<DEFAULT>
					<icq>ICQ</icq>
					<aol>AOL</aol>
					<yahoo>Yahoo!</yahoo>
					<msn>MSN</msn>
				</DEFAULT>
			</guestbook_msgtyp>
			<guestbook_opinion>
				<TYPE>select</TYPE>
				<LENGTH>1</LENGTH>
				<CONSTRAINT>
					<INSERT>$VALUE >= 0 && $VALUE <= 5</INSERT>
					<UPDATE>$VALUE >= 0 && $VALUE <= 5</UPDATE>
				</CONSTRAINT>
				<DEFAULT>
					<0>unentschlossen</0>
					<1>sehr gut</1>
					<2>gut</2>
					<3>befriedigend</3>
					<4>ausreichend</4>
					<5>ungenügend</5>
				</DEFAULT>
				<DESCRIPTION>Meinung</DESCRIPTION>
			</guestbook_opinion>
			<guestbook_date>
				<TYPE>time</TYPE>
				<REQUIRED>AUTO</REQUIRED>
				<DESCRIPTION>Datum/Zeit</DESCRIPTION>
				<DISPLAY>
					<HIDDEN>
						<NEW>true</NEW>
                    			</HIDDEN>
				</DISPLAY>
			</guestbook_date>
			<guestbook_comment>
				<TYPE>text</TYPE>
				<LENGTH>1024</LENGTH>
				<REQUIRED>false</REQUIRED>
				<DESCRIPTION>Kommentar</DESCRIPTION>
				<DISPLAY>
					<READONLY>
						<EDIT>true</EDIT>
					</READONLY>
				</DISPLAY>
			</guestbook_comment>
			<guestbook_is_registered>
				<TYPE>integer</TYPE>
				<LENGTH>1</LENGTH>
				<REQUIRED>AUTO</REQUIRED>
				<DEFAULT>0</DEFAULT>
				<DISPLAY>
					<HIDDEN>true</HIDDEN>
				</DISPLAY>
			</guestbook_is_registered>
		</CONTENT>
	</guestbook>
</TABLES>

How do I create a foreign-key?

The following code creates two tables "foo" and "bar". The table "bar" contains a foreign-key reference to table "foo".

<TABLES>

	<foo>

		Primary key constraint:
		<PRIMARY_KEY>foo_id</PRIMARY_KEY>

		<CONTENT>
			<foo_id>
				<TYPE>integer</TYPE>
				<DESCRIPTION>Primary key of table foo</DESCRIPTION>
				<REQUIRED>AUTO</REQUIRED>
			</foo_id>
			<foo_name>
				<TYPE>string</TYPE>
				<DESCRIPTION>A column containing a name or title</DESCRIPTION>
			</foo_name>
		</CONTENT>
	</foo>

	<bar>

		Primary key constraint:
		<PRIMARY_KEY>bar_id</PRIMARY_KEY>

		Foreign key constraint:
		<FOREIGN_KEYS>

			foo_id = column name
			foo = table name

			<foo_id>foo</foo_id>

		</FOREIGN_KEYS>

		<CONTENT>
			<bar_id>
				<TYPE>integer</TYPE>
				<REQUIRED>AUTO</REQUIRED>
				<DESCRIPTION>Primary key of table bar</DESCRIPTION>
			</bar_id>
			<foo_id>
				<TYPE>select</TYPE>
				<REQUIRED>true</REQUIRED>
				<DESCRIPTION>Foreign-key on table foo, presented as select-box</DESCRIPTION>
				<DEFAULT>

					foo_id = column, which is to be stored in the database
					foo_name = column, visible in the browser

					<foo_id>foo_name</foo_id>

				</DEFAULT>
			</foo_id>
		</CONTENT>
	</bar>
</TABLES>

Relation between database schemata and the template function [%create ...%]

The function "create" (see section on templates) calls the form generator. The form generator is capable of using the information in the database schema to generate forms to view, search, create, edit, and delete records in the database. Necessary queries are carried ot automatically. In database schemata, the tag "DISPLAY" is used to control the behavior of the form generator. This tag allows to hide individual columns of a table, depending on the type of the current form.

Representation of data types

The form generator shows table columns depending on the data type in different ways. The following is a comparison of data types and screenshots showing the respective representation in the GUI.

Type of column Representation in the GUI Description
integer, float, string Input Strings and numbers are displayed as input fields when edited. If the column is editable, the content is displayed as text.
boolean Checkbox Entries of type "boolean" use a checkbox. When viewing the column, a graphic indicates the state of the field.
text Textarea Multi-line texts use textarea fields for editing. If the column is not editable, it's contents are shown as text. If a text is too long, scrollbars are shown (CSS: "overflow: auto").
url, ip Input The data type "ip" offers the possibility to automatically store the IP address of the visitor. IP addresses are checked for syntactic validity. As a rule, columns of this type should not be editable. If they are, they use an input box for editing. The data type "url" is displayed just like a column of type "string", with the slight difference that every input is checked to be a syntactically correct URL.
mail HTML-Code This data type also uses an input box for editing. Inputs are checked for syntactical validity. Values of type "mail" are encoded when shown in the browser, to make data theft more difficult. This applies to all displayed mail addresses. The implementation is done automatically in the presentation layer of the framework, manual intervention is therefore unnecessary.
select Select box The data type "select" is an enumeration type. When editing such column, a select box is shown. The contents of the box may be predefined in the database schema (using the property "Default"). If the field is a foreign key (i.e., if this column has a foreign key constraint), the menu will automatically be filled using the entries of the linked table. You may also specify the columns the labels and values are taken from.
set Checkbox The data type "set" is another variant of an enumeration type, which permits the use of multiple values (unlike "select", which only allows a single value). When editing such column, a list of check boxes is presented. Values and labels of the boxes can set in the database schema (using the property "Default").
time Time presentation: German, English, Russian When editing columns of type "time", select boxes are shown to ease the input of dates. Entries of type "time" will be automatically saved as a time stamp and presented in the GUI as a date. The format of presentation can be chosen in the administration menu and adapt to the chosen language settings automatically. Using the framework, dates are automatically synchronized with the visitor's time zone and its respective locale settings.
array Array Entries of type "array" may be multidimensional arrays. When editing these, contents are presented as pairs of keys and values. By clicking "remove" a value is deleted, click on "add new entry" to append a new value. Values are presented as multidimensional foldable tree menus. When touching a key with the mouse, the list of all entries associated with this key, is shown.
array (numeric) list Numerical lists are defined using the property "display.numeric". They differ from "normal" arrays because they present no fields to input individual keys. Instead keys are chosen automatically when creating a new record. Values are presented as list elements.
image Upload field Columns of the data type "image" are presented as a thumbnail image plus an upload field to insert or replace the stored graphic. When clicking the thumbnail the complete graphic will be shown in a new window. The graphics file is automatically checked and converted during upload. You may specify settings in the database schema to control this conversion, such as the desired image size.
file Upload field The data type "file" is used to save files ("binary large objects"). When editing, it will present an upload field to upload a new file, and a button to download the current one. The files remain in the file system (not in the database) for better performance. In order to save disk space, they will automatically be GZip-compressed. This compression also ensures that files stores on the server are not executable and a potential attacker can't misuse such upload fields to store malicious code on the server. When you download the file will be unpacked automatically, so the user no disadvantages from the experiences compression and decompression not installed. To have a faster download, the file is automatically send as compressed data stream, if the user's browser supports this feature (most current browser do). The browser unpacks the file independently. A manual intervention is not necessary. The framework automatically takes care of this feature.

Comparison of data types and their representation by the form generator

Special configuration of the data types "image" and "file"

The downloading of files or opening of graphics for columns of data type "image" or "file", is implemented in the function "download_file", which is located in the plug-in "default_library". This usually happens automatically.

For security reasons, access to this action is by default limited to users of the user group "admin". This is the user group with the highest level of security. If you want to permit access to these data to users with a lower level of security as well, you need to reduce security restrictions for this action.

To do this, edit the file "plugins/default_library.config" in a text editor of your choice. Look for the following section.

<DOWNLOAD_FILE>
    <TYPE>primary</TYPE>
    <MODE>1</MODE>
    <TEMPLATE>NULL</TEMPLATE>
    <PERMISSION>100</PERMISSION>
</DOWNLOAD_FILE>

The security level is represented as a number between 0 and 100, with 100 representing the group "admin" and 0 the group "guest". Change the value "100" to a number that seems reasonable to you.

Some proposals are in the following table:

User group Security level
guest 0
registered user (registered) 1
moderator (mod) 30
owner 75
administrator (admin) 100

Comparison of user groups and levels of security

In this example, the value is set to "1".

<DOWNLOAD_FILE>
    <TYPE>primary</TYPE>
    <MODE>1</MODE>
    <TEMPLATE>NULL</TEMPLATE>
    <PERMISSION>1</PERMISSION>
</DOWNLOAD_FILE>

Save and close the file.

In order for the changes to take effect, you have to go to the administration menu of the Yana framework and refresh the plug-in cache.

Screenshot
Figure: Button to refresh plug-in cache

After clicking the button, the settings take effect immediately.

File system

How do I get the content of directories?

The function dirlist() returns a sorted list of files and directories for a directory . The compulsory directory entries "." and ".." are not listed. Files whose filenames begin with the character "." will also not be listed. This applies in particular file names such as ".htaccess" or ".password". The file names contain no paths. Directory names do not contain a final dash "/".

The optional parameter $filter can be used to limit the results to certain file extensions. Wildcards are not permitted. The file filter can only contain alphanumeric characters, as well as the characters '.', ' -' and ' _'. Other characters are removed automatically.

list all items in a directory:
<?php
echo implode("<br>", dirlist("foo/"));
?>

Output:

foo1.foo2
foo3.foo2
foo4.foo5
foo6.foo7

list all entries with a certain file extension:
<?php
echo implode("<br>", dirlist('foo/', '*.foo2'));
?>

Output:

foo1.foo2
foo3.foo2


... with multiple file extensions:

<?php
echo implode("<br>", dirlist('foo/', '*.foo2|*.foo5'));
?>

Output:

foo1.foo2
foo3.foo2
foo4.foo5

using OO-style notation:

<?php
$dirStream = new DirStream("foo/");
$dirStream->read();
$dirlist = $dirStream->dirlist('*.foo2|*.foo5');
echo implode("<br>", $dirlist);

/* Note: setting the file filter has a permanent effect. */

$dirStream->dirlist('*.foo2|*.foo5');
echo $dirStream->length();
/* Output: 2 */

echo count($dirStream->get());
/* Output: 2 */

/* A new call to dirlist() resets the file filter */
$dirStream->dirlist("");
echo $dirStream->length();
/* Output: 4 */
?>

How do I create or delete directories?

The functions $dirStream->create($mode) and $dirStream->delete() are responsible for creating and deleting directories. The optional parameter $mode can be used to set permissions of directories for LINUX / UNIX operating systems. The default value is 777.

Examples of valid values for $mode are listed in the following table. Where "r" is "readable", "w" is "writable", and "x" is "executable.

Owner Group Other users Value

rwx

rwx

rwx

777

rw-

rw-

---

660

rw-

rw-

rw-

666

rwx

r-x

r-x

755

rwx

r-x

---

750

rwx

---

---

700

Examples of valid values (corresponding to Unix: CHMOD)

<?php
$dirStream = new DirStream("foo/");
$dirStream->read();

/* create directory "foo/" */
$dirStream->create();

/* directory "foo/" with access restrictions set to 660 */
$dirStream->create(660);

/* get number of files in a directory */
print "Directory 'foo/' contains ".$dirStream->length()." files.";

/* delete directory "foo/" */
$dirStream->delete();

/* delete directory "foo/" including all files and subdirectories */
$dirStream->delete(true);

/* check if directory exists */
$test = $dirStream->exists();
if ($test === true) {
	print "directory exists";
} else if ($test === false) {
	print "directory does NOT exist";
}
?>

How do I read the contents of a configuration file or SML file?

The function SML::getFile() reads the specified configuration file in SML format and returns the contents as a PHP variable.

<?php
/* read contents from file and return result as an array */
$array = SML::getFile("foo.config");

/* transform keys to capital letters */
$array = SML::getFile("foo.config", CASE_UPPER);

/* transform keys to in small letters */
$array = SML::getFile("foo.config", CASE_LOWER);

/* leave keys untouched (default) */
$array = SML::getFile("foo.config", CASE_MIXED);

/* ... or: */
$array = SML::getFile(file("foo.config"));

/* ... or: */
$array = SML::decode(file_get_contents("foo.config"));

/* create and decode an SML-encoded string */
$input_array  = array(1,2,3,array(4,5),'a'=>6,'B'=>7);
$output_array = SML::decode(SML::encode($input_array));
$input_array == $output_array; // true

/* OO-style, mixed key case */
$configFile = new ConfigFile("foo.config");
$configFile->read();
$array = $configFile->get();

/* OO-style capitalized key names */
$smlFile = new SML("foo.config");
$smlFile->read();
$array = $smlFile->get();
?>

Navigating within a SML file:

Given the file "foo.config" with the following contents:

<ROOT>
	<FOO1>
		<FOO2>text</FOO2>
		<FOO3>
			<0>1</0>
			<1>foo</1>
		</FOO3>
	</FOO1>
</ROOT>

<?php
$mixed = $smlFile->get("ROOT.FOO1");
print_r($mixed);
?>

This query returns the subtree ROOT.FOO1 with FOO1 as the root element.

Output:

array(
	"FOO2" => "text",
	"FOO3" => array (
		0 => 1,
		1 => "foo"
	)
)

How do I write a variable to a config file or SML file?

The function SML::encode() returns a representation of a specified variable as SML code. The input must be either a variable with a scalar value or an array.

Pleas note: the function "SML: encode" does not detect infinite recursion. Therefore data fields need to be free of recursion. Otherwise, the compiler will present an error message.

Save the contents of an array to "foo.config":

<?php
$string = SML::encode($array, "ROOT");
file_put_contents("foo.sml", $string);
?>

Replace "ROOT" with the name of the root element!

Benutzen von Großbuchstaben, Kleinbuchstaben und gemischter Schreibweise analog zu SML::decode():

<?php
$string = SML::encode($array, "ROOT", CASE_UPPER); // groß
$string = SML::encode($array, "ROOT", CASE_LOWER); // klein
$string = SML::encode($array, "ROOT", CASE_MIXED); // gemischt (default)
?>

objektorientierte Variante, gemischte Schreibweise:

<?php
$configFile = new SML("foo.sml");
$configFile->read();
$configFile->create();
$configFile->insert($string);
$configFile->write();
?>

objektorientierte Variante, Schlüssel in Großbuchstaben:

<?php
$smlFile = new SML("foo.sml", CASE_UPPER);
$smlFile->read();
$smlFile->create();
$smlFile->insert($string);
$smlFile->write();
?>

How do I copy a file?

To copy a file, all YANA classes that represent files, implement a function "copy". When copying the file you may also set access restrictions. If the directory, in which the file is to be copied, does not exist, it can be created automatically. The latter is optional.

The following are some examples.

copy a file:

<?php
$foo = new SecureFileStream("foo.txt");
$foo->copy("bar.txt");

$foo->copy("bar.txt"); // error: file already exists
?>

copy a file and overwrite existing files:

<?php
$foo->copy("bar.txt", true);

$foo->copy("bar/foo2.txt"); // error: directory 'bar' does not exist
?>

copy a file and create missing files automatically:

<?php
$foo->copy("bar/foo2.txt", false, true);

/* The directory 'bar/' has been created and file 'foo2.txt' has been stored in it.
   Access restrictions of this directory have been set to 766 by default. */

/* deleting the directory */
$dir = new DirStream("bar");
$dir->delete(true);
?>

copy a file and set access restrictions of the copied file to 655:

<?php
$foo->copy("bar/foo2.txt", false, false, 655);

/* directory 'bar/' has been created again.
   The access restrictions for the directory have been set to 655 -
   as for the file. */
?>

Counters and statistics

How do I create a counter?

The framework provides the ability to create persistent counter variables, whose value is automatically saved.

There are two types of counters variables: those with IP-check, which only change if the user with the current IP has not already been to the page within the last 3 hours. And those without IP check, which always change when called.

The following are some examples.

<?php
/* Create counter with IP-check */
$counter = new Counter("my_stats", YANA_COUNTER_USE_IP);
/* ... or: */
$counter = new Counter("my_stats");

/* Create counter without IP-check */
$counter = new Counter("my_stats", YANA_COUNTER_IGNORE_IP);

/* Increment counter "test1" */
$counter->count("test1");
/* Increment counter "test2", and save a description */
$counter->count("test2", "score");

/* Increment counter "test1" by 3 */
$counter->count("test1", "score", 3);

/* Decrement counter "test1" by 2 */
$counter->count("test1", "score", -2);
?>

How do I recall the counter status?

<?php
$counter = new Counter("my_stats");

/* Get value of counter "test1" */
$int = $counter->getCount("test1");

/* Get description of counter "test1" */
$string = $counter->getInfo("test1");

print $string.":\t".$int;
/*
  Output
  score: 2
*/

/* get all counters */
$array = $counter->getCount("*");
foreach ($array as $a => $b)
{
    print $a.":\t".$b;
}
?>

How do I create a graphical counter?

<!-- Insert this in your template file: -->
<img src=[%"action=graphic_counter&target=0"|href%]>

The parameter "target" selects the background image. Valid values are the numbers 0-6.

Value of parameter "target" Presentation
0
1
2
3
4
5
6

E-mails

Version information: The functions send_mail and form_mail, and the class form_mailer were renamed to sendMail, formMail and formMailer in version 2.8 to comply with the naming convention of the framework. Since version 2.8 the function sendMail is a static function of class mailer. Since version 2.8 the function formMail is a static function of class mailer.

General introduction on sending mails

The framework offers the function Mailer::mail() to send mail. This function implements a series of security measures, for protection against various types of header-injection attacks.

The function "Mailer::mail" is a variant of the native PHP function "mail", that has been secured against misuse and "header-injection" attacks. Unlike "mail", all input data is checked and header information is checked and restricted.

It returns "true", when the mail was sent and "false" otherwise. However, the value "true" does not mean the mail successfully arrived at its destination. It just means that the input was syntactically correct.

The function adds to the header of the mail the additional entries "x-yana-php-header-protection" and "x-yana-php-spam-protection".

If you receive an e-mail, which was sent through YANA, see the headers of the e-mail to check whether the framework discovered irregularities in the text of the message.

bool Mailer::mail(string $recipient, string $subject, string $text [, array $header ] )

For security reasons, the following restrictions apply.

The list of accepted parameters for $header
Parameter Type Default Description
from mail - valid mail address
return-path mail - valid mail address
cc mixed - Either a valid mail address or a numeric data field with multiple valid mail addresses. A copy will be send to all these addresses. Unlike "bcc" the list of recipients is visible to all recipients.
content-type string text/plain;
charset="UTF-8"
Determines the MIME type of the message. Only MIME-type and charset are valid here. Other values are ignored.
mime-version float 1.0 Must be in accordance with the following regular expression (in Perl syntax): /^\d\.\d$/
content-transfer-encoding string - Must be in accordance with the following regular expression (in Perl syntax): /^\d{,2}bit$/i

Valid values for the parameter $header of the function Mailer::mail()

The use of "BCC" is not permitted for security reasons.

How do I send a text as mail?

<?php
$recipient = "myMail@domain.tld";
$subject = "Notice";

$mailer = new Mailer("skins/mytheme/example.mail");
$mailer->subject = $subject;
$mailer->sender  = $ARGS["mail"];
$mailer->insert("NAME", $ARGS["name"]);
$mailer->insert("NACHRICHT", $ARGS["message"]);
$mailer->insert("IP", $_SERVER["REMOTE_ADDR"]);
$test = $mailer->send($recipient);

if ($test === true) {
    print "Success";
} else if ($test === false) {
    print "Error";
}
?>

How do I send an e-mail based on a template?

The template "example.mail" could be as follows:

Hello [%$NAME%]!

Someone has left you a message, it reads:

[%$MESSAGE%]

Sender's IP: [%$IP%]

This is the source code to send the mail:
<?php
$recipient = "myMail@domain.tld";
$subject = "Notice";

$mailer = new Mailer("skins/mytheme/example.mail");
$mailer->subject = $subject;
$mailer->sender  = $ARGS["mail"];
$mailer->insert("NAME", $ARGS["name"]);
$mailer->insert("NACHRICHT", $ARGS["message"]);
$mailer->insert("IP", $_SERVER["REMOTE_ADDR"]);
$test = $mailer->send($recipient);

if ($test === true) {
    print "Success";
} else if ($test === false) {
    print "Error";
}
?>
A mail could be sent as follows:

Hello Thomas!

Someone has left you a message, it reads:

Hello World

Sender's IP: 127.0.0.1

Note: before sending the e-mail, the function $mailer->send() checks all input data automatically for attempts to inject header data and cleans all input where necessary. However, it does no harm to also check the input in your own script before calling the function.

How do I send form data as a mail?

This is the source code for sending the mail:
<?php
$formMailer = new FormMailer();
// Subject
$formMailer->subject = "Notice";
// header and footer
$formMailer->headline = "Contents of contact form:\n\n"
$formMailer->footline = "\n\nYANA form mailer at ".$_SERVER['SERVER_NAME'];
// Form contents
$formMailer->content = $_POST;

$test = $formMailer->send("myMail@domain.tld");

if ($test === true) {
    print "Success";
} else if ($test === false) {
    print "Error";
}
?>
A mail could be sent as follows:

Contents of contact form:

==========================================
  Notice
==========================================

    name:      Walter
    age:      35
    subject:   Registration
    urgent:   [x]
    message: Please send password!

==========================================
Date: 11.08.2006 10:06:41
IP: 127.0.0.1

YANA form mailer at foo.server.tld

Check form data

How do I check user input prior to processing it?

To do this, YANA offers a function named untaintInput().

The function has the following parameters:

Parameter

Type

Default

Description

value mixed - data to be cleaned
type string - Data type

In addition to the supported native types of PHP the following values are allowed:
  • time: UTC (type integer, length 11)
  • ip: IP-address (type string, length 15)
  • mail: mail address (type string)
  • select: treated as "string"
  • text: treated as "string"
length integer 0 Maximum length of data
escape integer 0 see table

List of parameters for untaintInput

The parameter $escape can be any one of the following constants.

Identifier Description
YANA_ESCAPE_NONE No changes (Default)
YANA_ESCAPE_SLASHED Converts single and double quotation marks to their respective escape sequences in C-notation
YANA_ESCAPE_TOKEN replaces token by their HTML entities
YANA_ESCAPE_CODED replaces HTML symbols, such as Tags, by entities
YANA_ESCAPE_LINEBREAK converts all whitespace characters (particularly line breaks) into spaces
YANA_ESCAPE_USERTEXT for treatment of input from text area fields

Valid values for parameter $escape, function untaintInput

For INPUT fields you should always call untaintInput() with the parameter YANA_ESCAPE_LINEBREAK. This will prevent an attacker from smuggling line breaks into the output, which might be a possible threat. For TEXTAREA fields you should use YANA_ESCAPE_USERTEXT. This prevents many forms of flooding, by constantly repeated texts (Copy'n'Paste Flooding), and will wrap oversized text strings, trim white space and thus will ensure, the layout of your page is not broken.

Logging and error messages

Where can I find the log data?

YANA writes all log entries in a table of the database. However, this is done only, when logging is enabled. You can disable this to reserve space. This feature is disabled by default.

How do I enable logging?

How do I create a log entry?

<?php
global $YANA;

/* Write a text to the logs */
$log = new Log("My log-entry");
$YANA->report($log);

/* Text and dump of data */
$dump = $some_data;
$log  = new Log("My Log-entry",$dump);
$YANA->report($log);

/* view the created log-entry */
print $log->getLog();
?>

How do I create an error message?

Creating error messages

You may output a text message by calling $YANA->report(Report $report).

The parameter $report is an instance of one of the following classes. These classes have the same parent class "Report".

Class Description
Log for output to log-file
Message Success messages (screen)
Warning Warning
Alert Note
Error Error

accepted types for parameter $report

The constructor function is called as follows:

new Log(string $text [, mixed $data]);

The following example writes a message in the log file, then generates a text for output on the screen.

$YANA->report( new Log("IO-ERROR: Unable to read file $a") );
$YANA->report( new Error('NOT_READABLE', array('FILE' => $a) ) );

Use

The parameter $text may either be an error code, or a text message. You should use error codes for screen output. For output in log files you should use English texts. The $data parameter is optional and may provide additional information. For example, the data set, which could not be saved, or the name of a file that could not be opened.

Valid error codes for parameter $text include (EXCERPT):

Code Description Text excerpt
200 Success Your changes have been saved. Thank You !!!
500 Generic error message An error occured...
403 Error: Permission denied. Password required. You are entering a protected area...
INPUT_INVALID Error: invalid input A chosen parameter is not valid...
404 Error: File not found Please check the URL and try again.
ALLREADY_EXISTS Error: Entry already exists Unable to create an entry with the id "%ID%" because another entry with the same name already exists.
FILE_NOT_CREATED Error: IO failure Unable to create file %FILE%.
NOT_WRITEABLE Error: IO failure Unable to perform write operation on resource "%FILE%". Data could not be saved.
NOT_READABLE Error: IO failure Unable to perform read operation on resource "%FILE%". Data could not be read.

accepted values for parameter $text

Included token, such as% FILE% will be replaced in the following manner:

<?php
$YANA->report( new Warning('FILE_NOT_CREATED', array('FILE' => $a)) );
?>

The special thing about $ YANA->report() is, that it may simultaneously be used to send text messages to a log file and output messages on the screen. If the method is called several times, several text messages are printed.

<?php
$YANA->report( new Warning('FILE_NOT_CREATED', array('FILE' => $a)) );
/* ... */
$YANA->report( new Alert('NOT_WRITEABLE', array('FILE' => $a)) );
/* ... */
$YANA->report( new Error('500') );
$YANA->report( new Log("Input/Output Error in File $a.", $lost_data) );
return false;
?>

The call to Yana->report only creates the text message, but does not exit the program. In order to exit the program, use the method Yana->exitTo. Note: Do NOT use the PHP methods exit () or die(). Otherwise, no output is produced.

Exiting the program

The method Yana->exitTo( [string $action] ) can be used to interrupt the program and define a follow-up action to be executed. This means that the processing of the current action will be canceled and instead the script will be continued with the action $action.

The framework will send all text messages to the browser, and then call itself again. The parameter $action corresponds to the URL parameter "action".

If the parameter $action is not specified, then the front page (whatever it is) will be called instead.

You may also use the special action "null", if you just want to quit the program and NOT continue with another action.

Here is a simple example:

<?php
// Creating some text messages
$YANA->report( new Log('An entry for the logs') );
$YANA->report( new Alert('NOT_WRITEABLE', array('FILE' => $a)) );
// stop current program and continue with action 'index'
$YANA->exitTo('index');

// create error message
$YANA->report( new Error('Access denied!') );
// exit the program (do NOT continue with another action)
$YANA->exitTo('null');

// exit the program and go to the front page
$YANA->exitTo();
?>

Reading and writing of profile settings

General introduction

YANA provides a global memory, that can be read and modified by all plug-ins. All variables stored this area will be accessible in all skins and templates. Please note: the global storage area in YANA is something other than the global namespace of PHP.

Values stored here are presented as a data tree and are organized as multidimensional, associative arrays. This data may be accessed via keys, analogous to Smarty - or, if you like, similar to XPath.

For example:

Assuming that the tree is as follows:

array(
    "a" => 2,
    "b" => array(
        "c" => 3,
         1  => 4
        )
);

Then you may use the following keys:
<?php
$array = $YANA->getVar("*"); // use the key "*" to get a copy of the whole array
$int = $YANA->getVar("a");   // output: 2
$array = $YANA->getVar("b"); // output: array("c" => 3, 1 => 4)
$int = $YANA->getVar("b.c"); // output: 3

/* Unlike to XML-files, numeric ids may be used: */
$int = $YANA->getVar("b.1"); // output: 4
?>

How do I read variables from the global memory?

The function $YANA->getVar() enables you to read values in the global storage area.

<?php
global $YANA;

/* get all values */
$array = $YANA->getVar("*");

/* get specific values */
$mixed = $YANA->getVar("FOO1.FOO2.FOO3");

/* alternative notation */
$mixed = $YANA->registry->getVar("FOO1.FOO2.FOO3");
?>

How do I write variables to the global memory?

The function $YANA->setVar() enables you to write values in the global storage area.

<?php
global $YANA;

/* register variable "MY_VAR" */
$bool = $YANA->setVar("MY_VAR", $new_value);

$bool = $YANA->setVar("FOO1.FOO2.MY_VAR", $new_value);

/* alternative notation */
$mixed = $YANA->registry->setVar("FOO1.FOO2.MY_VAR", $new_value);
?>

How do I deleted variables from the global memory?

To do so use the function $YANA->unsetVar(). For example:

<?php
global $YANA;

/* unset var "MY_VAR" */
$bool = $YANA->unsetVar("MY_VAR");

$bool = $YANA->unsetVar("FOO1.FOO2.MY_VAR");

/* alternative notation */
$mixed = $YANA->registry->unsetVar("FOO1.FOO2.MY_VAR");
?>

How do I change the type of a variable in global memory?

Use the function $YANA->setType() to do this. For example:

<?php
global $YANA;

/* set type of "MY_VAR" to string */
$bool = $YANA->setType("MY_VAR", "string");

$bool = $YANA->setType("FOO1.FOO2.MY_VAR", "string");

/* alternative notation */
$mixed = $YANA->registry->setType("FOO1.FOO2.MY_VAR", "string");
?>

What interesting data can be found in global memory?

List of installed plug-ins
Output a list of all installed plug-ins:
<?php
global $YANA;

$array = $YANA->getVar("INSTALLED_PLUGINS");
print_r($array);
?>
Output:
    Array
        (
            [CONFIG] => 1
            [DB_ADMIN] => 1
            [DEFAULT_LIBRARY] => 1
            [GUESTBOOK] => 1
            [GUESTBOOK_ADMIN] => 1
            [USER] => 1
        )
Checking whether a particular plug-in is installed:
<?php
$bool = $YANA->getVar("INSTALLED_PLUGINS.MY_PLUGIN");
if ($bool) {
    print "Plug-in 'MY_PLUGIN' found.";
} else {
    print "Plug-in 'MY_PLUGIN' not found.";
}

/* alternative notation */
$YANA->plugins->isInstalled('MY_PLUGIN');
?>
Current profile settings

The "Web site profile" is selected by using the URL-parameter "id". Each profile can have individual settings. These settings are also available via the global memory. If no profile is selected, the profile "default" will be used. In fact, as a programmer you don't need to care which one actually is selected. All you need to know is, that profile data contains settings like the background color of the page, preferred font family and other interesting data, which you can access when necessary.

<?php
global $YANA;

/* read current profile settings */
$array = $YANA->getVar("PROFILE");
print_r($array);
?>

Output:
Array
(

>> Web site layout

    [BGCOLOR] => #F0F0F0
    [PFONT] => Arial, Helvetica, sans-serif
    [BGIMAGE] =>
    [LOGO] =>
    [HSIZE] =>
    [HCOLOR] =>
    [HFONT] =>
    [PSIZE] =>
    [PCOLOR] => 

>> Selected language, skin and directory for emot-icons / smilies

    [LANGUAGE] => english.config
    [SKIN] => default.config
    [SMILEYDIR] => common_files/smilies/

>> Other options

    [USERMODE] => 1
    [AUTO] => 1
    [TIMEFORMAT] => 0

>> Logging

    [LOGGING] => 1
    [LOG_LENGTH] => 50

>> Settings for plug-in "rss to html factory"

    [RSS] => Array
        (
            [FILE] => plugins/rss/test.rss
            [MAX] => 5
        )

>> Settings for plug-in 'guestbook'

    [GUESTBOOK] => Array
        (
            [NOREPLY] => noReply@meineAdresse.tld
            [FLOODING] => 0
            [ENTPERPAGE] => 5
            [NOTIFICATION] =>
            [SENDMAIL] =>
            [MAIL] => myName@domain.tld
            [FILE] =>
            [SPAMPROTECT] => 1
            [PROFILE] =>
            [USE_DB] =>
        )

>> Settings for plug-in "search"

    [SEARCH] => Array
        (
            [TARGET] => _self
        )

>> Settings for plug-in "user_admin" (user management)

    [USER] => Array
        (
            [ALLOW_CREATE] => 0
        )

)

<?php
/* read the selected background color */
$string = $YANA->getVar("PROFILE.BGCOLOR");
print '<p style="background-color: '.$string.'">test</p>';
?>
Default settings
<?php
global $YANA;

/* read YANA default settings */
$array = $YANA->getVar("DEFAULT");
print_r($a);
?>
Output:
    Array
        (

>> Determines what action should be called automatically when the
   Argument "action" is empty or not set.
   Default is "sitemap" - but you may use something other, eg. "guestbook_read_read"
   to start the guestbook, or "search_start" for the search engine.
   This setting was introduced in version 2.8.0 

            [HOMEPAGE] => sitemap

>> Determines whether text messages (errors, notes, etc.) are viewed in a separate window (true)
   or in the text of the current page (false).
   This setting was introduced in version 2.8.2

            [MESSAGE] => false

>> Default event configuration for events that are not defined

            [EVENT] => Array
                (
                    [TYPE] => default
                    [MODE] => 0
                    [PERMISSION] => 0
                    [TEMPLATE] => index
                    [INSERT] =>
                )

>> Default interface configuration

            [INTERFACE] => Array
                (
                    [TEST] => Array
                        (
                            [TYPE] => private
                            [MODE] => 0
                            [PERMISSION] => 0
                            [TEMPLATE] => NULL
                        )

                )

>> Default skin configuration

            [SKIN] => Array
                (
                    [DIRECTORY] => default/
                )

>> Default language configuration

            [LANGUAGE] => Array
                (
                    [DIRECTORY] => en/
                )

>> Default for database connections

            [DATABASE] => Array
                (
                    [DSN] => Array
                        (
                            [USE_ODBC] =>
                            [DBMS] => mysql
                            [HOST] => localhost
                            [PORT] => 0
                            [USERNAME] => root
                            [PASSWORD] =>
                            [DATABASE] => yana
                        )

>> Configuration of the database interface

                    [OPTIONS] => Array
                        (
                            [AUTOFREE] => 1
                            [PERSISTENT] => 1
                            [SSL] =>
                        )

>> List of DBMS, which may be addressed via the ODBC interface

                    [REQUIRE_ODBC] => Array
                        (
                            [0] => db2
                            [1] => access
                        )

                )

        )
User Information
<?php
global $YANA;

/* User IP */
print $YANA->getVar("REMOTE_ADDR")."\n";
/* User name */
print $YANA->getVar("SESSION_USER_ID")."\n";
/* access restriction of users */
print $YANA->getVar("PERMISSION")."\n";
/* Session name */
print $YANA->getVar("SESSION_NAME")."\n";
/* Session id */
print $YANA->getVar("SESSION_ID")."\n";
?>

Output:
127.0.0.1
ADMINISTRATOR
100
YSID
480f97e69eae2311d3157cc3377a2d73

Using a CAPTCHA

General introduction

A CAPTCHA is a used for protection from Spam. Therefor a graphic is displayed containing a text, which the user must retype. While a human may solve this task easily, a Bot will most possibly fail. This way a large amount of junk messages may be avoided.

The Framework has a function for producing a CAPTCHA diagram in the png format (MIME type "image/png"), which contains a coincidentally produced code of numbers and letters. A parameter indicates the item number of this code in the current code table. The code table contains 10 entries and expires automatically within a period of 10 minutes through 3 hours after the call of the function. If the table has expired, a new table is created automatically.

How do I use a CAPTCHA in my plug-in?

The Yana PHP-Framework already implements such a function. You don't have to write it - just use it.

The CAPTCHA consists of two parts: a graphic with an input field, which is to be included in the template and a check-code, which is to be inserted in the source code of the plug-in and which returns true or false, if the input was correct or wrong.

See the following example:

Source code for the template:
[%captcha%]

Screenshot
Figure: Example of the representation in the Browser

Source code of the plug-in:
<?php
global $YANA;

/**
 * to check input:
 * 
 * The variable $form_data can be set to
 * $_POST, $_GET, or $ARGS.
 */
$bool = $YANA->handle("security_check_image", $form_data);
if ($bool) { print "Input correct."; } else { print "Input is false."; } ?>

For version older than 2.9.3

Support for CAPTCHAs has been introduced in version 2.8.0. The example above works for Yana PHP-Framework version 2.9.3. In this version handling of CAPTCHAs was simplified.

For the older version 2.8.0 through 2.9.2 use the following example:

Source code for the template:
<input type="hidden" name="security_image_index" value="[%$SECURITY_IMAGE_INDEX%]">
<img src=[%"action=security_get_image&security_image_index=$SECURITY_IMAGE_INDEX"|url%]>
<input type="text" name="security_image">
Source code of the plug-in:
<?php
global $YANA;

/* to create the form: */
/* The variable SECURITY_IMAGE_INDEX needs to be set for the form.
   It is an integer of 1 through 9. */
$YANA->setVar("SECURITY_IMAGE_INDEX", rand(1,9));

/**
 * to check input:
 * 
 * The variable $form_data can be set to
 * $_POST, $_GET, or $ARGS.
 */
$bool = $YANA->handle("security_check_image", $form_data);
if ($bool) { print "Input correct."; } else { print "Input is false."; } ?>

For even older versions (before version 2.8.0) this function has to be included by hand.

Editing plug-ins

How do I define the interfaces?

The interface of a plug-in is a stored as a file with the extension "*.config". You can find these files in the directory "plugins/".

/* see the interface description plugins/foo.config */

<INTERFACE>
	<name of action>

		<TYPE>primary|default|write|read|security|config</TYPE>
/* Type:
 - "primary"  for core processes of a main program
 - "security" for security functions, such as the testing of passwords
 - "config"   for functions to edit configuration files
 - "write"   for write access to the file system or database
 - "read"     for read access to the file system or database
 - "default"  is intended for developers who are undecided where to put the action
*/
		<MODE>0|1</MODE>
/* Mode:
 - 0 (default) normal operating mode
 - 1           start action in default configuration ("safe mode")
               (for safety critical tasks)
*/
		<TEMPLATE>Id of template (e.g. INDEX)</TEMPLATE>
		<INSERT>Id of template (e.g. MY_TEMPLATE)</INSERT>
/* Templates:
 Names of templates for the output
 - the file named in "INSERT" will be embedded in the "TEMPLATE"-file
 - "TEMPLATE" is a static "frame" around the content, default value is INDEX"
 - The name of the template is the name defined in the skin file.
   By comparison open, e.g. the file skins/default/default.config .
 - The special template "NULL" will prevent any output from being generated
   (Very convenient if you don't intend to output a HTML file but a file, e.g. a PNG graphics file)
 - The special template "MESSAGE" creates a text message.
*/

		<PERMISSION>1</PERMISSION>
/* Permission:
 Specifies which minimum permissions are required for a user,
 to be able to call this action. The value is between 0 and 100, where
 the value 0 = "no restriction". You can temporarily deactivate a function 
 by setting the property "permission" to -1. In this case, nobody can
 trigger this action.
*/
		<ONSUCCESS>
			<TEXT>Name of text message</TEXT>
			<GOTO>Name of action</GOTO>
		</ONSUCCESS>
		<ONERROR>
			<TEXT>Name of text message</TEXT>
			<GOTO>Name of action</GOTO>
		</ONERROR>
/* OnSuccess / OnError:
 You may specify an action to automatically go to,
 when the current action was successful, or an error occurred.

 Additionally you can indicate a text message, which is to be shown.
 You find a list of the text messages in the file "languages/en/message.config".
*/
	</name of action>

/* an example */
        <foo>
		<TYPE>write</TYPE>
		<MODE>0</MODE>
		<TEMPLATE>MESSAGE</TEMPLATE>
		<PERMISSION>75</PERMISSION>
		<ONSUCCESS>
			<GOTO>foo_read</GOTO>
		</ONSUCCESS>
		<ONERROR>
			<GOTO>foo_edit</GOTO>
		</ONERROR>
        <foo>
</INTERFACE>

the plug-in class plugins/foo/plugin.php:
<?php
class plugin_foo extends plugin
{

    /* ... */

    function foo($ARGS)
    {
        /* source code of action "foo" */
        if ($test) {
            return true; /* true = SUCCESS -> go to action "foo_read" */
        } else {
            return false; /* false = ERROR -> go to action "foo_edit" */
        }
    }

    function foo_edit($ARGS)
    {
        /* source code of action "foo_edit" */
    }

    function foo_read($ARGS)
    {
        /* source code of action "foo_read" */
    }

}
?>

How do I add new actions?

First you should have a basic structure generated for your new plug-in, with the help of the SDK. This makes the adaptation of the source code a lot simpler.

For adding a new action two things are necessary. On the one hand the source code and on the other hand, registration of this new action in the framework, by publishing the interface. This will allow the framework and all other plug-ins to find and use the action.

Note: All public functions of the class make a function available, which is called likewise, like the function. For example: in order to add an action "foo", you provide a function "foo()". You may run this by using the URL: "index.php?action=foo".

In the following example pay attention to the "Hot Spots". These mark places in the skeleton of the application, where you can fill in your own code.

<?php
class plugin_example extends plugin
{

    function plugin_example($plugin_name)
    {
        settype($plugin_name,"string");
        global $YANA;

        $this->plugin_name = $plugin_name;
    }

    /**
     * Default event handler
     * 
     * @param  string $event name of the called event in lower-case
     * @param  array  $ARGS  array of params passed to the function
     * @return boolean
     */
    function _default($event, $ARGS)
    {
        settype($event, "string");
        settype($ARGS, "array");

        # HOT-SPOT << you may define actions here

        # for example by using a Switch statement

        switch ($event)
        {
            case 'my_action1':
                # HOT-SPOT << code for action 'my_action1'
            break;
            case 'my_action2':
                # HOT-SPOT << code for action 'my_action2'
            break;
        }

        return true;
    }

    /**
     * Type: read
     * Permission: 0
     * Templates: index entries
     * 
     * @param array $ARGS array of params passed to the function
     * @return boolean
     */
    function guestbook_read_entries ($ARGS)
    {
        # HOT-SPOT << code for action 'guestbook_read_entries'
    }

/* { ... } */

    /**
     * Type: write
     * Permission: 100
     * Templates: MESSAGE
     * 
     * @param array $ARGS array of params passed to the function
     * @return boolean
     */
    function guestbook_write_write ($ARGS)
    {
        # HOT-SPOT << code for action 'guestbook_write_write'
        $YANA->message("OK", "200");
    }

    function my_action ($ARGS)
    {
        # HOT-SPOT << code for action 'my_action'
    }
}
?>

All "Hot-Spots", where the developer can write own source code or supplement existing code, are emphasized in this brief example. The function "my_action" is to demonstrate, how you can add own actions to the interface of the class and thus to the plug-in. Take a look at the function "_default". This function is inherited from the parent class "plugin" and serves as default event handler, which catches all events, that are send to the plug-in, but do not have a public event handler (function) in the classe's interface. In addition this function intercepts special cases, like events where the corresponding function is not public, the function is the class constructor, or it is inherited from the base class "plugin" like the public function "toString()". For security reasons this also applies to inherited functions which are reimplemented in the child class. The reason is, these methods are public. They are part of a common interface of all classes who inherit from the parent class "plugin". Thus the functionality of these functions is guaranteed by the given semantics of the base class. These semantics should be kept in the child class.

In order to create a function, which is not to part of the public interface, mark the function as "private".

Reload list

Please note: you need to refresh the plug-in cache to enable new functions. To do so, log in as administrator, open the administration menu and click "reload list".

How do I create a setup menu?

The "setup" menus can be found in the column "options" (left) of the administration menu, as shown in the following illustration.

Reload list

To create such a menu and setup, it is enough to first: add an entry to the configuration file of the plug-in and second: create an HTML template. It is not necessary to write PHP code. Look at the following example of the configuration file of a plug-in:

File "plugins/example.config"

<INFO>
	/* first the mandatory data - these have nothing to do
	 * with the setup menu itself.
	 */
	<ID>foo</ID>
	<NAME>Foo plugin</NAME>
	<AUTHOR>Thomas Meyer</AUTHOR>
	<DESCRIPTION>my example</DESCRIPTION>
	<LOGO>%PLUGINDIR%foo/preview.jpg</LOGO>
	<IMAGE>%PLUGINDIR%foo/icon.png</IMAGE>
	<TYPE>primary</TYPE>
	/* Here come the entries for the setup menu */
	<SETUP>
		/* The following code will create an entry
		 * labeled "Foo Options" using the icon
		 * "plugins/foo/setup1.gif". By clicking the
		 * button "Setup", the action
		 * "foo_setup_1" is triggered.
		 */
		<0>
			<ACTION>foo_setup_1</ACTION>
			<TITLE>Foo Options</TITLE>
		</0>
		/* A second example: */
		<1>
			<ACTION>foo_setup_2</ACTION>
			<TITLE>Bar Options</TITLE>
		</1>
	</SETUP>
</INFO>

<INTERFACE>

	/* The actions "foo_setup_1" <und "foo_setup_2"
	 * are now associated with a template
	 */
	<foo_setup_1>
		/* TYPE=config defines, that this is a setup menu */
		<TYPE>config</TYPE>
		/* PERMISSION=100 restricts access to users
		 * with security level 100 - these are administrators.
		 */
		<PERMISSION>100</PERMISSION>
		/* foo_template_1 is the name of the template */
		<INSERT>foo_template_1</INSERT>
	</foo_setup_1>

	<foo_setup_2>
		<TYPE>config</TYPE>
		<PERMISSION>75</PERMISSION>
		<INSERT>foo_template_2</INSERT>
	</foo_setup_2>

	/* ... other actions might follow here ... */

</INTERFACE>

Now all you need is a HTML page containing a form, like in the following example:

File "skins/default/example.html"
<!-- Just copy the head of the file. Here is nothing
     to be edited. Copy'n'paste will be enough. -->

<!-- BEGIN: Head -->
<form method="POST" enctype="multipart/form-data" action="[%$PHP_SELF%]">
[%if !$ID%][%if $PERMISSION==100%]
	<input type="hidden" name="action" value="set_config_profile">
[%/if%][%else%]
	<input type="hidden" name="action" value="set_config_profile">
[%/if%]
	<input type="hidden" name="[%$SESSION_NAME%]" value="[%$SESSION_ID%]">
	<input type="hidden" name="id" value="[%$ID%]">
<!-- END: Head -->

<!-- Now the part where you need to fill your information -->

	<!-- adding a headline is a good idea: -->
	<h1>Setup</h1>

	<!-- a list of options -->
	<label>Option 1
	<!-- $PROFILE.OPT1 is the stored value of this field - if any value has been stored yet.
	     The modifier |entities ensures, that tags and quotes
	    are converted to HTML entities, so that
	     HTML syntax errors are avoided.
	 -->
	<input type="text" name="opt1" value="[%$PROFILE.OPT1|entities]">
	</label><br>

	<label>Option 2
	<input type="text" name="path.opt2" value="[%$PROFILE.PATH.OPT2|entities]">
	</label><br>

	<label>Option 3
	<input type="text" name="opt3" value="[%$PROFILE.OPT3|entities]">
	</label><br>

	<!-- don't forget the submit button -->
	<input type="submit" value="[%$LANGUAGE.BUTTON_SAVE%]">

</form>

This HTML page causes the following array to be stored on submission of the form:

<?php
$PROFILE = array(
    'opt1' => "foo",
    'path' => array(
        'opt2' => "bar"
    ),
    'opt3' => "foobar"
);
?>

You can access the stored options from within your plug-in as follows:

<?php
global $YANA;
$opt1 = $YANA->getVar('PROFILE.OPT1');
$path_opt2 = $YANA->getVar('PROFILE.PATH.OPT2');
$opt3 = $YANA->getVar('PROFILE.OPT3');
?>

To have the form "example.html" shown when clicking the button "setup", it needs to be associated with the action "foo_setup_1". This is done as follows:

File "skins/default/example.config"
<!-- First the template with the name "foo_template_1" is defined ... -->
<foo_template_1>
	<FILE>example.html</FILE>
</foo_template_1>

File "plugins/example.config"

...

<INTERFACE>

	<foo_setup_1>
		<TYPE>config</TYPE>
		<PERMISSION>100</PERMISSION>
	<!-- ... Next the template "foo_template_1" is associated with the action "foo_setup_1". -->
		<INSERT>foo_template_1</INSERT>
	</foo_setup_1>
...

</INTERFACE>

Graphics and photos

How do I load an image from a file?

<?php
$image = new Image('file name');
// for example:
$image = new Image('folder/file.png');
?>

How do I load an image from a string?

<?php
$content = file_get_contents('folder/file.png');
$image = new Image($content, 'png');
?>

How do I create a new (empty) image?

<?php
$image = new Image();
$width = 640;
$height = 480;
$image->resize($width, $height);
?>

How do I create a thumbnail?

<?php
$image = new Image('folder/file.png');
$width = 50;
// if $height is not defined,
// the image is resized proportional
$image->resize($width);
$image->outputToFile('folder/thumbnail.png');
?>

 How do I create a form to upload pictures?

HTML-Code:
<form method="POST" action="%PHP_SELF%" enctype="multipart/form-data">
 (...)
Upload image: <input type="file" name="my_image" />
 (...)
</form>
PHP-Code:
<?php
// get data from form field 'my_image'
$id = 'my_image';
// output to directory 'foo/bar/' using filename 'image'
// (extension will be determined automatically)
$file = 'foo/bar/image';
// output as png image
$type = 'png';
// limit upload to 150 kbyte
$size = 150000;
// resize to 150px x 200px
$width = 150;
$height = 200;
// leave aspect-ratio untouched
$ratio = true;
// set background color to gray
$color = array(80, 80, 80);

// call method
$result = Image::uploadFile($id, $file, $type, $size, $width, $height, $ratio, $color);

// check for errors
if (is_string($result)) {

    print "The image was successfully uploaded to '$result'";

// error handling
} else {

    switch ($result)
    {
        case UPLOAD_ERR_SIZE:
        case UPLOAD_ERR_INI_SIZE:
        case UPLOAD_ERR_FORM_SIZE:
            die('File too big!');
        break;

        case UPLOAD_ERR_NO_FILE:
            die('No file has been uploaded!');
        break;

        case UPLOAD_ERR_INVALID_TARGET:
            die("Unable to write to file: '$file'");
        break;

        case UPLOAD_ERR_FILE_TYPE:
            die('The file is not a recognized image!');
        break;

        default:
            die('Some unexpected error occurred!');
        break;
    }

}
?>

How do I draw lines, geometric figures and text?

<?php
// create an empty image
$image = new Image();
$width = 180;
$height = 120;
$image->resize($width, $height);

// set line width to 3px (optional)
$image->setLineWidth(3);

// fill canvas with background color
$x = 0;
$y = 0;
$image->fill($image->white, $x, $y);

// draw a line
$x1 = 10;
$y1 = 10;
$x2 = 100;
$y2 = 30;
$image->drawLine($x1, $y1, $x2, $y2, $image->black);

// draw a circle
$x = 30;
$y = 50;
$width = 50;
$image->drawEllipse($x, $y, $width);

// draw a rectangle
$x = 10;
$y = 60;
$width = 100;
$height = 50;
$border = $image->black;
$fill = $image->navy;
$image->drawRectangle($y, $y, $width, $height, $border, $fill);

// draw a triangle (or other polygon)
$points = array(
0 => array( 20, 0 ),
1 => array( 40, 20 ),
2 => array( 0, 20 )
);
$x = 50;
$y = 80;
$border = $image->black;
$fill = $image->yellow;
$image->drawPolygon($points, $x, $y, $border , $fill);

// write a text
$text = 'Hello World';
$x = 70;
$y = 40;
$color= $image->black;
$image->drawString($text, $x, $y, $color);

// Use of brushes

// create new brush
$brush = new Brush('small star');
// set brush size to 10px
$brush->setSize(10);
// set brush color to red
$brush->setColor(255, 0, 0);
// select the brush
$image->setBrush($brush);

// draw a dot using the brush
$x = 150;
$y = 100;
$image->drawPoint($x, $y, IMG_COLOR_BRUSHED);

// output file to browser
$image->outputToScreen('png');
exit;
?>

figure
Figure: Script output

Details on use of colors

If you work with images, you need colors to draw lines, text or surfaces.

There are 17 predefined colors. These are:

  1. $image->aqua
  2. $image->black
  3. $image->blue
  4. $image->fuchsia
  5. $image->gray
  6. $image->green
  7. $image->grey (Alias von $image->gray)
  8. $image->lime
  9. $image->maroon
  10. $image->navy
  11. $image->olive
  12. $image->purple
  13. $image->red
  14. $image->silver
  15. $image->teal
  16. $image->white
  17. $image->yellow

You can get other colors by calling: $color = $image->getColor($red, $green, $blue); where $red, $green, $blue are integers between 0 and 255.

It is important to you understand, that a color is not an object by itself, but a specific property of a graphic. This is, each image has its own color palette and each color is a part (mathematically speaking an "element") of this palette.

The palette of the image is an indexed set of all contained in the graphic colors. A color is represented by an integer. To be more precise: it is the index number of this color within the color palette of the image. This means, number 1 names the color, which is stored at position 1 of the image's color palette. For example, this color might be "red" in this image. However, you should note, that depends on the color palette and differs from one image to another.

Which graphic filters and effects are there?

Here is a list with examples for some interesting methods of the class image:

<?php
$image = new Image('foo.png');

/* set brightness
 * 
 * integer of -1.0 trough +1.0 (-100% and +100%)
 */
$amount = 0.5; /* 0.5 = +50% */
$image->brightness($amount);
?>

figure
Figure: Script output

<?php
/* set contrast
 * 
 * integer of -1.0 trough +1.0 (-100% and +100%)
 */
$amount = 0.5; /* 0.5 = +50% */
$image->contrast($amount);
?>

figure
Figure: Script output

<?php
/* blur the image
 * 
 * integer of 0.0 trough +1.0 (0% and +100%)
 */
$amount = 0.8; /* 0.8 = 80% */
$image->blur($amount);
?>

figure
Figure: Script output

<?php
/* sharpen the image
 * 
 * integer of 0.0 trough +1.0 (0% and +100%)
 */
$amount = 0.8; /* 0.8 = 80% */
$image->sharpen($amount);
?>

figure
Figure: Script output

<?php
/* set to 256 colors grayscale palette
 */
$image->toGrayscale();
?>

figure
Figure: Script output

<?php
/* colorize the image
 * 
 * $red, $green, $blue = integer -255 through 255
 * 
 * the given color value is added to colors of the image
 */
$red = -80;
$green = -40;
$blue = 120;
$image->colorize($red, $green, $blue);
?>

figure
Figure: Script output

<?php
/* multiply
 * 
 * $red, $green, $blue = integer 0 through 255
 * 
 * the given color value is multiplied to the colors of the image
 */
$red = 100;
$green = 255;
$blue = 50;
/* this examples reduces the red- and clue channels */
$image->multiply($red, $green, $blue);
?>

figure
Figure: Script output

<?php
/* monochromatic filter
 * 
 * $red, $green, $blue = integer 0 through 255
 * 
 * Colorized the image using the given color.
 */
$red = 130;
$green = 180;
$blue = 200;
$image->monochromatic($red, $green, $blue);
?>

figure
Figure: Script output

<?php
/* invert color values (negate)
 */
$image->negate();
?>

figure
Figure: Script output

For more details and examples see the API documentation.

Create multicolumn layouts

General introduction

The Yana Framework provides predefined CSS to create text boxes or pages with 2, 3 or more vertical columns. No programming is required. The required CSS code will be loaded automatically in the "default" skin theme.

Below are some examples for demonstration

How do I create 2 columns?

Source code for the template:

<div class="multicol2">
    <div class="col_left">
        <h2>Column 1</h2>

        Nulla ultrices lacinia mi. Nulla dapibus, risus vitae imperdiet
        commodo, ipsum ligula lacinia orci, non venenatis metus ligula
        sit amet turpis. Nunc accumsan tempor nulla. Vivamus eleifend,
        s eu feugiat consequat, mi lacus vestibulum velit, eu

ege
        egestas purus nunc ac est. Mauris massa lorem, lacinia non,
        condimentum vel, cursus ac, lectus. Nulla tempor molestie quam.
        Aenean dapibus nisl nonummy quam. Mauris pellentesque ornare
        ante. Integer a urna ultricies neque bibendum dignissim.
        Suspendisse interdum nisl. In rhoncus. Vivamus risus mi, semper
        id, lobortis et, auctor in, nulla. Sed placerat posuere tortor.
        In vitae augue.

    </div>
    <div class="col_right">
        <h2>Column 2</h2>

        Aliquam auctor viverra ligula. Ut nisi felis, condimentum at,
        aliquam eget, tempus id, nisi. Maecenas a libero. Nulla metus
        mi, malesuada sit amet, sagittis bibendum, scelerisque a, purus.
        Nunc ipsum. Sed vel augue pellentesque ligula pharetra pulvinar.
        Class aptent taciti sociosqu ad litora torquent per conubia
        nostra, per inceptos hymenaeos. Nam orci. Donec neque pede,
        iaculis in, pellentesque ac, commodo eget, libero. Sed vel
        magna.

    </div>
    <div class="col_foot">&nbsp;</div>
</div>
Column 1
Nulla ultrices lacinia mi. Nulla dapibus, risus vitae imperdiet commodo, ipsum ligula lacinia orci, non venenatis metus ligula sit amet turpis. Nunc accumsan tempor nulla. Vivamus eleifend, lectus eu feugiat consequat, mi lacus vestibulum velit, eu egestas purus nunc ac est. Mauris massa lorem, lacinia non, condimentum vel, cursus ac, lectus. Nulla tempor molestie quam. Aenean dapibus nisl nonummy quam. Mauris pellentesque ornare ante. Integer a urna ultricies neque bibendum dignissim. Suspendisse interdum nisl. In rhoncus. Vivamus risus mi, semper id, lobortis et, auctor in, nulla. Sed placerat posuere tortor. In vitae augue.
Column 2
Aliquam auctor viverra ligula. Ut nisi felis, condimentum at, aliquam eget, tempus id, nisi. Maecenas a libero. Nulla metus mi, malesuada sit amet, sagittis bibendum, scelerisque a, purus. Nunc ipsum. Sed vel augue pellentesque ligula pharetra pulvinar. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Nam orci. Donec neque pede, iaculis in, pellentesque ac, commodo eget, libero. Sed vel magna.
 

How do I create 3 columns?

Source code for the template:

<div class="multicol3">
    <div class="col_left">
        <h2>Column 1</h2>

        Nulla ultrices lacinia mi. Nulla dapibus, risus vitae imperdiet
        commodo, ipsum ligula lacinia orci, non venenatis metus ligula
        sit amet turpis. Nunc accumsan tempor nulla. Vivamus eleifend,
        s eu feugiat consequat, mi lacus vestibulum velit, eu

ege
        egestas purus nunc ac est. Mauris massa lorem, lacinia non,
        condimentum vel, cursus ac, lectus. Nulla tempor molestie quam.
        Aenean dapibus nisl nonummy quam. Mauris pellentesque ornare
        ante. Integer a urna ultricies neque bibendum dignissim.
        Suspendisse interdum nisl. In rhoncus. Vivamus risus mi, semper
        id, lobortis et, auctor in, nulla. Sed placerat posuere tortor.
        In vitae augue.

    </div>
    <div class="col_center">
        <h2>Column 2</h2>

        Aliquam auctor viverra ligula. Ut nisi felis, condimentum at,
        aliquam eget, tempus id, nisi. Maecenas a libero. Nulla metus
        mi, malesuada sit amet, sagittis bibendum, scelerisque a, purus.
        Nunc ipsum. Sed vel augue pellentesque ligula pharetra pulvinar.
        Class aptent taciti sociosqu ad litora torquent per conubia
        nostra, per inceptos hymenaeos. Nam orci. Donec neque pede,
        iaculis in, pellentesque ac, commodo eget, libero. Sed vel
        magna.

    </div>
    <div class="col_right">
        <h2>Column 3</h2>

        Morbi eget dolor. Etiam ut velit sollicitudin massa laoreet
        lobortis. ices.

Morbi justo tortor, blandit  Morbi justo
        tortor, blandit sed, euismod at, eleifend nec, tortor. Lorem
        ipsum dolor sit amet, consectetuer adipiscing elit. Etiam eget
        enim. s venenatis volutpat.

Integer ipsum ante, porta eu
        Integer ipsum ante, porta eu, porta vel, euismod in, magna.
        Fusce dolor quam, lacinia eget, rutrum at, lacinia pulvinar,
        risus. Nulla imperdiet, lorem facilisis lacinia suscipit, diam
        mi aliquet dui, at fringilla lectus mauris sit amet ipsum.
        Morbi eget sem quis est congue pulvinar. Vivamus fermentum
        dolor nec nisi convallis posuere. Aliquam eu diam. Ut suscipit,
        nisi quis vehicula ullamcorper, lectus lorem fermentum tellus,
        ut egestas arcu sapien aliquam massa.

    </div>
    <div class="col_foot">&nbsp;</div>
</div>
Column 1
Nulla ultrices lacinia mi. Nulla dapibus, risus vitae imperdiet commodo, ipsum ligula lacinia orci, non venenatis metus ligula sit amet turpis. Nunc accumsan tempor nulla. Vivamus eleifend, lectus eu feugiat consequat, mi lacus vestibulum velit, eu egestas purus nunc ac est. Mauris massa lorem, lacinia non, condimentum vel, cursus ac, lectus. Nulla tempor molestie quam. Aenean dapibus nisl nonummy quam. Mauris pellentesque ornare ante. Integer a urna ultricies neque bibendum dignissim. Suspendisse interdum nisl. In rhoncus. Vivamus risus mi, semper id, lobortis et, auctor in, nulla. Sed placerat posuere tortor. In vitae augue.
Column 2
Aliquam auctor viverra ligula. Ut nisi felis, condimentum at, aliquam eget, tempus id, nisi. Maecenas a libero. Nulla metus mi, malesuada sit amet, sagittis bibendum, scelerisque a, purus. Nunc ipsum. Sed vel augue pellentesque ligula pharetra pulvinar. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Nam orci. Donec neque pede, iaculis in, pellentesque ac, commodo eget, libero. Sed vel magna.
Column 3
Morbi eget dolor. Etiam ut velit sollicitudin massa laoreet lobortis. ices. Morbi justo tortor, blandit Morbi justo tortor, blandit sed, euismod at, eleifend nec, tortor. nsectetuer adipiscing elit. Etiam eget enim. Maecenas v Etiam eget enim. s venenatis volutpat. Integer ipsum ante, porta eu Integer ipsum ante, porta eu, porta vel, euismod in, magna. Fusce dolor quam, lacinia eget, rutrum at, lacinia pulvinar, risus. Nulla imperdiet, lorem facilisis lacinia suscipit, diam mi aliquet dui, at fringilla lectus mauris sit amet ipsum. Morbi eget sem quis est congue pulvinar. Vivamus fermentum dolor nec nisi convallis posuere. Aliquam eu diam. Ut suscipit, nisi quis vehicula ullamcorper, lectus lorem fermentum tellus, ut egestas arcu sapien aliquam massa.
 

How do I create 4 or more columns?

It is possible to have nested multicolumn boxes. The following example demonstrates how two 2-column layouts are nested to create a 4-column layout.

Source code for the template:

<div class="multicol2">
    <div class="col_left">
        <div class="multicol2">
            <div class="col_left">
                <h2>Column 1</h2>

                Vestibulum tempor commodo mi. Integer sed nisi. Donec
                nulla elit, commodo porttitor, semper eget, sagittis
                viverra, urna. Duis pretium dui facilisis turpis.
                Mauris vel arcu. Donec ut lorem vel lorem aliquet
                commodo. Donec sollicitudin mattis lacus. Ut non magna
                sit amet tortor viverra sagittis. Ut venenatis.
                Vestibulum sodales sapien scelerisque erat. Integer
                accumsan orci et tortor vulputate mollis. Curabitur
                lacinia quam id libero. Nulla eu lorem eget mi mattis
                tempus. Suspendisse consectetuer. Vivamus at risus.
                Aenean malesuada.

            </div>
            <div class="col_right">
                <h2>Column 2</h2>

                Morbi id nisi. Proin fringilla eleifend mi. Nunc eget
                elit. Nam ligula nibh, euismod blandit, nonummy eu,
                porta non, magna. Ut id nisi id metus consectetuer
                euismod. Vestibulum ante ipsum primis in faucibus orci
                luctus et ultrices posuere cubilia Curae; Mauris
                blandit ultrices eros. Fusce in nisl sit amet enim
                consequat tristique. Sed facilisis eros convallis magna.
                Pellentesque justo. Praesent nec libero at velit
                malesuada nonummy. Mauris vitae lectus eget elit
                euismod bibendum. Nulla nec ligula. Curabitur metus.
                Integer lectus nulla, iaculis molestie, venenatis non,
                suscipit commodo, est. Sed pulvinar, justo eu pulvinar
                convallis, mi tellus pellentesque elit, eu aliquet
                sapien ligula non mauris.

            </div>
        </div>
    </div>
    <div class="col_right">
        <div class="multicol2">
            <div class="col_left">
                <h2>Column 3</h2>

                Praesent eu risus. Fusce feugiat. Maecenas eget lacus
                quis nisi blandit porttitor. Pellentesque sed turpis.
                Nulla facilisi. Aenean convallis dolor eget elit. Donec
                elementum, pede nec euismod varius, ante diam elementum
                lectus, vitae iaculis orci libero quis tortor.
                Vestibulum justo est, laoreet eu, consequat sed,
                molestie at, mi. Fusce luctus, odio eu imperdiet
                viverra, nunc arcu gravida ante, iaculis lacinia tortor
                arcu ac felis. Quisque sagittis aliquam nisi. Aliquam
                nonummy. Nulla mattis orci vel nunc. Nunc in odio.
                Proin mi risus, lobortis ac, mollis vitae, lacinia
                vehicula, nulla. Nunc nibh. Morbi laoreet pellentesque
                justo. ent ultrices.

Spa

            </div>
            <div class="col_right">
                <h2>Column 4</h2>

                Nullam eget lectus. Aenean non augue auctor erat luctus
                fermentum. Quisque aliquam eros vel ligula. Mauris pede
                velit, mollis eget, adipiscing non, placerat nec, erat.
                Vestibulum vel sem non orci porttitor congue. Cras
                pretium eros eget dui. Donec gravida. Sed mattis
                tincidunt enim. Donec aliquam, quam eu rhoncus gravida,
                nibh nunc egestas nulla, lobortis interdum nisi dui id
                nisi. Praesent tristique velit sit amet ante. Nam
                malesuada, nisi in laoreet venenatis, dolor pede
                vehicula nunc, tempus accumsan ante purus eget sapien.
                Praesent aliquam, lorem sit amet volutpat faucibus,
                ligula lorem auctor diam, vitae vulputate pede ligula
                et leo.

            </div>
        </div>
    </div>
    <div class="col_foot">&nbsp;</div>
</div>
Column 1
Vestibulum tempor commodo mi. Integer sed nisi. Donec nulla elit, commodo porttitor, semper eget, sagittis viverra, urna. Duis pretium dui facilisis turpis. Mauris vel arcu. Donec ut lorem vel lorem aliquet commodo. Donec sollicitudin mattis lacus. Ut non magna sit amet tortor viverra sagittis. Ut venenatis. Vestibulum sodales sapien scelerisque erat. Integer accumsan orci et tortor vulputate mollis. Curabitur lacinia quam id libero. Nulla eu lorem eget mi mattis tempus. Suspendisse consectetuer. Vivamus at risus. Aenean malesuada.
Column 2
Morbi id nisi. Proin fringilla eleifend mi. Nunc eget elit. Nam ligula nibh, euismod blandit, nonummy eu, porta non, magna. Ut id nisi id metus consectetuer euismod. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris blandit ultrices eros. Fusce in nisl sit amet enim consequat tristique. Sed facilisis eros convallis magna. Pellentesque justo. Praesent nec libero at velit malesuada nonummy. Mauris vitae lectus eget elit euismod bibendum. Nulla nec ligula. Curabitur metus. Integer lectus nulla, iaculis molestie, venenatis non, suscipit commodo, est. Sed pulvinar, justo eu pulvinar convallis, mi tellus pellentesque elit, eu aliquet sapien ligula non mauris.
Column 3
Praesent eu risus. Fusce feugiat. Maecenas eget lacus quis nisi blandit porttitor. Pellentesque sed turpis. Nulla facilisi. Aenean convallis dolor eget elit. Donec elementum, pede nec euismod varius, ante diam elementum lectus, vitae iaculis orci libero quis tortor. Vestibulum justo est, laoreet eu, consequat sed, molestie at, mi. Fusce luctus, odio eu imperdiet viverra, nunc arcu gravida ante, iaculis lacinia tortor arcu ac felis. Quisque sagittis aliquam nisi. Aliquam nonummy. Nulla mattis orci vel nunc. Nunc in odio. Proin mi risus, lobortis ac, mollis vitae, lacinia vehicula, nulla. Nunc nibh. Morbi laoreet pellentesque justo. ent ultrices. Spa
Column 4
Nullam eget lectus. Aenean non augue auctor erat luctus fermentum. Quisque aliquam eros vel ligula. Mauris pede velit, mollis eget, adipiscing non, placerat nec, erat. Vestibulum vel sem non orci porttitor congue. Cras pretium eros eget dui. Donec gravida. Sed mattis tincidunt enim. Donec aliquam, quam eu rhoncus gravida, nibh nunc egestas nulla, lobortis interdum nisi dui id nisi. Praesent tristique velit sit amet ante. Nam malesuada, nisi in laoreet venenatis, dolor pede vehicula nunc, tempus accumsan ante purus eget sapien. Praesent aliquam, lorem sit amet volutpat faucibus, ligula lorem auctor diam, vitae vulputate pede ligula et leo.
 

Create tree menus

General introduction

The Yana Framework provides several solutions to create foldable tree menus. Extensive programming is not required.

The following options are available:

  1. You can write these menus in HTML code yourself. The framework takes care of the presentation. You retain full control over the code.
  2. You can have the HTML code for the menus created automatically and only need to provide the list of entries. In this case, you don't have to care about the code any longer, just take care of the content.

For both variants, you have the choice between two different layouts. This chapter contains three sections. First you will be shown how to write the HTML source code for both menus 1 and 2 by hand. Then the text will demonstrate how to use the template function "printUnorderedList" to have these menus created automatically.

How do I create a foldable menu?

This section describes how to create a foldable menu directly in HTML. The Yana Framework will take care of the functionality and presentation automatically.

This is the 1st layout proposal. It uses JavaScript and CSS to display the menu. Following is a 2nd layout proposal, which can be used alternatively and uses exclusively CSS without JavaScript.

The layout is defined in the file "skins/default/styles/menu.css" and may be controlled by modification of the following CSS-classes:

The presentation of the menu corresponds to the current recommendations and should be accepted by search engines.

Proceed as follows:


Figure: Menu presentation

<ul class="menu root">
   <li class="entry"><a href="target.html">Link</a></li>
   <li class="menu">
       <div onclick="yanaMenu(this)">Menu 1</div>
       <ul class="menu">
           <li class="entry"><a href="1.html">Item 1</a></li>
       </ul>
   </li>
   <li class="menu">
       <div onclick="yanaMenu(this)">Menu 2</div>
       <ul class="menu">
           <li class="entry"><a href="2.html">Item 2</a></li>
<li class="menu">
           <div onclick="yanaMenu(this)">SubMenu 2.1</div>
               <ul class="menu">
                   <li class="entry">
<a href="2_1.html">Item 2.1</a>
</li>
                   <li class="entry">
<a href="2_2.html">Item 2.2</a>
</li>
                   <li class="entry">
<a href="2_3.html">Item 2.3</a>
</li>
             </ul>
           </li>
       </ul>
   </li>
   <li class="menu">
       <div onclick="yanaMenu(this)">closed</div>
       <ul class="menu" style="display: none;">
           <li class="entry"><a href="2.html">(invisible)</a></li>
       </ul>
   </li>
</ul>

The attribute "onclick" here triggers opening and closing of the menu. "Style" attributes set special properties, where "display: none" hides the menu. Menus are enclosed by an element of CSS-class "menu". Menu items are marked by an element of CSS-class "entry". The header of a menu is enclosed in "div" tags, which also have an attribute "onclick". By clicking the headline, the menu can be opened or closed.

Tip: it is usually easier to begin with a simple list of the tags "ul", "li" and add the CSS classes and JavaScript code later, rather than writing the menu line by line. This will keep the code clearer while editing.

There is the following predefinition: all menus are initially open, if they are not explicitly marked as closed. Multiple menus may be open at the same time.

If you want only 1 menu to be open at the same time, add the following source code:

<script type="text/javascript">
    yana_menu_auto_close = true;
</script>

If you want to have all menus closed at start, use the following code:

<script type="text/javascript">
    yanaCloseAll();
</script>

Both options can be combined.

<script type="text/javascript">
    yana_menu_auto_close = true;
    yanaCloseAll();
</script>

How do I create a horizontal menu?


Figure: Menu presentation

<ul class="hmenu">
  <li class="entry">
    <a href="target.html">Link 1</a>
  </li>
  <li onmouseover="yanaHMenu(this,true)" onmouseout=
  "yanaHMenu(this,false)" class="hmenu">
    <div class="menu_head">
      Menu 1
    </div>

    <ul class="hmenu">
      <li class="entry">
        <a href="1.html">Item 1</a>
      </li>
    </ul>
  </li>
  <li onmouseover="yanaHMenu(this,true)" onmouseout=
  "yanaHMenu(this,false)" class="hmenu">
    <div class="menu_head">

      Menu 2
    </div>
    <ul class="hmenu">
      <li class="entry">
        <a href="2.html">Item 2</a>
      </li>
      <li onmouseover="yanaHMenu(this,true)" onmouseout=
      "yanaHMenu(this,false)" class="hmenu">
        <div class="menu_head">
          Submenu 2.1
        </div>

        <ul class="hmenu">
          <li class="entry">
            <a href="2_1.html">Item 2.1</a>
          </li>
          <li class="entry">
            <a href="2_2.html">Item 2.2</a>
          </li>
          <li class="entry">

            <a href="2_3.html">Item 2.3</a>
          </li>
        </ul>
      </li>
    </ul>
  </li>
  <li class="entry">
    <a href="3.html">Link 2</a>

  </li>
</ul>

The menu works with CSS and does not need JavaScript. However, JavaScript is used anyway, so that older browsers and browser with incomplete CSS support, such as Internet Explorer 6.0, are still able to view the page as expected.

Each menu is initially closed and will be temporarily opened when the user hovers it with the mouse.

The CSS classes have only few differences to those of the vertical menu. Just be sure to use the class "hmenu" instead of class "menu".

How do I create a tree menu?

This is the 2nd layout proposal. The menus will open automatically when they are touched with the mouse. In Firefox, Opera and Co. opening and closing the menu is done entirely via CSS (without JavaScript). Only in Internet Explorer, including version 7, JavaScript has to be enabled, because the necessary CSS 2.1 pseudo-classes are not yet fully supported.

The layout is defined in the file "skins/default/styles/gui_array.css" and may be controlled via the following CSS classes:

Proceed as follows:


Figure: Menu presentation

<ul class="gui_array_list">
  <li class="gui_array_list">
    <a class="gui_array_value" href="target.html">Link 1</a>
  </li>
  <li class="gui_array_head"
      onmouseover="this.className='gui_array_head_open'"
      onmouseout="this.className='gui_array_head'">
    <span class="gui_array_key">Menu 1</span>
    <ul class="gui_array_list">
      <li class="gui_array_list">
        <a class="gui_array_value" href="1.html">Item 1</a>
      </li>
    </ul>
  </li>
  <li class="gui_array_head"
      onmouseover="this.className='gui_array_head_open'"
      onmouseout="this.className='gui_array_head'">
    <span class="gui_array_key">Menu 2</span>
    <ul class="gui_array_list">
      <li class="gui_array_list">
        <a class="gui_array_value" href="2.html">Item 2</a>
      </li>
      <li class="gui_array_head"
          onmouseover="this.className='gui_array_head_open'"
          onmouseout="this.className='gui_array_head'">
        <span class="gui_array_key">SubMenu 2.1</span>
        <ul class="gui_array_list">
          <li class="gui_array_list">
            <a class="gui_array_value" href="2_1.html">Item 2.1</a>
          </li>
          <li class="gui_array_list">
            <a class="gui_array_value" href="2_2.html">Item 2.2</a>
          </li>
          <li class="gui_array_list">
            <a class="gui_array_value" href="2_3.html">Item 2.3</a>
          </li>
        </ul>
      </li>
    </ul>
  </li>
  <li class="gui_array_list">
    <a class="gui_array_value" href="3.html">Link 2</a>
  </li>
</ul>

The attribute "onmouseover" is used to open the menus, and the attribute "onmouseout" to close them. These attributes are not mandatory, but are here for backward compatibility with the Internet Explorer and older browsers.

How can I have menus created automatically?

The template function "printUnorderedList" presents the contents of a multidimensional arrays as a menu.

To do this, 2 steps are required. Initially create an array containing the entries of your menu in the PHP source code of your plug-in. Then add the function "printUnorderedList" to your template to have the desired menu generated.

Proceed as follows:

  1. Open the PHP file with the source code of your plug-in.
  2. Look for the action, for which the menu is to be shown.
  3. The following is an example of an array, which could be created in the PHP source code:


Figure: Menu presentation

<?php
global $YANA;

$array = array(
  'target.html' => 'Link 1',
  'Menu 1' => array(
    '1.html' => 'Item 1'
  ),
  'Menu 2' => array(
    '2.html' => 'Item 2',
    'Submenu 2.1' => array(
      '2_1.html' => 'Item 2.1',
      '2_2.html' => 'Item 2.2',
      '2_3.html' => 'Item 2.3' ),
  ),
  '3.html' => 'Link 2'
);

$YANA->setVar('myMenu', $array);
?>

As you can see, the key to the arrays contain the URLs and the values contained the caption.

To generate the actual menu, proceed as follows:

  1. Open the HTML template, which you used for this action.
  2. Look in the HTML source code for a place to insert the menu.
  3. Add the following code:
[%printUnorderedList value=$myMenu keys_as_href="true" layout="2"%]

For the parameter "value" provide the array containing the items of the menu. Use the parameter "layout" to choose between the 1st and 2nd layout. You should set the parameter "keys_as_href" to "true".

Miscellaneous functions

How do I call an action of another plug-in?

To handle actions YANA provides the function $YANA->handle(), which returns bool(true) on success and bool(false) otherwise.

<?php
global $YANA;

/* call action "foo" */
$bool = $YANA->handle("foo", $parameter);
if ($bool) {
    print "The action 'foo' has been executed successfully.";
} else {
    print "The call of 'foo' returned an error.";
}
?>

How do I empty the template cache of the server?

This causes all files in the directory "cache/" to be deleted.

<?php
global $YANA;

/* empty server's template-cache */
$YANA->handle("clear_server_cache", array());
?>

How do I create a preview for an entry, eg. in a forum?

<!-- The following entry in the template is already enough: -->
<textarea id="inputfield" name="text"></textarea>
[%preview%]

Screenshot
Figure: Example of the representation in the Browser

How do I create "micro-summaries" for Firefox 2.0 and higher?

"Micro Summaries" are "active bookmarks", meaning hyperlinks, whose text can be dynamically updated when the content of the website changes, to which the link points. The Firefox browser offers Micro Summaries automatically, if the user creates a bookmark of a page that has that feature.

Examples of this would be:

Other variants are of course possible.

Micro Summaries are supported by the Yana Framework. They are created using the utility-class "Micro Summary". Take a look at the following examples:

<?php
/* creating a mirco-summary in your plug-in */
Microsummary::set($this->name, 'link text');

/* Please note:
 * The first parameter is an unique id,
 * which identifies the entry.
 * Here: $this->name is used as id,
 * because the name of the plug-in is unique.
 * However, it is possible to use any other text.
 * For example, when you want to create multiple entries for the
 * same plug-in.
 */

/* reading a micro-summary in your plug-in */
$microsummary = Microsummary::get($this->name);

/* publishing a micro-summary in your plug-in,
 * so that the browser can find it, when viewing the page
 */
Microsummary::publish($this->name);

/* To open the micro-summary in your browser: */
index.php?action=get_microsummary&target=guestbook

/* For testing, the URL must be copied to your browser.
 * The argument "target" names the id of the micro-summary you want to view.
 */
?>

How do I create RSS feeds?

"RSS-Feeds" are electronic, (mostly) automatically generated and updated lists, which may provide various information. A visitor may read these with any "RSS reader" software. Modern browsers already have this functionality built in, so that visitors no longer need additional software.

Examples for RSS-Feeds:

The Yana Framework supports the creation of RSS feeds for your applications. Therefor you can use the prepared classes "RSS" and "RSSitem". The following are some examples.

<?php
/* To create a RSS feed for your plug-in, first create a new function.
   (Change the name as you see fit. Note that the name must be unique.) */
function my_rss_feed($ARGS)
{
    $rss = new RSS();
    // choose a title and description
    $rss->title = 'Mike Miller\'s News';
    $rss->description = 'this is my RSS feed for my web site';
    // add an entry
    $item = new RSSitem();
    $item->title = 'Entry 1';
    $item->link = 'http://any.url';
    $item->description = 'A short text, describing the content, or a text excerpt.';
    // add the entry to the RSS feed
    $rss->addItem($item);
    // a 2nd eintry
    $item = new RSSitem();
    $item->title = 'another item';
    $item->link = 'http://any.other.url';
    $item->description = '2nd description';
    $rss->addItem($item);
    // output the RSS feed to the browser
    print utf8_encode($rss->toString());
    exit(0);
}

/* You can have a link to the RSS feed viewed in the application.
   To do so use the function RSS::publish() and the name of the function,
   that creates the RSS feed.
   Tip: The best place to do this is is the constructor
   of your plug-in.*/
function plugin_my_test($name)
{
    RSS::publish('my_rss_feed');
    // more source code may follow here
}
?>

Tutorials

my first plug-in

Chapter 1: Preparation

The following Tutorial will demonstrate, how to write your own plug-ins. Therefor the text will guide you through some practice, where you will create your first application with the Yana Framework, a so-called "Web log". The source code can be found in the appendix, at the end of the tutorial.

To complete all steps you will need approximately 1 hour. References to literature can be found at the end of each section. If you wish to skip a section, you will find prepared source code snippets after each step, which you may copy.

Before you start with this tutorial, you should make sure that you have finished some preparations, because otherwise you might get to a point of the tutorial, where you can not continue.

First, you need access to a web server with PHP. A local installation should be preferred, as this simplifies work. However, access to a web server via FTP will do it anyway.

You should also have the Yana Framework already installed and be able to log-in as an administrator. You must also have the "Software Development Kit" for the Yana Framework installed and activated.

Also I recommend you to install an appropriate editor for the Framework, either " PSPad " or " ConTEXT ". Syntax highlighting and code templates are available for both editors. Read the chapter on editors, for installation instructions. For the purpose of this tutorial, we will demonstrate all examples by using the software "PSPad".

Basic knowledge of the structure and functions of PHP and MySQL databases are assumed.

Chapter 2: Creating the database

Databases are represented in the Yana Framework by so-called "database schemes". These schemes are descriptions of the structure of a database. In other words, the tables, their columns, indexes and constraints. This information is stored in files. These "database structure files" are stored in the directory "config/db" of the framework.

In addition - and this distinguishes the Yana Framework fundamentally from other frameworks - these files also contain a description of the semantics of a database. In other words, the context in which a column is visible or invisible, how the information is to be presented to the user, or what label a column should have.

To create a database for the Yana Framework you don't need an expensive modeling tool, and no SQL knowledge, but merely a simple text editor.

To create a new database first open a blank text file in PSPad. Set the syntax highlighting to "Yana Framework" (if you haven't installed the syntax highlighting files and code templates yet, see the chapter "editors for the Yana Framework" ).

The new database will contain a table "blog", which will store the items of a web log. Each entry will be contain an unique ID (primary key), the author's name, date of creation, a title and text.

There are code templates to help you with the preparation of the database. Enter the text "db" and press the buttons <CTRL> + <SPACE> simultaneously. This will show a list of code templates. This list has two columns: the left in bold is a "shortcut" that you can enter directly (as in this case "db") and on the right a description of the templates.

Screenshot
Figure: Insert code templates

Choose the template "database definition" and press <ENTER>. The document should now look like the following figure:

Screenshot
Figure: Database body

Now insert a table. To do this, press <STRG> + <SPACE> and select the "table definition" (shortcut "tbl"). Rename the table by selecting the table name as the title of the opening and closing tags. In this tutorial the table will be named "blog".

Screenshot
Figure: Table "blog"

Add a primary key to the table. A table can always have only 1 primary key. The primary key must always consist of exactly one column of type "integer", "float" or "string". Compound primary keys are not supported.

To add a column, click inside the container "CONTENT" of the table, and insert the code template "primary key" (shortcut "id"). Name the column "blog_id".

To define that the column "blog_id" is a primary key, you must specify the name of the column as the "PRIMARY_KEY".

If you have completed these steps successfully, your file should look like the following:

Screenshot
Figure: Inserting the primary key

Now add a column of type "string" for the title and a column of type "text" for the text of the entry. A column of type "time" for the date of creation, and another column of type "string" for the name of the author.

The source code should now look like this:

<USE_STRICT>true</USE_STRICT>
<READONLY>false</READONLY>
<TABLES>
	<blog>
		<PRIMARY_KEY>blog_id</PRIMARY_KEY>
		<CONTENT>
			<blog_id>
				<TYPE>integer</TYPE>
				<LENGTH>8</LENGTH>
				<REQUIRED>AUTO</REQUIRED>
				<DISPLAY>
					<HIDDEN>true</HIDDEN>
				</DISPLAY>
			</blog_id>
			<blog_title>
				<TYPE>string</TYPE>
				<LENGTH>255</LENGTH>
				<DESCRIPTION>Title</DESCRIPTION>
			</blog_title>
			<blog_text>
				<TYPE>text</TYPE>
				<LENGTH>3000</LENGTH>
				<REQUIRED>true</REQUIRED>
				<DESCRIPTION>Text</DESCRIPTION>
			</blog_text>
			<blog_created>
				<TYPE>time</TYPE>
				<REQUIRED>AUTO</REQUIRED>
				<DESCRIPTION>Date</DESCRIPTION>
			</blog_created>
			<blog_author>
				<TYPE>string</TYPE>
				<LENGTH>255</LENGTH>
				<DESCRIPTION>Author</DESCRIPTION>
			</blog_author>
		</CONTENT>
	</blog>
</TABLES>

Compare the source code above with your result.

Introduction to the main properties of a table column

The property" TYPE " is the data type of the column, " DESCRIPTION " is a label of the column to show to the user. The property "LENGTH" indicates the maximum length for the contents of the field. For columns of type "string" (in MySQL this is interpreted as "VARCHAR"), this is the maximum length in characters.

The property " REQUIRED " indicates whether a value is mandatory. This property can take three values: "true", "false" and "AUTO". Where "true" = is a required field, "false" = an optional field and "AUTO" = the value is generated automatically, if the user does not specify a value.

Or in other words: the "REQUIRED" indicates whether a column is "nullable". If "REQUIRED" is set to "true", the column will be set "NOT NULL" in MySQL and vice versa.

If you set the value to "AUTO" on a column of type "integer" you get an auto-incremented column in MySQL. The column "blog_id" in the example above uses auto-increment. (In MSSQL and DB2 this is realized as "identity", as "sequence" in PostgreSQL and as trigger in Oracle). The value "AUTO" on a column of type "time" causes the current time to be inserted as value.

A property, which has not been used yet in the example above is " DEFAULT ". This can be used to set a "default value", which will automatically be used if there is no other input given.

Finally, the property " DISPLAY ". This property has no equivalent in SQL. It controls the presentation of the column in the Framework. It identifies all forms in which the column is: visible, visible and editable, or not visible at all. This distinguishes between forms to: view, search, create and edit records in the table.

Refining the input

The above table will now be completed with some more information.

For example, you should specify that the columns "blog_title" (title) and "blog_text" (text) always need to be filled. To do this, add the property "REQUIRED" with the value "true" (shortcut "req").

The date of creation is inserted automatically. It should therefore not be visible when writing the entry for the user and not be edited afterwards. For the column "blog_created" (date) add a property "DISPLAY" (shortcut "disp"). Set the values "HIDDEN.NEW" and "READONLY.EDIT" to "true". Set all other values to "false".

Multiple blogs in one table

You can, of course, store several blogs in a table. To do this you need an extra column, which identifies which blog an entry belongs to. This can be useful, for example, if you have several web pages per site and want to have a blog for each of them.

The functionality to distiguish between all these different blogs, and the assignment to individual web pages don't need to be programmed manually. The framework has prepared a template for this purpose.

First add a new column of type "profile" (shortcut "pid"). The column should be called "profile_id". Then enter a new line after the property "PRIMARY_KEY", add the property "PROFILE_KEY" (shortcut "prf"), and set the value of this property to "profile_id".

Compare your results with the following source code.

<USE_STRICT>true</USE_STRICT>
<READONLY>false</READONLY>
<TABLES>
	<blog>
		<PRIMARY_KEY>blog_id</PRIMARY_KEY>
		<PROFILE_KEY>profile_id</PROFILE_KEY>
		<CONTENT>
			<blog_id>
				<TYPE>integer</TYPE>
				<LENGTH>8</LENGTH>
				<REQUIRED>AUTO</REQUIRED>
				<DISPLAY>
					<HIDDEN>true</HIDDEN>
				</DISPLAY>
			</blog_id>
			<blog_title>
				<TYPE>string</TYPE>
				<LENGTH>255</LENGTH>
				<DESCRIPTION>Title</DESCRIPTION>
				<REQUIRED>true</REQUIRED>
			</blog_title>
			<blog_text>
				<TYPE>text</TYPE>
				<LENGTH>3000</LENGTH>
				<REQUIRED>true</REQUIRED>
				<DESCRIPTION>Text</DESCRIPTION>
				<REQUIRED>true</REQUIRED>
			</blog_text>
			<blog_created>
				<TYPE>time</TYPE>
				<REQUIRED>AUTO</REQUIRED>
				<DESCRIPTION>Date</DESCRIPTION>
				<DISPLAY>
  					<HIDDEN>
  						<NEW>true</NEW>
  					</HIDDEN>
  					<READONLY>
  						<EDIT>true</EDIT>
  					</READONLY>
  				</DISPLAY>
			</blog_created>
			<blog_author>
				<TYPE>string</TYPE>
				<LENGTH>255</LENGTH>
				<DESCRIPTION>Author</DESCRIPTION>
			</blog_author>
			<profile_id>
				<TYPE>profile</TYPE>
				<LENGTH>128</LENGTH>
				<REQUIRED>AUTO</REQUIRED>
				<DISPLAY>
					<HIDDEN>true</HIDDEN>
				</DISPLAY>
			</profile_id>
		</CONTENT>
	</blog>
</TABLES>

This ends the creation of the database Save your changes in the file "blog.config" and close the program.

References

If you would like a more detailed view on the issues discussed in this section, you will find instructions in the following articles:

The following section will look at the creation of the program codes.

Chapter 3: Creating a new plug-in

If you reconstructed the tutorial up to this point, you now already possess a simple database. However, to "bring it to life", you still need the business-logic of the application itself, as well as an user interface to view forms and data. You will create both in the following section.

Now open the sitemap of the Yana Framework in a scriptable web browser, for example, Firefox 2.0. Click the link "Login" an log on the system as "administrator". You will need administrator privileges to proceed with the next step.

At the sitemap click the link "Software Development Kit". Should this not yet been installed, or enabled, please do it now. For more information see the chapter "Creating plug-ins and applications".

If you opened the link, you should be able to see the following form.

Screenshot
Figure: Front page of the "Software Development Kit"

First, enter a name (for example, "my blog"), some description, and select a logo for your plug-in. Using the tab "author" you can provide information about your name, your web site address and contact.

On the page "other data" set the "type of application" to "primary". This declares the plug-in to be a "main program". This is optional, but may increase performance.

Screenshot
Figure: Setting the type of application

Next click the tab "Database".

Screenshot
Figure: Selecting the database structure file

Click the button "choose". Select the file "blog.config", you created, as the source.

Additional information about your plug-in is not necessary at this point. Click on "Finish". The source code and the user interface for your first plug-in will now be generated. Next you can immediately try your recently created plug-in.

Try the new plug-in

Open the administration menu in expert mode. In the right column, section "plugins", click on "refresh list." Look for the name of your plug-in in the list and activate it, by clicking the checkbox next to the plug-in's name. Click on the "Save changes".

The following figure shows this menu.

Screenshot
Figure: Activating a plug-in

Instructions for users with database support

Attention:

Attention! If you DON'T have database support activated you DON'T need to take the following step.
If you activated the database support and for example use a MySQL database for storing the entries, then you must install the tables for the plug-in first. To do so, open the menu "Database setup" and select "install marked databases" from the drop-down menu. A list of options will be shown. Enable only the entry of the database of your plug-in and click on "save changes".

See the following figure:

Screenshot
Figure: Installing a table on your database

Next go back to the sitemap of the framework. Now the name of your plug-in should be visible in the main menu. Click this link. You should now see the starting page of your application, as shown in the following figure.

Screenshot
Figure: Start page of the new plug-in

The text "no entries found" indicates that there are no entries stored in your blog yet. Click on the link "new" to create a new entry.

Screenshot
Figure: Creating a new entry

Next you should see the following index page. (The figure shows the form with two entries)

Screenshot
Figure: Viewing the entries

There are alternative views. Below the table you will find a list with 4 different layouts. So just choose the one you like best.

Screenshot
Figure: Detailed view

Click the link "edit" to edit entries. You should see the following form.

Screenshot
Figure: Editing entries

Notes on the operation of the editor

Clicking on the columns changes the sorting of columns. You need to mark all rows, which would like to change, by clicking the check box in the left column. The last row has only empty fields, which you can use to create a new entry the fast and easy way.

This form has a special feature. If you set the property "entries per page" to the value "1", the view for the current entry is maximized, which is more comfort editing it, as the following figure shows.

Screenshot
Figure: Editing a single entry

If you wish, you can also try the other functions of your new plug-in.

References
If you would like a more detailed view on the issues discussed in this section, you will find instructions in the following articles:

As you could see, the Yana Framework has already done a big part of the necessary work for you. However, there are certainly some details that you might want to customize to your needs. The following section of the tutorial will discuss how to do this.

Chapter 4: Editing templates and configuration files

The following section will introduce the definition files for plug-ins and templates. It is demonstrated, how these files can be edited, in order to adapt them to personal needs.

Insertion of emot-icons and embedded tags

As the first step is to be demonstrated, how to insert smilies (emot-icons) and embedded tags (to format text). Open the file "skins/default/blog/blog_default_new_blog.html" in PSPad. Please note, that the text "blog" should be replaced with the name of your project. Select the highlighter "Yana Framework Templates" from the list of the available highlighters.

For comparison see the following figure:

Screenshot
Figure: Templates in PSPad

The function "import" loads the tree menu from the file "index.html". The function "create" calls the form generator of the Yana Framework, to generate a form, where the user may add a new item.

For an explanation of the function "create" see the reference of template-functions and -modifiers.

There are predefined code snippets for templates too. Now insert a function "embeddedTags" (Shortcut "embTag") below the function "create". You don't have to provide any arguments for this function.

For an explanation of the function "embeddedTags" see the reference of template-functions and -modifiers.

The source code should now look like this:

<!-- Begin: menu -->
<div style="width: 250px; overflow: auto; float: left;">
[%import file="index.html"%]
</div>
<!-- End: menu -->
<!-- Begin: content -->
<div style="margin-left: 260px;">
[%create
  template="new"
  file="blog"
  table="blog"
  where=$WHERE
  titles=""
  on_edit="blog_write_edit_blog"
  on_delete="blog_write_delete_blog"
  on_new="blog_write_new_blog"
  on_search="blog_read_search_blog"
  on_download="blog_blog_download"
  sort=$SORT
  desc=$DESC
  page=$PAGE
  entries=$ENTRIES %]
[%embeddedTags%]
</div>
<!-- End: content -->
<!-- Begin: foot -->
<div style="clear: both;">
&nbsp;
<!-- your text here -->
</div>
<!-- End: foot -->

Compare the source code above with your result.

If you open your plug-in again in a browser, it should look like this.

Screenshot
Figure: Presentation in a browser (Firefox 3)

You may want to optimize this presentation for a better look and feel. There are many ways to do this and at most it is a matter of taste - however, here is a suggestion.


  <fieldset>
    <legend style="font-size: 12px; font-weight: bold;">Text formatieren:</legend>
    [%embeddedTags show="b,i,u,|,h,emp,code,hide,|,small,big,|,img,url,mail,-,mark,color,smilies"%]
  </fieldset>

If you haven't known the tags "fieldset" and "legend" till now: These tags produce subsections within forms. The tag "legend" specifies the label of the subsection. All "Fieldsets" are presented as framed sections. The text enclosed by the "Legend"-tag is printed at the border.
Also this source code demonstrates the use of arguments for the function "embeddedTags". Pay attention to the argument "show" of the function "embeddedTags". This accepts the two special values "|" and "-". Where "|" creates a vertical separator and "-" creates a line-break.

Finally let us create a preview of the new entry above both fieldsets (shortcut "preview"). You may take the snippet as is, the arguments "height" and "width" are not required and should be deleted.

The resulting source code should now look like this:


<!-- Begin: menu -->
<div style="width: 250px; overflow: auto; float: left;">
[%import file="index.html"%]
</div>
<!-- End: menu -->
<!-- Begin: content -->
<div style="margin-left: 260px;">
[%create
  template="new"
  file="blog"
  table="blog"
  where=$WHERE
  titles=""
  on_edit="blog_write_edit_blog"
  on_delete="blog_write_delete_blog"
  on_new="blog_write_new_blog"
  on_search="blog_read_search_blog"
  on_download="blog_blog_download"
  sort=$SORT
  desc=$DESC
  page=$PAGE
  entries=$ENTRIES %]
  [%preview%]
  <fieldset>
    <legend style="font-size: 12px; font-weight: bold;">Text formatieren:</legend>
    [%embeddedTags show="b,i,u,|,h,emp,code,hide,|,small,big,|,img,url,mail,-,mark,color,smilies"%]
  </fieldset>
</div>
<!-- End: content -->
<!-- Begin: foot -->
<div style="clear: both;">
&nbsp;
<!-- your text here -->
</div>
<!-- End: foot -->

The preview automatically uses the most recently used textarea-field as the source for creating the preview. The representation is loaded via AJAX directly from the server, without having to reload the page.

The result will be presented in the browser as follows:

Screenshot
Figure: Presentation in a browser (Firefox 3)

Changing the sorting order

Again click the menu option "browse". Note the sorting of the entries. By default they are sorted by the primary key in ascending order. This is usually not desired for a web log. Instead the entries should be sorted in descending order by the date of their creation. To correct this, open the file "skins/default/blog/blog_read_read_blog.html" in PSPad. Please note, that the text "blog" should be replaced with the name of your project. Select the highlighter "Yana Framework Templates" from the list of the available highlighters.

As stated before, the function "create" is used to call the form generator. This takes some parameters, which control the output. Set the parameter "sort" to "blog_created" to have the table sorted by the column "blog_created", which contains the date of creation. Set the parameter "desc" to the value "true", to get the results sorted in descending order.

Note that the parameter "where" is set to the variable "$WHERE". This will become important in a later stage of this tutorial.

The following source code snippet illustrates the result.

<!-- Begin: menu -->
<div style="width: 250px; overflow: auto; float: left;">
[%import file="index.html"%]
</div>
<!-- End: menu -->
<!-- Begin: content -->
<div style="margin-left: 260px;">
[%create
  template="view"
  layout=3
  file="blog"
  table="blog"
  where=$WHERE
  titles=""
  on_edit="blog_write_edit_blog"
  on_delete="blog_write_delete_blog"
  on_new="blog_write_new_blog"
  on_search="blog_read_search_blog"
  on_download="blog_blog_download"
  sort="blog_created"
  desc=$DESC
  page=$PAGE
  entries=$ENTRIES %]
</div>
<!-- End: content -->
<!-- Begin: foot -->
<div style="clear: both;">
&nbsp;
<!-- your text here -->
</div>
<!-- End: foot -->

Compare the source code above with your result.

Editing the entries of the tree menu

Next the tree menu is to be adapted, so that the items "new" and "About ..." are only visible to users, who are logged in to the system and have a appropriate security level. You might want to change the text of the labels as well.

The source code should now look like this:

<ul class="menu root" id="_menu">
  <li class="menu">
    <div onclick="yanaMenu(this)">blog</div>
    <ul class="menu">
<!-- [%if $PERMISSION >= 75%] -->
      <li class="entry"><a href=[%"action=mein_plugin_read_edit_blog"|href%]>[%$LANGUAGE.MENU.EDIT%]</a></li>
<!-- [%/if%] -->
<!-- [%if $PERMISSION >= 0%] -->
      <li class="entry"><a href=[%"action=mein_plugin_default_new_blog"|href%]>[%$LANGUAGE.MENU.NEW%]</a></li>
<!-- [%/if%] -->
<!-- [%if $PERMISSION >= 0%] -->
      <li class="entry"><a href=[%"action=mein_plugin_read_search_blog"|href%]>[%$LANGUAGE.MENU.SEARCH%]</a></li>
<!-- [%/if%] -->
<!-- [%if $PERMISSION >= 0%] -->
      <li class="entry"><a href=[%"action=mein_plugin_read_read_blog"|href%]>[%$LANGUAGE.MENU.VIEW%]</a></li>
<!-- [%/if%] -->
    </ul>
  </li>
  <li class="entry"><a href=[%"action=about&target=mein_plugin&type=plugin"|href%]>[%$LANGUAGE.ABOUT%] ...</a></li>
</ul>

Having the IF-statements put in comments is a notation to improve the readability of the code when viewing the template file in a browser. For the template itself, it doesn't matter if you use these comments or not.

The variable $PERMISSION stores the security level of the visitor. Guests have by default the security level "0", Administrators are level "100". Between both you can assign more levels. For example, predefined is level "30" for the moderator's user group. Use this variable to decide, if certain menu items are to be visible or not. Now change the value "Permission" for the operations "new" and "about" to ">= 30" (the value 30 is by default used for the user group of moderators).

Since the variable $PERMISSION always has a value >= 0, you may remove all such checks to improve performance.

Compare your result with the following source code.

<ul class="menu root" id="_menu">
  <li class="menu">
    <div onclick="yanaMenu(this)">blog</div>
    <ul class="menu">
<!-- [%if $PERMISSION >= 75%] -->
      <li class="entry"><a href=[%"action=mein_plugin_read_edit_blog"|href%]>[%$LANGUAGE.MENU.EDIT%]</a></li>
<!-- [%/if%] -->
<!-- [%if $PERMISSION >= 30%] -->
      <li class="entry"><a href=[%"action=mein_plugin_default_new_blog"|href%]>[%$LANGUAGE.MENU.NEW%]</a></li>
<!-- [%/if%] -->
      <li class="entry"><a href=[%"action=mein_plugin_read_search_blog"|href%]>[%$LANGUAGE.MENU.SEARCH%]</a></li>
      <li class="entry"><a href=[%"action=mein_plugin_read_read_blog"|href%]>[%$LANGUAGE.MENU.VIEW%]</a></li>
    </ul>
  </li>
  <li class="entry"><a href=[%"action=about&target=mein_plugin&type=plugin"|href%]>[%$LANGUAGE.ABOUT%] ...</a></li>
</ul>
Editing configuration files

As you might have guessed already, this was just the 1st step. The menu options are hidden now. However the actions are still callable for someone who knows the right URL. A skillful user could do so to circumvent this check of his security level and execute the function anyway. The following section will demonstrate, how may change the configuration file of your plug-in, in order to prevent this.

Off with HTML templates and on to configuration files. Open the file "plugins/blog.config" in PSPad (note, that the text "blog" should be replaced with the name of your project). Select the highlighter "Yana Framework"

Screenshot
Figure: Presentation of the configuration file in PSPad

The container "INFO" contains the header of the configuration file. This block may look a little different, depending on the settings you made when creating the plug-in.

First take a look at the element "START", where you will find the name of the front page of your plug-in (blog_read_read_blog). This value corresponds to the value of the argument "action", which is attached to the URL. The value identifies the plug-in and the action, which is to be carried out by the plug-in. The name was generated automatically. If you want to use another front page instead, just open the page you want in your browser. Look in the address in the browser, search for the value of the argument "action" and copy this value to the field "START".

The container "INTERFACE" describes all actions, that your plug-in can process. For each action there is a tag, which has further properties to describe it's characteristics. The names were also generated automatically. Note that the names must be unique. Therefor it doesn't make much sense to call an action something like "insert", since this would possible be used multiple times with different semantics.

For an explanation of all tags used in this configuration file, as well as code examples, see the developer's cookbook, chapter "editing plug-ins".

In the next step you will take over the changes you made in the file "index.html" to the configuration file, in order to make the circumvention of your security restrictions impossible.

To do so, first search for the action "blog_default_new_blog" and set the value of the field "PERMISSION" to "30", as you did in the file "index.html". Now do the same for "blog_write_new_blog". Note: The action "blog_default_new_blog" presents the form, while the action "blog_write_new_blog" handles the input from it. Both actions belong to the same form and this is why both values have to be changed accordingly.

The file "index.html" did contain the item "list" that you removed (compare with the source code above ). This item was linked with the action "blog_read_read_blog". Now look for this action and remove it.

The container "INTERFACE" should now look like this.

<INTERFACE>
	<BLOG_READ_EDIT_BLOG>
		<TYPE>read</TYPE>
		<MODE>0</MODE>
		<INSERT>BLOG_EDIT_BLOG</INSERT>
		<PERMISSION>75</PERMISSION>
		<ONSUCCESS>
			<GOTO>blog_read_edit_blog</GOTO>
		</ONSUCCESS>
		<ONERROR>
			<GOTO>blog_read_edit_blog</GOTO>
		</ONERROR>
	</BLOG_READ_EDIT_BLOG>
	<BLOG_WRITE_EDIT_BLOG>
		<TYPE>write</TYPE>
		<MODE>0</MODE>
		<TEMPLATE>MESSAGE</TEMPLATE>
		<PERMISSION>75</PERMISSION>
		<ONSUCCESS>
			<GOTO>blog_read_edit_blog</GOTO>
		</ONSUCCESS>
		<ONERROR>
			<GOTO>blog_read_edit_blog</GOTO>
		</ONERROR>
	</BLOG_WRITE_EDIT_BLOG>
	<BLOG_WRITE_DELETE_BLOG>
		<TYPE>write</TYPE>
		<MODE>0</MODE>
		<TEMPLATE>MESSAGE</TEMPLATE>
		<PERMISSION>75</PERMISSION>
		<ONSUCCESS>
			<GOTO>blog_read_edit_blog</GOTO>
		</ONSUCCESS>
		<ONERROR>
			<GOTO>blog_read_edit_blog</GOTO>
		</ONERROR>
	</BLOG_WRITE_DELETE_BLOG>
	<BLOG_DEFAULT_NEW_BLOG>
		<TYPE>default</TYPE>
		<MODE>0</MODE>
		<INSERT>BLOG_NEW_BLOG</INSERT>
		<PERMISSION>30</PERMISSION>
		<ONSUCCESS>
			<GOTO>blog_default_new_blog</GOTO>
		</ONSUCCESS>
		<ONERROR>
			<GOTO>blog_default_new_blog</GOTO>
		</ONERROR>
	</BLOG_DEFAULT_NEW_BLOG>
	<BLOG_WRITE_NEW_BLOG>
		<TYPE>write</TYPE>
		<MODE>0</MODE>
		<TEMPLATE>MESSAGE</TEMPLATE>
		<PERMISSION>30</PERMISSION>
		<ONSUCCESS>
			<GOTO>blog_read_read_blog</GOTO>
		</ONSUCCESS>
		<ONERROR>
			<GOTO>blog_default_new_blog</GOTO>
		</ONERROR>
	</BLOG_WRITE_NEW_BLOG>
	<BLOG_READ_SEARCH_BLOG>
		<TYPE>read</TYPE>
		<MODE>0</MODE>
		<INSERT>BLOG_SEARCH_BLOG</INSERT>
		<PERMISSION>0</PERMISSION>
		<ONSUCCESS>
			<GOTO>blog_read_search_blog</GOTO>
		</ONSUCCESS>
		<ONERROR>
			<GOTO>blog_read_search_blog</GOTO>
		</ONERROR>
	</BLOG_READ_SEARCH_BLOG>
	<BLOG_READ_READ_BLOG>
		<TYPE>read</TYPE>
		<MODE>0</MODE>
		<INSERT>BLOG_READ_BLOG</INSERT>
		<PERMISSION>0</PERMISSION>
		<ONSUCCESS>
			<GOTO>blog_read_read_blog</GOTO>
		</ONSUCCESS>
		<ONERROR>
			<GOTO>blog_read_read_blog</GOTO>
		</ONERROR>
	</BLOG_READ_READ_BLOG>
</INTERFACE>

Compare the source code above with your result.

References
If you would like a more detailed view on the issues discussed in this section, you will find instructions in the following articles:

Chapter 5: Editing the PHP source code

The following section will show you how to edit the PHP source code of a plug-in to add new, or modify existing functions.

In this section of the tutorial you will learn how to implement an automatically generated RSS feed for the 10 latest items in the blog. The tutorial takes you through the various stages of the creation.

Defining a new action in the interface section of the configuration file

Before you edit the PHP source code, you should register the new action in the interface of the plug-in. Only then it can be called in your web browser. On the one hand this serves performance and on the other hand it adds an additional safety restriction, in order to make it more difficult to smuggle foreign PHP code into your application to prevent and thus improve the security for all plug-ins.

In order to accomplish this step, open the file "plugins/blog.config" in PSPad (note that you should replace the name "blog" with the name of your plug-in). Select the highlighter "Yana Framework"

Add a new action to the container "INTERFACE" (Shortcut "act"). Name this action "blog_rss".

The source code should now contain the following new action:

<INTERFACE>
[...]
	<BLOG_RSS>
		<TYPE>default</TYPE>
		<MODE>0</MODE>
		<PERMISSION>0</PERMISSION>
		<TEMPLATE>INDEX</TEMPLATE>
		<INSERT></INSERT>
		<ONSUCCESS>
			<TEXT>200</TEXT>
			<GOTO></GOTO>
		</ONSUCCESS>
		<ONERROR>
			<TEMPLATE>ALERT</TEMPLATE>
			<TEXT>500</TEXT>
			<GOTO></GOTO>
		</ONERROR>
	</BLOG_RSS>
[...]
</INTERFACE>

Not all of the automatically generated fields are required. Adjust the definition of the action as follows:

<INTERFACE>
[...]
	<BLOG_RSS>
		<TYPE>read</TYPE>
		<MODE>0</MODE>
		<TEMPLATE>NULL</TEMPLATE>
		<PERMISSION>0</PERMISSION>
	</BLOG_RSS>
[...]
</INTERFACE>

The field "TYPE" with the value "read" tells you something about the intended behavior of this action. This type expresses that this action only requires read access to the database. This information may help to improve performance.

The field "MODE" with a value of "0" indicates that the standard operation mode is used. (The value "1" will make the program start in "safe mode" for this action).

The field "PERMISSION" with a value of "0" indicates that every visitor is allowed to access this action, without needing some higher security level.

The field "TEMPLATE" indicates, which template is to be selected for the action. While it is possible to change the template inside the PHP code, using this field in the configuration file is easier and supports readability.
The selected template "NULL" is a predefined special value. It expresses that "no template" is to be selected This is not necessary, because the output is created by an automatic RSS generator, with no need for a template.

This completes the preparations. The following section will look at the implementation in the PHP source code.

Writing the PHP source code

Open the file "plugins/blog/plugin.php" in PSPad (note that you should replace the name "blog" with the name of your plug-in). Select the highlighter "PHP".

To the class "plugin_blog" add a new function with the name "blog_rss". You can copy the following template to do so.

<?php
class plugin_blog extends plugin
{
// [...]
    /**
     * blog_rss
     * 
     * returns bool(true) on success and bool(false) on error
     * 
     * Type:        read
     * Permission:  0
     * Templates:   NULL
     * 
     * @access  public
     * @return  bool
     * @name    plugin_blog::blog_rss()
     * @param   array  $ARGS  array of params passed to the function
     */
    function blog_rss ($ARGS)
    {

    }
// [...]
}
?>

Make it a habit to always immediately document any new function you write, while the idea and details are still fresh in your memory. This has the advantage, that you have to review your code, which helps you to identify possible problems very soon.

Query the database

In order to provide a RSS feed of the 10 most current contributions, you need to load them from the database. Therefor you have to query the table "blog". By setting the limit-clause to "10" you limit the maximum number of rows in the result set to 10. To ensure these are the latest entries you have to sort them by the date of creation in descending order. This means, sort them by the column "blog_created". To do so, use "order_by" and "desc".

The following source code demonstrates this:

<?php
/* get entries from database */
$query = array(
    'key' => 'blog.*',
    'order_by' => 'blog_created',
    'desc' => true,
    'limit' => 10
);
$rows = $this->database->get($query);
?>

As you can see in this example, the Yana Framework has a query generator for producing simple SQL statements. So you don't need any SQL knowledge to be able to query the database with the Yana Framework The framework also takes care of mapping the real SQL statements to the syntax of your DBMS.

The Query Generator also does simple automated safety checks, does automatic quoting of input values and does plausibility checks on SQL queries, before they are ever send to the database. This reduces the risk of SQL-injections and thus increases the security of your application.

The result set, here $rows, consists of a multidimensional, associative array. Each entry corresponds to a row. Each row has one item per column.

A call to var_export($rows) demonstrates this. This output is as follows:

<?php
array (
  2 =>
  array (
    'BLOG_TITLE' => 'Test',
    'BLOG_TEXT' => 'This is a test[br]This is a test[br]This is a test[br]This is a test',
    'BLOG_AUTHOR' => 'Thomas Meyer',
    'BLOG_CREATED' => '1173276511',
    'PROFILE_ID' => 'default',
    'BLOG_ID' => 2,
  ),
  1 =>
  array (
    'BLOG_TITLE' => 'Test',
    'BLOG_TEXT' => 'This is a test[br]Just another test.[br][br]New line.',
    'BLOG_AUTHOR' => 'Thomas Meyer',
    'BLOG_CREATED' => '1173276442',
    'PROFILE_ID' => 'default',
    'BLOG_ID' => 1,
  ),
)
?>

You can tell by the index of the array, that the entries are sorted in reverse order. The column names are written in capital letters.

For further details and code examples see the developer's cookbook, chapter databases and the API documentation of the class "DbStream".

To create RSS feeds you may use the class "RSS". In order to produce a new feed, an instance of this class should be produced in the next step.

<?php
$rss = new RSS();
$rss->title = 'Blog';
$rss->description = 'the 10 most recent blog entries';
?>

Title and description of the RSS feed can be chosen freely. To do so use the properties "title" and "description", as in the following example.

Subsequently, an entry in the RSS feed is to be provided for each entry of the database. An entry is represented by the class "RSSitem". In order to produce an entry, a new instance of this class is produced and added to the RSS feed by using the function RSS::addItem(). The entries are stored in the RSS feed in the order in which they are inserted - regardless of the date, as date is an optional field.

The following source code demonstrates how to create new entries.

<?php
foreach ($rows as $row)
{
    $item = new RSSitem();
    // Title
    $item->title = $row['BLOG_TITLE'];
    // Link
    $action = 'blog_read_read_blog';
    $blog_id = $row['BLOG_ID'];
    $link = DisplayUtility::url("action=$action&blog_id=$blog_id", true);
    $link = str_replace(session_name() . '=' . session_id(), '', $link);
    $item->link = $link;
    // Text
    $item->description = $row['BLOG_TEXT'];
    // Date
    $item->pubDate = date('r', $row['BLOG_CREATED']);
    // add item
    $rss->addItem($item);
} /* end foreach */
?>

Take a closer look at the code that creates the link. It refers to the action, which is used to output the blog. This action is "blog_read_read_blog". As already mentioned, this name is generated automatically. The function "url()" as member of the utility class "DisplayUtility" creates, as the implies, an URL included the server's address, name and path of the script and all required parameters.

The resulting link, however, also contains the current session Id. It naturally changes with every call. Actually, this should not be a problem, but unfortunately there is a weakness of some modern RSS reader products. For some (not all) of them are programmed to interpret the link of a RSS record as it's unique id. Even if explicitly a GUID is given, it is sometimes ignored, and the link is used anyway. For this reason, it may not change, because these programs otherwise would mix it up. That is the reason why the session Id necessarily has to be removed from the link and that is also the reason why the primary key of the item appears in the link. This is not a limitation of the framework, but an unpleasant feature of some RSS reader products. Unfortunately you will have to live with it for now.

Further details on the use of this class can be found in the API documentation for the classes "RSS" and "RSSitem".

The following extract of the source code, shows the action in context:

<?php
/* get entries from database */
$query = array(
    'key' => 'blog.*',
    'order_by' => 'blog_created',
    'desc' => true,
    'limit' => 10
);
$rows = $this->database->get($query);
$rss = new RSS();
$rss->title = 'Blog';
$rss->description = 'the 10 most recent blog entries';
foreach ($rows as $row)
{
    $item = new RSSitem();
    // Title
    $item->title = $row['BLOG_TITLE'];
    // Link
    $action = 'blog_read_read_blog';
    $blog_id = $row['BLOG_ID'];
    $link = DisplayUtility::url("action=$action&blog_id=$blog_id", true);
    $link = str_replace(session_name() . '=' . session_id(), '', $link);
    $item->link = $link;
    // Text
    $item->description = $row['BLOG_TEXT'];
    // Date
    $item->pubDate = date('r', $row['BLOG_CREATED']);
    // add item
    $rss->addItem($item);
} /* end foreach */
print utf8_encode($rss->toString());
exit(0);
?>

How you could see from the above source code, each entry refers to the action "blog_read_read_blog" with the parameter "blog_id". Up to now this action does not possess this parameter. The following section will demonstrate, how to add this parameter to this action.

Add a new parameter

Search in the document for the action "blog_read_read_blog". It should currently contain the following source code:

<?php
/**
 * blog_read_read_blog
 * 
 * returns bool(true) on success and bool(false) on error
 * 
 * Type:        read
 * Permission:  0
 * Templates:   BLOG_READ_BLOG
 * 
 * @access  public
 * @return  bool
 * @name    plugin_blog::blog_read_read_blog()
 * @param   array  $ARGS  array of params passed to the function
 */
function blog_read_read_blog ($ARGS)
{
    /* check input data */
    assert('is_array($ARGS);');
    settype($ARGS, 'array');

    /* global variables */
    global $YANA;

    /* do something */
    return true;
}
?>

First, you should check whether the parameter "blog_id" is set, as this is not always the case. Since the column "blog_id" in table "blog" is of type "integer", you should also check, if the input value is numeric. Next you should "cast" the type of the input value to an integer value. The following source code demonstrates this.

<?php
if (isset($ARGS['blog_id']) && is_numeric($ARGS['blog_id'])) {
    $id = (int) $ARGS['blog_id'];
}
?>

At this point, you should remember ( see above ) that you edited the function "create" in the template file and set the parameter "where" to the variable "$WHERE". Because you have done that, you can now set the variable "$WHERE" in the plug-in to influence this parameter.

To set the value of a template var call the function Yana::setVar. Set the value of this variable to "blog_id=$id".

<?php
if (isset($ARGS['blog_id']) && is_numeric($ARGS['blog_id'])) {
    $id = (int) $ARGS['blog_id'];
    $YANA->setVar('WHERE', "blog_id=$id");
}
?>

Finally, another feature is to be demonstrated. If a RSS feed is present, the framework will automatically create a graphics labeled "RSS" plus a link to the RSS feed of the application. To activate this feature all you need to do is set a variable. The name of this variable is "RSS_ACTION" and its value should be the name of the operation, which outputs the RSS feed. In this case "blog_rss".

The following figure shows how this symbol is displayed in a browser.

Screenshot
Figure: Presentation in a browser (Firefox 2.0)

The following extract of the source code, shows the action in context:

<?php
/**
 * blog_read_read_blog
 * 
 * returns bool(true) on success and bool(false) on error
 * 
 * Type:        read
 * Permission:  0
 * Templates:   BLOG_READ_BLOG
 * 
 * @access  public
 * @return  bool
 * @name    plugin_blog::blog_read_read_blog()
 * @param   array  $ARGS  array of params passed to the function
 */
function blog_read_read_blog ($ARGS)
{
    /* check input data */
    assert('is_array($ARGS);');
    settype($ARGS, 'array');

    /* global variables */
    global $YANA;
    $YANA->setVar('RSS_ACTION', 'blog_rss');
    if (isset($ARGS['blog_id']) && is_numeric($ARGS['blog_id'])) {
        $id = (int) $ARGS['blog_id'];
        $YANA->setVar('WHERE', 'blog_id=' . $id);
    }
    return true;
}
?>

Compare your results with the source code above.

This step concludes the successful creation of your first own plug-in.

References
If you would like a more detailed view on the issues discussed in this section, you will find instructions in the following articles:

Chapter 6: Summary

In this Tutorial you learned, how to:

This ends the tutorial. Further information and code examples can be found in the developer's cookbook and the API documentation of the Yana Framework.

Source codes

All in this source codes used in this tutorial can also be found in the following package web log plug-in

Put PHP programs on CD-ROM

The following Tutorial will demonstrate, how to use Server2Go and the Yana PHP-Framework to put web application on a CD/DVD-ROM and start them from there.

To complete all steps you will need approximately 1/2 hour plus the time you might need for necessary downloads. References to literature can be found at the end of each section.

system requirements

Chapter 1: Download the sources

Before you start with this tutorial, you should make sure that you have finished some preparations, because otherwise you might get to a point of the tutorial, where you can not continue.

Server2Go is donation-ware and may be used, even for commercial applications, for free. The software is developed and maintained by Timo Haberkern.

Basic knowledge of the structure and functions of PHP and MySQL databases are assumed.

Here are some predefined configuration files for Server2Go. These files are based on version 1.5.0. However there are no guarantees.Here are some links to other useful sources of information:

Chapter 2: Preparations for CD-ROM version

To be able to start a PHP program with the Yana Framework directly from CD-ROM, proceed as follows:

  1. Basic installation
    1. Unpack Server2Go.
    2. Copy the Yana PHP-Framework in the directory "htdocs/".
    3. Edit the file "server/config_tpl/php.ini" in a text editor of your choice. Change the value "memory_limit" from "8M" to "16M" or more. Recommended are "128M", the actual value is, however, dependent on your PHP application. Since only one user at a time will access the CD program, you don't hesitate to use a high value.
    4. Open the configuration file "htdocs/yana/library.php" in a text editor (eg. ConTEXT, or PSPad).

    Change the file "library.php" as follows: define('YANA_CDROM', true);

  2. Database configuration

    If your program requires a MySQL database, do the following:

    1. Edit the file "pms_config.ini" in the Server2Go directory and activate MySQL using "UseMySQL=1".
    2. Start Server2Go by running the file "Server2Go.exe".
    3. It will automatically open your browser. On the front page, in the right menu, click the link to PHPMyAdmin. The URL is: http://127.0.0.1:4001/phpmyadmin/ Click this link, or call the address directly by typing it in your browser.
    4. Install your databases using PHPMyAdmin. Be sure to use the same name for your database, as you have used for your local installation. Otherwise, you need to change the database connection settings of the Yana PHP-Framework accordingly.
    5. If you need to change the database connection for the framework, you will find the connection settings in the file "htdocs/yana/config/".
    6. Install PEAR DB. Open the download page and download the archives for DB and the PEAR package to your computer. You will need a program to decompress these two TGZ archives. If you have none, for example 7-Zip, a free open source program, will do the job.
    7. The archives are packed 2 times - after you have unpacked the first file, you have to unpacked the second file also. These include some XML files, which you can ignore, and one directory with installation files. Copy the contents of both installation directories to the directory "server/php/pear/ ", so that the files "DB.php" and "PEAR.php" both are placed directly in this directory. This ends the creation of PEAR-DB You can test this by running Server2Go and call the Yana PHP-Framework with the parameter "yana/index.php?action=test". On the side it should be confirmed in green letters that PEAR-DB is available.
  3. Server2Go - Configuration

    After installing your databases, continue with the configuration.

    1. To make the task easier, all settings, which you need for your CD-ROM, were preconfigured and the files included in this manual. You only need to unpack the files in the Server2Go installation directory. Replace the old configuration files. The ZIP archive can be found here.
    2. Then configure the database settings in the file "pms_config.ini" and the auto-start settings as you need it. You also may want to include your own logo.

Then the package can be put on a CD-ROM and used immediately.

In the next chapter, you will learn how Server2Go and Yana PHP-Framework can be used together with the portable edition of the Firefox browser.

Chapter 3: Use with Firefox Portable

You have the opportunity to use the Firefox browser with Server2Go on a CD . The following guide explains how to do that.

Caution: Portable Firefox (as far as is currently known) can NOT be used with version 1.5.1 of Server2Go on CD-ROM, but only on a USB drive or hard disk. The problem is to be solved in a future version. In the meantime, it is possible to use an alternative browser. One of several possibilities is to use the supplementary program "Simple browser", which can be found on the Server2Go Website. In return the author expects a small donation to support the further development of the software.
  1. Install Firefox Portable
    1. First, you will need a current, portable version of the browser. It can be found on the web site http://portableapps.com. Download this file from the internet and extract it to the Server2Go installation directory (where "Server2Go.exe" resides).
    2. The browser is in English. However, if you need a German version, it can be converted. To do so, download the German language pack. You can get it on www.firefox-browser.de. Copy the file "de.xpi" to your computer.
    3. Start FirefoxPortable by executing the file "FirefoxPortable/FirefoxPortable.exe". Install the file "de.xpi", by either dragging it to the firefox window, or by using the menu "File/Open file ..." to open "de.xpi".
    4. When you are done, restart the browser.
  2. Configure Server2Go for Firefox Portable

    Note: There is a ZIP archive containing all required configuration files, which you may use alternatively. Unpack the archive and replace the original files. This is it already. You just need put it on a CD-ROM and run your application.

    However, if you don't want to use the archive and prefer to complete the steps by hand, then continue with the configuration as follows.

    1. Open the file "pms_config.ini".
    2. Change the value "BrowserTyp " to EXTERNAL"
    3. Change the value "BrowserPath" to "FirefoxPortable/FirefoxPortable.exe"
    4. Save and close the file.
    5. Create a new file: "Firefox Portable/FirefoxPortable.ini" with the following contents:
      [FirefoxPortable]
      FirefoxDirectory=App\firefox
      ProfileDirectory=Data\profile
      PluginsDirectory=Data\plugins
      SettingsDirectory=Data\settings
      FirefoxExecutable=firefox.exe
      AdditionalParameters=
      LocalHomepage=
      DisableSplashScreen=true
      DisableIntelligentStart=false
      AllowMultipleInstances=true
      SkipChromeFix=false
      SkipCompregFix=false
      WaitForFirefox=true
      RunLocally=true
      Information on the options used here can be found in the file "FirefoxPortable/Other/FirefoxPortableSource/Readme.txt". This file is delivered with Firefox Portable. You may set the value "AdditionalParameters" to "-contentLocale de-DE -UILocale de-DE" if you want to use the German version (or any other locale: just replace the argument "de-DE").

Subsequently, the package can be put on CD-ROM.

Warning: there is the possibility that Server2Go does not work properly on some machines, because of local firewalls or restricted user rights. It is therefore strongly recommended that you test your CD-ROM at least on one other system. Some operating systems may require administrator privileges to start the CD.
Windows Vista: Support for MS Vista was introduced for Server2Go starting from version 1.5.0. It may occur, that users are asked, whether the application may access the system. Up to version 1.5.0 administrator privileges are necessary to start Server2Go from a CD-ROM.
Windows 98: For computers with Windows 98, which have NO Microsoft Office package installed, a patch must be installed, which the user may get from Microsoft. You will find the page at this URL: http://www.microsoft.com/downloads/details.aspx?FamilyID=6c050fe3-c795-4b7d-b037-185d0506396c
To ensure a stable operation it is also necessary to downgrade the MySQL database to version 4. It is sufficient to exchange the files of the database application in the directory "server/mysql/". A suitable installation package can be found at the Server2Go web site.

Article

XML Data Definition Language Format

Abstract

This document defines the XDDL file format. This format serves the purpose of a vendor-independent representation of the structure of databases and their presentation within the context of an application. Attachted to this document are DTDs, which describe the structure of a valid XDDL file. The document contains a syntactic and semantic description of it's elements. In addition informal recommendation are given, concerning the presentation of elements by a client.

State of this document

This section describes the state of the document at the time of it's publication. Later documents may supersede this version. The most current version may be found online at the project website of the Yana Framework.

This document is not yet a final release and it's contents are subject to changes. We welcome anybody to comment on the current version of this work.

The current version of this document is maintained within in the Subversion repository. A list of changes may be extracted from the repository at any time.

Working Draft
A state where all elements may be subject to changes at any time.
Call for Comments
The document is locked and any changes (except for misspelled words and grammar) are prohibited. Comments on the current state of the document are being collected. The next state will probably be "Working Draft" (to apply comments) or "Final Call" if there are not comments.
Final Call
The document is locked and any changes (except for misspelled words and grammar) are prohibited. If no comments are filed, the next document will be released without changes.
Released Candidate Recommendation / Unstable
The document is a released version, but comments are taken on the current state of the document as preparation for any later versions.
Released Recommendation / Stable
The document is a released, stable version. It is still actively maintained.
Superseded
The document has been replaced by a later, stable version. It is no longer actively maintained.
VersionStateDate
unversionedWorking Draft2009-07-01 - 2009-08-31
0.5Call for Comments2009-09-17 - 2009-10-05
0.5.1Working Draft2009-10-06 - current
0.6Call for Commentsnext
Editors
Thomas Meyer, CC CarConsult GmbH
Last modified
$Date: 2009-10-07 11:41:48 +0200 (Mi, 07 Okt 2009) $

Definitions

Terminology

This section defines fixed terms used in this document.

May
An optional definition. Programmers may decide whether or not to comply.
Must
A mandatory requirement. Implementations must comply with these conditions.
Optional
see "May"
Shall
see "Must"
Should
A recommendation. Programmers are encouraged to follow this rule, but they may decide to ignore it.
Supported
A supported feature must be provided by the implementation and behave as specified.
Unspecified
No requirements are specified. The programmer may decide whether or not and how to react if the situation occurs. But the implementation must not depend on a certain behavior of an implementation in an unspecified situation.

Glossary

DTD
A defintion of the legal structure, elements, and attributes for a valid XML document, that complies with the rules of the DTD.
Element
Part of the document structure defined in the DTD. Each element may have multiple attributes.
Attributes
Part of the document structure defined in the DTD. Basically a key-value-pair.
Document
Refers to a valid XML file, that complies with the rules as defined by the DTD. Each document may have multiple elements.
PCData
Part of the document structure defined in the DTD. It defines that an element may have text content.
Empty
Part of the document structure defined in the DTD. It defines that an element must not have any content.

Miscellaneous

A reference implementation of this format is given by the Yana Framework. This is a recommendation and not a part of the XDDL definition. This document explains optional elements by the example of this reference implementation. These explanations are informal. Information on the presentation of an element in a client, figures showing ways of presentation and examples of HTML codes are always recommendations and are informal.

Database

Database is the root level element of a XDDL document. It may contain several child elements. Those may be categorized using 5 basic groups: Tables, Views, Forms, Functions and Change-logs. The database element defines basic properties of the database itself, as well as information for the client and applications that may connect with the database.

Element Database

  ELEMENT database (description?, (include | table | view | form | function |
                     sequence | initialization)*, changelog?)
  ATTRIBUTE
       name        string
       title       string
       charset     string
       datasource  string
       readonly    bool
  
Attributes
Attribute Type Mandatory Default Description
name string - - An unique name that identifies this database. Defaults to the filename. It should be lowercased and a valid XML and SQL identifier.
title string - - The title is a label text that should be displayed in the UI when viewing this object.
Note that this may also be a language token, which is to be translated to the selected language.
charset string - - The preferred charset to use when creating database objects and communicating with the database. The charset may only be set upon creation of the database. If you decide to use an existing database, please note, that the charset, which is actually used, might be another.
datasource string - - The interpretation of the data source attribute depends on the implementation. For the Yana Framework it is a named data source. You may set up named database connections via the administration panel.
readonly bool - no You may set the database to be read-only to prevent any changes to it. Use this if you wish to create a database viewer, or CD-ROM application.
Description

A root element that specifies important properties and/or requirements. These may be used if the database is created via script, or if by a database client, opens a connection.

Implementation

The charset is to be set during creation of the database. Typical charsets are: utf-8, ascii, iso-8859-1. Note that some DBMS have different writings of the same charset, or may support charsets that other DBMS won't. The implementation must provide a list of valid charsets and automatically convert them to the correct writing for the target DBMS. The implementation must provide a list of valid charsets and automatically convert them to the correct writing for the target DBMS.
The data source in general it is an identifier for a particular set of connection parameters to a specific database. his may either be a JNDI data source for Java, an ODBC data source for C#, or any other named data source for any other language.

Presentation

When displaying an user interface (UI) for a database, the implementation may choose to display the database title attribute as a header.

Element Description

  ELEMENT description (#PCDATA)
  
Description

The description serves two purposes: 1st is offline documentation 2nd is online documentation.
A description is always optional. Note that this may also be a language token, which is to be translated to the selected language.

Presentation

The form generator may use the description to provide context-sensitive help or additional information (depending on it's implementation) on a automatically generated database application.

Element Include

  ELEMENT include (#PCDATA)
  
Description

Database definitions may be split in several files and recursively included. E.g. this may be necessary if you wish to create a reference to another table, which was defined elsewhere.

The list of includes may contain either filenames, or identifiers, which can be converted to filenames by the application.
Note that you should only include database definitions that use the same data source.

Implementation

The child nodes of the database tags of the included files should be included in order of appearance to the database node of the source file. The database nodes themselves are to be ignored. If the included file includes further files, then these should be handled recursively. If a file is listed multiple times, the file must be included only once. If a file recursively includes another, which has already been loaded, the file must be skipped and not be loaded twice. Included files are not allowed to redefine already existing objects. For example, an included file may not overwrite a table definition in the source file. If an already existing element is found, an error must be thrown.

Element Sequence

  ELEMENT sequence (description?)
  ATTRIBUTE
       name        string
       start       integer
       increment   string
       min         integer
       max         integer
       cycle       bool
    
Attributes
Attribute Type Mandatory Default Description
name string yes - An unique name that identifies this sequence. It should be lowercased and a valid XML and SQL identifier.
start integer - - A sequence always starts with an initial value. The value defaults to the minimal value for ascending sequences and to the maximal value for descending sequences.
Note: the start value must lay within range of the minimal and maximal sequence number.
increment string - 1 An increment value (or step width) specifies the number that is added to the sequence when calculating the successor of it's current value.
min integer - - The minimal value is a lower boundary for a sequence. All sequence values must be larger or equal the minimal value. The minimal value may not be larger than the maximal value. The default is 1 for ascending and PHP_INT_MIN for descending sequences.
max integer - - The maximum value is an upper boundary for a sequence. All sequence values must be smaller or equal the maximum value. The maximum value may not be smaller or equal the minimum value. The default is PHP_INT_MAX for ascending and -1 for descending sequences.
cycle bool - no If a sequence is a number cycle and the value of the sequence reaches an upper- or lower boundary, it will be reset to the minimum value for an ascending sequence or the maximum value for a descending sequence.
Description

Sequences are integer values, combined with a successor function. They serve various purposes. Usually they are used to auto-generate unique id's.
Note that there are implicit and explicit sequences. For example, an implicit sequence is created when you create an auto-increment column. You must not specify implicit sequences, as these are created and maintained by the DBMS itself.
Also note that some DBMS interpret the integer 0 to be equal to NULL. Thus you are encouraged NOT to create sequences that may contain the value 0 at any time. In addition, some applications may reserve index 0 for default values (e.g. as in data-warehousing).

Implementation

While sequences are part of the SQL-2003 standard, they are not widely supported by most vendors. Except for PostgreSQL, where they are well known feature. They may be simulated for other DBMS though.

Element Initialization

  ELEMENT initialization (#PCDATA)
  ATTRIBUTE
       dbms        string
  
Attributes
Attribute Type Mandatory Default Description
dbms string - generic The name of the target DBMS. The value "generic" means that the definition is suitable for any DBMS. Usually this is used as a fall-back option for DBMS you haven't thought of when creating the database structure or for those that simply doesn't have the feature in question.
Description

Initializing SQL statements, which are carried out right after the database structure has been published on a database. The syntax may either be portable or DBMS-specific.

Implementation

The following values are suggested for the DBMS-attribute: 'generic', 'db2', 'dbase', 'frontbase', 'informix', 'interbase', 'msaccess', 'mssql', 'mysql', 'oracle', 'postgresql', 'sybase', 'sqlite'.

Tables

This section defines the syntax of all elements required to declare the structure of tables and related objects within a database. This includes columns, constraints, triggers and indexes.

Element Table

  ELEMENT table (description?, (grant* | primarykey | foreign* | trigger* |
                 constraint* | declaration | index)*)
  ATTRIBUTE
       name        string
       title       string
       readonly    bool
       inherits    string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - An unique name that identifies this table. It should be lowercased and a valid XML and SQL identifier.
title string - - The title is a label text that should be displayed in the UI when viewing this object.
Note that this may also be a language token, which is to be translated to the selected language.
readonly bool - no You may set the table to be read-only to prevent any changes to it.
inherits string - - Name of the parent table.
Description

A table is a set of column definition plus properties, which all columns of the table have in common like: triggers and indexes.

Implementation of inheritance

Table-Inheritance means the following: a source table FooBar extends the structure and data of a target table Bar with Foo elements. All rows in FooBar have any columns defined in Bar + all columns of FooBar. Bar remains unchanged for that. If the structure of Bar changes, the structure of FooBar changes as well. Each element of FooBar may also be interpreted as an element of Bar, but not vice versa.
Technically spoken: the primary key of the source table is a foreign key that points to the primary key of the source table.
This is unlike PostgreSQL where you may define multiple-inheritance with n parent tables - with all consequences and issues linked to such behavior.

Element Grant

  ELEMENT grant EMPTY
  ATTRIBUTE
       role        string
       user        string
       level       integer
       select      bool
       insert      bool
       update      bool
       delete      bool
       grant       bool
  
Attributes
Attribute Type Mandatory Default Description
role string - - The role a user plays inside a user group.
user string - - The group a user belongs to.
level integer - - The security level may be any integer number of 0 through 100. You may translate this to 0-100 percent, where 0 is the lowest level of access and 100 is the highest.
select bool - yes Tells whether the user is granted to issue a select-statement on the database object.
insert bool - yes Tells whether the user is granted to issue an insert-statement on the database object.
update bool - yes Tells whether the user is granted to issue an update-statement on the database object.
delete bool - yes Tells whether the user is granted to issue a delete-statement on the database object.
grant bool - yes Tells whether the user may temporarily grant his security permissions to other users.
Description

The rights management provides 3 layers, each of which is optional in this document. An implementation must however implement at least one of these options.

User groups
like Sales, Human Resources
User roles
like Project Manager
Security level
an integer of 0 through 100

In analogy to databases, there is also a grant option, which allows users to temporarily grant any right they own in person, to any other user. So a manager may grant (and later revoke) all his rights to an assistant while he is on vacation.

Beispiele

You may decide that every manager of Human Resources, who has at least a security level of 50 may create a new employee and view salaries, but that it requires a manager of HR with security level 80 to update them.

You may even skip any of the levels to perhaps allow anybody to view a catalog form, who has at least a security level of 1, or grant access for any member of the sales department to sales data.

You may precisely choose what each member may or may not do: select (view), insert (create), update (edit), delete.

Note that you may have multiple grant elements to define several alternatives. For example, users may either be a member of group sales OR a have the role of a company manager to view and edit sales information.

Implementation

If no grant element is present, the element is supposed to be public. This means, no security checking is done. If at least 1 grant element exists, no user is allowed to carry out any operation unless there is an explicit grant that allows him to do so.

The Yana Framework implements a profile system on top of all that as well. Application profiles may define different subsidiaries inside your company. For example, Europe or Asia. If implementations should support the management of multiple independent instances on the same installation, it is recommended to implement comparable systematics.

Element Primarykey

  ELEMENT primarykey (#PCDATA)
  
Description

The primary key is the name of a single column inside the table that has unique values and will be used to identify each row within the table.

Implementation

The software must check if the specified column exists. If it doesn't, it must throw an error.
Each table must have exactly 1 primary key. Compound primary keys are not supported.

Element Declaration

  ELEMENT declaration (array | bool | color | date | enum | file | float |
                       html |image | inet | integer | list | mail |
                       password | range | reference | set | string | text |
                       time | timestamp |url)*
  
Description

The declaration element is a simple container that may contain a number of column definitions.

Element Trigger

  ELEMENT trigger (#PCDATA)
  ATTRIBUTE
       name        string
       dbms        string
       on          string
       insert      bool
       update      bool
       delete      bool
  
Attributes
Attribute Type Mandatory Default Description
name string - - An unique name that identifies this trigger. It should be lowercased and a valid XML and SQL identifier.
dbms string - generic The name of the target DBMS. The value "generic" means that the definition is suitable for any DBMS. Usually this is used as a fall-back option for DBMS you haven't thought of when creating the database structure or for those that simply doesn't have the feature in question.
on string - before
before
fires BEFORE the statement is carried out.
after
fires AFTER the statement or transaction has been successfully carried out. It is not fired if the statement results in an error.
instead
fires INSTEAD of the statement. The statement is not executed. This option is not supported by all DBMS. However: if it is not, you may emulate this (with some limitations) by using PHP code.
insert bool - no fire on insert statements
update bool - no fire on update statements
delete bool - no fire on delete statements
Description

Triggers are a database feature that allows to execute code on certain database events. They fire on insert, update, or delete statements, or a combination of those. Triggers are fired either before, after or instead of a statement. When triggered, the given code is executed by the database.

The implementation however depends heavily on the chosen DBMS. Not all DBMS support all features. For example: not all DBMS support a trigger to be executed "instead" of a statement, thus replacing it.

Note that a trigger implies a user defined function. Some DBMS require thus that the function is explicitly created and only the name of the function must be specified for the trigger. Other DBMS allow that you specify the function body along with the trigger.

Implementation

If a trigger uses a functionality which is not supported by the chosen DBMS, the DBMS will throw an error when trying to create the trigger. The implementation itself does not need to check that.

Triggers, that use the DBMS-type "generic", must be emulated. In that case the XDDL-compliant software must execute the code instead of the database. The implementation itself may define the required syntax of the code. In such case it is recommended, that the attribute code for such a trigger is limited to be a valid callback (function- or method names). The Yana Framework requires the code attribute of emulated triggers to be a valid callback of a user-defined PHP-function. See the manual of the database API for more details on that topic.

Element Constraint

  ELEMENT constraint (#PCDATA)
  ATTRIBUTE
       name        string
       dbms        string
  
Attributes
Attribute Type Mandatory Default Description
name string - - An unique name that identifies this constraint. It should be lowercased and a valid XML and SQL identifier.
dbms string - generic The name of the target DBMS. The value "generic" means that the definition is suitable for any DBMS. Usually this is used as a fall-back option for DBMS you haven't thought of when creating the database structure or for those that simply doesn't have the feature in question.
Description

A constraint is a boolean expression that must evaluate to true at all times for the row to be valid. The database should ensure that. For databases that don't have that feature, the DBMS-type "generic" may be used to simulate this.

Implementation

If a constraint uses the DBMS-type "generic", and the XDDL-implementation can't ensure, that the database supports that feature, it must validate the constraint instead of the database. The implementation itself may define the required syntax of the code. On simulation column-level constraints may be validated equivalent to table-level constraints. The Yana Framework uses PHP as language for the definition of generic constraints. It provides an associative array $ROW, that contains copies of the values of the current row. The keys of this array are the lowercased column names.

Element Bool

  ELEMENT bool (description?, grant*, constraint*, default*)
  ATTRIBUTE
       name            string
       notnull         bool
       readonly        bool
       title           string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - An unique name that identifies this column. It should be lowercased and a valid XML and SQL identifier.
notnull bool - no A not-null column may not contain undefined (NULL-)values.
readonly bool - no You may set the column to be read-only to prevent any changes to it. Note that rows may still be inserted or deleted, but the column may not be updated.
title string - no A text that may be used in the UI as a label for the control element associated with the column. Note that this may also be a language token, which is to be translated to the selected language.
Description

Columns of type Bool may have two values: true and false.

Implementation

Boolean values are not supported by all DBMS but may be easily simulated using integer types.

Presentation

Checkbox

Input fields of type boolean are presented as checkboxes on edit. When viewing the column, a graphic indicates the state of the field.

HTML:

<input type="checkbox" value="true" name="{name}"/>
undefined <input type="radio" value="*" name="{name}"/>
null <input type="radio" value="true" name="{name}"/>
false <input type="radio" value="false" name="{name}"/>
<select name="{name}">
<option value="*">null</option>
<option value="true">true</option>
<option value="false">false</option>
</select>

Element Default

  ELEMENT default (#PCDATA)
  ATTRIBUTE
       dbms            string
  
Attributes
Attribute Type Mandatory Default Description
dbms string - generic The name of the target DBMS. The value "generic" means that the definition is suitable for any DBMS.
Description

In newly inserted rows, columns are set to the default value automatically, if no other input is provided.

Implementation

The type of the value depends on the type of column. The physical default value also depends on the DBMS.
A good example is data type Boolean, which is natively supported by PostgreSQL and thus the physical default value would be true or false. For MySQL it is stored as TinyInt with the default values 1 or 0.
However the DBMS-independent (generic) default values would be true or false. The implementation must convert these values automatically.

Element Color

  ELEMENT color (description?, grant*, constraint*, default*)
  ATTRIBUTE
       name            string
       notnull         bool
       readonly        bool
       title           string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - An unique name that identifies this column. It should be lowercased and a valid XML and SQL identifier.
notnull bool - no A not-null column may not contain undefined (NULL-)values.
readonly bool - no You may set the column to be read-only to prevent any changes to it. Note that rows may still be inserted or deleted, but the column may not be updated.
title string - no A text that may be used in the UI as a label for the control element associated with the column. Note that this may also be a language token, which is to be translated to the selected language.
Description

Columns of type "color" contain a hexadecimal color value with a preceding '#'-character. Example: #F0F0F0.

Implementation

These columns may be implemented as text with a length of 7 characters.

Presentation

colorpicker

The element should be displayed as input control. An additional input support element may be implemented to aid users.

HTML5:

<input type="color" {required="notnull"} value="{default}" name="{name}"/>

Element Integer

  ELEMENT integer (description?, grant*, constraint*, default*)
  ATTRIBUTE
       name            string
       autoincrement   bool
       unsigned        bool
       fixed           bool
       length          integer
       notnull         bool
       unique          bool
       readonly        bool
       title           string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - An unique name that identifies this column. It should be lowercased and a valid XML and SQL identifier.
autoincrement bool - no Auto-increment is a MySQL-feature, that may be emulated on other DBMS. It can however only be used on columns of type integer. You should note, that the user input takes precedence over the auto-increment feature, which defines a default value.
unsigned bool - no An "unsigned" number must always be a positive value. This means, any value less 0 is invalid. An implementation must throw an error, if a negative value is given for an unsigned column. (Note that MySQL automatically and silently replaces an negative value by 0.)
fixed bool - no This sets the zerofill-flag for MySQL. For fixed length numbers, the value must be expanded to the defined maximum number of digits, by adding leading zeros. If the attribute length is not set, the attribute fixed must be ignored.
length integer - - The maximum number of digits.
notnull bool - no A not-null column may not contain undefined (NULL-)values.
unique bool - no An unique constraint means, that the column must not contain duplicate values. An unique constraint means, that the column must not contain duplicate values. Note that a unique constraint technically implies an unique index on this column and vice versa.
readonly bool - no You may set the column to be read-only to prevent any changes to it. Note that rows may still be inserted or deleted, but the column may not be updated.
title string - no A text that may be used in the UI as a label for the control element associated with the column. Note that this may also be a language token, which is to be translated to the selected language.
Description

Columns of type Integer may contain any whole number, which can be displayed by the database and programming language.

Implementation

The upper and lower boundaries for integer values depend on the type of system. In general: on 32-bit systems the displayable numbers are in the range [-2^31, +2^31]. For 64-bit systems numbers may be bigger [-2^63, +2^63]. But only if all your software supports 64-bit integer values.

Note! 64-bit and 32-bit applications may be incompatible. Especially when using a 64-bit database server with a 32-bit application or vice versa. Be warned that a number overflow or underflow may occur when converting a large 64-bit to a small 32-bit number. Note that this applies to dates and times as well!

Presentation

Input

Strings and numbers are displayed as input fields when edited. If the column is editable, the content is displayed as text.

HTML5:

<input type="number" step="1" {required="notnull"} name="{name}"/>

Element Float

  ELEMENT float (description?, grant*, constraint*, default*)
  ATTRIBUTE
       name            string
       unsigned        bool
       length          integer
       precision       integer
       notnull         bool
       unique          bool
       readonly        bool
       title           string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - An unique name that identifies this column. It should be lowercased and a valid XML and SQL identifier.
unsigned bool - no An "unsigned" number must always be a positive value. This means, any value less 0 is invalid. An implementation must throw an error, if a negative value is given for an unsigned column. (Note that MySQL automatically and silently replaces an negative value by 0.)
length integer - - The maximum number of digits.
precision integer - - Defines the length of the decimal fraction. When present, the attribute length must be set as well. The maximum number of full digits is: length - precision. Be aware, the precision may not be larger than the length of the number.
notnull bool - no A not-null column may not contain undefined (NULL-)values.
unique bool - no An unique constraint means, that the column must not contain duplicate values. An unique constraint means, that the column must not contain duplicate values. Note that a unique constraint technically implies an unique index on this column and vice versa.
readonly bool - no You may set the column to be read-only to prevent any changes to it. Note that rows may still be inserted or deleted, but the column may not be updated.
title string - no A text that may be used in the UI as a label for the control element associated with the column. Note that this may also be a language token, which is to be translated to the selected language.
Description

Columns of type Float may contain any number, which can be displayed by the database and programming language. These may either be whole, or floating point, or fixed point numbers.

Implementation

These may either be whole, or floating point, or fixed point numbers. Note that, for floating point values, "maximum" means the maximum number of digits including fraction. Thus you will loose precision for very large and very small numbers. For fixed point numbers the maximum and minimum numbers are reduced by the number of digits reserved for displaying the fraction.

Note! 64-bit and 32-bit applications may be incompatible. Especially when using a 64-bit database server with a 32-bit application or vice versa. Be warned that a number overflow or underflow may occur when converting a large 64-bit to a small 32-bit number. Note that this applies to dates and times as well!

Presentation

Input

Strings and numbers are displayed as input fields when edited. If the column is editable, the content is displayed as text.

HTML5:

<input type="number" {required="notnull"} name="{name}"/>

Element Range

  ELEMENT range (description?, grant*, constraint*, default*)
  ATTRIBUTE
       name            string
       notnull         bool
       unique          bool
       readonly        bool
       min             float
       max             float
       step            float
       title           string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - An unique name that identifies this column. It should be lowercased and a valid XML and SQL identifier.
notnull bool - no A not-null column may not contain undefined (NULL-)values.
unique bool - no An unique constraint means, that the column must not contain duplicate values. An unique constraint means, that the column must not contain duplicate values. Note that a unique constraint technically implies an unique index on this column and vice versa.
readonly bool - no You may set the column to be read-only to prevent any changes to it. Note that rows may still be inserted or deleted, but the column may not be updated.
min float yes - Smallest valid value.
max float yes - Largest valid value.
step float - 1.0 Minimal step width.
title string - - A text that may be used in the UI as a label for the control element associated with the column. Note that this may also be a language token, which is to be translated to the selected language.
Description

Columns of type Range may contain any number, which can be displayed by the database and programming language. These may either be whole, or floating point, or fixed point numbers.

Implementation

This type must be treated in the same way as columns of type Float. The attributes "min" and "max" implicitly define one or several constraints. The column's value may not be less than "min" and may not be greater than "max". The attribute "max" may not be less than "min". The column must allow at least 2 valid values. This means: "min" + "step" must be less or equal "max". The value "max" - "min" should be a multiple of the value "step". The value "step" may not be negative.

Presentation

Numbers of this type should be displayed as horizontal slide control ("imprecise number-input control"). The client may choose the exact presentation itself.

HTML5:

<input type="range" {required="notnull"} min="{min}" max="{max}" step="{step}" name="{name}"/>

Element String

  ELEMENT string (description?, grant*, constraint*, default*)
  ATTRIBUTE
       name            string
       length          integer
       notnull         bool
       unique          bool
       readonly        bool
       title           string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - An unique name that identifies this column. It should be lowercased and a valid XML and SQL identifier.
length integer - - The maximum number of characters.
notnull bool - no A not-null column may not contain undefined (NULL-)values.
unique bool - no An unique constraint means, that the column must not contain duplicate values. An unique constraint means, that the column must not contain duplicate values. Note that a unique constraint technically implies an unique index on this column and vice versa.
readonly bool - no You may set the column to be read-only to prevent any changes to it. Note that rows may still be inserted or deleted, but the column may not be updated.
title string - - A text that may be used in the UI as a label for the control element associated with the column. Note that this may also be a language token, which is to be translated to the selected language.
Description

Columns of type String contain a single line of text.

Implementation

Note that string values must not contain line breaks. These are the characters \f, \r and \n. For security reasons values must not contain the character \#0.

Presentation

Input

Strings and numbers are displayed as input fields when edited. If the column is editable, the content is displayed as text.

HTML5:

<input type="text" maxlength="{length}" {required="notnull"} name="{name}"/>

Element Mail

  ELEMENT mail (description?, grant*, constraint*, default*)
  ATTRIBUTE
       name            string
       length          integer
       notnull         bool
       unique          bool
       readonly        bool
       title           string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - An unique name that identifies this column. It should be lowercased and a valid XML and SQL identifier.
length integer - - The maximum number of characters.
notnull bool - no A not-null column may not contain undefined (NULL-)values.
unique bool - no An unique constraint means, that the column must not contain duplicate values. An unique constraint means, that the column must not contain duplicate values. Note that a unique constraint technically implies an unique index on this column and vice versa.
readonly bool - no You may set the column to be read-only to prevent any changes to it. Note that rows may still be inserted or deleted, but the column may not be updated.
title string - - A text that may be used in the UI as a label for the control element associated with the column. Note that this may also be a language token, which is to be translated to the selected language.
Description

Columns of type Mail may contain any valid e-mail address.

Implementation

Mails are implemented as string values. The syntax of e-mail addresses is specified inRFC 822. For PHP implementations it is recommended to use the following code for validation: filter_var($email, FILTER_VALIDATE_EMAIL) === true. Other implementations may use the following regular expression: [\w\d-_\.]{1,}\@[\w\d-_\.]{2,}\.[\w\d-_\.]{2,}.

Presentation

HTML-Code

On edit the column is presented as input field. This is equal to the presentation of strings.

Values of type "mail" should automatically be encoded when shown in a browser, to make data theft more difficult. This applies to all displayed e-mail addresses. The Yana Framework utilizes a filter within the presentation layer to solve this issue. A manual intervention is not necessary.

HTML5:

<input type="email" maxlength="{length}" {required="notnull"} name="{name}"/>

Element Url

  ELEMENT url (description?, grant*, constraint*, default*)
  ATTRIBUTE
       name            string
       length          integer
       notnull         bool
       unique          bool
       readonly        bool
       title           string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - An unique name that identifies this column. It should be lowercased and a valid XML and SQL identifier.
length integer - - The maximum number of characters.
notnull bool - no A not-null column may not contain undefined (NULL-)values.
unique bool - no An unique constraint means, that the column must not contain duplicate values. An unique constraint means, that the column must not contain duplicate values. Note that a unique constraint technically implies an unique index on this column and vice versa.
readonly bool - no You may set the column to be read-only to prevent any changes to it. Note that rows may still be inserted or deleted, but the column may not be updated.
title string - - A text that may be used in the UI as a label for the control element associated with the column. Note that this may also be a language token, which is to be translated to the selected language.
Description

Columns of type URL are equivalent to type String, except that every input is checked to be a syntactically correct URL.

Implementation

URLs are implemented as string values. The syntax of URIs is specified in RFC 3986. For PHP implementations it is recommended to use the following code for validation: filter_var($url, FILTER_VALIDATE_URL) === true.

Presentation

Input

Strings and numbers are displayed as input fields when edited. If the column is editable, the content is displayed as text.

HTML5:

<input type="url" {required="notnull"} name="{name}"/>

Element Password

  ELEMENT password (description?, grant*, constraint*, default*)
  ATTRIBUTE
       name            string
       notnull         bool
       unique          bool
       readonly        bool
       title           string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - An unique name that identifies this column. It should be lowercased and a valid XML and SQL identifier.
notnull bool - no A not-null column may not contain undefined (NULL-)values.
unique bool - no An unique constraint means, that the column must not contain duplicate values. An unique constraint means, that the column must not contain duplicate values. Note that a unique constraint technically implies an unique index on this column and vice versa.
readonly bool - no You may set the column to be read-only to prevent any changes to it. Note that rows may still be inserted or deleted, but the column may not be updated.
title string - - A text that may be used in the UI as a label for the control element associated with the column. Note that this may also be a language token, which is to be translated to the selected language.
Description

A column of type Password is used to store encrypted password information.

Implementation

Passwords are hash strings. The values must be calculated from the input using a , like MD5. Passwords must not be stored as clear text.

Presentation

Password

Passwords are not shown as text, but as an input element of type "password".

HTML5:

<input type="password" {required="notnull"} name="{name}"/>

Element Inet

  ELEMENT inet (description?, grant*, constraint*, default*)
  ATTRIBUTE
       name            string
       notnull         bool
       unique          bool
       readonly        bool
       title           string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - An unique name that identifies this column. It should be lowercased and a valid XML and SQL identifier.
notnull bool - no A not-null column may not contain undefined (NULL-)values.
unique bool - no An unique constraint means, that the column must not contain duplicate values. An unique constraint means, that the column must not contain duplicate values. Note that a unique constraint technically implies an unique index on this column and vice versa.
readonly bool - no You may set the column to be read-only to prevent any changes to it. Note that rows may still be inserted or deleted, but the column may not be updated.
title string - - A text that may be used in the UI as a label for the control element associated with the column. Note that this may also be a language token, which is to be translated to the selected language.
Description

Columns of type Inet offer the possibility to automatically store the IP address of the visitor.

Implementation

Inet columns are implemented as string values. The datatype must support IPv4 and IPv6. The syntax of IPv6 is specified in RFC 2460.

For PHP implementations it is recommended to use the following code for validation: filter_var($inet, FILTER_VALIDATE_IP) === true. Other implementations may use the following regular expressions:

IPv4:
\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}
IPv6:
[a-f0-9]{1,4}:[a-f0-9]{1,4}:[a-f0-9]{1,4}:[a-f0-9]{1,4}:[a-f0-9]{1,4}:[a-f0-9]{1,4}:[a-f0-9]{1,4}:[a-f0-9]{1,4}
Presentation

Input

As a rule, columns of this type should not be editable. If they are, they use an input box for editing.

HTML5:

<input type="text" {required="notnull"} name="{name}"/>

Element Text

  ELEMENT text (description?, grant*, constraint*)
  ATTRIBUTE
       name            string
       length          integer
       notnull         bool
       unique          bool
       readonly        bool
       title           string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - An unique name that identifies this column. It should be lowercased and a valid XML and SQL identifier.
length integer - - The maximum number of characters.
notnull bool - no A not-null column may not contain undefined (NULL-)values.
unique bool - no An unique constraint means, that the column must not contain duplicate values. An unique constraint means, that the column must not contain duplicate values. Note that a unique constraint technically implies an unique index on this column and vice versa.
readonly bool - no You may set the column to be read-only to prevent any changes to it. Note that rows may still be inserted or deleted, but the column may not be updated.
title string - - A text that may be used in the UI as a label for the control element associated with the column. Note that this may also be a language token, which is to be translated to the selected language.
Description

Columns of type Text may contain multiple lines of text. They are unbounded in length.

Implementation

Note that the length may be limited for technical reasons. The physical data type depends on the chosen DBMS. Also the DBMS may disallow you to create an index on a text column, or might create a special full text index with specific properties.

For security reasons values must not contain the character \#0.

Any HTML special characters must be masked. Additionally the implementation may define input and output filters. E.g. these may be used to add emot-icons and/or prevent spam and flooding.

For example, the Yana Framework implements several filters to prevent obvious vandalism.

Presentation

Textarea

Multi-line texts use textarea fields for editing. If the column is not editable, it's contents are shown as text. If a text is too long, scrollbars are shown (CSS: "overflow: auto").

HTML5:

<textarea maxlength="{length}" {required="notnull"} name="{name}"/>

Element Html

  ELEMENT html (description?, grant*, constraint*)
  ATTRIBUTE
       name            string
       length          integer
       notnull         bool
       unique          bool
       readonly        bool
       title           string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - An unique name that identifies this column. It should be lowercased and a valid XML and SQL identifier.
length integer - - The maximum number of characters.
notnull bool - no A not-null column may not contain undefined (NULL-)values.
unique bool - no An unique constraint means, that the column must not contain duplicate values. An unique constraint means, that the column must not contain duplicate values. Note that a unique constraint technically implies an unique index on this column and vice versa.
readonly bool - no You may set the column to be read-only to prevent any changes to it. Note that rows may still be inserted or deleted, but the column may not be updated.
title string - - A text that may be used in the UI as a label for the control element associated with the column. Note that this may also be a language token, which is to be translated to the selected language.
Description

Columns of type HTML may contain multiple lines of hypertext in XHTML format.

Implementation

You should keep in mind that hypertext may contain tags and entities. Determining the true length of such a text may be tricky.

No files (like images) may be attached to or embedded in a HTML value of the column. But the column may contain a tag that refers to a file stored elsewhere.

For security reasons values must not contain the character \#0.

Additionally the implementation may define input and output filters. E.g. to prevent XSS-attacks and vandalism.

Presentation

To edit HTML columns, the implementation may present an inline HTML editor. The properties and behavior of this editor are not specified. For output, HTML columns must be shown as interpreted hypertext according to the XHTML 1.0 standard or a successor of this standard (e.g. HTML 5).

Element Date

  ELEMENT date (description?, grant*, constraint*, default*)
  ATTRIBUTE
       name            string
       notnull         bool
       unique          bool
       readonly        bool
       title           string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - An unique name that identifies this column. It should be lowercased and a valid XML and SQL identifier.
notnull bool - no A not-null column may not contain undefined (NULL-)values.
unique bool - no An unique constraint means, that the column must not contain duplicate values. An unique constraint means, that the column must not contain duplicate values. Note that a unique constraint technically implies an unique index on this column and vice versa.
readonly bool - no You may set the column to be read-only to prevent any changes to it. Note that rows may still be inserted or deleted, but the column may not be updated.
title string - - A text that may be used in the UI as a label for the control element associated with the column. Note that this may also be a language token, which is to be translated to the selected language.
Description

Columns of type Date are used to store a date without time or timezone.

Implementation

The physical data type depends on the chosen DBMS. For DBMS which do not support such a type, it may be simulated using an integer.

Presentation

Select

When editing select boxes may be shown to ease the input.

Values are presented as text. The presentation may depend on the chosen language.

HTML5:

<input type="date" {required="notnull"} name="{name}"/>

Element Time

  ELEMENT time (description?, grant*, constraint*, default*)
  ATTRIBUTE
       name            string
       notnull         bool
       unique          bool
       readonly        bool
       title           string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - An unique name that identifies this column. It should be lowercased and a valid XML and SQL identifier.
notnull bool - no A not-null column may not contain undefined (NULL-)values.
unique bool - no An unique constraint means, that the column must not contain duplicate values. An unique constraint means, that the column must not contain duplicate values. Note that a unique constraint technically implies an unique index on this column and vice versa.
readonly bool - no You may set the column to be read-only to prevent any changes to it. Note that rows may still be inserted or deleted, but the column may not be updated.
title string - - A text that may be used in the UI as a label for the control element associated with the column. Note that this may also be a language token, which is to be translated to the selected language.
Description

Columns of type Time are used to store dates with time and timezone.

Implementation

The physical data type depends on the chosen DBMS. For DBMS which do not support such a type, it may be simulated using an integer.

Presentation

Select

When editing select boxes may be shown to ease the input.

Values are presented as text. The presentation may depend on the chosen language and timezone.

HTML5:

<input type="datetime" {required="notnull"} name="{name}"/>

Element Timestamp

  ELEMENT timestamp (description?, grant*, constraint*, default*)
  ATTRIBUTE
       name            string
       notnull         bool
       unique          bool
       readonly        bool
       title           string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - An unique name that identifies this column. It should be lowercased and a valid XML and SQL identifier.
notnull bool - no A not-null column may not contain undefined (NULL-)values.
unique bool - no An unique constraint means, that the column must not contain duplicate values. An unique constraint means, that the column must not contain duplicate values. Note that a unique constraint technically implies an unique index on this column and vice versa.
readonly bool - no You may set the column to be read-only to prevent any changes to it. Note that rows may still be inserted or deleted, but the column may not be updated.
title string - - A text that may be used in the UI as a label for the control element associated with the column. Note that this may also be a language token, which is to be translated to the selected language.
Description

Columns of type Timestamp are used to store dates with time in UTC.

This type is identical to Time, except for the technical implementation.

Implementation

The physical data type depends on the chosen DBMS. Usually it is stored and returned as an integer.

Note that there may be issues with negative timestamps on some OS, like older Win32 systems.

Be warned not to mix the value 0 (1.1.1970) with NULL (undefined).

Presentation

Select

When editing select boxes may be shown to ease the input.

Values are presented as text. The presentation may depend on the chosen language and timezone.

HTML5:

<input type="datetime-local" {required="notnull"} name="{name}"/>

Element Enum

  ELEMENT enum (description?, grant*, constraint*, default*, option+)
  ATTRIBUTE
       name            string
       notnull         bool
       unique          bool
       readonly        bool
       title           string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - An unique name that identifies this column. It should be lowercased and a valid XML and SQL identifier.
notnull bool - no A not-null column may not contain undefined (NULL-)values.
unique bool - no An unique constraint means, that the column must not contain duplicate values. An unique constraint means, that the column must not contain duplicate values. Note that a unique constraint technically implies an unique index on this column and vice versa.
readonly bool - no You may set the column to be read-only to prevent any changes to it. Note that rows may still be inserted or deleted, but the column may not be updated.
title string - - A text that may be used in the UI as a label for the control element associated with the column. Note that this may also be a language token, which is to be translated to the selected language.
Description

The data type Enum is an enumeration type. The list of valid values can be defined using option elements.

Note that the default value (if provided) must be a valid enumeration option.

Implementation

Enumerations are stored as strings. The value is equivalent to the value of the chosen option.

Presentation

Select

When editing such column, a select box is shown. Alternatively radio buttons may be used. The elements equal to the option elements of the tag.

HTML5:

<select name="{name}">
<option value="">null</option>
<option value="1">1</option>
<option value="2">2</option>
...
<option value="n">n</option>
</select>

Element Option

  ELEMENT option (#PCDATA)
  ATTRIBUTE
       value        string
  
Attributes
Attribute Type Mandatory Default Description
value string - - Text or integer value of this option
Description

An option represents one valid element of an enumeration. It has a textual description (pcdata) and a value. Only the value of the target column is stored in the database. The description (pcdata) is only shown in UI (instead of the value).

The description represents the option as a human readable text. Note that this may also be a language token, which is to be translated to the selected language.

If an invalid or undefined enumeration item is found in the database, the UI must show the stored value without conversion. The implementation may additionally throw an error message.

Element Set

  ELEMENT set (description?, grant*, constraint*, default*, option+)
  ATTRIBUTE
       name            string
       notnull         bool
       unique          bool
       readonly        bool
       title           string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - An unique name that identifies this column. It should be lowercased and a valid XML and SQL identifier.
notnull bool - no A not-null column may not contain undefined (NULL-)values.
unique bool - no An unique constraint means, that the column must not contain duplicate values. An unique constraint means, that the column must not contain duplicate values. Note that a unique constraint technically implies an unique index on this column and vice versa.
readonly bool - no You may set the column to be read-only to prevent any changes to it. Note that rows may still be inserted or deleted, but the column may not be updated.
title string - - A text that may be used in the UI as a label for the control element associated with the column. Note that this may also be a language token, which is to be translated to the selected language.
Description

The data type Set is equivalent to the type Enum, but permits the selection of multiple values.

Note that the default value (if provided) must be a valid enumeration option.

Implementation

The physical data type depends on the chosen DBMS. Some DBMS have native support. Where available, the native type should be used. Otherwise the data may be stored as a comma-separated string. The values are equivalent to the values of the chosen options.

Presentation

Checkbox

When editing such column, a list of check boxes is presented. Alternatively a select box may be used, that allows the selection of multiple options. In both cases the values and labels equal the option elements of the tag.

HTML5:

<label><input type="checkbox" name="{name}[]" value="1"> 1 </label>
<label><input type="checkbox" name="{name}[]" value="2"> 2 </label>
...
<label><input type="checkbox" name="{name}[]" value="n"> n </label>
<select multiple="multiple" name="{name}">
<option value="">null</option>
<option value="1">1</option>
<option value="2">2</option>
...
<option value="n">n</option>
</select>

Element List

  ELEMENT list (description?, grant*, constraint*)
  ATTRIBUTE
       name            string
       notnull         bool
       unique          bool
       readonly        bool
       title           string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - An unique name that identifies this column. It should be lowercased and a valid XML and SQL identifier.
notnull bool - no A not-null column may not contain undefined (NULL-)values.
unique bool - no An unique constraint means, that the column must not contain duplicate values. An unique constraint means, that the column must not contain duplicate values. Note that a unique constraint technically implies an unique index on this column and vice versa.
readonly bool - no You may set the column to be read-only to prevent any changes to it. Note that rows may still be inserted or deleted, but the column may not be updated.
title string - - A text that may be used in the UI as a label for the control element associated with the column. Note that this may also be a language token, which is to be translated to the selected language.
Description

A column of type List is a numeric array of strings.

Implementation

PostgreSQL has native support for arrays. For other DBMS this feature must be simulated. It may be implemented by storing the values as a serialized or comma-seperated string. The string should be deserialized when loaded and returned as an array.

Presentation

Select

On edit lists are presented as lists of input fields. Additionally a control element is shown to remove or add new entries.

If the element is not editable, then the elements are enumerated as a list.

The displayed list items may be numerated.

Element Array

  ELEMENT array (description?, grant*, constraint*)
  ATTRIBUTE
       name            string
       notnull         bool
       unique          bool
       readonly        bool
       title           string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - An unique name that identifies this column. It should be lowercased and a valid XML and SQL identifier.
notnull bool - no A not-null column may not contain undefined (NULL-)values.
unique bool - no An unique constraint means, that the column must not contain duplicate values. An unique constraint means, that the column must not contain duplicate values. Note that a unique constraint technically implies an unique index on this column and vice versa.
readonly bool - no You may set the column to be read-only to prevent any changes to it. Note that rows may still be inserted or deleted, but the column may not be updated.
title string - - A text that may be used in the UI as a label for the control element associated with the column. Note that this may also be a language token, which is to be translated to the selected language.
Description

A column of type Array is a (possibly multidimensional) array of strings.

Implementation

For most DBMS this feature must be simulated. It may be implemented by storing the values as a serialized or comma-separated string. The string should be deserialized when loaded and returned as an array. In that case it is recommended to encode the string using JSON.

Presentation

Array

On edit arrays are presented as key-value pairs. Additionally a control element is shown to remove or add new entries.

If the element is not editable, then the elements are enumerated as a multidimensional list. Keys and values are optically separated from each other. The presentation may be implementation as a foldable tree menu.

Element File

  ELEMENT file (description?, grant*, constraint*)
  ATTRIBUTE
       name            string
       notnull         bool
       maxsize         integer
       readonly        bool
       title           string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - An unique name that identifies this column. It should be lowercased and a valid XML and SQL identifier.
notnull bool - no A not-null column may not contain undefined (NULL-)values.
maxsize integer - - The maximum size of the file in bytes.
readonly bool - no You may set the column to be read-only to prevent any changes to it. Note that rows may still be inserted or deleted, but the column may not be updated.
title string - - A text that may be used in the UI as a label for the control element associated with the column. Note that this may also be a language token, which is to be translated to the selected language.
Description

The data type "file" is used to save files ("binary large objects").

Implementation

The files should remain in the file system for better performance. In order to save disk space, a compression (like GZip) may be used. This compression also ensures that files stores on the server are not executable and a potential attacker can't misuse such upload fields to store malicious code on the server.

For security reasons the file should be stored in a place, where it is not directly accessible for a client. When you download the file should be unpacked automatically, so the user no disadvantages from the experiences compression and decompression not installed. To provide a smaller download size, the file may automatically be send as compressed data stream, if the user's browser supports this feature. The browser unpacks the file independently. A manual intervention is not necessary.

Presentation

Array

On edit, an upload field is shown to upload a new file, and a button to download the current one. The implementation may offer a preview for files, whose Mime-type is known.

Element Image

  ELEMENT image (description?, grant*, constraint*)
  ATTRIBUTE
       name            string
       notnull         bool
       width           integer
       height          integer
       ratio           bool
       background      string
       maxsize         integer
       readonly        bool
       title           string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - An unique name that identifies this column. It should be lowercased and a valid XML and SQL identifier.
notnull bool - no A not-null column may not contain undefined (NULL-)values.
width integer - - Contains the image horizontal dimension in pixel
height integer - - Contains the image vertical dimension in pixel
ratio bool - no This applies only to the process of resizing images, when the attributes height and width are given. Otherwise the attribute must be ignored. If this attribute is set to "yes", the image's aspect-ratio must be kept when resizing it. If set to "no": image must be stretched to the given size.
background hex-value - - This applies only to the process of resizing images, when the attribute ratio is set to "yes" and the attributes height and width are given. In this case you may specify the background color here. Otherwise the attribute must be ignored. It must be a hexadecimal color value. Example: #f0a080
maxsize integer - - The maximum size of the image in bytes.
readonly bool - no You may set the column to be read-only to prevent any changes to it. Note that rows may still be inserted or deleted, but the column may not be updated.
title string - - A text that may be used in the UI as a label for the control element associated with the column. Note that this may also be a language token, which is to be translated to the selected language.
Description

Columns of type Image are files (binary large objects). These must be graphics of a supported type and should be displayable on the user interface as an image.

Implementation

The graphics file must automatically be checked and converted during upload. If the given file is not a valid graphic, the implementation must throw an error message. When the image is resized, the implementation must respect the attributes "width", "height", "ratio" and "background". The dimensions of the image are adapted to the given height and width. If the attribute "ratio" is set to "no" and width and height are given, the image dimensions are asymmetrically stretched to the exact given values. Otherwise the width and/or height is changed, but the aspect ratio of the image is kept intact. If these changes result in a part of the canvas being empty, then this part is to be filled with the given background color. The background color is given by the attribute "background". If no background color is defined, the implementation may chose a color.

Note: not all image files are suited to be shown in a browser. For example, many browsers (and other programs) may have problems viewing images that use a CMYK color palette.

For reasons of performance, image files should be stored outside the database. Compressing bitmap graphics usually doesn't have any benefits and should be avoided.

A list of valid image formats and/or rules for their interpretation are not specified in this document. The treatment of vector graphics is not specified in this document.

Presentation

Upload field

Columns of the data type Image are presented as a thumbnail image plus an upload field to insert or replace the stored graphic. When clicking the thumbnail the complete graphic should be shown. For the creation of the thumbnail, the implementation should also respect the attributes ratio and background.

Element Reference

  ELEMENT reference (description?, grant*, constraint*, default*)
  ATTRIBUTE
       name            string
       table           string
       column          string
       label           string
       notnull         bool
       unique          bool
       readonly        bool
       title           string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - An unique name that identifies this column. It should be lowercased and a valid XML and SQL identifier.
table string - - Name of target table
column string - - Name of value-column (must be unique). The values in this column are stored as the value of the reference.
label string - - Name of label-column (should be unique). This column should contain human readable description, which are displayed to the user.
notnull bool - no A not-null column may not contain undefined (NULL-)values.
unique bool - no An unique constraint means, that the column must not contain duplicate values. An unique constraint means, that the column must not contain duplicate values. Note that a unique constraint technically implies an unique index on this column and vice versa.
readonly bool - no You may set the column to be read-only to prevent any changes to it. Note that rows may still be inserted or deleted, but the column may not be updated.
title string - - A text that may be used in the UI as a label for the control element associated with the column. Note that this may also be a language token, which is to be translated to the selected language.
Description

Columns of type Reference are used to represent foreign keys. The real type of the column depends on the type of the target column.

Implementation

A reference itself does not automatically imply a foreign key constraint.

Only the value of the target column is stored in the database. The physical type and properties of the column are thus inherited from those of the target column. If the physical type of the target column changes, the physical type of the reference column must change too. If the target column has no suitable type, the implementation must throw an error.

Presentation

Select box

When editing such column, a select box is shown. The options are filled with the entries of the referenced table. The labels are taken form the column "label" and the values are taken from the target "column" of the target table.

Element Foreign

  ELEMENT foreign (key+)
  ATTRIBUTE
       name            string
       table           string
       match           string
       ondelete        string
       onupdate        string
       deferrable      bool
  
Attributes
Attribute Type Mandatory Default Description
name string - - An unique name that identifies this foreign key constraint. It should be lowercased and a valid XML and SQL identifier.
table string yes - Name of target table
match string - simple full | partial | simple
ondelete string - no-action no-action | restrict | cascade | set-null | set-default
onupdate string - no-action no-action | restrict | cascade | set-null | set-default
deferrable bool - no Deferrable means, the DBS should wait till the end of a transaction before it checks inserted or updated foreign keys.
This is meant for situations, where you push data in both: the parent and the child table within one transaction, or when you use circular references (if supported by your DBMS).
Description

Foreign-key constraints are meant to ensure referential integrity between tables. This feature is not supported by all DBMS. The implementation my emulate that feature.

Each foreign-key consists at least of a source and a target. Note that the types of the source columns of a foreign-key depend on the types of the target columns. Columns containing a foreign-key should thus be defined as type "reference". If so, the implementation must determine the correct type automatically.

The attributes "ondelete" and "onupdate" define, how the DBMS should react when an reference is updated.

no-action
The reference in the child table may not be set to a value, that has no corresponding row in the parent table.
restrict
The value of a key in the parent table may not be changed, if at least one reference to it still exists.
cascade
If the key in the parent table is deleted or updated, all references to it in the child table are also updated or deleted.
set-null
If the key in the parent table is updated, the value of the references will be set to NULL.
set-default
If the key in the parent table is updated, the value of the references will be reset to the default value.

The attribute "match" defines, how the DBMS should evaluate the given rules for referential integrity. This applies to compound foreign-keys including multiple columns only.

full
All columns must match
partial
At least one column must match
simple
Any column that has a value must match (some columns may be null)
Implementation

The attribute "deferrable" is not supported by some DBMS. Note that this feature may not be emulated. Various DBMS have different approaches to circumvent this problem. For example, temporarily deactivating constraints. See your manual for details.

The attribute "match" is not supported by some DBMS. This applies to compound foreign-keys including multiple columns only. This feature is rarely used and should be avoided for portable database applications for reasons of compatibility.

Some values of the attributes "onupdate" and "ondelete" are not supported by some DBMS. These may be simulated using triggers.

Element Key

  ELEMENT key EMPTY
  ATTRIBUTE
       name           string
       column         string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - Name of column in source table.
column string - - Name of column in target table. It defaults to the primary key of the target table.
Description

A column reference including a source and target column. If a foreign-key constraint defines more then one column reference, it is a so-called "compound" foreign-key. Note that compound keys are more complicated to handle and are not supported by all DBMS. Compound keys should be avoided where possible.

Element Index

  ELEMENT index (description?, column*)
  ATTRIBUTE
       name           string
       unique         bool
       clustered      bool
       title          string
  
Attributes
Attribute Type Mandatory Default Description
name string - - An unique name that identifies this index. It should be lowercased and a valid XML and SQL identifier.
unique bool - no An unique index always implies an unique-constraint. An unique constraint means, that the column must not contain duplicate values.
clustered bool - no Installs a setting, that all columns in the tablespace should be stored in the order of this index. This is a performance setting.
title string - - The title is a label text that should be displayed in the UI when viewing this object.
Description

Indexes are meant to improve the performance of select-statements. An index is a sorted list of column values. Scanning an index is usually faster than scanning a whole table. However: as creating and maintaining an index causes some overhead in calculation time, insert- and update-statements will thus be slower when using an index.

Implementation

Note: it is not required to explicitly define an unique-constraint on a primary key. Primary keys implicitly have an unique-constraint.

Important! Even if an unique index exists, the column is not reported to have an unique-constraint. It is recommended to avoid unique indexes, whenever it is possible to express the same using a constraint. Note that an unique constraint may not be defined using multiple columns. In that case you should use an unique index.

Clustered indexes apply to MSSQL only. A clustered index means, that the DBS should try to store values, which are close to each other in the index, close to each other in the tablespace, so that they fit inside the same memory page, when retrieving data from a table.
Typically a clustered index is created on the primary key (which is the default), or on another column which is used for sorting the table. Each table may only have one clustered index. If the attribute "clustered" of an index is to be set to "yes", the implementation must check, if the table has another clustered index already. If this is the case, the attribute "clustered" of this index must be set to "no". The implementation may throw an error message, if two clustered indexes are found within one table.

Element Column

  ELEMENT column EMPTY
  ATTRIBUTE
       name           string
       sorting        string
       length         string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - The name of the indexed column in the source table.
sorting string - ascending In an index, each column may be sorted separately for performance reasons. This is especially used for indexes with multiple columns.
ascending
sort in ascending order
descending
sort in descending order
length integer - - maximum length of index values
Description

The column list of an index specifies which columns of a table are indexed and how these values are stored.

Implementation

The length attribute is used for performance optimization and is only supported by MySQL. Other DBMS don't support this argument. Note, that the attribute "length" may not be greater than the length of the source column.
The implementation may automatically decide if a full-text index is to be created, if the DBMS supports that feature.

Views

Basically "views" are stored select-statement. Thus they may span multiple tables, aggregate data or hide information. To the user they may act like real tables. Views are widely used for forms and user interfaces.
However, you should note that there are some restrictions. If you want to change data in a view, the view has to be updatable. The support for this features depends on the chosen DBMS. Some vendors limit this to certain (very simple) scenarios. Basically spoken, an "updatable view" must know the primary key and source table for each and every column in the view. The where-clause of the statement specifies some sort of constraint and a view may demand, that every updated or inserted column is still part of the view and thus justifying this constraint.

Element View

  ELEMENT view (description?, grant*, field+, select*)>
  ATTRIBUTE
       name        string
       readonly    bool
       tables      string
       where       string
       orderby     string
       sorting     string
       checkoption string
       title       string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - An unique name that identifies this view. It should be lowercased and a valid XML and SQL identifier.
readonly bool - no You may set the view to be read-only to prevent any changes. A view that is "read-only" is not updatable.
tables nmtokens - - A comma-separated list of tables used in the current view. If the list contains more than one table, the first is the base table and all other tables are joined to this.
For don't forget to define a where-clause for all joined tables.
where string - - This is a generic information. It sets the where-clause of the view.
orderby string - - A comma-separated list of columns, after whose the output is sorted. By default the table-output is ordered by it's primary-key.
sorting string - ascending
ascending
sort in ascending order
descending
sort in descending order
checkoption string - none
none
no check option
cascaded
recursive check
local
local check only
title string - - The title is a label text that should be displayed in the UI when viewing this object.
Description

The "view" element defines the name, documentation and generic properties of a database view. These are it's base tables, where-clause and other information, which should allow an application to map elements of the view to elements of real tables. This should enable an implementation to simulate simple and updatable view for such DBMS, that don't support this feature. An implementation may use the generic information for the simulation of views. The implementation may throw an error message, if it is not possible to simulate the view and the DBMS doesn't support the requested feature.

Implementation

The software must consider a view to be "updatable", unless the attribute "readonly" is set to "yes". It may however decide that the view is not updatable, if a required primary key is missing. Where applicable the implementation may automatically add a primary key column to the generic query, to make the view updatable. For DBMS that don't support updatable views, the implementation may simulate these by generating insert or update statements based on the given generic information. The implementation may throw an error if the user tries to update a view for such DBMS and it is unable to simulate the desired behavior.

The attribute "where" defined the where-clause of the view. This information must be taken into account when simulating the views. The implementation may define the required syntax of this element itself. Note! The following example is not recommended "time < now()" since the function "now()" may not be compatible between various DBMS. However, you may define explicit SQL-statements for any target-DBMS of your choice. The syntax for generic where-clauses, as supported by the Yana Framework, is: {[column]=[value]{ AND [column]=[value]}*}.

The attribute "checkoption" influences the evaluation of the where-clause. The difference between 'local' and 'cascaded' applies only to situations, where a view is built recursively upon another view and the parent view declares a check-option itself. If this is the case, the setting 'local' will prevent the DBS from recursively evaluating the check option(s) of the parent view(s). Note that this is not supported by all DBMS.
For example, MySQL and PostgreSQL both support this feature, while MSSQL does not.

Element Select

  ELEMENT select (#PCDATA)
  ATTRIBUTE
       dbms     string
  
Attributes
Attribute Type Mandatory Default Description
dbms string - generic The name of the target DBMS. The value "generic" means that the definition is suitable for any DBMS. Usually this is used as a fall-back option for DBMS you haven't thought of when creating the database structure or for those that simply doesn't have the feature in question.
Description

The "select" element is a DBMS-dependent SQL-statement. It must comply with the defined names of the base tables, fields and other properties of the view. For simple views the generic information may already be enough, so that no additional select elements need to be defined. Thus you should avoid the select element where possible.

Element Field

  ELEMENT field EMPTY
  ATTRIBUTE
       table        string
       column       string
       alias        string
  
Attributes
Attribute Type Mandatory Default Description
table string - - Name of base table
column string - - Name of base column
alias string - - optional alias
Description

A column reference, that identifies the name of a column in the view (see attribute "alias") with the names of the physical table and column it is based on.

Forms

This section defines the syntax of elements required to declare the structure of forms, which are related to objects in the database. These forms may be used by a client application and refer to database elements, defined within the same document. Forms bind a presentation element (template) to a data source (table or view). The implementation may define the required syntax and interpretation of the templates. These aspects are unspecified in this document.

Element Form

  ELEMENT form (description?, grant*, fieldset*, event*)
  ATTRIBUTE
       name        string
       table       string
       template    string
       title       string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - An unique name that identifies this form. It should be lowercased and a valid XML and SQL identifier.
table string - - Name einer Quelltabelle oder eines Views. Note that the view should be updatable. Otherwise the form should not be editable
template string - - The attribute template may contain any text. The implementation used to generate the forms may provide a list of valid values for this attribute. The template should provide all required information, which the implementation needs, to create the form.
title string - - The title is a label text that should be displayed in the UI when viewing this object.
Description

Forms are named objects, that are used as input for a form- or GUI-generator. They must have at least a base table and a template. The base table may of course also be an (updatable) view.

Implementation

Forms are not a database feature. They need to be generated by software for all known DBMS. The contents of the form however are based on the database schema defined in the XDDL file. Based on this information, the implementation should generate select-, update-, insert- or delete-statements automatically.

Presentation

For HTML output, forms should be displayed as HTML form elements. The presentation of the form must be defined in the associated template.

Element Fieldset

  ELEMENT fieldset (description?, grant*, input+)
  ATTRIBUTE
       name     string
       title    string
  
Attributes
Attribute Type Mandatory Default Description
name string - default An unique name that identifies this fieldset. It should be lowercased and a valid XML and SQL identifier.
title string - - The title is a label text that should be displayed in the UI when viewing this object.
Description

Fieldsets are meant to group fields. If a fieldset is unnamed, it's name defaults to "default". Thus note, that there must be no more than 1 unnamed fieldset per form. Throughout the form, each fieldset's name must be unique.

Presentation

Fieldset elements should be presented in HTML using "fieldset" tags. The attribute title may be used as "legend" element for the tag.

Element Input

  ELEMENT input (description?, grant*, action*)
  ATTRIBUTE
       name        string
       label       string
       hidden      bool
       readonly    bool
       cssclass    string
       tabindex    integer
  
Attributes
Attribute Type Mandatory Default Description
name string yes - An unique name that identifies this input field. It should be lowercased and a valid XML and SQL identifier.
label string - - The label is a description field, which provides more information about the content of the input field.
hidden bool - no The input field must not be visible in the UI when the value of this attribute is set to "yes". Otherwise the attribute is to be ignored.
readonly bool - no You may set the input field to be read-only to prevent any changes. An input field that is "read-only" is not updatable.
cssclass string - - A HTML-based UI should provide id attributes and CSS classes for fields automatically. Additionally this attribute may be used to add a user-defined CSS class.
tabindex integer - 0 A HTML-based UI should provide the tab-index for fields automatically. Alternatively this attribute may be used to set a user-defined value. The tab-index defines the order in which fields should get the edit focus, when the user hits the tab-key on a keyboard.
Description

The input element is a single field inside a fieldset.

Presentation

A HTML-based UI should display this as an "input" element, or something familiar, depending on the type of the underlying column. If the attribute "readonly" is set to "yes", the element should not be editable. The implementation should create CSS class- and/or id-attributes for each input field. Additionally if the attribute "cssclass" is set, this value has to be added to the class attribute of the tag. If the attribute "tabindex" is provided, it is to be copied to the attribute list of the control element. If the "input" element is represented by more then one HTML field, the tabindex should be set to the first editable field.

Element action

  ELEMENT action (#PCDATA)
  ATTRIBUTE
       name     string
       language string
       title    string
       label    string
       icon     image
  
Attributes
Attribute Type Mandatory Default Description
name string yes - An unique name that identifies this action. It should be lowercased and a valid function name.
language string - - The programming language of the action implementation. May be any string. If the option is not set, the implementation may decide itself how to handle the action. The Yana Framework will interpret the action as the name of a defined plug-in action.
title string - - Title-attribute for clickable links.
label string - - Text label used for clickable links.
icon image - - Image file used as icon for clickable links.
Description

The action element is meant to be triggered when the user clicks the form field.

Presentation

The presentation of the element depends on the attributes language and name.

If the language is set to PHP, or if the attribute language is empty, it is recommended to be displayed as an A-tag next to the form field. The content of the tag is the attribute "label" and/or an img-tag, where the src-attribute is taken from the "icon" attribute of the element. The href-attribute of the tag should be a reference to PHP_SELF plus the name of the action as parameter, provided as "action={name}". The value of the field should be provided as a second parameter "target[{primary_key}]={value}"

Example:

  <action name="foo" label="click me"
      icon="common_files/icon.png"/>

  <a href="?action=foo&target[12]=bar">
    click me
    <img src="common_files/icon.png"/>
  </a>

If the language is set to JavaScript, the name must be a valid JavaScript event and the pcdata section must be valid JavaScript code.

Example:

  <action name="onchange" language="javascript">validate(this)</action>

  <input ... onchange="validate(this)"/>

If a label or icon is provided, the action should also be triggered when clicking the label and/or icon.

Example:

  <action name="onchange" label="validate"
      icon="common_files/validate.png"
      language="javascript">validate('foo')</action>

  <input ... onchange="validate('foo')" />
  <a onclick="validate('foo')">
      validate
      <img src="common_files/validate.png"/>
  </a>

Element Event

  ELEMENT event (#PCDATA)
  ATTRIBUTE
       name     string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - Name of event to be triggered (as defined by the chosen template)
Description

The event is triggered when the user performs a certain action in the form, like submitting the form, clicking a download or something similar. The available types of events must be defined by the chosen template. The implementation may define how to execute the event. The attribute "name" defines the name of the event and the PCDATA section provides the name of the function to be called.

Example: event "submit" with pcdata "foo" will send the form data (using the POST method) to the server application at the URL "?action=foo", when the user submits the form. The implementation must define, what "submit" actually means for the given form template.

Functions

This section defines the syntax of all elements required to declare user defined functions, which are to be used within the context of the database. Functions consist of signature information and implementation. Note: While the function's signature should be portable among several DBMS, the implementation may be not.

Element function

  ELEMENT function (description?, implementation+)
  ATTRIBUTE
       name        string
       title       string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - Name of the function. It should be lowercased and a valid XML and SQL identifier.
title string - - The title is a label text that should be displayed in the documentation of this object.
Description

A user defined function with the given name. The function may have at most one implementation for each supported DBMS.

Element implemetation

  ELEMENT implementation (param*, return?, code)
  ATTRIBUTE
       dbms        string
       language    string
  
Attributes
Attribute Type Mandatory Default Description
dbms string - generic The name of the target DBMS. The value "generic" means that the definition is suitable for any DBMS. Usually this is used as a fall-back option for DBMS you haven't thought of when creating the database structure or for those that simply doesn't have the feature in question.
language string - - The target programming language. This applies only to DBMS, that support more then one language.
Description

The implementation defines the function signature and source code. The number of parameters must be the same for each DBMS (and implementation tag).

Implementation

If the attribute dbms is set to "generic", the XDDL-implementation may decide if the generic function definition is compatible with a certain DBMS. If it decides that the definition is not compatible, it may try to simulate the function. In that case the implementation may define the required syntax for the values of it's child elements "code", "param" and "return". If the definition does not match the required syntax, the XDDL-implementation may throw an error.

Note that the attribute "language" is DBMS-dependent. Most DBMS should at least support the value "SQL" as a language, while others may also include "Java", "C++" or more.
If no language is given, the default language is used. Note that this implementation may not check if the given language is really supported by the given DBMS.

For simulated functions the language attribute must be blank.

Element param

  ELEMENT param EMPTY
  ATTRIBUTE
       name     string
       type     string
       mode     string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - unique parameter name
type string yes - DBMS-dependent data type
mode string - in The attribute "mode" identifies the element as
in
input-parameter (call by value)
out
output-parameter (call by reference)
inout
reference-parameter (call by reference)
Description

A parameter definition as part of the function signature. The attribute "mode" identifies the behavior of the parameter. The type of the parameter is DBMS-dependent.

Example:

  <function name="foo">
    <implementation>
      <param name="p1" type="string" mode="in"/>
      <param name="p2" type="float" mode="inout"/>
      <param name="p3" type="int" mode="out"/>
      <return>int</return>
      <code>...</code>
    </implementation>
  </function>

  May be interpreted as:

  int foo ( string $p1,  float &$p2,  int &$p3 = null ) { ... }
  

Note that some DBMS only support input parameters (call by value). In that case the attribute "mode" must have the value "in". The implementation may throw an error if an unsupported definition is found.

Element return

  ELEMENT return (#PCDATA)
  
Description

The data type returned by the function. This setting is DBMS-dependent.

If no return element is defined, or if it is left empty, the return type defaults to "void", which means, the function doesn't return a value. If the function needs to return more then one value, it should use output-parameters instead. Be aware that in MySQL functions without a return value are called "methods".

Element code

  ELEMENT code (#PCDATA)
  
Description

The function body. The syntax of the content depends on the attributes for DBMS and programming language. If the DBMS is set to "generic", then this code is executed by the server application. In that case the implementation may define the required syntax itself. It is recommended, to limit the code to being the name of a function, that the implementation may call. All input parameters must be redirected to that function. Note that generic functions can't be used inside SQL-statements as these are not executed by the DBMS.

ChangeLog

This section defines the syntax of all elements required to keep track of any significant changes made to the database, which require an update of the physical structure of the database. This includes newly created, renamed, modified and dropped database objects. SQL-statements may be provided for any additional action, which is not covered by this definition. Implementations may provide functionality to generate valid SQL-statements to automatically update a given database to a later version of the XDDL document. he change-log should provide any information, that is necessary, to perform such an action.

Element changelog

  ELEMENT changelog (create|rename|drop|update|sql|change)*
  
Description

The changelog element of a database definition is meant to document updates. Updates must be sorted by version number in descending order. This means, that the first element is always the most recent update.
The updates contain the type of the modification, a version number and the subject of modification. A description of the changes may be added as PCDATA.

Implementation

The changelog may be used for automated database updates. The implementation may provide an application that scans the changelog and updates the database schema from a given version number to a later version. This implementation should try to execute all changes within a transaction. In case of a nonrecoverable error it should try to rollback all changes.

Element create

  ELEMENT create (description?)
  ATTRIBUTE
       name        string
       subject     string
       version     string
       ignoreError bool
  
Attributes
Attribute Type Mandatory Default Description
name string yes - Name identifier of the changed object. It should be lowercased and a valid XML and SQL identifier.
subject string yes - table | column | index | view | sequence | trigger | constraint
version string - - The version this log-entry applies to.
ignoreError bool - no Used to tell the handler function, which performs the update, how to react, if this step causes an error while updating the database structure. If the value is set to "yes", the database update should continue, even if this step fails. If set to "no", the update should stop and all previous changes should be rolled back.
Description

Documents the creation of a new database element. The type of element is stored as attribute "subject". The element itself is identified via the attribute "name". The name must specify a unique identifier for the element. Where necessary it must specify the namespaces, within which the element's name is unique. In that case the character '.' must be used as a delimiter. The implementation may throw an error if the element is not found or if it is ambiguous. The "description" element may be used for documentation purposes and may contain any text.

Element rename

  ELEMENT rename (description?)
  ATTRIBUTE
       name        string
       subject     string
       version     string
       ignoreError bool
       oldname     string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - Name identifier of the changed object. It should be lowercased and a valid XML and SQL identifier.
subject string yes - table | column | index | view | sequence | trigger | constraint
version string - - The version this log-entry applies to.
ignoreError bool - no Used to tell the handler function, which performs the update, how to react, if this step causes an error while updating the database structure. If the value is set to "yes", the database update should continue, even if this step fails. If set to "no", the update should stop and all previous changes should be rolled back.
oldname string - - Previous name of the renamed object. Note: For columns the name must include the table ("table.column").
Description

Documents renaming an existing database element. The type of element is stored as attribute "subject". The element itself is identified via the attribute "name". The previous name of the element is stored as the attribute "oldname". The name must specify a unique identifier for the element. Where necessary it must specify the namespaces, within which the element's name is unique. In that case the character '.' must be used as a delimiter. The implementation may throw an error if the element is not found or if it is ambiguous. The "description" element may be used for documentation purposes and may contain any text.

Element drop

  ELEMENT drop (description?)
  ATTRIBUTE
       name        string
       subject     string
       version     string
       ignoreError bool
  
Attributes
Attribute Type Mandatory Default Description
name string yes - Name identifier of the changed object. It should be lowercased and a valid XML and SQL identifier.
subject string yes - table | column | index | view | sequence | trigger | constraint
ignoreError bool - no Used to tell the handler function, which performs the update, how to react, if this step causes an error while updating the database structure. If the value is set to "yes", the database update should continue, even if this step fails. If set to "no", the update should stop and all previous changes should be rolled back.
version string - - The version this log-entry applies to.
Description

Documents that an existing database element has been removed. The type of element is stored as attribute "subject". The element itself is identified via the attribute "name". The name must specify a unique identifier for the element. Where necessary it must specify the namespaces, within which the element's name is unique. In that case the character '.' must be used as a delimiter. The implementation may throw an error if the element is not found or if it is ambiguous. The "description" element may be used for documentation purposes and may contain any text.

A typical situation where you might want to set the attribute "ignoreError" to "yes" is when dropping an element. Usually a database update should not fail, just because an element can't be drop because it doesn't exist (anymore).

Note that some DBMS are unable to rollback dropped database elements. In case of an error this restriction may have influence on the behavior of an implementation that updates the database structure.

Element update

  ELEMENT update (description?)
  ATTRIBUTE
       name        string
       subject     string
       version     string
       ignoreError bool
       property    string
       value       string
       oldvalue    string
  
Attributes
Attribute Type Mandatory Default Description
name string yes - Name identifier of the changed object. It should be lowercased and a valid XML and SQL identifier.
subject string yes - table | column | index | view | sequence | trigger | constraint
version string - - The version this log-entry applies to.
ignoreError bool - no Used to tell the handler function, which performs the update, how to react, if this step causes an error while updating the database structure. If the value is set to "yes", the database update should continue, even if this step fails. If set to "no", the update should stop and all previous changes should be rolled back.
property string - - Specifies which property of the object has been updated.
value string - - New value of the updated property. The syntax of this attribute may be defined by the implementation. It may be a serialized string.
oldvalue string - - Old value of the property that has changed. The syntax of this attribute may be defined by the implementation. It may be a serialized string.
Description

Documents the alteration of an existing database element. The type of element is stored as attribute "subject". The element itself is identified via the attribute "name". The name must specify a unique identifier for the element. Where necessary it must specify the namespaces, within which the element's name is unique. In that case the character '.' must be used as a delimiter. The implementation may throw an error if the element is not found or if it is ambiguous. The "description" element may be used for documentation purposes and may contain any text. Where available the modified "property" plus it's "oldvalue" and new "value" may be defined.

Element sql

  ELEMENT sql (description?, code)
  ATTRIBUTE
       version     string
       ignoreError bool
       dbms        string
  
Attributes
Attribute Type Mandatory Default Description
dbms string - generic The name of the target DBMS. The value "generic" means that the definition is suitable for any DBMS. Usually this is used as a fall-back option for DBMS you haven't thought of when creating the database structure or for those that simply doesn't have the feature in question.
version string - - The version this log-entry applies to.
ignoreError bool - no Used to tell the handler function, which performs the update, how to react, if this step causes an error while updating the database structure. If the value is set to "yes", the database update should continue, even if this step fails. If set to "no", the update should stop and all previous changes should be rolled back.
Description

Documents a custom SQL statement that is to be executed. It's execution may be limited to a certain DBMS. SQL-statements may be provided for any additional action, which is not covered by the other elements of this definition. For example, to copy data to a newly created table. The "description" element may be used for documentation purposes and may contain any text. The "code" element must contain the SQL code.

Element code

  ELEMENT code (#PCDATA)
  
Description

Contains the SQL code which is to be executed. The syntax of the content depends on the parameter "dbms".

Element change

  ELEMENT change (description?, logparam*)
  ATTRIBUTE
       version     string
       ignoreError bool
       type        string
       dbms        string
  
Attributes
Attribute Type Mandatory Default Description
version string - - The version this log-entry applies to.
ignoreError bool - no Used to tell the handler function, which performs the update, how to react, if this step causes an error while updating the database structure. If the value is set to "yes", the database update should continue, even if this step fails. If set to "no", the update should stop and all previous changes should be rolled back.
dbms string - generic The name of the target DBMS. The value "generic" means that the definition is suitable for any DBMS. Usually this is used as a fall-back option for DBMS you haven't thought of when creating the database structure or for those that simply doesn't have the feature in question.
type string - default A text that unambiguously identifies the type of change to the database structure.
Description

This is a generic element to add user-defined types of database changes. An implementation to execute these changes must allow the definition of a function or method to handle the element for each available type. The function or method must be given the "logparam" elements as parameters in the order of their definition. It's execution may be limited to a certain DBMS. The "description" element may be used for documentation purposes and may contain any text.

Element logparam

  ELEMENT logparam (#PCDATA)
  ATTRIBUTE
       name     string
  
Attributes
Attribute Type Mandatory Default Description
name string - - Name of this parameter.
Description

A parameter which is passed to the called function. The PCDATA section contains the value of the parameter. Optionally a name may be given. There must not be two parameters with the same name.

The SML file format

The name "SML" is an abbreviation of "Simple Markup Language". The SML format was delevoped 2001 through 2003 with the aim to create an easy to read and syntactically clean presentation, which would be acceptable to the masses.

SML is equally a relative to "JSON" ("JavaScript Object Notation") and "XML" ("Extensible Markup Language"). All constructs, which can be expressed in JSON may as well be expressed in SML format, and vice versa. It is nevertheless important to stress that SML was developed independent from JSON. Both dialects have no common historical intersections.

In simple words, the semantics used SML is comparable with that of JSON, while a the syntax is comparable to that of XML. It thus combines the widely accepted use of tags with intuitive semantics.

Purpose of the SML format

The task of the file format is to store non-recursive data fields in the form of a sequential file. These data fields can be either numeric or associative, and use any scalar values or other data fields, if they meet the same requirements. References are not allowed. Objects are - analogous to JSON - represented by their public object variables in the form of an associative array data. Included non-scalar values are treated recursively.

The purpose of this file format is to store parameters to initialize components of the framework. The purpose is not to store larger amounts of data in this format. In particular, the SML format is not designed as a replacement for XML.

Infinite recursions are not recognized.

Discussion on detection of infinite recursion

Note: The test, whether a structure contains infinite recursions or not, is not trivial. The usual approach is a path tracking procedure. However, this produces an overhead of T = O(n) steps, where n is the length of the path. The proof for this lower bound is trivial. In order to avoid such an overhead it might be considered to run this test only every x steps, where an appropriate value is to be chosen for the positive integer x with x> 1. Despite this potential optimization, quadratic running time can be expected, for the whole program, including the path checks. This is not acceptable for many applications. For reasons of performance it was decided to refrain from this type of check.

At this point I would like to note that the naive approach, to count the number of elements to check whether the number of iterations exceeds the number of elements, is not practical. In order to "count" all elements a complete traversion would be carried out. If, however, the structure did contain an infinite recursive path, the program would never terminate and the result would be undefined.

XML to SML

The task to provide files for initialization or configuration of a framework is not totally trivial. Since the framework may have multiple plug-ins, whose configuration options should be stored in the same files as the rest of the configuration. The numbers, structure, type and names of these files is however unknown at the time of development. Eventually the plug-ins, the framework will work with, do not even exist at this point. To be still able to provide a practical DTD or schema in advance, with respect to the these restrictions, the logical solution would be to retreat to the natural properties of the used variables. Stable properties, like the data type of a variable, may be used to create a structure, which is predictable, because it is dictated by syntactical properties of PHP.

Because PHP is not strictly typed and references and objects may not be part of this configuration by definition, this relaxes the task to scalar variables and arrays (non-recursive and recursive data structures). This two kinds of nodes are defined. The Tag "scalar" is introduced to represent scalar values. It may only contain CDATA section ? this matches a scalar context ? but no other tags or elements. The Tag "array" is introduced to represent array values. It may not contain CDATA sections, but tags only. These tags represent the items of the array. Since XML explicitely expects a single root element the tag "root" is introduced. No further tags are required.

Take a look at the following example, which represents two data fields.

<?xml version="1.0" ?>
<!DOCTYPE example SYSTEM "example.dtd">
<root>
	<array name="a">
		<array name="0">
			<scalar name="a"><![CDATA[value]]></scalar>
			<scalar name="b"><![CDATA[value]]></scalar>
			<scalar name="c"><![CDATA[value]]></scalar>
		</array>
		<array name="1">
			<scalar name="a"><![CDATA[value]]></scalar>
			<scalar name="c"><![CDATA[value]]></scalar>
		</array>
	</array>
	<array name="b">
		<array name="0">
			<scalar name="q"><![CDATA[value]]></scalar>
			<scalar name="r" />
			<scalar name="s"><![CDATA[value]]></scalar>
			<scalar name="t"><![CDATA[value]]></scalar>
		</array>
	</array>
</root>

By this example some characteristics are noticeable. First of all the element "root" is complete without semantic meaning. It exists only, because the syntax requires it.

The empty tags may also be considered to be somewhat atificial constructs. They are included, but it is not completely clear, how to interpret these tags regarding to the variables they represents. PHP expects no definition of variables prior to their first use. So an initialization of a non-existing variable with an "undefined" value, even with the constant "null", doesn't make much sense. Particularly since in a configuration file a "null" value has no semantic value at all. Therefor it will be defined by convention, that empty tags should be avoided. Any access to empty or nonexistent tags will be converted to the boolean value "false", as implied by common PHP conventions.

Furthermore, it is foreseeable that this representation could be confusing if used to store larger amounts of data. Ffor example, in the event that a data field contains50 or more items, which may also be nested and may contain large CDATA sections with a number of line breaks, it is likely that the readability would be affected. For reasons of readability it would be an advantage, if the viewer could tell by the end-tag, which item is closed.

The separate marking of CDATA sections could also affect readability, especially since the sense of this syntax may not be obvious to an inexperienced layman. In addition, the previously made definition explicitly demands nothing other than a CDATA section may be used at this anyway, which is why no real extra value is introduced by this syntax. There is a very exceptional case, namely that the CDATA section contains another closing "scalar" tag. However, this case may be easily avoided by the use of entities, which causes the whole syntax to be obsolete.

Next the focus is to be set on the tags themself. For example, the text "<array name="b">". This text may seem to be unnecessarily complex to a layman. Since it is already obvious by it's structure this element has to be an array anyway. The tag "scalar" may not even be used at this point, since it may not contain other tags but just CDATA. It is of no extra value to stress that this is an (empty) array. The attribute "name" is common to all tags. This derives directly from syntaktik properties of PHP, since the attribute represents the identifier of a variable in PHP. So it would be more intuitiv to use this identifiers instead of the more generic names "array" and "scalar" to name the tags. The same argument could be used againts the tag "scalar".

In respect to what has been discussed so far the presentation could be simplified.

<a>
	<0>
		<a>value</a>
		<b>value</b>
		<c>value</c>
	</0>
	<1>
		<a>value</a>
		<c>value</c>
	</1>
</a>
<b>
	<0>
		<q>value</q>
		<s>value</s>
		<t>value</t>
	</0>
</b>

Even without extensive explanation is apparent that both variants represent the same content. Similarly, it should probably not be necessary to discuss which of the two syntaxes are more intuitive to read.

But: because of it's syntactic properties, this variant is not a well-formed XML document. For example, there is no single root element and also "0" is not even a valid name for a tag in XML. It was therefore necessary to choose a different name. This name is "SML" for "simple markup language", in allusion to "XML" ("eXtensible markup language").

Syntactic properties

SML demands some syntactic properties which differ from XML. First, it should be mentioned that encoding a file in UTF-8 or UTF-16, as common in XML, unfortunately still may cause problems with many languages when reading these files. Among these languages are at the present time, inter alia, Perl 5 and regrettably PHP 4. While this might be seen as a failure of the developers and not a property of XML as such, it is still an unacceptable condition, when it comes to initialize an application. Therefore, it is useful for the purpose of the initialization of the framework not to use these encodings (at least for the current moment). Instead ISO Latin-1 is used as default charset. Characters, which are not included in this charset, should be encoded by using proper entities.

Unlike XML the SML syntax does not demand, that a document must contain exactly one single tag as root element. If a document contains a forest of multiple node trees, they are interpreted as child elements of a virtual anonymous root element.

Furthermore the SML format demands that all opened tags need to have a corresponding closing tag. The names of these tags are by definition not case-sensitive.

There is a convention, that a line break has to be entered after each closing tag. Also by convention, only white-space characters are allowed prior to an opening tag. A tag may either contain character data (CDATA) or other tags, but not both. If the tag is a CDATA-section, both start and end tags need to be in one line. If the tag contains other tags, a line break must be inserted directly after the opening tag. Line breaks in CDATA-section may be inserted by using escape sequences like "\n".

All CDATA-section outside a tag and all section which do not comply with this syntax are ignored and treated like comments. Unlike XML, no special handling for CDATA-sections or comments is demanded.

It is not allowed to use the XML-like syntax for empty tags "<br />" (use "<br></br>" instead).

Finally, the SML format uses no attributes by definition.

The reason for these rather restrictive syntax will become clearer with the following example.

Example

The following examples will be described, how native data types in PHP scripts are represented by the SML format.

scalar variables

Presentation in PHP-code:
<?php
$a = 1;
$b = 'string';
$c = 12.5;
$d = true;
?>
equivalent presentation in SML:
<a>1</a>
<b>string</b>
<c>12.5</c>
<d>true</d>

At this point, it is important to understand a syntactic constraint of PHP. A PHP function may have only one single return value. (The case that the function takes it's parameter by reference is not considered here.) A PHP function the reads the contents of a SML document, is thus forced to return the (multi-dimensional) content as an array. The contents of this array can be copied to variables within the context of the calling program fragment, so that the initial condition is restored.

To a second example: Documents in JSO-notation are subject to the same restrictions.

numeric data fields
Presentation in PHP-code:
<?php
$A = array();
$A[0] = 1;
$A[1] = 'string';
$A[2] = 12.5;
$A[1000] = true;
$B = array();
$B[0] = 2;
?>
equivalent presentation in SML:
<A>
    <0>1</0>
    <1>string</1>
    <2>12.5</2>
    <1000>true</1000>
</A>
<B>
    <0>2</0>
</B>
associative data fields
Presentation in PHP-code:
<?php
$SCREEN = array();
$SCREEN["width"] = 1024;
$SCREEN["height"] = 768;
$SCREEN["depth"] = "32-Bit";
?>
equivalent presentation in SML:
<SCREEN>
    <width>1024</width>
    <height>768</height>
    <depth>32-Bit</depth>
</SCREEN>
multi-dimensional and mixed data fields
Presentation in PHP-Code:
<?php
$A = array();
$A["a"] = 1;
$A["b"] = 2;
$A[0] = "three";
$B = array();
$B[0] = true;
$B["a"] = 1000;
$B["b"] = array();
$B["b"][0] = 1;
$B["b"][1] = 2;
$B["b"]["a"] = 3;
?>
equivalent presentation in SML:
<A>
	<a>1</a>
	<b>2</b>
	<0>three</0>
</A>
<B>
	<a>1000</a>
	<b>
		<0>1</0>
		<1>2</1>
		<a>3</a>
	</b>
</B>

As can be seen from the above examples, the format is well suited to store scalar variables and data fields, which are used as parameters for the initialization of the framework. The specification of a data type is not necessary, because PHP is not strictly typed. The type of a variable is chosen dynamically at runtime. The only essential distinction is between scalar values, and data fields. This distinction can be easily made based on the syntax.

Alternatives to SML

There are a number of popular alternatives to the SML format used in this framework. The following section describes some of these alternatives, compares and discusses the results.

Below several aspects will be discussed:

Since it can be predicted, that some users may want to edit configuration files by hand, the readability of their source code is crutial. The syntax should be intuitive. This is particularly important, since at the present time a graphical interface can only be provided for a subset of the configuration settings of the framework. Given the wealth of options, it seems unlikely that a full coverage can be achieved already in the near future. Also it is questionable, wether this is desirable. Finally, it should also be taken into account that in principle experienced users may decide deliberately and intentionally to ignore the functions of a "cumbersome" GUI, because they feel like it is slowing them down.

An interesting factor is the performance, because the task to initializate the framework may be time critical. As the files are read on each call of the framework, the process of reading must be efficient. While it does not always make sense to try to compare the "speed" of different implementations, it should not be avoided where it is possible.

Also it might be usefull to take a look at the limitations of a file format. If there are K.O. factors for a certain format under certain precondition, this should be mentioned.

XML parser

One of the powers of XML is it's portability to other programming languages. Parser for XML are integrated in nearly all modern dialects. However, this is no extra value when the data meant to initialize a framework, which is only available in a special programming language.

The current tool of choice for handling XML files, which is available in both versions 4 and 5 of PHP is the native XML parser. However, this XML parser has a weakness. It may only read, but not write, XML files. A standard component for writing XML files is available with PHP version 5.

Also it is to be mentioned that to get an array from a XML file using the XML parser wouldn't require less effort than it was to write the whole script to read SML files. It also comes with the risk that the implementation of the XML parser might be subject to change in a later version of the language (as any other third-party implementation) even if this is unlikely to happen. In addition there are no real advantages of this implementation, other than the fact, that XML is a popular dialect and widely accepted.

Let us shed some light on the performance. However, this is anything but straightforward and all results might as well be subject to discussions, as they are dependent on the tool used. There are many of the them for the XML format, even if you restrict your search to PHP.

In the following scenario, a simple XML document is to be loaded and put into a data field.

Here isthe source code used for the XML document.

<?xml version="1.0"?>
<root>
     <array name="channel">
             <scalar name="title">Test</scalar>
             <scalar name="link">about:blank</scalar>
             <scalar name="description" />
             <scalar name="language">de-de</scalar>
             <array name="0">
                     <scalar name="title">test item 1</scalar>
                     <scalar name="pubDate">Do, 9 Jun 2005 13:23:18 +0200</scalar>
                     <scalar name="description"><![CDATA[test item]]></scalar>
                     <scalar name="link" />
                     <scalar name="author">test author</scalar>
             </array>
             <array name="1">
                     <scalar name="title">test item 2</scalar>
                     <scalar name="pubDate">Do, 9 Jun 2005 13:23:53 +0200</scalar>
                     <scalar name="description"><![CDATA[test item]]></scalar>
                     <scalar name="link" />
                     <scalar name="author">test author</scalar>
             </array>
             <array name="2">
                     <scalar name="title">test item 3</scalar>
                     <scalar name="pubDate">Do, 9 Jun 2005 13:24:18 +0200</scalar>
                     <scalar name="description"><![CDATA[test item]]></scalar>
                     <scalar name="link" />
                     <scalar name="author">test author</scalar>
             </array>
             <array name="3">
                     <scalar name="title">Linktest</scalar>
                     <scalar name="pubDate">Do, 9 Jun 2005 13:24:36 +0200</scalar>
                     <scalar name="description"><![CDATA[test item]]></scalar>
                     <scalar name="link">about:blank</scalar>
                     <scalar name="author">test author</scalar>
             </array>
     </array>
</root>

Now the same in SML format.

<channel>
             <title>Test</title>
             <link>about:blank</link>
             <language>de-de</language>
             <0>
                     <title>test item 1</title>
                     <pubDate>Do, 9 Jun 2005 13:23:18 +0200</pubDate>
                     <description>test item</description>
                     <author>test author</author>
             </0>
             <1>
                     <title>test item 2</title>
                     <pubDate>Do, 9 Jun 2005 13:23:53 +0200</pubDate>
                     <description>test item</description>
                     <author>test author</author>
             </1>
             <2>
                     <title>test item 3</title>
                     <pubDate>Do, 9 Jun 2005 13:24:18 +0200</pubDate>
                     <description>test item</description>
                     <author>test author</author>
             </2>
             <3>
                     <title>Linktest</title>
                     <pubDate>Do, 9 Jun 2005 13:24:36 +0200</pubDate>
                     <description>test item</description>
                     <link>about:blank</link>
                     <author>test author</author>
             </3>
</channel> <Se

For comparison, PHP 4 with the standard XML parser will be used. Before and after each parser step the command "microtime" will be used to measure the time. The difference between these values will be given in milliseconds. It is not important to see how many milliseconds on implementation is faster than the other. Obviously you can't judge this by this kind of testing, since the procedure is far too rudimentary to produce representative results.

The configuration of the test system can be neglected in this case, since only the magnitude of the difference between the two techniques should be considered.

It's even more problematic, that hand-write PHP-Code is needed to have the XML parser handle the tags. Therefor it might be possible that less performant PHP code could have an impact on the results. To avoid this the XML parser will be measured two times: first with and second without the implementation.

This test does not aim to vote pro or contra the use of this XML parser. The aim of the tests is therefore not a vote for or against the use of the XML parser. Much more it should show whether there are clear differences between the two versions. In particular, whether the SML variant may not be suitable for the framework, because of poor perfomance.

For this modest task this simple test should provide sufficiently accurate results.

XML-Parser 1 XML-Parser 2 SML-Script Difference
SML to XML 1
Difference
SML to XML 2
0,003685s 0,001813s 0,001626s -0,001153s (31%) -0,000187s (10%)
0,003410s 0,001666s 0,001255s -0,002155s (63%) -0,000411s (24%)
0,003367s 0,001629s 0,001312s -0,002055s (61%) -0,000317s (19%)
0,003394s 0,001614s 0,001240s -0,002154s (63%) -0,000374s (23%)
0,003381s 0,001618s 0,001228s -0,002153s (63%) -0,000390s (24%)
0,003386s 0,001613s 0,001400s -0,001986s (59%) -0,000213s (13%)
0,003387s 0,001620s 0,001296s -0,002091s (61%) -0,000324s (20%)
0,003658s 0,001617s 0,001247s -0,002411s (65%) -0,000370s (22%)
0,003678s 0,001633s 0,001454s -0,002224s (60%) -0,000179s (10%)
0,003725s 0,001613s 0,001573s -0,002152s (58%) -0,000040s (2%)

Comparison of the performance of XML parser and SML script (excerpt)

1 XML parser when using a comparable implementation to the one of the SML script

2 XML parser running empty, without processing the input at all

Considering the above short excerpt of the results, it comes clear that the XML parser with the PHP-code included is clearly less performant than the SML script. As already mentioned, this might also be due to less efficient programming. But when considering column 2 ( "XML parser 2") compared with column 3 ( "SML script"), you can see that even in this case the SML variant comes close to the values of the XML parser. This is particularly remarkable because the XML parser in this case ran empty and produced no results at all, since no implementation was given to handle the content. Therefore, the argument that an unfavourable implementation might be slowing down the XML parser, can no longer be maintained.

This test provided no indication that the efficiency of the implementation might have a negative effect on the performance of the entire application.

SimpleXML

The use of SimpleXML only slightly differs from that of SML, as the following source code indicates.

<?php
$sml = SML::getFile("test.config");
$xml = simplexml_load_file("test.xml");
?>


The only obvious difference is the type of the return value. While the SML variant returns an array as result, SimpleXML returns an object of type "SimpleXMLElement". One might argue which solution is better.

One argument against SimpleXML is that it uses object properties in PHP to represent names of tags and attributes. This is problematic because these may contain different characters in PHP and XML. For example, this concerns the character '-', which may not be included in the name of a variable in PHP. Consequently, at the time of preparing this document, it was very difficult to acces a tag like <foo-bar> via SimpleXML.

Initialization files

Initialization files, often abbreviated with the file extension "ini", are themselves a suitable alternative, unless mapping of complex structures is necessary. For example, the presentation of keys, especially in deeply nested structures, may be confusing. The format provides simple syntax. But the format is not as flexible and powerfull as XML. For example the typical tag-notation, which is currently very popular, is missing. Creating a DTD or a similar file, which describes the structure of the file format is not possible, for practical and theoretical reasons. Still this is no disadvantage. Considering that the primary task of this file format is to store any structured associative data fields taken from a script or programming language, it is clear that demanding a static structure doesn't make sense in this scenario (so does the DTD). A combination, which unites the advantages of both formats in itself, would be ideal.

The following code snippet shows an example of an initialization file.

[STORE\0]
type=book
author=Dr. E Xample
[STORE\0\TITLE]
main=Proto matter
subtitle=Alpha et Omega
[STORE\1]
type=cd
author=Barbara Singer
[STORE\1\TITLE]
main=Best-Of

JSON

Another alternative is to use JSON. The following is an example:

"STORE" : {
    "0" : {
        "type" : "book",
        "author" : "Dr. E Xample",
        "TITLE" : {
            "main" : "Proto matter",
            "subtitle" : "Alpha et Omega"
        }
    },
    "1" : {
        "type" : "cd",
        "author" : "Barbara Singer",
        "TITLE" : {
            "main" : "Best-Of"
        }
    }
}

Using "serialize" and "unserialize"

PHP offers the possibility, like Java, to serialize and restore variables by using the commands "serialize" and "unserialize".

The following code snippet shows an example.

a:1:{s:7:"CHANNEL";a:7:{s:5:"TITLE";s:4:"Test";s:4:"LINK";s:11:"about
:blank";s:8:"LANGUAGE";s:5:"en-us";i:0;a:4:{s:5:"TITLE";s:10:"test i
tem 1";s:7:"PUBDATE";s:29:"Th, 9 Jun 2005 13:23:18 +0200";s:11:"DESC
RIPTION";s:8:"Test item";s:6:"AUTHOR";s:11:"Test author";}i:1;a:4:{s:
5:"TITLE";s:11:"Test item 2";s:7:"PUBDATE";s:29:"Th, 9 Jun 2005 13:
23:53 +0200";s:11:"DESCRIPTION";s:9:"Test item";s:6:"AUTHOR";s:11:"
Test author";}i:2;a:4:{s:5:"TITLE";s:11:"Test item 3";s:7:"PUBDATE";s
:29:"Th, 9 Jun 2005 13:24:18 +0200";s:11:"DESCRIPTION";s:8:"Test item
";s:6:"AUTHOR";s:11:"Test author";}i:3;a:5:{s:5:"TITLE";s:8:"Linktest
";s:7:"PUBDATE";s:29:"Th, 9 Jun 2005 13:24:36 +0200";s:11:"DESCRIPTIO
N";s:9:"Test item";s:4:"LINK";s:11:"about:blank";s:6:"AUTHOR";s:11:
"Test author";}}}

This solution is, without doubt, very performant. The command "unserialize" works app. 2 times faster than SimpleXML. However, the readability of this data stream is problematic. Obviously it is not a good solution for editing the file by hand. As good readability is essential for the Framework, this solutions was not chosen.

for further reading

  1. Self-PHP, http://www.selfphp.info
  2. Official PHP website, http://www.php.net

Spam und Flooding

Definition

Unwanted advertising in e-mails, forums and guest books are a current topic. This section will explain what it is and tell some methods used by attackers. Subsequently, it will show what has been done to handle these threats.

There is a fine difference between "Spam" and "Floods".

The word "Spam", as it is used here, means the repeated and unrequested transmission of contents to a person or a group. For example, the sending of unsolicited e-mails with promotional content to third parties. Even massive, unsolicited phone calls are a form of "spam". Sending "spam messages" is illegal in many countries (including Europe and the United States) and will be prosecuted.

The site "spamhaus.org" defined "spam" as follows.

The word " Spam " as applied to Email means Unsolicited Bulk Email ("UBE").

  1. (Bulk) the recipient's personal identity and context are irrelevant because the message is equally applicable to many other potential recipient
  2. (Unsolicited) the recipient has not verifiably granted deliberate, explicit, and still-revocable permission for it to be sent

A message is Spam only if it is both Unsolicited and Bulk.

The word " Flooding " means the deliberate, unsolicited "flooding" of a medium with content. Floods are meant to disturb the normal operation of a medium. For example massive publishing of senseless texts in a public forum. Also, a person, who disturbs a public meeting by permanent interruptions, does a form of flooding. Flooding may be illegal in some cases. For example if it causes economic damage.

In most cases "Spam" is produced to publish advertisements. While "Floods" are at most a form of vandalism.

Spam in forums and guest books

Spam in forums and guest books are an increasing problem. This also applies to web logs an wikis. Often, these records are quickly identified and then removed, but the removal of unwanted entries of this kind takes time and money. The following section describes what an attacker could do and ways to react to it.

The attacker might do as follows. First, the attacker might looks for a possibly vulnerable script like a guestbook or forum. He examines the forms that are used to create new entries, and notes the names of the required form fields. The attacker search for specific texts within the targeted application. Using these texts as search terms, the attacker identifies web sites where the script is installed.

The attacker creates a list of URLs for the pages and stores the list in a file. With the help of a short program, the messages are published to all forms on the list.

If the attacker is lucky enough, search engines like Google index these pages before the administrator removes the ad. This way the embedded hyperlinks also increase the PageRank of the so-advertised web sites.

There are multiple ways to react to such attacks.

Reactive measures

The easiest way is to simply change the URL. Usually, the scripts of most attackers work asynchronously. Therefore usually he will not recognize so soon that the targeted site is no longer available. You may expect that the attacker might either wait for the search engine to refresh the URL, which might easily take up 8 weeks, or even longer, until he re-visits the site and extracts the new link manually. In both cases, the victim should temporarily be safe.

For the Framework changing the URL is easy and can be done without any difficulties. It is so simple, because the Framework can handle multiple instances, each identified by a unique id, as "configuration profiles" . Which profiles are active and which are not, is set by the administrator.

Thus, to change the URL of an application you only have to rename the profile and correct the links. This is a question of just a few minutes. The positive side effect consists of the fact that the aggressor does not receive an error 404 (side not found) during synchronous transmission, or with examination of the URL over an automatic program from the server. Even by manual examination he does not notice that the profile is inactive. Depending on the configuration of the framework the attacker will continue to submit messages to the inactive forms and (unless the profile is write-protected) write entries.

By personal experience I can report that aggressor may be lead astray for months without noticing their error.

A further measure is to block IPs or IP-ranges. That is above all useful if the aggressor always uses the same proxies for the attacks. Unfortunately this is not often the case. Nonetheless, this possibility was considered and an included as an optional feature for the Framework. This function can also be used for applications that run within a local network, to prevent unauthorized access from the Internet.

Preventive measures

In order to prevent from the start that an aggressor can write entries using an automatic program, you may add CAPTCHAs to threatened forms. In order to be able to write an entry, the visitor must type in the shown code. While this is an easy task for a human, it is a lot more difficult for an automatic program.

There are problems that arise from this for the attacker. The program needs to retrieve the code from the server ? even if the code is successfully extracted it requires valueable time. An asynchronous communication is not longer possible: since the request has to be send and the code retrieved. There is also the risk that the attacker's program is slowed down by an intentionally modified server. Also it will cause high effort for the attacker.

It is to be assumed that an aggressor will avoid such protected web applications.

The Framework implements such a check. The standard library provides the actions "security_get_image" and "security_check_image", that do produce the required graphics in PNG format using the PHP GD library and that do maintain a code table to check the user input. This table is hidden from the public via ".htaccess" and is refreshed automatically.

Vanadalism and flooding in forums and guest books

Flooding can be both, unintentional and intended.

This may happen unintentionally if the server temporarily experiences high traffic. In this case it might occur that a user has to wait a longer time for the server's response. Impatience may cause that he refreshes the page and resends the request, which will create a second identical entry. Even if the web site itself gets low traffic, this may still occur, if the web site is hosted on a "shared server". This is when the web site where the framwork is installed is shared with other customers. It is not uncommon, that 1000 or even 10000 customers on one single server have to share the same resources.

Flooding by intention is often a kind of "vandalism". Goal of the aggressor is it to disturb and/or to hinder the normal use of the targeted media.

Whether intended or accidental - flooding can be reduced by automated measures.

Example Scenario

Two methods are to be explained, which attackers could try to disturb the operation of the application. First: by entering extremly long character strings without line breaks, lining an excessive chain of line breaks, writing extremly long (senseless) texts in general, or extra large graphics, where all of these contents may break the layout of application. This may cause that parts of the graphical layout are no longer shown as intended by the author of the web site. In addition it could force other users to scroll the page, and the presentation of contributions by other authors may thereby be impaired.

The second method is mass-submission of irrelevant contributions. This will make it difficult for other users, to recognize the important messages and notice their contents. For this attack, the aggressor sends several identical or similar contributions to the server.

Prevention of such scenarios

For the second method, the Framework provides the following solution. When two successive contributions are send, it checks whether the contributions have the same content. If so, the second entry is rejected. This esp. prevents accidental flooding, but also makes attacks in general a little more difficult.

Additionally, there is an option, which allows to restrict the number of consecutive entries written by a user to a certain number. The user is identified by the IP. If the option is active and the user writes more entries than expected the user will be denied further contributions until another user writes an entry.

For the avoidance of the first method the framework provides a number of filters. These filters restrict the length of text, limit the proportions of graphics to maximum values, remove conspicuous repetitions and force line breaks, when a string is more than 80 long.

These methods make the disturbance of the normal operation more difficult, yet they can't offer 100% protection. This would not be desirable anyway. This is due to a well-known dilemma. If one narrows the effective region of the filters, then this reduces the number of the false alarms ("false-positives"). These are those submission which are recognized as flooding-attempts, but in fact are none. At the same time however also a higher number of real attacks will slip by the net of the filters. Conversely: if one expands the target area of the filters, the will prevent a larger number of attacks. However, this will also increase the number of "false-positives" accordingly. Ultimately, it is thus always a "trade-off" between adequate security and reasonable tolerance, to prevent false alarms.

for further reading

  1. spamhaus.org
  2. PHP Manual. PHP Documentation Group, 03/23/2005, http://php.net/manual/en/

Encryption

In order to increase the security of passwords it is reasonable to encrypt these. One-way encryption algorithms [ I ] are well suited for this. In this section two of the most important representatives are explained. In PHP they are implemented by the functions "md5()" and/or "crypt()".

DES-algorithm

The DES-procedure [ II ] was for many years the standard encryption algorithm in the US. With the increasing power of available hardware on the market, the simple DES-algorithm is no longer safe. The multi-encryption variant "Triple-DES" may be considered a more safe modification. As follow-up algorithm for DES the US-american National Institute of Standards selected the so-called RIJNDAEL algorithm AES [ III ].

DES works on the binary representation of the input text. It produces an initial permutation of this binary notation. Subsequently, a block cipher is used for encryption. The DES-algorithm is a Feistel cipher. Feistel ciphers do apply an internal block cipher multiple times on parts of the message, to create a number of round keys.

For the safety of Feistel ciphers, the safety of the used internal block cipher is of crucial importance. Currently DES may be considered safe enough for most of your everyday-applications and the algorithm is still used in many environments. For example DES is used on Unix systems for the authentication of users.

MD5-algorithm

The MD5-algorithm [IV] is a hashing algorithm. Important for the security of a hash process is the property of the collision freedom. A collision is when two different clear texts produce the same cipher text. If a collision is found, it is possible to calculate the plaintext with the help of appropriate procedures. It is known, that MD 5 is not free of collisions. However, it is unknown whether there any compression functions, which have no collisions. Therefore, it is acceptable in practice, if it is sufficiently difficult to find a collision. Such procedures are considered "collision resistent". Equally important is the speed of the algorithm because the encryption itself should be - in order to be suitable for authentication - most efficient.

Name of procedure Block length in bits Speed percent
MD4 128 100
MD5 128 68
RIPEMD-128 128 39
RIPEMD-160 160 24

Comparing the performance of different authentication procedures

The security of MD5 is now, however, in doubt [V]. The Heise Verlag first reported in early 2005 that researchers apparently succeeded in creating a faster procedure for the calculation of MD5 hashes [VI] to find collisions. A short time later, the message appeared that it has been able to create different certificates with the same MD5 hash [VII]. Only slightly more than half a year [VIII] a company offered a paid service, to reverse MD5 and SHA1 hashes by using pre-calculated tables, as long as the fee is right. It is therefore only a matter of time before MD5 won't provide enough security for sensitive applications anymore.

Also the safer algorithm SHA-1 [IX], with a bit length of 160-bit as opposed to MD5 with 128-bit, is already broken. As reported by Schneier [X], a group of Chinese cryptologists (Xiaoyun Wang, Yiqun Lisa Yin, Hongbo Yu), from the Shandong University succeeded in finding a collision in SHA-0 and SHA-1. For SHA-0 within 2 39 steps of operation, for SHA-1 it was 2 69 steps. This is much faster than the brute-force approach, with about 2 80 required steps of operation.

The framework also uses MD5 to encrypt passwords. In addition, SHA-1 is used to generate unique session IDs. A change of password encryption from MD5 to SHA-1 will be considered in due time.

for further reading

  1. Prof. Dr. Buchmann, J.: Einführung in die Kryptographie, 2nd edition. Springer-Verlag, 2001, pages 167 ff., 200 ff.
  2. FIPS PUB 197 - ADVANCED ENCRYPTION STANDARD. U.S. DEPARTMENT OF COMMERCE; National Institute of Standards and Technology, 26.11.2001 http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
  3. FIPS PUB 46-3 - DATA ENCRYPTION STANDARD. U.S. DEPARTMENT OF COMMERCE; National Institute of Standards and Technology, 25.10.1999 http://csrc.nist.gov/publications/fips/fips46-3/fips46-3.pdf
  4. Rivest, R.: RFC 1321 - The MD5 Message-Digest Algorithm. MIT Laboratory for Computer Science / RSA Data Security Inc.,1992 http://www.faqs.org/rfcs/rfc1321.html
  5. Kaminsky, D.: MD5 To Be Considered Harmful Someday. http://www.doxpara.com/md5_someday.pdf
  6. Forscher erzeugen unterschiedliche X.509-Zertifikate mit gleichem MD5-Hash. Heise-Verlag, 03.03.2005 http://www.heise.de/newsticker/meldung/57038
  7. Verunsicherung um Sicherheit von Kryptoalgorithmen. Heise-Verlag, 18.08.2004 http://www.heise.de/security/news/meldung/50148
  8. Passwort-Cracker als Bezahldienst. Heise-Verlag, 11.11.2005 http://www.heise.de/security/news/meldung/66039
  9. FIPS PUB 180-1 - SECURE HASH STANDARD. U.S. DEPARTMENT OF COMMERCE; National Institute of Standards and Technology, 17.04.1995 http://www.itl.nist.gov/fipspubs/fip180-1.htm
  10. Schneier, Bruce: SHA-1 Broken. Weblog, 15.02.2005 http://www.schneier.com/blog/archives/2005/02/sha1_broken.html

Author: Thomas Meyer, www.yanaframework.net