Obliquid Manual

Stefano Locati

Nick Vrtis

Abstract

Since Obliquid software and concepts are getting more stable, we are now starting the documentation effort. This manual has been started on May 18th, 2003. Many sections have to be reviewed.

Obliquid is a PHP/XML framework for building groupware Web portals. It provides Lego bricks that may be composed to build an Internet application. It has multilingual support and themes. Modules include user management, calendar, news, CMS, and messaging. For more information dev.obliquid.com

Last modified June 09, 2004


Table of Contents

I. Welcome to Obliquid
1. Introduction to Obliquid
What is Obliquid?
Features Overview: calendar
Features overview: editing month view of the calendar
Features overview: User search
Features overview: user search configuration
Features overview: user edit
Features overview: user edit configuration
Features overview: documentation management
Features overview: approval of a new document
How to try Obliquid
Case studies
Kines
Italian branch of a worldwide famous motorbike brand
Why Obliquid is free software
2. Setting up Obliquid on a Unix system
Requirements
Short installation instructions
Detailed installation instructions
Downloading Obliquid
Unpacking Obliquid
Ready to setup
Step 1: Check your environment
Step 2: Configure DB
Step 3: Create DB
Step 4: Import messages
Step 5: Final instructions
3. Setting up Obliquid on a Windows system
Requirements
Short installation instructions
Detailed installation instructions
Downloading Obliquid
Unpacking Obliquid
Ready to setup
Step 1: Check your environment
Step 2: Configure DB
Step 3: Create DB
Step 4: Import messages
Step 5: Final instructions
II. Usage and administration
4. Users, groups and security administration
Introduction
User Administration
Group Administration
Configure Security
5. Using the calendar
Introduction
6. Using the NEWS
Introduction to the NEWS module
NEWS Administration
NEWS category administration
NEWS item administration
NEWS item file attachments
7. Using the POSTS features of Obliquid
Introduction to the POSTS feature
Administering the POSTS features
Administering POSTS categories
Administering POSTS items
Administering POSTS objects
8. Using the documentation management
Introduction
Preparing the doc module
Preparing categories
Inserting your first document
Approving a document
Searching documents
9. Using the message center
Introduction to the message center feature module
Messages administration
Send emails in the queue
Subscribing and Blocking messages
III. Customizing Obliquid
10. Obliquid components
Introduction
Pages in module core
Slots in module core
Pages in module user
Slots in module user
Pages in module cal
Slots in module cal
Pages in module news
Slots in module news
Pages in module doc
Slots in module doc
Pages in module msg
Slots in module msg
Pages in module posts
Slots in module posts
Pages in module dev
Slots in module dev
Pages in module prj
Slots in module prj
11. Customizing the graphic
What are themes?
Obliquid template system
The frame outer template
Block template
Slot template
CSS Styles
Creating your own custom theme
IV. Extending Obliquid
12. Basic concepts
Introduction
Global environment
Database access
First step with forms
Fetching form results
Uploading files with db_form
Building queries with db_form
Dynamic web pages
Slots, brick components to build a page
Writing a PHP script
Writing a Smarty template
Creating a new local module
Adding columns to an Obliquid table
13. Permission system
Introduction
Special groups
Basic authorization
Advanced autorization
Code examples
Removing a functionality
14. Built in facilities
Url manipulation
Internationalization
Help button
Print button
Source and template buttons
Hiding a slot
Redirect to another page
Error messages
Javascript confirms
Onload Javascript code
Javascript Popup windows
How to choose a person with a popup window
Multiple sites with a single database
15. Site navigation facilities
Introduction to navigation facilities in Obliquid
Contents of the XML navigation files
How to customize navigation entries
Creating a navigation script
V. Obliquid for the core team
16. Basic concepts
Joining the team
CVS Access
Coding standards
Naming convenction
17. Module programming
How to create a new common module
How to create module navigation
Calendar module
18. Common operations
How to build a release
How to build an upgrade package
VI. Appendices
A. How to write this manual
Introduction
Available Docbook Elements
B. How to upgrade
Introduction
Upgrading Obliquid from 0.9.3 to 0.9.4
Upgrading Obliquid from 0.9.2 to 0.9.3
Upgrading Obliquid from 0.9.1 to 0.9.2
Upgrading Obliquid from 0.9.0 to 0.9.1
Upgrading Obliquid from 0.8.1 to 0.9.0
Upgrading Obliquid from 0.8.0 to 0.8.1
Upgrading Obliquid from 0.5.0 to 0.6.0
C. Code snippets
Introduction
Create a pronounceable password
Check an email address
Automatic login
Profiling your PHP scripts
Renaming files
D. GNU LESSER GENERAL PUBLIC LICENSE
Preamble
Terms and conditions
How to apply the License terms

List of Tables

10.1. Pages in module core
10.2. Slots in module core
10.3. Pages in module user
10.4. Slots in module user
10.5. Pages in module cal
10.6. Slots in module cal
10.7. Pages in module news
10.8. Slots in module news
10.9. Pages in module doc
10.10. Slots in module doc
10.11. Pages in module msg
10.12. Slots in module msg
10.13. Pages in module posts
10.14. Slots in module posts
10.15. Pages in module dev
10.16. Slots in module dev
10.17. Pages in module prj
10.18. Slots in module prj

List of Examples

12.1. Metabase Query method
12.2. Metabase GetSequenceNextValue method
12.3. Metabase QueryField method
12.4. Metabase QueryRow method
12.5. Metabase QueryAll method

Welcome to Obliquid

Chapter 1. Introduction to Obliquid

What is Obliquid?

Obliquid is more than a groupware application, it's an application framework to build web based PHP applications. For this reason Obliquid may be used, administered, customized and programmed by people with different backgrounds, like employees, managers, web agencies, graphic designers and programmers. The logical organization of this book takes this into consideration and the book may be read from cover to cover, or skipping chapters, as needed.

Even the simple question "What is Obliquid" may have different answers according to the audience. So, for end users it is a groupware application with user management, a calendar application, a news publishing system, and a documentation management system with the ability to send alert emails on certain events. For administrators it is a web application with a fine grained permission system, able to manage thousands contacts, helping to manage the workflow with alert emails. Graphic designers may be pleased to hear that the appearence may be fully customized by creating a new theme, and that since every part of html layout is in template files, pixel alignments are possibile. Programmers may like the concept of building the page with prebuilt blocks, and adding their own blocks, the possibility to separate common parts from customizations, and the powerful permission system.

Features Overview: calendar

Why should somebody bother to evaluate Obliquid when there are many similar open source groupware applications? If you install a groupware application and it already works exactly the way you need, then you are a lucky guy. But if you have to make modifications, even small ones, then you will understand the big difference between Obliquid and similar projects. Keep on reading to know more.

As an example we examine the month view of the calendar module: supported events are availabilities (a person gives his/her availabilities for meetings), phone calls (to keep a record of phone calls we made and to whom), recalls (to remember to phone again, since the first call was not enough), meetings (with the possibility to send confirmation emails), accounting entries and work sessions.

Features overview: editing month view of the calendar

By clicking the rightmost icon of the top bar, anybody with an appropriate permission can see the skeleton of a page. For example this is the structure of the previous calendar page.

A page is not a monolith script, but it's composed of slots. In this example there are five slots used. CORE/NAV is the navigation bar across the top, CAL/MONTHSMALL is the mini-calendar on the left, CAL/INDEX is the mini-navigation under the small calendar, and USER/LOGIN is logout option under that, and CAL/MONTH is the month view on the right. Slots are placed on a 3x6 grid and they can be moved around, they can be deleted, they can be edited, they can be replaced with your modified ones and new slots can be added. Slots have a PHP source and a Smarty template, to keep source code separated from HTML.

In a famous open source groupware application, the calendar homepage did use Smarty templates, but was a monolith of over 1300 lines. In our example cal/monthsmall is 103 lines and cal/month is 210 lines. This is not just numbers, it can save you headaches.

Features overview: User search

User search features search by surname, name, organization, city and username. It's possible to see only members of a group, or to find people not members of any group. It's then possible to filter by an Italian District: soon this filter could be replaced by search by Country, if the site administrator prefers. Search results are divided in pages, if a search exceedes 50 results.

Features overview: user search configuration

In the user search configuration it's possible to decide which fields will be shown in the search result table. The order in which the fields are shown can be modified too.

Features overview: user edit

In the user edit page an administrator can change user data, enable or disable a user to login, change his/her password, and change group memberships to allow access or restrict to parts of the site.

Features overview: user edit configuration

In the user edit configuration, the site configurator can choose which of the fields defined in the user database table are enabled or disabled and in which order they are to be shown.

Features overview: documentation management

Objects can be categorized according to three different categories. Moreover they can be classified by document type and by language. The titles for the three categories are "Category 1", "Category 2" and "Category 3". These can be changed during the system installation, or they can be changed with the site configuration parameters.

Features overview: approval of a new document

The person in charge with document approvals can be notified of new documents with an email, or can see the list of documents to be approved from the site. He/she can approve or not a document: in any case an email, with an optional comment will be sent to the person who inserted the document. This email can be blocked with the message center configuration, if desired.

How to try Obliquid

We have an online demo of the latest released version. There are three different demo logins with different access rights. In Obliquid, access rights are assigned to groups, and persons may belong to zero or more groups.

In the demo example Blaise Pascal is member of the Developer group which has full access, Albert Einstein is member of the Administrator group which can administer the site, but not to change it, Jan van de Snepscheut is member of the Customer group and has the lowest access rights. This is just an example. More groups can be created as needed and permissions can be fine tuned.

The demo site may be accessed at demo.obliquid.com. Since developer access has full rights to the site, including deleting pages, the demo site may be broken from time to time. For this reason a responsible use of the demo site is encouraged. On the other hand if you notice that the demo site is not working, please let us know this through the support section of dev.obliquid.com.

Case studies

Some case studies on who uses Obliquid and how.

Kines

Kines uses Obliquid to manage more than ten thousand business contacts. Five employees manage work meetings for nearly a hundred photographers, scattered across Italy.

Italian branch of a worldwide famous motorbike brand

I cannot write the name here, because I have to ask them the authorization to do so first. The intranet/extranet of this firm is built around Obliquid with many custom additions. Many different groups have different views of the system: administrators, about 200 dealers, support people, technical agents.

Why Obliquid is free software

Obliquid was first released as free software in July 2002. Obliquid is also the name of a software firm and an Internet provider. The name of the project was chosen to be the same as our company name because it's our main project and most of our customers' site are built around it. Our typical customers are medium to big sized firms that require organizational consultancies and custom solutions, more than a boxed product. We decided to release it as free software for a very simple reason: there was no reasons against it, no reasons to keep it secret.

The first result of releasing it as free software was to force us to make Obliquid cleaner and more organized. A programmer writes messy code from time to time, to meet impossible deadlines or for just being lazy. But he/she will be very ashamed of it, if other programmers look at it. Going open source the whole world can read your code. Scary. We rewrote it from scratch while improving the features of our previous engine.

In a second phase we started to receive bug reports from a wider user base. Bug reports help us to improve Obliquid, because we can fix any bug reported, but we can't fix bugs that we don't know about.

The next phase has just started: we are receiving translations and help from programmers to improve Obliquid. Obliquid was built with multilanguage support from the very beginning, because we are located in Italy and releasing it in Italian only would have been an unwise move. Moreover we've already built sites in Chinese and Japanese for our customers, and we are aware of the problems posed by different character encodings. Obliquid uses UTF-8 encoding which is safe for nearly all the languages of the world. Programmers are also starting to help and join the team to be part of this adventure.

The Obliquid developers' group now goes beyond Obliquid and so any of the active developers is the most qualified person available to give professional support and to customize Obliquid for specific needs. Beyond the free support anyone can receive on the forum, Obliquid firm can offer professional pay support, customizations, hosting or housing for your site.

Chapter 2. Setting up Obliquid on a Unix system

Requirements

Obliquid needs a platform with the following components

  • a database (currently only MySQL 3.23.x or higher is supported)
  • a web server (Apache 1.3.x - don't use Apache 2.0.x because PHP has problems to work with this version)
  • PHP scripting language (PHP 4.1.x or higher is required)

If needed database and webserver may be on different hosts.

Short installation instructions

If you are an expert, or you want an overview of the setup process, read this section, otherwise follow the step by step installation instructions in the next section.

  • Download the latest tar.gz package of Obliquid from http://dev.obliquid.com/. The zip package will also work, but it has Windows line endings.
  • Extract Obliquid to your location of choice.
  • Point your browser to see that directory through the webserver. For example http://www.example.com/your_obliquid_dir/
  • Follow the step by step installation instructions

Detailed installation instructions

Make sure your target system meets the requirements. Explaining how to install Apache 1.3.x, PHP 4.1.x or higher, MySQL 3.23.x or higher is beyond the scope of this document.

Downloading Obliquid

You can download the latest release of Obliquid from dev.obliquid.com or directly from sourceforge. It's recommended that you download the tar.gz package of the latest release.

Unpacking Obliquid

If you've downloaded Obliquid directly on the system where it has to be installed you can move it to a location visible through the webserver.

For example:

mv obliquid-rel#.tar.gz /var/www
tar xfvz obliquid-rel#.tar.gz

It will create a directory named obliquid-rel#, where rel# will be the release number, such as 0.5.0, for example. You are now free to rename this directory with the name you prefer.

If you have only ftp access, you can unpack the archive on your system and then upload it to the final destination.

Ready to setup

Point your browser to the just unpacked or uploaded Obliquid directory, for example to http://localhost/obliquid-rel# - you should see the welcome screen. Click on the I agree button if you agree to GNU LGPL licencing terms. Obliquid is free software, a software that gives you many freedoms, but remember that it's not public domain software. For example you can't remove the copyright notice, take away the credits, give to others without giving them the same rights given to you.

Step 1: Check your environment

In this step your environment will be checked. It will be checked that at least PHP 4.1.0 is installed and that the memory limit is big enough. Further, file permissions and magic_quotes settings are checked too.

Turning off PHP magic_quotes.

If you have root access to the webserver, locate your php.ini and make sure that magic_quotes_gpc, magic_quotes_runtime, magic_quotes_sybase are all set to Off.

; Magic quotes for incoming GET/POST/Cookie data.
magic_quotes_gpc = Off

; Magic quotes for runtime-generated data, e.g. data from SQL, from exec(), etc.
magic_quotes_runtime = Off

; Use Sybase-style magic quotes (escape ' with '' instead of \').
magic_quotes_sybase = Off

If you don't have root access you can disable magic quotes using a .htaccess file. Write a text file with the content below, and drop it in the main Obliquid directory, naming it .htaccess.

php_value magic_quotes_gpc Off
php_value magic_quotes_runtime Off
php_value magic_quotes_sybase Off

Starting the setup script

If you have command line access, running sh common/scripts/startsetup in Obliquid directory will fix file permissions. Otherwise you've to make the listed files writeable to the webserver.

If an important requirement is not met, the go to step 2 button won't appear. You can try to fix the problems, and pushing the Retry button until the problems are solved.

Step 2: Configure DB

Create new database checkbox: lets you decide to create tables in a new database or to use an existing one. Note that, by using two different table prefixes, two Obliquid installations may live in the same database.

Don't modify Database Type, since only MySQL is supported at the moment, and skip directly to Database Host. This setting lets you connect to a database on a different machine. Most of the time you won't need this feature, so you can leave "localhost" there.

Provide a reasonable "Database Name". Don't use spaces, use only alphabetic letters and numbers. If the Create a new database? option is not checked, you have to provide the name of an existing database.

Step 3: Create DB

As you reach step 3 you will either see a Connection OK message or some error message. For example if you mistyped the password you will have Could not connect to MySQL server message, in this case a Retry Step 2 button will appear.

If the connection was successfully established, then a Create DB and go to step 4 button will appear. This process takes some time, and should not be interrupted.

Step 4: Import messages

Multilanguage messages will get imported in this step. All you have to do is click on Import messages and go to step 6 button and wait. This step may take a long time, so be sure to click only once and to leave it running.

Step 5: Final instructions

Now Obliquid is installed. The only thing left behind is to replace the setup script with the main index. This can be done by running sh common/scripts/endsetup or by simply replacing index.php with common/scripts/index.php.

After this step you can reload the page and start to use Obliquid.

Chapter 3. Setting up Obliquid on a Windows system

Requirements

Obliquid needs a platform with the following components

  • a database (currently only MySQL is supported)
  • a web server (Apache 1.3.x - don't use Apache 2.0.x because PHP has problems to work with this version. You can try to use IIS, but Obliquid wasn't tested deeply with it. If you find any problems using IIS as webserver, please tell us on the forum.)
  • PHP scripting language (PHP 4.1.x or higher is required)

If needed database and webserver may be on different hosts.

Short installation instructions

If you are expert, or you want an overview of the setup process, read this section, otherwise follow the step by step installation instructions in the next section.

  • Download the latest zip package of Obliquid from http://dev.obliquid.com/. The tar.gz package will also work, but it has Unix line endings.
  • Extract Obliquid to your location of choice.
  • Point your browser to see that directory through the webserver. For example http://www.example.com/your_obliquid_dir/
  • Follow the step by step installation instructions

Detailed installation instructions

Make sure your target system meets the requirements. Explaining how to install Apache 1.3.x, PHP 4.1.x or higher, MySQL 3.23.x or higher is beyond the scope of this document. Easyphp provides an easy installation of a bundle of PHP, MySQL and Apache for your convenience.

Make sure that PHP has magic_quotes off.

If you have root access to the webserver, locate your php.ini and make sure that magic_quotes_gpc, magic_quotes_runtime, magic_quotes_sybase are all set to Off.

; Magic quotes for incoming GET/POST/Cookie data.
magic_quotes_gpc = Off

; Magic quotes for runtime-generated data, e.g. data from SQL, from exec(), etc.
magic_quotes_runtime = Off

; Use Sybase-style magic quotes (escape ' with '' instead of \').
magic_quotes_sybase = Off

If you don't have root access you can disable magic quotes using a .htaccess file. Write a text file with the content below, and drop it in the main Obliquid directory, naming it .htaccess.

php_value magic_quotes_gpc Off
php_value magic_quotes_runtime Off
php_value magic_quotes_sybase Off

Downloading Obliquid

You can download the latest release of Obliquid from dev.obliquid.com or directly from sourceforge. It's recommended that you download the zip package of the latest release.

Unpacking Obliquid

If you've downloaded Obliquid directly on the system where it has to be installed you can move it to a location visible through the webserver.

For example:

mv obliquid-rel#.tar.gz /var/www
tar xfvz obliquid-rel#.tar.gz

It will create a directory named obliquid-rel#, where rel# will be the release number, such as 0.5.0, for example. You are now free to rename this directory with the name you prefer.

If you have only ftp access, you can unpack the archive on your system and then upload it to the final destination.

Ready to setup

Point your browser to the just unpacked or uploaded Obliquid directory, for example to http://localhost/obliquid-rel# - you should see the welcome screen. Click on the I agree button if you agree to GNU LGPL licencing terms. Obliquid is free software, a software that gives you many freedoms, but remember that it's not public domain software. For example you can't remove the copyright notice, take away the credits, give to others without giving them the same rights given to you.

Step 1: Check your environment

In this step your environment will be checked. It will be checked that at least PHP 4.1.0 is installed and that the memory limit is big enough. Further, file permissions and magic_quotes settings are checked too.

If you have access to the system, running common/scripts/startsetup.bat in Obliquid directory should fix file permissions. Otherwise you've to make the listed files writeable to the webserver.

If an important requirement is not met, the go to step 2 button won't appear. You can try to fix the problems, and pushing the Retry button until the problems are solved.

Step 2: Configure DB

Create new database checkbox, lets you decide if to create a new database or to use an existing one. Note that, by using two different table prefix two Obliquid installations may live in the same database.

Don't modify Database Type, since only MySQL is supported at the moment, and skip directly to Database Host. This setting let you connect to a database on a different machine. Most of the time you won't need this feature, so you can leave "localhost" there.

Provide a reasonable "Database Name". Don't use spaces, use only alphabetic letters and numbers. If Create a new database? option is not checked, you have to provide the name of an existing database.

Step 3: Create DB

As you reach step 3 you will either see a Connection OK message or some error message. For example if you mistyped the password you will have Could not connect to MySQL server message: in this case a Retry Step 2 button will appear.

If the connection could be successfully established, then a Create DB and go to step 4 button will appear. This process takes some time, and should not be interrupted.

Step 4: Import messages

Multilanguage messages will get imported in this step. All you have to do is click on Import messages and go to step 6 button and wait. This step may take a long time, so be sure to click only once and to leave it running.

Step 5: Final instructions

Now Obliquid is installed. The only thing left behind is to move away this setup script and to replace it with the main index. This can be done by running common/scripts/endsetup.bat or by simply replacing index.php with common/scripts/index.php.

After this step you can reload the page and start to use Obliquid.

Usage and administration

Chapter 4. Users, groups and security administration

Introduction

The USER module provides functions to control who has access to your site, and what they can do. For security reasons, you should closely control who has access to the various user functions.

User Administration

User administration is relatively simple and straight forward. You can add a user, delete a user, or change their personal information.

There is a set of filters available which will allow you to select which users to display, and in what order to display them. This can simplify your tasks if there are a large number of users. You can limit the display to only those users in a particular group. There is also an Address Book alphabet which will limit the display to only those users where the first letter of the "Sorted By" field matches. Note that you must hit the "OK" button after you make the list box selection in order for the filter to be activated. Obliquid will display 50 user records at a time, with paging arrows if more records are available.

When creating a new user, there are two fields which you should pay special attention to. The first is the checkbox field: "Enable to login". If this box is not checked, then the user will not be able to login, even though they exist. The second special field is the Group select box. This defaults to "none". If you do not assign a user to a group, then they will have very limited access to the rest of Obliquid.

Group Administration

Groups are the way to organize users into collections. This will simplify some of the administration of users. Rather than try to keep track of which user can access what, you only need to assign users to a group, and then decide what that group can access.

Obliquid does not limit the number of groups in the system. A large number of groups could result in increased administration if they are not carefully selected. A user can belong to any number of groups. The default installation defines three groups.

  • Developer. This is the most powerful group. It has access to all the areas in the site, and can customize system wide parameters. Membership in this group should be very limited in order to avoid an unstable site. It is possible to remove access to some areas within the site from the Developer ID, but that is not recommended.
  • Administrator. This group can access all the administrative areas within the site. It is almost as powerful as the Developer, but it cannot change system parameters or edit the page layouts.
  • Customer. This group is for normal customers. Generally, it only has view access to items within the site, and cannot create new items.

Configure Security

Security is an important part of Obliquid. Various features and functions of Obliquid are represented by Security Objects. Permission to access these objects is assigned to a group. If a user is a member of a group that has access to the object, then that user has access to the object. Since a user can be a member of any number of groups, and any group can be given access to any security object, security configuration is a complex task and should be done carefully.

There are four different types of security objects defined in Obliquid.

  • Navigation Page. A Navigation Page security object controls which navigation icons and indexes a user sees. For example, the default installation has a security object defined for "posts_admlistcats". If the user has access to this security object, they will see a navigation icon which will take them to the main administration page for POSTS administration. Note that if the user does not have access to this security object, they could still type in the full URL for the page, and get to that page. What they actually see when the page is displayed depends on their access to the slots that are defined on the page.
  • Slot. This is the security object which controls most of the access within Obliquid. If the user requests that a page is displayed, each slot on that page is checked to make sure that the user has access to the slot. If the user does not have access, the code for the slot is NOT executed, and nothing is displayed in that slot location. So, even if the user types in a URL for a page or function that they do not have access to, no code will be executed, and very little will be displayed. Generally, the slot "core/nav" is available to everyone, so this would display on the page.
  • Operation. This type of security object is used to represent a logical security item rather than a code construct such as a page or a slot. It is used to add program checks within a page to provide a finer grained access control.
  • Table/key. This security object is used to control access to a particular key within a table. It is used by modules such as POSTS to control access to a particular category within the POSTS module. The module that creates the table entry should also create the Table/key entry and then let the user that requested the new entry to assign access rights to the desired groups.

Chapter 5. Using the calendar

Table of Contents

Introduction

Introduction

Chapter 6. Using the NEWS

Introduction to the NEWS module

The NEWS module provides a simple, easy to use way to provide news and information to your users. The news can be grouped into categories, and each category can have a set of files for different languages. There are a lot of overlapping features in NEWS and POSTS, but NEWS makes it a little easier to publish the same information in multiple languages.

NEWS Administration

Administration of the news functions is a relatively simple process. There are only three steps involved in publishing something in news.

First, you need to decide which category you want to put the news article in. If it does not fit into a current category, or you are just starting out, you will need to create a new category.

Second, add an item entry to the category. This will provide a title and sub-title to the item, so your users can know which item they want to view.

Third, you need to add the content, and attach any files that should go with the item. The content can be either simple text information, or it can contain HTML (if you have IE 5.5 or higher).

NEWS category administration

The news category is a way of grouping news items to make it easier for your users to find the information that they need. When you create a category, you will be given an opportunity to provide a title and description in a number of different languages. The user will see the category titles in their selected language, and be shown how many categories are available in the other Obliquid languages.

There are a number of pieces of information about a category that the administrator should be aware of. They will help you organize the category to suit your needs.

  • Category title. A separate title and description is kept for each language supplied. The title can be up to 180 characters.
  • Description. The description can be up to 255 characters.
  • Restricted. This flag (yes or no) tells whether this particular category is restricted to only those allowed access by the security system. The default setup for Obliquid is that anybody (even those not logged in) can view the news category list. If you only want logged in users to view a category, then you would have to flag the category as restricted=yes and then assign the security object to "logged in users". The other option is to change the security for the "news_viewcats" navigation entry to "logged in users". Then only logged in users will be able to view the list of categories. If a category is not restricted, then all items in the category are not restricted. If a category IS restricted, then all items in the category ARE restricted. The default is NO.
  • Order By. This field indicates which ITEM field to use to order the items when an item list is displayed. There is a drop down list to select from. The default is Last modification date.
  • Position. When a new item is added to the category, the order field of the item will be incremented by this amount. If the Order By field is set to Custom Ordering, then the order field will be used to sort the item list.

NEWS item administration

Most of the administration of an item is simple. Care needs to be taken when adding an item to ensure that the language is properly set. If this is not done, then your users may be confused, or not be presented with all the information.

The language that an item is stored in depends on which language version of the category it is added to. When you administer NEWS, be sure that your language selection is correct. NEWS will display a category in the user language if a version is available for that language. Otherwise, it will display the English version. The language setting for an item is based on the version of the category it is added to. For example.... You have created a category that has both an English and an Italian version.

If you set your language preference to English, then the English version of the category will be displayed, and if you add an item to that category entry, it will be added with an English language setting.

If you do not set your language preference to Italian and add an item to the Italian version of the category, then there will be no items to display for your Italian visitors, even though the category will be listed.

When your users display the category and items, those who have English as their language preference will see the category, and an item. Those who have Italian as their langugage preference will only see the category, but no items.

NEWS item file attachments

If you want to include files with an item, this is possible. When you add or modify and item, there is a button Add a file. If you press this, the system will pop up a standard browser upload window where you can select a file to be uploaded to the server. When your user clicks on the filename while viewing and item, the file will be downloaded to their computer using the standard browser download functions.

Please note: After you select a file to upload and the popup window closes, the file is not available for your users until you hit the OK button for the item page. This tells Obliquid that you want to accept all the changes and additions for the item. If you do not do this, then the file might be orphaned on your server. It will not harm anything, but it will take up space.

Chapter 7. Using the POSTS features of Obliquid

Introduction to the POSTS feature

The POSTS feature of Obliquid allows you to easily post content on your web site. You can put items for display or to be used as part of a newsletter.

  • preformatted text files
  • files which contain HTML code that can be used to add formatting
  • pictures (Obliquid will automatically generate the thumbnail file from your original)
  • attach files which can be downloaded

Administering the POSTS features

There are three steps to making a posting available to your users.

The first step is to determine which category you want to put the new posting in. If the item does not fit into one of the existing categories, or you are just starting out, you will need to create a new category.

Once you have picked the category, then you will need to decide whether the new object will be part of an existing item, or will be a new item. An item represents a group of objects which will be displayed on the same Obliquid page, or be part of the same Obliquid newsletter.

Finally, you will need to add the object itself to the item. An object can be a preformatted text file, an file which contains HTML to provide additional formatting functions, a file which can be downloaded by the users, or an image file (currently only JPG, GIF and PNG files are supported).

Administering POSTS categories

A POSTS category is a way of grouping items that are related. The POSTS administrator should decide what to call each category. There are a number of pieces of information which are kept about a category.

  • Language. This is the language of the category and all the items in it. The default is the language of the person who is creating the category.
  • Title. This is the short (less than 100 characters) title for the category.
  • Description. This is a longer (less than 255 characters) description of the types of items which can be expected in this category.
  • Restricted. This flag (yes or no) tells whether this particular category is restricted to only those allowed access by the security system. The default setup for Obliquid is that anybody (even those not logged in) can view the POSTS category list. If you only want logged in users to view a category, then you would have to flag the category as restricted=yes and then assign the security object to "logged in users". The other option is to change the security for the "posts_viewcats" navigation entry to "logged in users". Then only logged in users will be able to view the list of categories. If a category is not restricted, then all items in the category are not restricted. If a category IS restricted, then all items in the category ARE restricted. Default is NO.
  • Auto Approved. This flag (yes or no) indicates whether items added to this category will be automatically approved for viewing. If this flag is set to NO, then an item which is added will not be available for viewing by normal users until it has been manually approved by the POSTS administrator. Default is YES.
  • Order By. This field indicates which ITEM field to use to order the items when an item list is displayed. There is a drop down list to select from. Default is Last modification date.
  • Sequence. This field is used to determine the sequence that categories are listed in when they are displayed. Default is NEXT which will automatically generate a sequence number 10 higher than the previous sequence number.
  • Last Modified. This field is automatically maintained by the system and is updated when ever a new item or object is added to the category.

Administering POSTS items

A POSTS item is a way of collecting a number of objects which are to be displayed on the same page, or included in the same newsletter. You would normally expect to have more than one item in a category. But an item can ONLY be in ONE category. There are a number of pieces of information which are kept about an item.

  • Language. This is the language for this item. Default is the same as the category
  • Title. This is the short (less than 100 characters) title for the item.
  • Comments. This is a longer comment (less than 255 characters) about what is contained in this item.
  • Approved. This flag (yes or no) indicates whether the item has been approved for viewing by users. Default is the same as the Auto Approve flag in the category.
  • Insert Date. This field is automatically maintained and is the date that the item was added to the system (not the date that it was approved).
  • Modification Date. This field is automatically maintained and is the last date that changes were made to the item. Default is the same as the Insert Date.
  • Sequence. This field is to manually sequence items instead of using the Title or Modification Date. Default is NEXT which generates a number which is 10 higher than the previous highest sequence field.

Administering POSTS objects

A POSTS object is actually a file that has been uploaded to Obliquid and is part of a page that gets displayed to the user or is part of the newsletter. You would normally expect to have more than one object in an item. But an object can ONLY be in ONE item. There are a number of pieces of information which are kept about an object.

  • Object Name. This is the name of the file the file that got uploaded.
  • Object Size. This is the size in bytes of the file that got uploaded.
  • Object Type. This indicates the type of the file that got uploaded, and it also controls what actually is displayed to the user when the item page is displayed.
    • Type=t. This is a preformatted text file. When this file is displayed, it will be surrounded by <PRE> and </PRE>. The text will be sent to the browser "as is" by Obliquid. It will not be executed, even if it is valid PHP. Note that not all browsers totally ignore formatting and HTML between the <PRE> and </PRE>. IE 5.5 for example will ignore anything between a <? until a -> or ?>. So, if you upload php code, you should check to make sure it displays the way you wanted it to. Or, avoid special characters and HTML code.
    • Type=h. This is a file with HTML formatting included. It is handled the same as the "Type=t" files, except that the <PRE> and </PRE> are not added before and after the file is inserted.
    • Type=a. This is an attached file. The filename and the size are used to create an HTML link on the page that is displayed. When the user clicks on the link, the standard browser download screen is displayed, and the user can download the file.
    • Type=p. This is an image file. When the file is uploaded, Obliquid automatically creates a thumbnail image of the file, proportionally scaled to approximately 100 x 100 pixels. This thumbnail image is shown to the user when the page is displayed. If the user clicks on the thumbnail, a new popup window will be opened. The popup window will be as large as the users screen if necessary, and the brower will be told to scale the image to fit this screen. If the user then right clicks on the image, the complete image file will be saved on their PC.
    • Type=m. This is a text message for the newsletter.
    • Type=l. This is an HTML formatted message for the newsletter. Note that since an HTML newsletter normally contains images, the references to these needs to be adjusted because the directory structure will change as they get uploaded. When you upload an HTML formatted message, Obliquid will ask for the image directory you used when you created the newsletter. When Obliquid uploads the HTML file, it goes through and finds all the references to that directory, and replaces them with the appropiate directory where Obliquid will store them. If you code complete URLs, then they will not be adjusted.
    • type=i. This is an image that is part of the HTML newsletter.
  • Location. Each object in an item can be displayed on the left, the right, or the center part of the viewing page. There is also an option to display this object on ALL three parts of the viewing page. Default is CENTER.
  • Sequence. This is a numeric field which determines in which order this object will be displayed on the page. Default is NEXT which generates a number 10 higher than the previous high sequence field.

Chapter 8. Using the documentation management

Introduction

The documentation management was done to be able to manage a good deal of categorized documentation, like in a medium sized organization. In one installation is used to manage more than three thousands documents stored in various formats. This project is now in the test phase.

Preparing the doc module

Each item of documentation may contain zero or more attached objects and they can be searched through different criteria. Each item can be of a Document type, and can have a title, a codification number, and a year. Further an item can be assigned to three different types of categories. Category type 1 and 2 can be assigned only once, while category type 3 can be assigned multiple times.

After a successful setup, Obliquid sets these three categories names to “Category 1”, “Category 2” and “Category 3”. This is of course not meaningful, because you have to change these labels depending on your application. For example if you load the demo data these categories will be renamed “Type”, “Body part” and “Animal”.

Another example is “Brand”, “Part”, “Product”. In this example, a document may describe a part of a product or of many similar products.

To change the labels for your own needs, click on Configure site, or any icon or link you have to access the core module. Then click on Configure parameters link. There locate cat1_name, cat2_name, cat3_name parameters and assign your own values.

Preparing categories

The next step would be to insert a few values in the categories, so you can then add an item. To do so, visit “Manage Category 1” and add at least a category label. Note that if you've renamed “Category 1” as you should, also this link will be renamed. It could be “Manage Brand”, for example.

Add at least a value also to all other categories, so visit “Manage Category 2”, “Manage Category 3” and also “Manage Document type”. Document types are not meant for storing the file type (such as PDF files, Word files, or the like), but to store the type of document of an organization, like invoices, internal documentations, proposals and so on. After this you are not yet done because you've to add at least a language: click on “Manage Language” and insert a two letter language code and a language name. Don't make your own language abbreviations, please refer to ISO639 standard. In some cases you may want to use the longer abbreviation, like for traditional chinese: zh_TW, since zh only is still ambiguous between traditional and simplified chinese.

Inserting your first document

If you followed all the recommendations of the two previous sections, you are now ready to insert your first item; if you didn't go back and execute all the steps before going further.

In the Add a new document page you have to fill in at least the title, but an item without a file is useless, because it can't contain any useful information, so, normally you would add at least a file too.

If an item is inserted without attaching any object it won't trigger any event, but if one object is attached it will trigger an event called doc_newobj. If this event is active in the message center, it will send an email to any people subscribed to this event, refer to the message center documentation for further details. Normally people subscribed to this kind of event will be the ones responsible for approving it.

Approving a document

If you have the permission to do so, you may have received a warning email that a document has been inserted, or if not, you can click on Documents to be approved. You will see a simple list of documents not approved yet, you can choose one, review, and decide wether to approve objects in it or not.

Approval is on objects not on documents, because objects represent the real content. Further is a new object is added to an existing item, there is no reason to hide the whole object from public view until the new object is approved. By clicking on Approve object you get into a screen where you can insert a comment and press on Approve object button. This comment will be sent to the person who inserted the object, provided that the doc_appr event is enabled. This message will be sent also to any other people subscribed to this event.

If you press on the Don't approve object button instead, nothing will happen, but the doc_notappr event will be triggered. If this event is active, the person who inserted the object will receive this comment, together with any people subscribed to this event.

Searching documents

The search slot is shown when clicking on Documentation management, and can be reached from within the doc module by clicking on Search documents. Documents may be searched by title, categories, document type, document number range and reference year.

After clicking on a document, if you have the permission to modify it, a Modify document link will appear. By clicking on it the document may be modified: new objects can be added or deleted or the basic document data may be changed. If a new object is added, a doc_newobj event is triggered, and messages will be sent to persons subscribed to this event. Also doc_moditem event will be triggered and messages will be sent to any person subscribed to this event.

Chapter 9. Using the message center

Introduction to the message center feature module

The message center feature acts as a central clearing house for the sending of predetermined messages. Some modules such as POSTS allow you to post items which need to be approved before they are available. Rather than have the POSTS module maintain a distribution list, and then have the DOC maintain another distribution list for the information it needs to send, Message Center provides a centralized facility for control of messages.

Messages administration

Messages within the message center can be individually enabled or disabled. If a message is disabled, then no messages will be sent, even if individuals or groups are subscribed to them. The system allows the creation of versions of the messages for different languages. Only one language version of the message can be enabled at a time. Obliquid does not pick the language.

Messages administration provides the ability to modify the template of the message that gets sent. A message is composed to two parts. One is a template which provides the basic text, and uses variables to indicate where each of the pieces of information the script supplies should go. You are free to rewrite the text template, put the variables anywhere you want. You can even choose to not display some of the information. Without program modifications, you cannot add aditional variables.

Send emails in the queue

Normally, emails are sent as they are created. This behavior can be changed, and the Message center provides a page which allows you to manually request that the queued messages be sent. This is in case the automatic processes are not set to send an important message quick enough.

There are a few parameters in the system configuration which control the operation of the sending portion of the Message center.

  • queue_min - This parameters controls how 'old' a message should be before it is sent. The default is 0 minutes, so messages are eligable to be sent as soon as they are queued. Message center is smart enough to combine multiple messages to the same person into one email. For a busy site, this can cut down the number of emails that an administrator needs to deal with.
  • multiple_msg_subject - This parameter just supplies a "standard" subject line for an email that is a combination of messages.
  • runqueue - This parameter controls when the queue is checked to see if there are messages to be sent. The default (YES) means that the queue is checked during the processing of each login. In a milding active site, this should be frequently enough to keep messages moving. In a very active site, or there are a lot a emails that are queued, this operation could slow down login, so it can be changed to NO. If it is set to NO, emails will only be sent on a manual request, or through a cron job on the host system which requests that they be sent.

Subscribing and Blocking messages

Even if a message is enabled, it still needs somebody to deliver the message to. This is controlled through the Subscribe and Block features of the Message center. The actual email address that is used for the sending is obtained from the user information. There are four links for subscribing and blocking messages. These all perform the same basic function.

You can subscribe people to a message either by individual user ID, or by group ID. It may happen that an individual is subscribed to a particular message both by individual user ID, and because they are a member of a group that is subscribed. If that is the case, they will still only receive one copy of the message.

You can also block people who would normally receive a message from getting it. This is particularly useful if a person is a member of a group that gets a message, but really does not want to be bothered by that message.

A person can be added by the slot code when the message is queued. This is used for example, when a new person signs up, and we want to send them a welcome message.

Customizing Obliquid

Chapter 10. Obliquid components

Introduction

Obliquid building blocks are pages and slots. A page is composed by placing one or more blocks (called slots) in a table. Each slot is a visual area on the screen interacting with a computer program. In the following sections you'll find the list of Obliquid pages and slot with their description

Pages in module core

Module core, Author(s) Stefano Locati, Nicholas Vrtis

Base module supplying services to others: composes the web page, provides language and theme support

Table 10.1. Pages in module core

NameDescription
homeHome
core_editpagegroupList pages in a module. Each page can be edited, previewed or deleted and has a list of slots.
core_editpageEdit a page where slots are shown on a grid 3x6 and can be moved, added or deleted
core_editslotEdit a slot
core_showsourceView PHP source code
core_showtemplateView Smarty template source
core_newpageAdd a new page
core_conflangEnable or disable languages
core_confpagegroupList modules and their pages
core_eweVisual HTML editor for newsletter body
core_eweeditVisual HTML editor for slot templates
core_configparmsView or change modules parameters
core_creditsCredits for Obliquid project
core_demodataLoad demo data
core_txteditText file editor, sends a message to people subscribed to core_edit on save
core_admthemeAdministration for graphic themes allowing to change name, sort order and to enable, add or delete themes
core_newmoduleAdd a new module
core_pagecommentEdit description for a page
core_slotcommentEdit description for a slot
core_resethasReload your security
core_cronExecute any pending task and it's usually called by some system batch job
core_listslotsList slots in a module

Slots in module core

Table 10.2. Slots in module core

NameDescription
core/changelangChange language with a drop down menu
core/changethemeChange theme with a drop down menu
core/conflangEnable or disable languages
core/confpagegroupList modules and their pages
core/logoSite logo that can be changed with sitelogo configuration parameter
core/configparmsView or change modules parameters
core/navTop icon bar
core/editpagegroupList pages in a module: each page can be edited, previewed or deleted and has a list of slots.
core/editpageEdit a page where slots are shown on a grid 3x6 and can be moved, added or deleted
core/pagedataModify page attributes
core/indexCore module side navigation menu
core/newslotAdd a new slot to a page, creating the necessary files if neeeded
core/newpageAdd a new page
core/demodataLoad demo data
core/editslotEdit a slot, allowing to change its attributes
core/slotshowChange fields of the slot, it works only for slots programmed to use them
core/showsourceView PHP source code
core/showtemplateView Smarty template source
core/txteditText file editor, sends a message to people subscribed to 'File modified' event
core/msgmoduleObsolete
core/creditsCredits for Obliquid project
core/msgshowObsolete
core/admthemeAdministration for graphic themes allowing to change name, sort order and to enable, add or delete themes
core/cronExecute any pending task and it's usually called by some system batch job
core/eweeditVisual HTML editor for slot templates
core/homeStandard installation home page
core/listslotsList slots in a module
core/newmoduleAdd a new module
core/pagecommentEdit description for a page
core/slotcommentEdit description for a slot
core/resethasReload your own security
core/slotparamObsolete

Pages in module user

Module user, Author(s) Stefano Locati, Nicholas Vrtis

Users, contacts and groups administration, security management

Table 10.3. Pages in module user

NameDescription
user_groupadmList of groups and links to pages to change groups details, list members, delete groups, change groups security
user_groupaddCreate a new group
user_groupmodModify name and description of a group
user_useradmList of users and contacts applying the filters of slot user/userfilter
user_usernewCreate a new user or contact
user_usermodModify an existing user or contact
user_logoutLog yourself out and reset security
user_group_objAllow to assign security objects for a particular group
user_choosePopup to choose a user applying the filters of user/userfilter
user_usermymodModify logged user own data
user_userviewDetails of a user or a contact
user_securityList of all security objects of a type. For each security object there is a list of the groups that have access and a link to delete or create new security objects
user_securityobjCreate or delete security objects
user_securityasgnAllow to assign groups to a security object
user_ops2groupsShow old operations assigned to each group.
user_specialasgnLimited function page to assign groups to a single new object
user_usersignupAllow visitors to register
user_meetingList of lastest meetings of a person
user_exportExport users to Excel
user_statsessSession statistics

Slots in module user

Table 10.4. Slots in module user

NameDescription
user/loginLog yourself in the site
user/detailboxView your own personal details
user/usermodCreate or modify a user or a contact
user/useradmList of users and contacts applying the filters of slot user/userfilter
user/groupadmList of groups and links to change groups details, list group members, delete groups, change groups security
user/indexUser module side navigation menu
user/groupaddCreate a new group
user/groupmodModify name and description of a group
user/userfilterUsers search filter
user/usergroupsList groups a user belongs to and allows changing them
user/logoutLog yourself out and reset security
user/choosePopup to choose a user applying the filters of user/userfilter
user/userviewDetails of a user or a contact
user/users2groupsObsolete
user/objs2groupsList of all security objects of a type. For each security object there is a list of the groups that have access and a link to delete or create new security objects
user/objaddCreate a new security object
user/group_objAllow to assign security objects to a group
user/groupassignAllow to assign groups to a security object
user/exportExport users to Excel
user/statsessSessions statistics
user/meetingList of lastest meetings of a person
user/groups2usersGroups and users counts

Pages in module cal

Module cal, Author(s) Stefano Locati, Federico Carrara, Nicholas Vrtis

Calendar managing availabilities, call, recall, meetings, accounting entries and work sessions

Table 10.5. Pages in module cal

NameDescription
cal_homeCalendar monthly view of events
cal_dayCalendar daily view of events
cal_addavailAdd an availability
cal_modavailModify an availability
cal_addcallAdd a call
cal_modcallModify a call
cal_modrecallModify a recall
cal_addmeetingAdd a meeting
cal_addmeetavailAdd a meeting starting from an availability
cal_modmeetingModify a meeting
cal_addaccountAdd an accounting entry
cal_modaccountModify an accounting entry
cal_accreportAccountancy reports
cal_accdetailDetails of an account
cal_accpersonAccount details for a single person
cal_addworkAdd a work session
cal_modworkModify a work session
cal_prjlistWork reports by project
cal_prjdetailAdd or modify a project
cal_prjreportWork report for a project
cal_wrkreportWork report for a person
cal_cConfirm or refuse meetings emails containing a security code, so it's safe to leave this slot assigned to everyone
cal_cacheGenerate repeated events
cal_cleanDelete old availabilities

Slots in module cal

Table 10.6. Slots in module cal

NameDescription
cal/dayworkDaily work report by project and by person
cal/monthworkMonthly work report by project and by person
cal/monthsmallCompact monthly calendar
cal/indexCalendar module Side navigation menu
cal/filterMenu to filter calendar events by type
cal/monthCalendar monthly view of events
cal/dayCalendar daily view of events
cal/addavailAdd or modify an availability
cal/addcallAdd or modify a call
cal/modrecallModify a recall
cal/addmeetingAdd or modify a meeting
cal/meetavailAdd a meeting starting from an availability
cal/meetingslistList meetings between two persons and may send a confirmation email
cal/confirmConfirm or refuse meetings emails containing a security code, so it's safe to leave this slot assigned to everyone
cal/addaccountAdd or modify an accountancy entry
cal/accreportAccountancy reports
cal/accdetailDetails of an account
cal/accpersonAccount details for a single person
cal/addworkAdd or modify a work session
cal/prjlistWork reports by project
cal/prjdetailAdd or modify a project
cal/prjreportWork report for a project
cal/wrkreportWork report for a person
cal/cacheGenerate repeated events
cal/cleanDelete old availabilities

Pages in module news

Module news, Author(s) Stefano Locati, Nicholas Vrtis

Multilingual news publishing system with a simple security system allowing to restrict topics. Articles are grouped into topics, each article can be in multiple languages and has a title, a subtitle a html body and attached files or pictures

Table 10.7. Pages in module news

NameDescription
news_admNews administration: list of topics with security restrictions applied
news_uploadNews administration: popup to upload a file into an article
news_catNews administration: create or modify a topic
news_listNews administration: list of articles to modify in a topic
news_itemNews administration: create or modify an article
news_editNews administration: edit an article text body
news_viewcatsList news topics with security restrictions applied
news_viewlistList articles within a topic
news_viewitemView a news article
news_downloadDownload a file attached to an article

Slots in module news

Table 10.8. Slots in module news

NameDescription
news/admNews administration: list of topics with security restrictions applied
news/uploadNews administration: popup to upload a file into an article
news/catNews administration: create or modify a topic
news/listNews administration: list of articles to modify in a topic
news/itemNews administration: create or modify an article
news/viewcatsList news topics with security restrictions applied
news/viewlistList articles within a topic
news/viewitemView a news article
news/editNews administration: edit an article text body
news/downloadDownload a file attached to an article

Pages in module doc

Module doc, Author(s) Stefano Locati, Nicholas Vrtis

Document management system with advanced security

Table 10.9. Pages in module doc

NameDescription
doc_homeMain page providing document search
doc_detailView details of a document
doc_admcat1Manage category 1 items
doc_admcat2Manage category 2 items
doc_admcat3Manage category 3 items
doc_admdoc_typeManage document type items
doc_admlangManage language list for documents
doc_notapprovedList of documents containing at least one object not approved yet
doc_moditemModify a document and may send a message to people subscribed to 'New object uploaded' event or to 'Document changed' event
doc_additemAdd a document and may send a message to people subscribed to 'New object uploaded'
doc_appritemApprove or not an object and sends a message to people subscribed to 'Object approved' or to 'Object NOT approved' events
doc_seclistcatsFirst screen to manage security: list all 'category 1' items
doc_seclisttypesSecond screen to manage security: list 'document type' and 'category 1' combinations and groups allowed
doc_seclistguGroup and user permissions for a 'category 1' and 'document type' combination
doc_secadcChange operations for a group or a user on a specific 'category 1' and 'document type' combination. It can also be used to change operations for a group or a user on a specific document
doc_seclistitemsList all items with specific security for a 'category 1' and 'document type' combination
doc_checkfilesVerify consistency

Slots in module doc

Table 10.10. Slots in module doc

NameDescription
doc/homeSearch form
doc/detailView details of a document
doc/searchDocument list resulting from a search made with doc/home search form
doc/indexDocumentation module side navigation menu
doc/admcat1Manage category 1 items
doc/admcat2Manage category 2 items
doc/admcat3Manage category 3 items
doc/admdoc_typeManage document type items
doc/admlangManage languages for documents
doc/notapprovedList of documents containing at least one object not approved yet
doc/moditemAdd or modify a document, may send a message to people subscribed to 'New object uploaded' event or to 'Document changed' event
doc/appritemApprove or not an object and sends a message to people subscribed to 'Object approved' or to 'Object NOT approved' events
doc/seclistcatsFirst screen to manage security: list all 'category 1' items
doc/seclisttypesSecond screen to manage security: list 'document type' and 'category 1' combinations and groups allowed
doc/seclistguGroup and user permissions for a 'category 1' and 'document type' combination
doc/secadcChange operations for a group or a user on a specific 'category 1' and 'document type' combination. It can also be used to change operations for a group or a user on a specific document
doc/seclistitemsList all items with specific security for a 'category 1' and 'document type' combination
doc/noattachList all documents without any attached object
doc/checkfilesCheck for existence and correct size of files
doc/fixapproveRecomputes the approved flag for all documents

Pages in module msg

Module msg, Author(s) Stefano Locati

Workflow email management

Table 10.11. Pages in module msg

NameDescription
msg_homeMessage module main page showing some statistics
msg_textadmMessage administration providing a list of event messages
msg_textmodModify a message subject and body
msg_sendpersonManage a list of persons subscribed to specific events emails
msg_blockpersonManage a list of persons who won't receive specific events emails
msg_sendgroupManage a list of groups subscribed to specific events emails
msg_blockgroupManage a list of persons who won't receive specific events emails
msg_personaddAdd a person to either the send or the block list
msg_groupaddAdd a group to either the send or the block list
msg_runqueueSend a batch of messages waiting in the queue

Slots in module msg

Table 10.12. Slots in module msg

NameDescription
msg/homeMessage module main page showing some statistics
msg/indexSide navigation menu
msg/textadmMessage administration providing a list of event messages
msg/textmodModify a message subject and body
msg/sendpersonManage a list of persons subscribed to specific events emails
msg/blockpersonManage a list of persons who won't receive specific events emails
msg/sendgroupManage a list of groups subscribed to specific events emails
msg/blockgroupManage a list of persons who won't receive specific events emails
msg/personmodAdd a person to either the send or the block list
msg/groupmodAdd a group to either the send or the block list
msg/runqueueSend a batch of messages waiting in the queue

Pages in module posts

Module posts, Author(s) Nicholas Vrtis, Stefano Locati

Posts and newsletter management

Table 10.13. Pages in module posts

NameDescription
posts_admlistcatsPOSTS administration category selection list
posts_admcatadcPOSTS category add, delete, change
posts_admlistitemsPOSTS item selection list
posts_admitemadcPOSTS item add, delete, change
posts_admlistobjsPOSTS object selection list
posts_admobjadcPOSTS object add, delete, change
posts_admviewobjsSpecial version to allow administrator to view an object page
posts_viewcatsMain entry for POSTS user viewing
posts_viewitemsUser selection list of items in a category
posts_viewobjsUser object display page.. note same slot in three locations
posts_downloadUser download of attached POSTS file
posts_showpicturePopup to show full size picture from POSTS image
posts_admitemapvSet approved flag for an item
posts_admobjapvSet approved flag for an object
posts_admsendSend (test or full) Newsletter item
posts_admsendconfAsk for confirmation before sending a newsletter
posts_statscatNewsletter statistics: list of categories
posts_statsitemsNewsletter statistics: list of newsletters in a category
posts_statsnewslNewsletter statistics: statistics for a single newsletter
posts_viewAllow to view a newsletter image updating the statistics

Slots in module posts

Table 10.14. Slots in module posts

NameDescription
posts/admlistcatsMain entry for posts administration, displaying a list of all categories
posts/admcatadcAdd, delete, or change information about a post category
posts/admlistitemsDisplay a list of all items in a category and links to changes.
posts/admitemadcAdd, delete, or change information about a post item
posts/admlistobjDisplay a list of all objects in an item and links to changes.
posts/admobjadcAdd, delete, or change information about a post object
posts/viewcatsDisplays a list of all categories
posts/viewitemsDisplay a list of all items in a category
posts/viewobjsDisplay objects for an item in the slot requested. What is shown depends on what type of object it is
posts/downloadDownload an attach item file to the user
posts/showpictureDisplay the full size picture from a photo object in a new window
posts/admitemapvSet the approved flag for a particular item and return to admlistitems
posts/admobjapvSet the approved flag for a particular object and return to admlistobjs
posts/admsendSend either the test or full newsletter

Pages in module dev

Module dev, Author(s) Stefano Locati

Tools for developers

Table 10.15. Pages in module dev

NameDescription
dev_docProject documentation
dev_dbdocGenerate Database documentation from Metabase XML common configuration
dev_langsearchSearch in compendium and system messages
dev_dumpdbShow DB table information, create config/*.tpl files, dump a table to screen
dev_resettableReset table with default data
dev_extramsgWrite a PHP file with database messages to be translated
dev_translistTranslation work area (experimental)
dev_transsaveSave translations to file (experimental)
dev_rndtransTranslate a sentence (experimental)
dev_localdataView local field descriptions
dev_mergetableMerge tables step 1
dev_mergetable2Merge tables step 2
dev_sequenceCheck database sequences
dev_uploadpoLoad PO translation files
dev_checkdataCheck database consistency
dev_checkdbCheck database structure
dev_componentsWrite components chapter for the manual
dev_langencoderEncode multiple translations in one string

Slots in module dev

Table 10.16. Slots in module dev

NameDescription
dev/dbdocGenerate Database documentation from Metabase XML common configuration
dev/extramsgWrite a PHP file with database messages to be translated
dev/translistTranslation work area (experimental)
dev/docProject documentation
dev/resettableReset table with default data
dev/transsaveSave translations to file (experimental)
dev/rndtransTranslate a random sentence
dev/localdataView local field descriptions
dev/indexSide navigation menu
dev/dumpdbShow DB table information, create config/*.tpl files, dump a table to screen
dev/langencoderEncode multiple translations in one string

Pages in module prj

Module prj, Author(s) Nicholas Vrtis

Project management

Table 10.17. Pages in module prj

NameDescription
prj_activityadcManage activity types
prj_mandatoryadcManage mandatory phases
prj_useradmUsers administration
prj_usermodEdit user info
prj_addbid1Start a new project
prj_basicacBasic information
prj_phaseacPhase information
prj_supplieracSupplier information
prj_costacCost information
prj_custbidsProject list for an organization

Slots in module prj

Table 10.18. Slots in module prj

NameDescription

Chapter 11. Customizing the graphic

One of the most common questions I am asked from people approaching to Obliquid, is how deeply the graphic may be customized. In fact, if you build your own theme, it's possible to compose your page in a custom way. Once you are satisfied with your own theme you may want to lock the site with your theme by disabling core/changetheme slot.

What are themes?

Themes are plug-in skins that allow to change the appearance of a web site. To achieve this result some rules must be followed in order to create a theme that can be interchanged with others.

In practice a theme is composed of a few Smarty templates, a stylesheet and images.

Obliquid template system

Obliquid makes extensive use of templates. Templates are a way to separate HTML from PHP code and are designed to look as much like regular HTML as possible. The HTML code is displayed by the browser normally, while some special codes are substituted by the Smarty template engine. The most simple code is the one for variable substitution. A variable inside a Smarty template is a placeholder that looks like this {{$variable}}. From a PHP script it's possible to substitute the variable with any value, including another template.

Obliquid core implements a three level template system. Three levels means that templates are substituted inside templates which in turn are substituted inside the top level templates. The top level template is called frame level and corresponds to the outer layout of a served page. It's possible to have many different frames templates for different pages, or always reuse the same one for every page. The second level is the block level and is used to put some window like border to the elementary content. This level is not mandatory and may be bypassed. The third level is the slot level and is used to deliver the content and the functionality of a simple part of an application. A slot may or may not have an associated PHP script that delivers dynamic functionality.

The frame outer template

The frame level corresponds to the outer layout of the served page. The frame is a template and may be manipulated directly using the handle $_obweb->smframe (created for you by obliquid core). The {{$page_title}} variable can be setted through configuration parameters. {{$metadata}} variable can be used to add any content in the head section, tipically metadata tags.

A frame normally contains other variables. In a custom approach you can choose whatever name you want for them. Using a theme-compatible approach the variables are called {{$left1}}, {{$center1}}, {{$right1}}, {{left2}}, ..., {{$right6}}. Themes allows to have a one, two or three columns layout. It's also possible to have different sections with a different number of columns. You can have up to six sections.

For example we may put a logo on the left and a banner on the remaining space in the first section using {{$left1}} and {{$center1}}, in the second section we have a menu bar that takes all the width by using a {{$center2}}, in the third section we have only a date on the right using {{$right3}}, in the fourth section a three column layout with many {{$left4}}, {{$center4}} and {{$right4}} blocks that gets piled stacked from top to bottom, in the fifth section a copyright notice on the right using {{$right5}}. We decide not to use the sixth section.

            {{$metadata}}
            {{$page_title}}

      +-------------+--------------+--------------+
      | {{$left1}}  | {{$center1}} | {{$right1}}  |
      +-------------+--------------+--------------+
      | {{$left2}}  | {{$center2}} | {{$right2}}  |
      +-------------+--------------+--------------+
      | {{$left3}}  | {{$center3}} | {{$right3}}  |
      +-------------+--------------+--------------+
      | {{$left4}}  | {{$center4}} | {{$right4}}  |
      +-------------+--------------+--------------+
      | {{$left5}}  | {{$center5}} | {{$right5}}  |
      +-------------+--------------+--------------+
      | {{$left6}}  | {{$center6}} | {{$right6}}  |
      +-------------+--------------+--------------+

          A frame conceptual schema. 

Block template

A block is the second level of our three level template system, it's used to put windows like decorations to the bare content. For each single slot (the inner content), is possible to decide if to use an outer block or not. It's also possible to choose between different styles of block. Blocks are templates and they may be manipulated directly using the handle $_obweb->smblock (created for you by obliquid core). We can choose to have a strongly emphasized titled window (title_em), a simple titled window (title), a bordered window with no title (border) or no decorations (none). Any of these types, except none corresponds to a filename in the template directory.

To summarize a bit in a theme approach is mandatory to have a frame.tpl file for the outer frame and title_em.tpl, title.tpl, border.tpl for the blocks. In a custom approach, the block level may be skipped (using none), or may be used with custom theme filenames or by introducing new block files.

title_em.tpl and title.tpl have a mandatory title and a content section, and may have a caption and a button section. The button section is for adding some small graphic buttons for extra functions like an help button. The slot is automatically associated to the content section. Other sections have to be manipulated manually from the slot PHP code.

+--------------------------------+
| {{$title}}         {{$button}} |
+--------------------------------+
| {{$content}}                   |
+--------------------------------+
| {{$caption}}                   |
+--------------------------------+

Slot template

At the third and last level of the templates system there are slots. Slots are templates and they may be manipulated directly using the handle $_obweb->smslot (created for you by obliquid core). Their content and structure is absolutely custom.

CSS Styles

To implement a theme CSS, the best thing is to modify a CSS for an existing approved theme. The public styles are the following:

pagebody: to be inserted in the body tag

titlebig: big titles
titlestandard: standard titles
titlemini: small titles or subtitles

textstandard: normal text
textmini: smaller text
textform: form elements

tablemain: to be inserted in the table tags
tableheader: to be inserted in td tags holding a title
tablebody: to be inserted in td tags holding content

passed: text to notify a successful event
failed: text to notify a failed event

enabled:  text to convey that something is enabled
disabled: text to convey that something is disabled

picture: to be inserted in img tags

Creating your own custom theme

If you understood the description about themes in the previous sections, you are now ready to create your own. Don't be afraid to experiment, and if you reach noticeable results feel free to contact the Obliquid team.

Copy one of the existing themes in common/theme directory, inside local/theme directory. To avoid yourself problems, choose a directory name composed only by lowercase a-z letters and the underscore. For example I create a directory named floating inside local/theme, then I choose a theme to start from, for example obliquid because it's simple and so I copy content from common/theme/obliquid to local/theme/floating.

The first file to edit is frame.tpl, as explained before it's the outer layout of a page. The first thing to change here is the stylesheet. Instead of pointing to common/theme/obliquid/style.css, make it point to local/theme/floating/style.css.


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>{{$page_title}}</title>
<link rel="stylesheet" type="text/css" href="common/theme/obliquid/style.css">
<script language="JavaScript">
<!--
function popupErr(err_msg) {

From here make your changes to any file in this directory. Pay attention to path pointing to your starting theme, make sure to fix them all.

The best thing is to test your changes early, so you have to activate your custom theme. To do so click on Configure site and then on Manage theme. There you can add a new theme by specifying the theme name, the relative path, the order in which it has to appear in the list, and if it has to be enabled or not.

Extending Obliquid

Chapter 12. Basic concepts

Introduction

What you need to know to be able to modify Obliquid proficiently. This documentation is still a work in progress. In case anything is not explained clearly or is not explained at all, ask to the forum.

Global environment

We tried to minimize the number of variables in the global environment. Some of the global variables are provided through $_obweb, an instance of obliquidweb class generated by obliquid core.

  • $_obweb->tprefix prefix string for all table names. Default: "sl_"
  • $_obweb->mb Metabase database object
  • $_obweb->smframe Smarty template for the frame
  • $_obweb->smblock Smarty template for the block (slot decoration)
  • $_obweb->smslot Smarty template for the slot

Other variables are instead available through $_SESSION, the standard PHP array with session data

  • $_SESSION["_obtheme"] current theme (ex: common/theme/rounded)
  • $_SESSION["_obthemename"] name of current theme (ex: rounded)
  • $_SESSION["_oblocale"] current locale (ex: en_GB)
  • $_SESSION["_oblang"] current language (ex: en)
  • $_SESSION["_obshortdate"] Short date in PHP format. see date() for how to format. Call Locale::shortDate($isodate) to format a date (since 0.7.0).
  • $_SESSION["_oblongdate"] Long date in PHP format. see date() for how to format. Call Locale::longDate($isodate) to format a date (since 0.7.0)
  • $_SESSION["_oberr"] an error message to show
  • $_SESSION["_obsortby"] sorts users by this fields (ex: surname)
  • $_SESSION["_obfgroup"] shows only users belonging to this id group
  • $_SESSION["_obcountry"] shows only users belonging to this country
  • $_SESSION["_obprovincia"] shows only users belonging to this provincia (Italian district)
  • $_SESSION["_obsearch"] free text search filter (on name, surname, organization and email)
  • $_SESSION["_obperson"] associative array with login info
  • $_SESSION["_obperson"]["id_person"] if it's 0 or not set the user is not logged in, otherwise it contains the user id
  • $_SESSION["_obperson"]["display"] info to display of the logged person (ex. surname and name)
  • $_SESSION["_obsecurity"] index array of security objects this user has access to
  • $_SESSION["_obfcal"] index array of calendar event types to be shown to filter the displayed events
  • $_SESSION["_obdochome"] array of search conditions from the previous document search selection
  • $_SESSION["_obprjsearch"] array of search conditions from the previous project search selection

Database access

Obliquid uses Metabase as database astraction layer. Metabase is better than many other database access layers, because it manages the database structure with XML files and is able to talk to many different vendors DBMS.

Despite this, Obliquid supports only MySQL, and even if the database structure could be easily installed on other DBMSs, the queries will simply not work. Sometimes in the future the team will probably decide to support a new database too. This will mean to check every query, and will also make releasing upgrade packages more difficult: that's why it will probably happen after version 1.0 is released.

A Metabase instance is already created for you by Obliquid framework in $_obweb->mb. Metabase has extensive documentation and a tutorial. Below you will find some examples for the most used operations.

Example 12.1. Metabase Query method

In this example, we are deleting a row from news_ctext, specified by cat GET parameter. The method Query is used because we don't have to fetch any result from this query. sprintf is used because a GET parameter can be easily manipulated by a user to contain anything.
   $sql="DELETE FROM ".$_obweb->tprefix
      .sprintf("news_ctext WHERE id_news_cat=%d", $_GET["cat"]);
   $success=$_obweb->mb->Query($sql);
   if (!$success) echo $_obweb->mb->Error();
   

Example 12.2. Metabase GetSequenceNextValue method

In this example, we call GetTextFieldValue method manually to prepare the text values. Then we check wether catid is set to new or to a numeric value. If catid is new we do an INSERT. To find the new id to be inserted, we call the method GetSequenceNextValue: the new id will be in $catid variable. For this method to work a sequence has to be defined on the table.
    //Make sure all text is fit to be inserted
    $lang = $_obweb->mb->GetTextFieldValue($_POST["lang"]);
    $title = $_obweb->mb->GetTextFieldValue(htmlspecialchars($_POST["title"]));
    $desc = $_obweb->mb->GetTextFieldValue(htmlspecialchars($_POST["desc"]));
    $rstd = $_obweb->mb->GetTextFieldValue($_POST["rstd"]);
    $aa = $_obweb->mb->GetTextFieldValue($_POST["aa"]);
    $ob = $_obweb->mb->GetTextFieldValue($_POST["ob"]);

    if ($_POST["catid"]=="new") {       //Finally.. see if new or update
        $_obweb->mb->GetSequenceNextValue($_obweb->tprefix."posts_cat", $catid);
        $sql = sprintf("INSERT INTO %sposts_cat VALUES (%d, %s, %s, %s, %s, %s, %s, '', %d)",
            $_obweb->tprefix, $catid, $lang, $title, $desc, $rstd, $aa, $ob, $ord);
    } else {
        $catid = $_POST["catid"];
        $sql = sprintf("UPDATE %sposts_cat SET lang=%s, title=%s, description=%s, restricted=%s, autoapprove=%s, ordby=%s, ord=%d WHERE id_posts_cat=%d",
            $_obweb->tprefix, $lang, $title, $desc, $rstd, $aa, $ob, $ord, $catid);
    }
    $_obweb->mb->Query($sql);
    

Example 12.3. Metabase QueryField method

Here we find the number of unsent events. This is a simple select query with just a field to retrieve, so we use the method QueryField. Variable $total will be filled with the query result.
    //number of total unsent events
    $sql="SELECT count(*) FROM ".$_obweb->tprefix."msg_queue WHERE sent='N'";
    $success=$_obweb->mb->QueryField($sql, $total);
    if (!$success) echo $_obweb->mb->Error();
    

Example 12.4. Metabase QueryRow method

Most of the times a query will fetch more then a field at a time. We can use QueryRow when only a row of results is expected.
    //Get the title and description of the category with this language
    $sql="SELECT title, body FROM ".$_obweb->tprefix
      .sprintf("news_ctext WHERE id_news_cat=%d AND lang = '%s'",
      $_GET["cat"], $catlang);
    $_obweb->mb->QueryRow($sql, $catinfo);
    //$catinfo[0] contains the title, and $catinfo[1] the description
  

Example 12.5. Metabase QueryAll method

This query gets a list of post objects. QueryAll method gets the whole result set in a matrix stored in $objs variable.
    //Get the list of objects
    $sql = "SELECT id_posts_object, object_name, object_type, ord, location, approved FROM "
        .$_obweb->tprefix."posts_object WHERE id_posts_item='".$_GET["id"]
        ."' ORDER BY location, ord";
    $_obweb->mb->QueryAll($sql, $objs);
   
This variable is then passed to a Smarty template that displays the results.

{{foreach from=$objs item=obj}}
<tr>
{{section loop=$obj name=i start=1}}
  <td class="tablebody">{{$obj[i]}}</td>
{{/section}}
</tr>
{{/foreach}}

$_obweb->mb->GetBooleanFieldValue(true) Convert a boolean value into a DBMS specific format that is suitable to compose query statements.

[Caution]Don't use your own database connections

They will make your application a lot harder to maintain, and in core Obliquid programming it's wrong

First step with forms

HTML form building may pose problems for unesperienced people because there are many little and subtle things to know; even experienced people may find confortable to have a more consistent interface. Since long ago I've trying to build a class that would help that process. The idea actually came from a guy that was working for us at the time. Nothing new, there are many classes to build forms, but not even one in the way I wanted, at the time.

My form class had to help to build form controls, to retrieve their value and to build database queries to give the best efficiency and to reduce bugs. It started to support MySQL, then we had to do a job with Oracle and so we expanded it to handle Oracle too. After starting the free Obliquid project I switched to Metabase to be able to support different databases without having to write special code for any of them.

The most basic way to use the db_form class is without the database: even if this is a rare case it will help to grasp the concepts in a first step. I usually form controls in a form array that I call $frm that I then pass to the Smarty template. In this example I simply build a form with the available controls.

getTextInput method returns a text box input, the first parameter name is the html variable name that will be stored in $_POST["name"] after submitting the form. All other parameters are optional, but typically the SIZE would be specified, as the CSS class "textform". Having a common CSS class helps: if we want to change the font sizes for forms in a theme we can do by modifying a single value.

getTextArea method returns a text area. In HTML forms the basic syntax of a textArea is different from the syntax of a text box, while in db_form such difference disappear. The first parameter of the method is, as usual, the variable name, description in this case; after this there is an optional array of additional parameters.

getDateInput helps to input dates: as always the first parameter is the variable name, in this case sdate and the other parameters are optional, the start year is the first optional parameter 1998 in our example, which is also the default, the second parameter is the end year, by default it's the current year, but here we defined it to be two years later date("Y")+2, as last parameter the textform style is applied.

getCheckbox returns a HTML checkbox. As always the first parameter is the variable name ( restricted ) and it's the only one used in this example.

getRadio returns a radio button, the first parameter is th variable name, in this case recur. As you can notice, there are two radio buttons with the same name, and it means they are mutually exclusive.

getSubmit returns a submit button: the first parameter is the variable name, which value is usually not considered, since it's its label.

  PHP excerpt
  
  $dbf =& new db_form();
  $frm["name"]=$dbf->getTextInput("name",
      array("CLASS" => "textform", "SIZE" => "25"));
  $frm["description"]=$dbf->getTextArea("description",
      array("CLASS" => "textform", "ROWS" => "3", "COLS" => "45"));
  $frm["moddate"]=$dbf->getDateInput("moddate", 1998, date("Y")+2,
      array("CLASS" => "textform"));
  //radio buttons must start with a default, for the html specification
  $frm["author_no"]=$dbf->getRadio("author", "");
  $frm["author_st"]=$dbf->getRadio("author", "Stefano");
  $frm["author_ni"]=$dbf->getRadio("author", "Nick");
  $frm["enable"]=$dbf->getCheckbox("enable");
  $frm["version"]=$dbf->getSelect("version", array(array("1.0", "stable"),
      array("beta", "beta"),
      array("alpha", "alpha")
    ));
  $frm["submit"]=$dbf->getSubmit("submit", _l("Save"), array("CLASS" => "textform"))
      .$dbf->getHidden("action", "exampleform");

  $_obweb->smslot->assign(array("frm" => $frm));
  
  
  Smarty template
  
  <form method="post" NAME="exampleform">
    Module name: {{$frm.name}}<br><br>
    Description: {{$frm.description}}<br><br>
    Last modified: {{$frm.moddate}}<br><br>
    Author: Not chosen {{$frm.author_no}} &nbsp;
    Stefano {{$frm.author_st}} &nbsp;
    Nick {{$frm.author_ni}}<br><br>
    Enabled: {{$frm.enable}}<br><br>
    Version: {{$frm.version}}<br><br>
    {{$frm.submit}}
  </form>
  
  

Fetching form results

In this section I will explain how to retrieve values from the form. This values may be stored in database, or they may go through validation checking. I will keep on extending on the previous example, so we introduce concepts bit by bit, the code added is emphasised.

Form values can be read with the method getValue of the class db_form, they also can be read with $_POST[] php variable, but some of them are more confortably fetched by the method because of added fancy processing. This is true for dates and checkboxes

An example output, echo are used here for testing purposes. Normally echo are not used inside Obliquid because any result is assigned to Smarty templates, but they are a convenient way to test something fast.

test | test
meaningless words | meaningless words
2002-04-26 | dateinput
Nick | Nick
0 |
beta | beta
  
  $dbf =& new db_form();
  
  if ($_POST["action"]=="exampleform") {
      //we are here, so the form was posted
      $dbf->parseForm(); //parse the form and reassign it back to controls
      //any variable can be accessed with $dbf->getValue()
      echo $dbf->getValue("name")." | ".$_POST["name"]."<br>";
      echo $dbf->getValue("description")." | ".$_POST["description"]."<br>";
      echo $dbf->getValue("moddate")." | ".$_POST["moddate"]."<br>";
      echo $dbf->getValue("author")." | ".$_POST["author"]."<br>";
      echo $dbf->getValue("enable")." | ".$_POST["enable"]."<br>";
      echo $dbf->getValue("version")." | ".$_POST["version"]."<br>";
  }
  
  $frm["name"]=$dbf->getTextInput("name",
      array("CLASS" => "textform", "SIZE" => "25"));
  $frm["description"]=$dbf->getTextArea("description",
      array("CLASS" => "textform", "ROWS" => "3", "COLS" => "45"));
  $frm["moddate"]=$dbf->getDateInput("moddate", 1998, date("Y")+2,
      array("CLASS" => "textform"));
  //radio buttons must start with a default, for the html specification
  $frm["author_no"]=$dbf->getRadio("author", "");
  $frm["author_st"]=$dbf->getRadio("author", "Stefano");
  $frm["author_ni"]=$dbf->getRadio("author", "Nick");
  $frm["enable"]=$dbf->getCheckbox("enable");
  $frm["version"]=$dbf->getSelect("version", array(array("1.0", "stable"),
      array("beta", "beta"),
      array("alpha", "alpha")
    ));
  $frm["submit"]=$dbf->getSubmit("submit", _l("Save"), array("CLASS" => "textform"))
      .$dbf->getHidden("action", "exampleform");

  $_obweb->smslot->assign(array("frm" => $frm));
  

Uploading files with db_form

db_form can also handle uploaded files. The first thing to remember is that the form METHOD should be “POST” and that the ENCTYPE should be “multipart/form-data”.

  
  <form enctype="multipart/form-data" method="post">
  {{$frm.image}}
  {{$frm.submit}}
  </form>
  
  
  
  if ($_POST["action"]=="exampleupload") {
      $dbf->parseForm();
      //the following lines are here to document the available values
      $original_name=$dbf->getFormValue("image");
      $tmp_name=$dbf->getFormValue("image_tmp_name");
      $size=$dbf->getFormValue("image_size");
      $mime=$dbf->getFormValue("image_mime");
      //test if a file was uploaded or not
      if ($dbf->is_uploaded_file("image")) {
          move_uploaded_file($tmp_name, $final_destination);
      }
  }
  $dbf =& new db_form();
  $frm["image"]=$dbf->getFileInput("image", array("CLASS" => "textform"));
  $frm["submit"]=$dbf->getSubmit("submit", _l("Save"), array("CLASS" => "textform"))
      .$dbf->getHidden("action", "exampleupload");

  $_obweb->smslot->assign(array("frm" => $frm));
  
  

Available since Obliquid 0.7

Building queries with db_form

db_form has also the ability to help with simple query creation. In order to do so, it must know the structure of a table that is stored inside tabledesc Obliquid table. Before switching to Metabase db_form class was fairly complex, in order to handle many db datatypes, and when a datatype not used before was found it stopped to work. Now these problems are solved because Metabase has just a few simple datatypes.

Further in order to support the creation of the next value a sequence must exist on the table. A sequence is a small table, added to Metabase XML table definition file. For example:

    <sequence>
        <name>{{$tprefix}}module</name>
        <start>1</start>
        <on> <table>{{$tprefix}}module</table>
             <field>id_module</field>
        </on>
    </sequence>
  
  

To build an insert query you need to create a new Id and call executeSqlInsert method.

  $dbf =& new db_form();
  if ($_POST["action"]=="add_record") {
      //the form was posted for insert
      $dbf->setDbConnection($_obweb->mb);
      $dbf->setTable("module", $_obweb->tprefix); //setTable clears form values!
      $dbf->parseForm(); //parse the form and reassign it back to controls
      $dbf->newId();     //creates a new id
      $dbf->executeSqlInsert(); //executes an insert query
  }
  //here the form will be built as explained in the previous sections
  

To build an update query you need to fetch the existing record from database with fetch method, and then call executeSqlUpdate method.

  $dbf =& new db_form();
  $dbf->setDbConnection($_obweb->mb);
  $dbf->setTable("module", $_obweb->tprefix); //setTable clears form values!
  //fetches the current record for the update or the form below
  $dbf->fetch($_GET["id"]);
  if ($_POST["action"]=="mod_record") {
      //the form was posted for update
      $dbf->parseForm(); //parse the form and reassign it back to controls
      $dbf->executeSqlUpdate(); //executes an insert query
  }
  //here the form will be built as explained in the previous sections
  

This part needs some more explanations: the fetch method stores the record in internal private variables. Just after a fetch, I can get the value for author with $dbf->getValue("author"), but after a parseForm, the same instruction will return the value coming from the form. The reason for this apparently strange behaviour is that form values have higher priority, and this is a design feature of db_form. In this way if any of the column existing in the table is not added in the form it will be preserved, because when a form value is not found, the db value will be returned instead.

In some cases you need to explicitly refer to form values or db_values, this is possible with the methods getFormValue (or using PHP $_POST array) and with getDbValue.

Dynamic web pages

Every web page calls index.php file in the main directory. This is because this script sets up the environment, opens a connection to the db, includes classes that will be always used and so on. Besides this it implements the main processing loop assembling a page from its slots, and provides basic security.

Pages are assembled by their slot pieces, as defined in pagexml directories. Since Obliquid 0.6.0, there are two such directories, one in the common directory tree and one in the local directory tree: so we have common/pagexml and local/pagexml. Pages can be defined both in the common tree or in the local tree, but when a page with the same name is found in both the common and the local tree, the local tree takes precedence; in fact this is the way to redefine (object oriented programmers may like the term overload) an existing page to make your own changes. Using the site configuration interface, a page will in a locked state, with no changes allowed until you push the button Copy to local to modify. By pushing this button, a page definition will be copied from common/pagexml to local/pagexml, for example the home page would be copied from common/pagexml/core/home.xml to local/pagexml/core/home.xml.

Let's look in more detail to the directory structure inside a pagexml directory first. There we can find a list of directories, one directory for each module. There is a simple rule for naming a page: <module name>_<short name>. For example the page named news_item is from module news and has item as short name. This page will be saved either in common/pagexml/news/item.xml or in local/pageml/news/item.xml. The only exception to this rule is for the page named home, that is part of module core, but doesn't require the core_ prefix.

Each page definition is a little XML that explains how the page is going to be built starting from the slot bricks. The format is not complicated, but there is no need to know it, since the Configure site function will change these files for you.

If you are curious, or want to know more about Oliquid, I give some basic explanations here. The first line in this example is the XML definition line and must always exist. The second line has a PAGE element: the NAME attribute of this element defines the page name, and should match the name coming from the file path (news/item.xml -> news_item). The relative url for this page is '/index.php?page=news_item'. Pages names are used in the url, and stored in XML, and for this reason it's advised to stick to a conservative set of characters (lowercase letters a-z, and underscore _).


<?xml version="1.0" encoding="UTF-8"?>
<PAGE NAME="news_item" TPL="common" FRAME="frame" GROUP="news">
  <SLOT NAME="core/nav" PHP="common" TPL="common" POSITION="center1" BLOCK="none"/>
  <SLOT NAME="core/changelang" PHP="common" TPL="common" POSITION="left2" BLOCK="title"/>
  <SLOT NAME="news/item" PHP="common" TPL="common" POSITION="center2" BLOCK="title_em"/>
  <SLOT NAME="news/viewitem" PHP="common" TPL="common" POSITION="center2" BLOCK="title"/>
</PAGE>

[Note]Previous format, valid until Obliquid 0.5.0 included

Pages were defined in 'local/configs/config.xml' in a single XML file. This file had a PAGE element for each page type, and was not that easy to upgrade, because if modified, either by hand, or with Obliquid configuration, an upgrade needed the tedius task to find differencies manually between the new config.xml file and the existing one. Another concern that has been addressed with the new syntax is about the size of the file (reaching 600 lines), which made XML dom parsing slow. The syntax of the PAGE element has not been modified (Obliquid 0.6.0).

Slots, brick components to build a page

Every page is composed of slots which are the elementary blocks. The slots that a page type includes are defined in pagexml in the SLOT tags as children of that PAGE tag.

Slot name is in relationship with its file name. In this example the NAME attribute has value core/logo and since TPL is common, the template for this slot will be looked in common/templates/core/logo.tpl. The directory has the same name as the module name, in this case core.

In this example there is no PHP script associated to this template (a simple static template), and thus PHP attribute is set to 'NONE'. The POSITION attribute specifies where the slot template should be shown in the frame as described in themes. The BLOCK attribute, instead defines the decoration to be applied to the slot, this is also described in the themes document.

<SLOT NAME="core/logo" TPL="common" PHP="none" POSITION="left2" BLOCK="title"/>

In this second example there is also a PHP script associated to the template since PHP attribute value is common. This means that a PHP script located in common/pages/core/nav.php will be executed and will be able to address the common/templates/core/nav.tpl template.

<SLOT NAME="core/nav" PHP="common" TPL="common" POSITION="center1" BLOCK="none"/>

This slot defines a system wide menu that changes dinamically according to the permissions of the logged user. It is reasonable to change this menu from site to site to insert new local functionalities or the like. To accomplish this result I could modify the common template and php file but this would not be advisable because an upgrade would overwrite the common files. Moreover we would like to see easily which modifications we made to implement a particular site and this approach would make this more difficult.

If we want to simply change the table in the php code, for adding a new icon, or for changing all of them we can define the PHP attribute as local. In this case the script would be loaded from local/pages/core/nav.php.

<SLOT NAME="core/nav" PHP="local" TPL="common" POSITION="center1" BLOCK="none"/>

In other case we may also want to alter the templates and we can do this by defining TPL attribute as local: in this case our smarty template would be loaded from local/templates/core/nav.tpl. Of course any combination of 'common', 'local' and 'none' for PHP and TPL is valid.

While it is a good rule to have a PHP script named in the same way as the template, in some particular cases you may also want to call a PHP script and a template with different names. This is possibile and can be achieved by using NAME_PHP and NAME_TPL attributes instead of NAME.

A slot script should only define functions. It should not execute any code when it is loaded. This will allow the system to check the security authorization of the user before any slot code is executed. The default function that the system will call after the code is loaded is the name of the slot, with the slash replaced by an underscore, and an underscore added to the end. As an example, the system will call core_nav_() as the main function for the core/nav slot.

You can also define a different function to be called for a slot. Use the FUNCTION parameter in the SLOT definition.

Each slot is loaded with a require_once statement. So it will be loaded only once, no matter how many times the slot is defined in a page. The defined function will be called for each slot entry though.

Writing a PHP script

The simple approach would be to simply write the script in the global scope. Unfortunately this can bring to variable names clashing. If the same variable name is used by two slots in the same page, the second slot would see the variable of the first slot. This may cause misterious bugs and so this approach is not allowed by Obliquid security system.

    <?php
      ...
      $_obweb->smslot->assign("templatevar", $templatevar);
    ?>

Generally slot script are short: this is an advantage of this architecture that divides the page in its elements. Even if a slot has to deliver complicated functionality it is better to put this in a separate class and keep the script short.

    <?php
    function core_nav_() {
        global $_obweb;
        ...
        $_obweb->smslot->assign("templatevar", $templatevar);
    }

The function name must be modulename_slotname_() for Obliquid to be able to call it. Also notice that there is no PHP end tag, to avoid any headers already sent problem, caused by extra blank lines added by mistake after the closing tag.

Writing a Smarty template

Each slot PHP script may refer to a Smarty template instance for the slot as $_obweb->smslot, to the block as $_obweb->smblock, to the frame as $_obweb->smframe. Smarty is extensively documented on smarty.php.net.

Creating a new local module

[Warning]The following information will be outdated soon

In Obliquid CVS a new page to create a new local module has been added

To create a new module as a local module add a row to the module table: module(id_module, name, description, version, moddate, author, authoremail, authorsite, supportemail, copyright, license, ord, enable)

As you can see module has many fields but you can just insert id_module, name and enable for a test.

    INSERT INTO sl_module (id_module, name, is_common, enable)
    VALUES (1001, 'test', 'N', 'Y')
[Warning]Choose an Id over 1000

You have to choose an Id over 1000, because every upgrade could delete and re-insert common modules, with lower Ids.

It is strongly advised that the module name contains only lowercase a-z letters plus the underscore. Pages for the modules will be stored in local/pages/test directory and templates in local/templates/test directory.

Adding columns to an Obliquid table

When customizing it may be necessary to store additional information in an existing table. You could just add columns to that table and use them in your scripts, and that would be enough, but you won't be able to use those columns through db_form class. Further, some tables, like the person table have a special configuration in the administration, and the new columns won't appear.

I explain the process of adding a column through an example: I want to add a column "eyes" to the person table to store the color of the eyes of a person.

I add the column to the person table.

ALTER TABLE sl_person ADD eyes VARCHAR(32) NOT NULL;

I then add the column description to the tabledesc table. The column type is one of the supported Metabase column types.

INSERT INTO sl_tabledesc (id_tabledesc, tablename, field, type, length, is_primary)
VALUES (100001, 'person', 'eyes', 'text', 32, 'N');

The third and last step is to add rows in slotfield table. This is needed only for the person table at the moment, and can be done through the slot administration.

[Warning]Choose a high Id for local records

You have to choose a high Id, because every upgrade will delete and re-insert tabledesc and slotfields rows, with lower Ids, but will preserve those with higher ones. Even Reset table with default data will preserve local data following this rule, even if reloading a table, provided that an Id over 100.000 is used.

Chapter 13. Permission system

Introduction

Obliquid has a very flexible permission (security) system. A security object can be assigned to any number of users or a groups (or combination). It is recommended that only groups be used in order to simplify the administration of the system. Currently there are four types of security objects defined. These are mainly for convenience and formatting. Once an object is defined, it can be used anywhere. The four types are:

  • Navigation Pages : these objects are checked by core/nav to see which icons should be displayed in the navigation slot
  • Slot : these objects are checked by the system as part of loading a slot. If the user does not have permission to the slot function, then the code is not loaded, and the slot is not displayed.
  • Operation : these are general purpose objects that can be created by the programmer for unique situations.
  • Table/Key : these are a combination of a table name (without the $tprefix) and a key from the table. It can be used to check that a user has permission to a specific record in a table.

[Note]Performance consideration

The system keeps the security information for a particular user in the $_SESSION array, so checking for permission is a relatively inexpensive operation.

Special groups

In addition to the real groups, there are also for special groups

  • logged on users : "g*" this is anybody who has logged in to Obliquid has permission to the security object
  • not signed in : "g0" these are people who either do not have an ID, or have not logged in yet (guest users)
  • everybody : "**" both regular users and guests have access to the security object
  • unassigned : "--" nobody has access to these security objects. This assignment might be useful to disable a section of code, or to define a security object in advance of the code actually being done.

Basic authorization

Basic authorization is built into the system, and is used for navigation and slots security objects.

The icons displayed by core/nav are the entry points into the major functional groups areas in the system. Authorization to these entry pages is automatically checked by core/nav, so the user only sees those entry points that they are authorized to. In a similar way, the slots module/index, displays only pages that the user is authorized to see.

In addition, authorization to each slot is determined as the slot is loaded. So, even if a user were to type in the page name into the URL, they would not be shown any of the slots that they were not authorized to.

Advanced autorization

The system does not do any further security authorization automatically. There are two ways to address this. One is to insert appropiate calls to the security check function ($_obweb->has($a, $b, $c)). This is generally the way that access to specific records within a database would be handled. See the NEWS modules and how they handle restricted categories for an example. The second method would be to create a separate function, and use that as a parameter in the SLOT definition. So, you could created functions news_show_all() and news_show_unrestricted(). Depending on which function was called, you could determine what to show.

For example, language configuration can be used by anybody who has modlang operation for module core. An user will have modlang operation if he belongs to a group that has this operation. At present there is no code written to assign a security object directly to a user. You would need to manually update the database table.

[Warning]Never check for users or groups

It's not a good idea to grant permissions checking for a specific user or group id, but you have to check if he has a given operation. Add a new one only if an appropriate operation doesn't exist.

Code examples

To check if a user has access to a security object, you need to use the has($a, $b, $c) function in Obliquidweb.

function slot_test_() {
   global $_obweb;
       
   //checks the modlang operation for the core module
   if (!$_obweb->has(1, "modlang","")) {
       /* no permission: take an appropriate action */
   }
}
[Tip]The appropriate action to take depends on the context

It may be redirect to another page, hide the slot or remove a functionality (for example don't show a delete button).

has() method accepts three parameters. Notice that currently the third option is not used, but is included for future expansion, and is part of the call. What is placed in each depends on what type of security object is being tested.

  • Navigation Page : $a="", $b="Page Name", $c=""
  • Slot : $a="Slot PHPName", $b="Slot Function", $c=""
  • Operation : $a="Module ID", $b="Operation name", $c=""
  • Table/Key : $a="Table Name", $b="Table Key ID", $c=""

Removing a functionality

Removing a functionality from a slot has often a dual impact: on html presentation and on php code. For example to remove a delete functionality, you've both to remove a delete button from html and not allow to execute the delete code section. It's very important to also not allow code execution because an expert user may post a forged form and delete what he/she's not allowed to delete.

Example PHP script:
<?php

function core_test_()
{
    global $_obweb;

    $user =& new user();
    /* showing delete language button depends on having
     * dellang operation (just an example, not a real operation)
     */
    $show["dellang"]=$user->has(1, "dellang");
    /* checking for dellang button push AND if it's allowed to push it */
    if ($_GET["action"]=="dellang" AND $show["dellang"]) {
        /* delete language code */
    }
    /* pass show array to Smarty slot template, so we can hide delete
     * button if needed
     */
    $_obweb->smslot->assign(array("show" => $show));
}
?>
Example HTML Smarty template:
{{if $show.dellang}}
<a href="index.php?page=core_test&action=delete">delete</a>
{{/if}}

Chapter 14. Built in facilities

Url manipulation

All urls defined into Obliquid must be relative. To improve further the quality and usability of single slots they can use the url class. This class is described in code documentation.

  $url =& new url();
  $url->addVal("action", "modlang");
  echo $url->Build();
  $url->Clear();
  $url->dropVals(array("action", "langpos"));
  $url->reload();
  

Internationalization

Internationalization is done with a GNU gettext approach, but without requiring PHP gettext extension. This approach is more comfortable than others for many reasons, but it is advised to keep messages as coherent as possible and to reuse _exactly_ the same message where appropriate. As nobody could have such strong memory there is a search function in the language support area where a programmer could look for reusable messages.

All the programmer should know is that every message to be internationalized in the core Obliquid should be in English and has to be passed to _l() function. On the other hand any internationalization occouring in local directory could be done with any language of choice as a base language. In any case, no message should be written directly into the template but always passed through templates variables from PHP script.

Translators will have to translate a text file in PO gettext format, like the one in this example: Italian common PO file found in common/locale/it_IT/obliquid_utf-8.po. The file has to be saved in UTF-8 format and it can be done with a text editor, by changing only the msgstr part of the file. There are also good tools that make the tranlation work easier, expecially when some of the messages were modified and a second translation has to occour. In this case all the message translations that didn't change won't be lost, but messages, even slightly changed needs to be fixed. Gettext tools make an attempt to find a similar message and suggest the translation in those cases, the message will be marked as fuzzy, that means that a human translator have to review and fix it.

Example PHP script

  //defines a few messages
  $msg["alert"]=_l("Pay attention please");
  $msg["other"]=_l("Just another message");

  //passes them to the slot template
  $_obweb->smslot->assign("msg", $msg);
Example Smarty template

<span class="titlebig">{{$msg.alert}}</span><br>
<span class="textstandard">{{$msg.other}}</span>

A free text editor that supports UTF-8 encoding is SciTE. This editor works both on Linux/Unix and on Windows Operative systems. I personally use KBabel that is a specialized tool for PO files, working only on Linux afaik. poedit works on both Windows and Linux and should do the job with PO files, but I didn't try it yet.

Help button

Every slot inside a block of type title or title_em may have a help icon, normally not shown. To add it you have to call $_obweb method addButtons(), as in the example. By clicking on this help icon, a popup help window will be displayed.

$_obweb->addButtons(array("help"));

This method has to be called inside slot code. For example if we want to add help to common/pages/core/conflang.php we have to call addButtons(). Obliquid will add the help icon for you and when somebody clicks on it, an help window will be shown. This window will be rendered by displaying common/core/templates/conflang_hlp.tpl and common/core/pages/conflang_hlp.php.

If the help button is called from php script in the local directory then the help window will be rendered by displaying local/core/templates/conflang_hlp.tpl and local/core/pages/conflang_hlp.php.

[Tip]Displaying more than a button at once

We can call addButtons with a list of button types: $_obweb->addButtons(array("print", "help"));

Print button

It's possibile to add a print icon to any slot: by clicking on the print icon the slot will be opened on its own in a new window, ready to be printed without any other slot. The new window will contain an additional GET parameter print=1 so the slot may adjust itself for print.

 $_obweb->addButtons(array("print"));

Source and template buttons

It's also possible to add popup windows to show the PHP source code and the Smarty template for a slot.

 $_obweb->addButtons(array("source", "template"));

Hiding a slot

It's also possible to hide a slot (for example when an user is not authorized to see a content or a functionality). To do this is enough to set $_obweb->showslot=false in the slot PHP script. It is strongly advised to return from the slot function after this otherwise the rest of the script would be executed anyway.

<?php
function core_test_()
{
    global $_obweb;

    if (some condition) {
        $_obweb->showslot=false;
        return;
    }
}
?>

Redirect to another page

You may need to redirect an user to a different page when he/she's not allowed to see the whole page, or after a form submission, to avoid browsers asking to resend form data when hitting the back button.

To do this, you've to call PHP header function, followed by exit(). Adding exit() is very important otherwise code after header() will be still executed.

  header("Location: index.php?page=home");
  exit();

Error messages

To display an error message as a javascript alert, call the function addErr(). If addErr() is called more than one time in the same page messages are appended, so it's advisable to end every message with a newline (\n).

$_obweb->addErr("Error message\n");

Error messages get automatically cleaned after display, but they are still remembered even after redirecting to another page. Even after redirecting in page mypage, our error message is still shown.

  $_obweb->addErr("You did a 'bad' thing\n");
  header("Location: index.php?page=mypage");
  exit();
 

Javascript confirms

To display a popup confirmation request as a javascript confirm, call the function confirm(). Calling the function will popup a javascript dialog box with OK and Cancel buttons. Pressing OK the browser will be redirected to a page with the same url of the previous page with 'confirm' GET parameter set to Y. Pressing Cancel 'confirm' GET parameter will be set to N.

Here is an example showing how the function can be used. The confirmation, in this example, is shown when a delobj GET parameter is found.

  if ($_GET["delobj"]) { //first time here: ask for confirm
      $_obweb->confirm(_l("Are you sure?"));
  }
  if ($_GET["delobj"] AND $_GET["confirm"]=="Y") {
      //second time and user confirmed: do your actions
  }
  if ($_GET["delobj"] AND $_GET["confirm"]) {
      //after second time user confirmed or refused
      $url =& new url(); //creates an url object
      $url->dropVals(array("confirm", "delobj")); //removes params from url
      $url->reload();  //reloads the page
  }

Onload Javascript code

You can execute arbitrary Javascript code when you move to a new page or open a popup. Since HTML body tag is in the theme frame.tpl, you have to pass to onload Smarty variable an appropriate Javascript command or commands, with rows terminated by a semicolon.

$_obweb->smframe->assign("onload", "alert('test');");

[Note]Generic onload XHTML syntax

The simplest way of calling a simple Javascript command is calling it directly in the onload tag.

<body onload="alert('hihi!')">

You can also call multiple commands separated by a semicolon.

<body onload="alert('hihi!');alert('how are you?')">

If your script is not just a simple command, it's better to call a separate function.

<body onload="myfunction()">

Javascript Popup windows

Use the javascript popup() function, defined by Obliquid framework inside html page frame of each theme.

  
  <a href="#" onclick="popup('index.php?page=home')">open</a>

It's also possible to change the default size of the popup.

  
  <a href="#" onclick="popup('index.php?page=home','width=200,height=100')">open</a>

How to choose a person with a popup window

If a site has less than a hundred users, it's viable to use a simple drop down menu to choose a person, but if a site has thousands of users, then it's not a good idea.

The solution for this problem is to have a javascript popup allowing to search a user that pass the search result to our main form. This is already implemented in Obliquid.

To add it to a form inside a slot, you have first to copy and paste the following javascript function at the beginning of your slot template.


 <script language="JavaScript">
 function OpenChoosePerson(value) {
 p="toolbar=no,location=no,directories=no,status=no,menubar=no,width=700,height=400"
 +",resizable=yes,scrollbars=yes";
 URLtoOpen="index.php?page=user_choose&value="+value;
 window.open(URLtoOpen,"choose",p);
 return false;
 }
 </script>

Then you've to make sure that your form is named "pform". This is very important because the form will be referenced by name from the popup.


   <form method="post" NAME="pform">

The next step is to add the following piece of html inside the slot template, where you want to make appear the control to choose a person.


     <a href="" onClick="javascript: return OpenChoosePerson(1);"
     class="textstandard"><span class="textstandard">{{$msg.select}} &raquo;</span></a>
     <input type="text" size="35" name="person1" value='{{$frm.person1}}'
     class="textform" disabled>
     <input type="text" size="5" name="id_person1" value='{{$frm.id_person1}}'
     class="textform">

This is enough to do the trick. Note that i used frm.person1 and frm.id_person1 Smarty variables to pass values to the form when in modify mode, but you should feel free to choose your own names. Remeber to close the form.


</form>

Multiple sites with a single database

Obliquid allows multiple sites to share a single database with the trick of table prefix. Setting up two sites in the same database, but with a different table prefix will make them two separate sites without any shared data. This concept is widespread and supported by many other applications.

Sometimes it's needed to run multiple sites with a single database, instead. For example we want to address different users and we need to have a different content and a different graphic style, but we want a single user base and a single administration. Users from different sites will normally belong to different groups, and in this way we can identify them.

The table configparms contains site-specific information like the site title or the site logo. To solve this problem I added an optional table suffix that will apply to configparms table only.

To use it, just two steps are needed. First change or add the following line to local/configs/base.php, second copy table configparms to a name with both prefix and suffix. Repeat these steps for all the sites that have to share the same database, and you are done.

/additional suffix for configparms table. default ""
$_obweb->tsuffix="_we";

Chapter 15. Site navigation facilities

Introduction to navigation facilities in Obliquid

There are two levels of navigation within Obliquid.

One is the typical icon navigation bar across the top. These icons typically represent the main entry points within the various modules in Obliquid. So, there are a "view posts", and an "administer posts" icons. All the icons are defined in an XML file common/navxml/nav.xml. Which icons actually are displayed to the current user depends on their permissions. The function 'getNav' in common/classes/obliquid/nav.php automatically checks permissions, and only returns those icons which the current user is allowed access to.

The second type of navigation is typically a set of text links that is on the left of the screen. These are normally used for navigation within a module, and are displayed by common/pages/mmmm/index.php. The contents of these links are also controlled in XML files in common/navxml/. The files are named mmmm.xml, where mmmm is the module name. Not all modules have an index.php and a corresponding text link navigation because there is typically only one entry point, and all the rest of the navigation is via pick lists from the main screen.

Contents of the XML navigation files

As mentioned in the introduction, the icons and text are controlled by XML navigation files contained in common/navxml/ and local/navxml/. These all have the same definition. The items in local/navxml/ override those in common/navxml. But, it is not necessary to copy all the parameters from common/navxml to local/navxml. Only those parameters in a particular NAV item need to be specified in local/navxml.

Here is a simple NAV.XML file which will serve as an example.

  
  <?xml version="1.0" encoding="UTF-8"?>
  <NAVOBJECTS>
   <NAV PAGE="home" ORD="10">
    <THEME NAME="*default*">
     <TEXT>Home</TEXT>
     <IMAGE>common/images/core/home.gif</IMAGE>
    </THEME>
   </NAV>
   <NAV PAGE="user_usermymod" ORD="140">
    <THEME NAME="*default*">
     <TEXT>My profile</TEXT>
     <IMAGE>common/images/user/profile.png</IMAGE>
     <PARMS>"&amp;id_person=".$_SESSION['_obperson']['id_person'];</PARMS>
    </THEME>
   </NAV>
   <NAV LINK="http://dev.obliquid.com" ORD="55">
    <THEME NAME="*default*">
     <TEXT>Obliquid Development Site</TEXT>
    </THEME>
   </NAV>
  </NAVOBJECTS>
   
   

NAV has three attributes.

  • LINK. This specifies a URL. Normally, this would point to an external URL. If no PAGE attribute is supplied along with LINK, then it is assumed that this URL is available to all users, whether logged on or just visiting. LINK should be a complete URL, suitable for use in an HREF.
  • PAGE. This specifies a "nav" security object within Obliquid. The permissions of the current user will automatically be checked, and this entry will NOT be returned if the current user does not have permission to this. If no LINK is specified, PAGE is considered an Obliquid page, and the necessary Obliquid prefix will be prepended so that the page will be displayed.
  • ORD. This specifies the order that the icon will appear. It MUST be specified in either the common or local file. If no ORD is supplied, then the item will not appear.

THEME has one attribute.

  • NAME. This specifies the theme name that the other entities apply to. If NAME="*default*" is used, then the entities will be used if no other information is supplied that applies to the theme that the current user is using. If no NAME is supplied, *default* is assumed.

THEME has three elements.

  • TEXT. This specifies the text that will be displayed as a popup text if an IMAGE is supplied. Otherwise it will be the text displayed in the link. Note that TEXT is translated into the current user language prior to being returned.
  • IMAGE. This specifies the path to the image which should be displayed. If no IMAGE is supplied, then the value *none* will be returned.
  • PARAM. This specifies any additional parameters that are to be included in the URL that is used. This is EVALed with the following PHP code eval("\$parms = $parms").

How to customize navigation entries

In order to override any of the attributes or entries, all you need to do is specify them in a file in local/navxml/ with the same name as the one that contains what you want to override (nav.xml, posts.xml, and such).

The overrides are processed in the following order.

  • common/navxml/, THEME="*default*"
  • local/navxml/, THEME="*default*"
  • common/navxml/, THEME="xxxx" (where xxxx=the theme the current user is using).
  • local/navxml/, THEME="xxxx" (where xxxx=the theme the current user is using).

Creating a navigation script

Actually creating a navigation script is a simple process. All the hard work is in creating the XML file. The function getNav("module") in common/classes/obliquid/nav.php returns an index array. Each entry in the array with three values. The array is in order by ORD.

  • [0] = the URL. This URL does not require any additional information to be used as an HREF. If it is an Obliquid URL, the index.php?page=... has already been added. And, any additional PARAMs have also been appended.
  • [1] = the TEXT. This is the translated text from the TEXT element.
  • [2] = the IMAGE path and name. If no IMAGE element was supplied, this will contain the string "*none*".

Here is what core/index.php looks like.

    
require_once "common/classes/obliquid/nav.php";

/** access structure for core module */
function core_index_()
{
    global $_obweb;

    $nav = & new nav();
    $corelinks = $nav->getNav("core");    //Get the array for the icons

    //Now assign them for the template, and we are done
    $_obweb->smslot->assign("corelinks", $corelinks);
}

  

And here is what index.tpl looks like.

    
{{* $Id: navigation.xml,v 1.6 2003/11/30 19:44:48 slocati Exp $ *}}
<table width="160">
{{foreach from=$corelinks item=alink}}
<tr><td>
{{if $alink[2] eq "*none*"}}
<a href="{{$alink[0]}}" class="textmini">{{$alink[1]}}</a>
{{else}}
<a href="{{$alink[0]}}" class="textmini">
<img src="{{$alink[2]}}" width="16" height="16" border="0">&nbsp;{{$alink[1]}}</a>
{{/if}}
</td></tr>
{{/foreach}}
</table>

  

Obliquid for the core team

Free software is a collaborative effort that brings better software. You may decide to join Obliquid team because you like programming, or to improve your programming skills with the help of other members. For an IT professional joining the team may be convenient to be able to influence future directions of Obliquid, to have the code tested by a broad user base, to receive help from others.

Chapter 16. Basic concepts

Joining the team

To be involved in the project the suggested way is to go to the forum, read a bit of the latest discussion and then introduce yourself and tell us what would you like to do, or simply offer help for things left behind.

Besides this we are maintaining a todo list in the 'Tasks' link at the top of this sourceforge forum. Once a person is part of the team he/she can take any of the unassigned tasks. Before doing that, expecially the first time, it is required to tell others about this on the forum.

What are we looking for:

  • Documentation editors: this manual is a first version, but has to be reviewed. Grammar or spelling may need to be fixed, or some unclear sentences to be improved or rewritten. We have to make sure that the overall organization is logical and there aren't missing pieces. Having a good manual is very important for the project to succed.
  • Translators: the list of supported language is increasing but it's still quite small, we need help here. Also even if your language is in the list it may need to be reviewed or completed. Being a translator doesn't require technical skills. Even if you just speak English you can help on this by improving messages.
  • Developers: both to implement new features, or to improve/fix existing ones. If you want to start a new module you will have much freedom on it.

CVS Access

Register yourself on sourceforge, the site providing tools and bandwith for open source projects, and tell us your username. CVS is the Versioning System that allows remote collaboration. If you need to edit directly source code or the manual, then you need CVS write access.

“ CVS (Concurrent Versions System) is a tool used by many software developers to manage changes within their source code tree. CVS provides the means to store not only the current version of a piece of source code, but a record of all changes (and who made those changes) that have occurred to that source code. Use of CVS is particularly common on projects with multiple developers, since CVS ensures changes made by one developer are not accidentally removed when another developer posts their changes to the source tree. ” -- from sourceforge help.

Fetching obliquid source code

export CVS_RSH=ssh
cvs -z3 -d:ext:developername@cvs.sourceforge.net:/cvsroot/obliquid co obliquid
Fetching the manual

export CVS_RSH=ssh
cvs -z3 -d:ext:developername@cvs.sourceforge.net:/cvsroot/obliquid co manual

Coding standards

A great effort was spent to write Obliquid in a tidy and organized way, so try to keep things tidy by following this coding standards, and by using common sense.

  • Indenting: use an indent of 4 spaces, with no tabs. Be sure that tabs are automatically expanded to four spaces in your editor. In Emacs do the following:
        (setq c-basic-indent 4)
        (setq tab-width 4)
        (setq indent-tabs-mode nil)
        
    In vim write the following into your .vimrc:
        set shiftwidth=4
        set expandtab
        set tabstop=4
        
  • File format: when committing from windows use a dos file format and when committing from unix use a unix file format. In vim write the following commands to convert from dos to unix file format:
        :set ff=unix
        :w
        
    To convert from unix to dos file format use :set ff=dos instead.
  • Control structures: control statements should have one space between the control keyword and opening parenthesis, to distinguish them from function calls. You are strongly encouraged to always use curly braces even in situations where they are technically optional. Having them increases readability and decreases the likelihood of logic errors being introduced when new lines are added.
  • Function Calls: Functions should be called with no spaces between the function name, the opening parenthesis, and the first parameter; spaces between commas and each parameter, and no space between the last parameter, the closing parenthesis, and the semicolon.
  • Comments: user phpDocumentor syntax. Be descriptive. Even comments within code are strongly encouraged. Use English so everybody in the project will be able to understand them.
  • PHP Code Tags: Always use <?php to start PHP code, not the <? shorthand. This is the most portable way to include PHP code on differing operating systems and setups. Don't use the PHP closing tag ?> whenever possible, since adding extra lines after it causes the infamous headers already sent problem.

Naming convenction

Classes should be given descriptive names. Avoid using abbreviations where possible. Class names should always begin with an uppercase letter, further words inside the class name should be capitalized also. Use English language. Examples: NewsItem, Group.

Functions and methods should begin with a lower case letter and should be given descriptive names. Avoid using abbreviations where possible. Use English language. Examples: getNews(), saveGroup(). Private method shold start with an underscore. Example: _tidy()

Db table names should be singular, all lower case and so should be field names. Primary/Unique key identifier should be "id_" + tablename. Words separated by an underscore. The most significative name first. Example date_start and not start_date.

Chapter 17. Module programming

How to create a new common module

A common module is a module integrated into Obliquid, it will be stored inside the common directory tree. The common directory tree will be overwritten by upgrades, so it's not advised to store anything there before agreeing with us.

It will be now explained the steps necessary to create a news module. First it will be necessary to add a row to the module table, but it can't be simply added to the database, because this row will have to be created by the next setup, and has to work for every possible table prefix. This is done by adding a row to common/configs/module.tpl file. In our case:

INSERT INTO {{$tprefix}}module VALUES (4, 'news',
'Simple content management for publishing purposes', '0.1', '2003-01-04',
'Stefano Locati', 'stefano@obliquid.it', 'www.obliquid.com', 'info@obliquid.it',
'Copyright (c) 2003 Obliquid', 'LGPL', 'common/docs/license.txt', 4, 'Y')

This insert is here broken on several rows for convenience, but it has to be on a single line in module.tpl file, or the insert will fail. To activate the change, we have to go to a core module page, like "Page groups, pages, slots", and choose "Resets a table to default" in the left menu, select module and press ok. This operation will reload the module table. A new setup will do it too.

A module will also need php scripts and html templates, our news module php scripts will be in common/pages/news directory and our templates will be in common/templates/news directory. This two news directory has thus to be created, notice that this name is the same of the module. This is a mandatory rule.

A module will need an access point, generally this is done through core/nav slot. This is normally shown as a top icon bar, we will then need to find an appropriate icon that will be stored in common/images/news directory that we have to create for the purpose. We will store there a news.gif icon, of size 32x32 pixels.

We can now open common/pages/core/nav.php to add the new icon and text to the $navpages array.

    $navpages["news_adm"] = array(_l("News Administration"),"common/images/news/news.gif");

Note that we need to call _l() to translate the popup text into the correct language.

We have then to decide when this news icon will be shown, and it will be to users that have the news_adm page. Since this security object still doesn't exist the icon won't be shown yet.

Therefore we open common/configs/security.tpl and add a row for the new news_adm page. We can just pick the next available ID number for this row. It will be a objtype='n' (Navigation Page). Be careful to make sure that you have the ':' in the right places. The demo site has three groups defined. g1 is Administrators g2 is Developers, and g3 is Customers. Since we want to permit both the Administrators and Developers to administer news, we need to add both to the security table.

INSERT INTO {{$tprefix}}security VALUES (14,':news_adm:','g1','n')
INSERT INTO {{$tprefix}}security VALUES (15,':news_adm:','g2','n')

Now we need this new security to really appear to continue our work. This can be done as we did before to reload the module table, but this time we need to reload the security table. Of course a new setup will reload this new values too.

We could do this also by logging on as pascal and going through the Security and Groups icon. But that would only change the database, and the next time the database got reloaded our changes would be gone.

Now, despite all this, clicking on the new icon will give an error, because we didn't create any news_adm page yet. This can be done by editing manually local/configs/config.xml or by using site configuration. We use the second way: click on Page groups, pages, slot icon and choose add a new page. Insert news_adm as name and news as group and don't modify the other two fields, then click on the ok icon: the new page is now created (empty).

To add core/nav slot in this page we go down to "Creates a new slot in page news_adm" window and type core/nav as name, center1 for position and none for block, leaving the other fields unchanged, then we can click on the ok icon. We can already test our page by clicking on it. It will have only the icon bar.

For example we can now add a core/changelang slot to the left by typing core/changelang for name, left2 for position, title for block and clicking ok. We also have to add the most important slot for this page: news/adm. We have to type news/adm for name, center2 for position and click ok. If we test the news_adm page now it will show two errors: this is because there is still no php script and no template file.

We start by creating a adm.php file inside common/pages/news directory, including the disclaimer too.


<?php

/* Obliquid - PHP/XML application framework for groupware websites.
 * Copyright (C) 2003 Stefano Locati <info@obliquid.it>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/** News administration
 *  @version $Id: common_mod.xml,v 1.5 2004/02/11 00:38:31 vrtisworks Exp $
 *  @package news
 */

function news_adm_()
{
    global $_obweb;

    $_obweb->smblock->assign("title", _l("News Administration"));
}

?>

Then we create a template file called adm.tpl inside common/templates/news directory.

Now when we click on the icon, we still do not see anything. That is because we did not create any security access to the news/adm slot. Remember that there might be some slots on this page that everybody can see (such as core/nav), but not everybody should be allowed to get to news/adm. So we need to go back to security.tpl and add the two entries for news/adm.

INSERT INTO {{$tprefix}}security VALUES (31,'news/adm::','g1','s')
INSERT INTO {{$tprefix}}security VALUES (32,'news/adm::','g2','s')

Notice that these entries are type 's' (Slots). Now after we reload the security table, as explained before, we can click on the icon and see our work!

How to create module navigation

A common and useful slot that is normally present in every page of a module is the module index that allows navigation within the module. This slot is usually called index.php. For example for the core module it will be stored in common/pages/core/index.php, common/templates/core/index.tpl and common/navxml/core.xml

Here is what core/index.php looks like.

    
require_once "common/classes/obliquid/nav.php";

/** access structure for core module */
function core_index_()
{
    global $_obweb;

    $nav = & new nav();
    $corelinks = $nav->getNav("core");    //Get the array for the icons

    //Now assign them for the template, and we are done
    $_obweb->smslot->assign("corelinks", $corelinks);
}

  

And here is what index.tpl looks like.

    
{{* $Id: common_mod.xml,v 1.5 2004/02/11 00:38:31 vrtisworks Exp $ *}}
<table width="160">
{{foreach from=$corelinks item=alink}}
<tr><td>
{{if $alink[2] eq "*none*"}}
<a href="{{$alink[0]}}" class="textmini">{{$alink[1]}}</a>
{{else}}
<a href="{{$alink[0]}}" class="textmini">
<img src="{{$alink[2]}}" width="16" height="16" border="0">&nbsp;{{$alink[1]}}</a>
{{/if}}
</td></tr>
{{/foreach}}
</table>

  

And finally, here is what core.xml looks like. For details about the format and information in a navxml file, please see the section on "Site Naviation".

    
<?xml version="1.0" encoding="UTF-8"?>
<!-- $Id: common_mod.xml,v 1.5 2004/02/11 00:38:31 vrtisworks Exp $ -->
<NAVOBJECTS>
 <NAV PAGE="core_confpagegroup" ORD="10">
  <THEME NAME="*default*">
   <TEXT>List modules and their pages</TEXT>
  </THEME>
 </NAV>
 <NAV PAGE="core_configparms" ORD="20">
  <THEME NAME="*default*">
   <TEXT>Configure parameters</TEXT>
  </THEME>
 </NAV>
 <NAV PAGE="core_cron" ORD="25">
  <THEME NAME="*default*">
   <TEXT>Scheduled jobs</TEXT>
  </THEME>
 </NAV>
 <NAV PAGE="core_admtheme" ORD="30">
  <THEME NAME="*default*">
   <TEXT>Manage themes</TEXT>
  </THEME>
 </NAV>
 <NAV PAGE="core_conflang" ORD="40">
  <THEME NAME="*default*">
   <TEXT>Configure languages</TEXT>
  </THEME>
 </NAV>
 <NAV PAGE="core_resethas" ORD="45">
  <THEME NAME="*default*">
   <TEXT>Reload your security</TEXT>
  </THEME>
 </NAV>
 <NAV PAGE="core_demodata" ORD="50">
  <THEME NAME="*default*">
   <TEXT>Load demo data</TEXT>
  </THEME>
 </NAV>
</NAVOBJECTS>

  

To make this slot appear we will need to insert the permission to see the slot in the security table.

INSERT INTO {{$tprefix}}security VALUES (141,'core/index::','g1','s')
INSERT INTO {{$tprefix}}security VALUES (142,'core/index::','g2','s')

Further, we will need a navigation permission for every link in this slot. In this way we are able to deliver a different set of links for each group.

INSERT INTO {{$tprefix}}security VALUES (143,':core_mypage:','g2','n')

Calendar module

The calendar supports multiple types of events: meetings, work sessions, accounting, calls and recalls and availabilities.

Availabilities can be inserted as repeated events: for example every monday from 10 to 12am. Until Obliquid 0.5.0 only the generation rule was saved into database and the derived events were computed at each page reload. Unfortunately this approach, even if works it gets easily too slow.

In new versions of Obliquid, generated events are saved in database. To distinguish the inserted events from the generated ones, the field id_categ of cal table is used. When id_categ = 0, then our availability event is an inserted event, when id_categ > 0 then it's a generated one and the value of id_categ is the id_cal of the original event.

Chapter 18. Common operations

A cookbook to remind how to do common operations.

How to build a release

Some short notes for developers: how to build a release. Execute these steps carefully and in the suggested order.

Phase 1: Keeping the house clean

  • Check all the pages pages without any parameters, called using the display icon. If any of them gives error, fix them.
  • Generate a new message template pot file directly from sources.
    • Click on Developers' tools, click on Write a PHP file with database messages to be translated on the left navigation menu.
    • Build the pot files, since Obliquid 0.8.0 a pot for each module will be created inside common/locale/pot directory. $ cd common/locale/scripts ; ./build_pot
  • Merge it for the languages available. For example: $ cd common/locale/scripts ; ./msg_merge it_IT
  • update changelog and move previous changes to changelog2 $ cvs2cl.pl --delta rel-x-x-x:HEAD
  • update README.txt, common/templates/core/home.tpl and common/templates/core/welcome.tpl
  • generate phpdocumentor documentation on dev.obliquid.com $ cd dev/obdata/obliquid ; cvs -q update ; cd .. ; ./gendoc

Phase 2: build the package

  • checkout fresh cvs sources as a normal user
    export CVS_RSH=ssh
    cvs -z3 -d:ext:slocati@cvs.sourceforge.net:/cvsroot/obliquid co obliquid
    
  • rename obliquid directory to obliquid-release mv obliquid obliquid-1.5.2
  • put the system in setup mode sh common/scripts/startsetup
  • remove unneeded CVS directories find . -name CVS | xargs rm -r
  • Delete old CVS directories. cd common/scripts ; ./delolddirs
  • build a temptative tar ball, zip file tar cfvz obliquid-1.5.2.tgz obliquid-1.5.2
  • on a windows system checkout fresh sources, remove unneeded CVS directories, and put the system in startup mode zip -r obliquid-1.5.2.zip obliquid-1.5.2
  • test a setup with both tgz and zip file, setup demo.obliquid.com. If anything goes wrong do the changes and reiterate the process.
  • when everything is fine tag the sources cvs tag rel-1-5-2
  • upload the release in the incoming directory of upload.sourceforge.net and fill the release form
  • make a release announcement on sourceforge, freshmeat and on the forum

How to build an upgrade package

  • create a directory for the upgrade package named upgrade
  • find the differences between the two versions with this command diff --recursive --brief --new-file obliquid-0.4.5 obliquid-0.4.4
  • create the needed directories inside the upgrade package, don't worry if some are missing, but don't create local/configs
  • copy the files that differ in the upgrade package with this command diff -rqN obliquid-0.4.5 obliquid-0.4.4|sed 's/Files \(obliquid-0.4.5\)\(.*\) and .*/\1\2 upgrade\2/' | xargs -l1 cp create the missing directory found and reissue the command until no errors are shown
  • issue the opposite find to find the files to be deleted diff --recursive --brief obliquid-0.4.5 obliquid-0.4.4 | grep Only | grep 0.4.4
  • copy index.php in normal mode from the latest version
  • copy README.upgrade and upgrade.php from the previous upgrade package and modify them
  • diff config.xml and explain the differences in upgrade.php
  • diff the database structure and database data to create the db upgrade script
  • test the upgrade before releasing it, both with a find and a real test
  • upload the release to upload.sourceforge.net in incoming directory and fill the release form
  • make a release announcement on sourceforge, freshmeat and on the forum

Appendices

Appendix A. How to write this manual

Introduction

“DocBook provides a system for writing structured documents using SGML or XML. It is particularly well-suited to books and papers about computer hardware and software, though it is by no means limited to them. (From Docbook, The definitive guide) ”

For learning how to write documentation using docbook, I found this tutorial. I just read a chapter of it, but it seems fine as an introduction. For details you can always search The definitive guide. Writing Documentation Using DocBook -- A Crash Course

Available Docbook Elements

The complete docbook specification is quite large, but not all is necessary for writing this manual. I keep a list of used markup here. If it's in this list it can be used, if it is not in this list, but it's necessary, it can be added. It has to be added in this list and configured in the style sheet too.

abstract -- A summary
appendix -- An appendix in a Book or Article
author -- The name of an individual author
authorgroup -- Wrapper for author information when a document has multiple authors or collaborators
book -- A book
bookinfo -- Meta-information for a Book
caption -- A caption
classname -- The name of a class, in the object-oriented programming sense
chapter -- A chapter, as of a book
command -- The name of an executable program or other software command
guibutton -- The text on a button in a GUI
computeroutput -- Data, generally text, displayed or presented by a computer
copyright -- Copyright information about a document
emphasis -- Emphasized text
example -- A formal example, with a title
filename -- The name of a file
function -- The name of a function or subroutine, as in a programming language
guilabel -- The text of a label in a GUI
imagedata -- Pointer to external image data
imageobject -- A wrapper for image data and its associated meta-information
important -- An admonition set off from the text
itemizedlist -- A list in which each entry is marked with a bullet or other dingbat
listitem -- A wrapper for the elements of a list item
literal -- Inline text that is some literal value
literallayout -- A block of text in which line breaks and white space are to be reproduced faithfully
markup -- A string of formatting markup in text that is to be represented literally
mediaobject -- A displayed media object (video, audio, image, etc.)
member -- An element of a simple list
methodname -- The name of a method
note -- A message set off from the text
orderedlist -- A list in which each entry is marked with a sequentially incremented label
para -- A paragraph
part -- A division in a book
partinfo -- Meta-information for a Part
partintro -- An introduction to the contents of a part
programlisting -- A literal listing of all or part of a program
quote -- An inline quotation
screenshot -- A representation of what the user sees or might see on a computer screen
title -- The text of the title of a section of a document or of a formal block-level element
ulink -- A link that addresses its target by means of a URL (Uniform Resource Locator)
userinput -- Data entered by the user
varname -- The name of a variable
warning -- An admonition set off from the text
year -- The year of publication of a document
para -- A paragraph

Appendix B. How to upgrade

Introduction

One of the goals of Obliquid project is to allow customizability of your site or application, while making the upgrade to the next version easy. This is a hard problem. We are trying our best to reach this goal, but we didn't reach it yet. One step forward in this direction is the new pagexml layout that has a little xml file for each page instead of a big one, that allows you to redefine pages. This new layout was introduced in Obliquid 0.6.0.

We are trying to release upgrade packages that will help users to upgrade. They were introduced early on, but they were discontinued then, because making upgrade packages was stealing too much time from development. Starting from Obliquid 0.5.0, we are reaching a more mature status, so we try to always release those upgrade packages.

The upgrade process is only in part automatic. It will require human support to do some of the operations. It could also be done manually, if you want the greatest control. We also have the need to have a documentation of changes between versions, so I thought to write it as an appendix in our manual.

Upgrading Obliquid from 0.9.3 to 0.9.4

Overwrite all the common files with the new ones. You can do it with ftp, with a file manager or with the shell cp -r obliquid-0.9.4/common/ obliquid-0.9.3/

Execute the following queries -- If your prefix is not sl_,

INSERT INTO sl_tabledesc (id_tabledesc, tablename, field, type, length, is_primary) VALUES (2720, 'log', 'id_log', 'integer', 0, 'Y');
INSERT INTO sl_tabledesc (id_tabledesc, tablename, field, type, length, is_primary) VALUES (2721, 'log', 'id_change', 'integer', 0, 'N');
INSERT INTO sl_tabledesc (id_tabledesc, tablename, field, type, length, is_primary) VALUES (2722, 'log', 'id_person', 'integer', 0, 'N');
INSERT INTO sl_tabledesc (id_tabledesc, tablename, field, type, length, is_primary) VALUES (2723, 'log', 'logtime', 'timestamp', 0, 'N');
INSERT INTO sl_tabledesc (id_tabledesc, tablename, field, type, length, is_primary) VALUES (2724, 'log', 'operation', 'text', 8, 'N');
INSERT INTO sl_tabledesc (id_tabledesc, tablename, field, type, length, is_primary) VALUES (2725, 'log', 'level', 'integer', 0, 'N');
INSERT INTO sl_tabledesc (id_tabledesc, tablename, field, type, length, is_primary) VALUES (2726, 'log', 'tablen', 'text', 64, 'N');
INSERT INTO sl_tabledesc (id_tabledesc, tablename, field, type, length, is_primary) VALUES (2727, 'log', 'prikey', 'text', 128, 'N');
INSERT INTO sl_tabledesc (id_tabledesc, tablename, field, type, length, is_primary) VALUES (2729, 'log', 'newvalue', 'text', 0, 'N');
INSERT INTO sl_tabledesc (id_tabledesc, tablename, field, type, length, is_primary) VALUES (2730, 'log', 'oldvalue', 'text', 0, 'N');
INSERT INTO sl_tabledesc (id_tabledesc, tablename, field, type, length, is_primary) VALUES (2731, 'log', 'description', 'text', 255, 'N');

CREATE TABLE sl_log (
  id_log int(11) NOT NULL default '0',
  id_change int(11) NOT NULL default '0',
  id_person int(11) NOT NULL default '0',
  logtime datetime default NULL,
  operation varchar(8) NOT NULL default '0',
  level int(11) NOT NULL default '1',
  tablen varchar(64) NOT NULL default '',
  prikey varchar(128) NOT NULL default '',
  newvalue text NOT NULL,
  oldvalue text NOT NULL,
  description text NOT NULL,
  UNIQUE KEY id_log (id_log)
) TYPE=MyISAM;

CREATE TABLE _sequence_sl_log (
  sequence int(11) NOT NULL auto_increment,
  PRIMARY KEY  (sequence)
) TYPE=MyISAM AUTO_INCREMENT=1 ;

CREATE TABLE _sequence_sl_log2 (
  sequence int(11) NOT NULL auto_increment,
  PRIMARY KEY  (sequence)
) TYPE=MyISAM AUTO_INCREMENT=1 ;

Upgrading Obliquid from 0.9.2 to 0.9.3

Overwrite all the common files with the new ones. You can do it with ftp, with a file manager or with the shell cp -r obliquid-0.9.2/common/ obliquid-0.9.1/

Upgrading Obliquid from 0.9.1 to 0.9.2

Overwrite all the common files with the new ones. You can do it with ftp, with a file manager or with the shell cp -r obliquid-0.9.2/common/ obliquid-0.9.1/

Copy common/scripts/index.php over the main index.php, copy favicon.ico too if you want.

Execute the following queries -- If your prefix is not sl_, adjust accordingly

ALTER TABLE sl_person CHANGE provincia provincia VARCHAR(32) DEFAULT NULL;

INSERT INTO sl_country (name, code) VALUES ('Guyana', 'GY');

INSERT INTO sl_slot (id_slot, id_module, slot, comment) VALUES (225,'3','addnote','Add or modify a Note entry');

UPDATE sl_slotfield SET enable = 'Y' WHERE id_slotfield = 10;
UPDATE sl_slotfield SET enable = 'Y' WHERE id_slotfield = 61;
UPDATE sl_slotfield SET intype = 'TITLE' WHERE id_slotfield = 56;
UPDATE sl_slotfield SET intype = 'TITLE' WHERE id_slotfield = 64;
UPDATE sl_slotfield SET intype = 'TITLE' WHERE id_slotfield = 69;
UPDATE sl_slotfield SET intype = 'PASSWORD' WHERE id_slotfield = 71;
UPDATE sl_slotfield SET intype = 'TITLE' WHERE id_slotfield = 107;
UPDATE sl_slotfield SET enable = 'Y' WHERE id_slotfield = 112;
UPDATE sl_slotfield SET intype = 'TITLE' WHERE id_slotfield = 115;
UPDATE sl_slotfield SET intype = 'TITLE' WHERE id_slotfield = 120;
UPDATE sl_slotfield SET intype = 'TITLE' WHERE id_slotfield = 157;
UPDATE sl_slotfield SET enable = 'Y' WHERE id_slotfield = 162;
UPDATE sl_slotfield SET intype = 'TITLE' WHERE id_slotfield = 165;
UPDATE sl_slotfield SET intype = 'TITLE' WHERE id_slotfield = 170;
UPDATE sl_slotfield SET intype = 'PASSWORD' WHERE id_slotfield = 172;
UPDATE sl_slotfield SET enable = 'Y' WHERE id_slotfield = 211;
UPDATE sl_slotfield SET enable = 'Y' WHERE id_slotfield = 257;
DELETE FROM sl_slotfield WHERE id_slotfield = 258;
UPDATE sl_slotfield SET enable = 'Y' WHERE id_slotfield = 356;
UPDATE sl_slotfield SET enable = 'Y' WHERE id_slotfield = 412;
UPDATE sl_slotfield SET enable = 'Y' WHERE id_slotfield = 606;
UPDATE sl_slotfield SET intype = 'TITLE' WHERE id_slotfield = 706;
UPDATE sl_slotfield SET intype = 'TITLE' WHERE id_slotfield = 714;
UPDATE sl_slotfield SET intype = 'TITLE' WHERE id_slotfield = 719;
UPDATE sl_slotfield SET intype = 'TITLE' WHERE id_slotfield = 726;

UPDATE sl_tabledesc SET length = 32 WHERE id_tabledesc = 318;

Go to developers' tools, select 'Reset table with default data' from the left side menu and choose msg_text table and 'Empty and reload table completely', press OK. If you've customized your email messages, the changed message is doc_appr, both in English and Italian. Beware that also now you enable or disable a message, not a message/language combination as it was happening before. The language sent will depend on the language of the sender (if there is interest, it could be in the language of who receives).

Bid Chief, Bid quality and Statistics roles has been modified, reload them. The change is the addition of the slots prj/phasehours and prj/hourconf

Lastly, go to developers' tools, choose 'Load PO translation files' on the left menu and press OK to reload traslations

Upgrading Obliquid from 0.9.0 to 0.9.1

First of all overwrite all common files with the new ones. For example with unix shell cp -r obliquid-0.9.1/common/ obliquid-0.9.0/, or you can overwrite the common dir using ftp

Execute the following queries -- If your prefix is not sl_, adjust accordingly

CREATE TABLE _sequence_sl_tabledesc (
  sequence int(11) NOT NULL auto_increment,
  PRIMARY KEY  (sequence)
) TYPE=MyISAM AUTO_INCREMENT=100001;

INSERT INTO _sequence_sl_tabledesc VALUES (100000);

ALTER TABLE sl_doc_cat1 ADD id_parent INT NOT NULL;

CREATE TABLE _sequence_sl_doc_cat1_parent (
  sequence int(11) NOT NULL auto_increment,
  PRIMARY KEY  (sequence)
) TYPE=MyISAM AUTO_INCREMENT=1;

CREATE TABLE sl_doc_cat1_parent (
  id_doc_cat1_parent int(11) NOT NULL default '0',
  name char(80) NOT NULL default '',
  UNIQUE KEY doc_cat1 (id_doc_cat1_parent)
) TYPE=MyISAM;

INSERT INTO sl_tabledesc (id_tabledesc, tablename, field, type, length, is_primary) VALUES (2705, 'doc_cat1', 'id_parent', 'integer', 0, 'N');
INSERT INTO sl_tabledesc (id_tabledesc, tablename, field, type, length, is_primary) VALUES (2706, 'doc_cat1_parent', 'id_doc_cat1_parent', 'integer', 0, 'Y');
INSERT INTO sl_tabledesc (id_tabledesc, tablename, field, type, length, is_primary) VALUES (2707, 'doc_cat1_parent', 'name', 'text', 80, 'N');

INSERT INTO sl_configparms (name, setting, description, id_module, ord) VALUES ('cat1_parent', 'N', 'Use category 1 with parent (doublecombo)', 5, 50);
INSERT INTO sl_configparms (name, setting, description, id_module, ord) VALUES ('signup_enable', 'Y', 'Allow guests to create a new user record.', 2, 15);

UPDATE sl_slotfield SET label = '#passwd' WHERE `d_slotfield = 422;

The new roles cal_view and cal_adm have been introduced. security_partial, security_full, doc_adm and site_conf have been modified.

Upgrading Obliquid from 0.8.1 to 0.9.0

First of all overwrite all common files with the new ones. For example with unix shell cp -r obliquid-0.9.0/common/ obliquid-0.8.1/ or you can overwrite the common dir using ftp

Execute the following queries

INSERT INTO sl_configparms (name, setting, description, id_module, ord) VALUES ('cat3_group', '0', 'Id group to connect to category 3, 0 to use a custom category 3', 5, 40);
INSERT INTO sl_configparms (name, setting, description, id_module, ord) VALUES ('err_handling', 'none', 'Error handling one of (none, email, popup) Popup will be shown only if the user can see core/confpagegroup slot', 8, 5);

INSERT INTO sl_msg_text VALUES (84, 9, 'prj_prepare_invoice', 'en', 'Prepare invoice', 'The following phases have been closed, and an invoice should be prepared:\r\n\r\n{{foreach from=$p.phase item=ph}}Phase description:{{$ph}}\r\n{{/foreach}}Organization: {{$p.organization}}\r\nWork type: {{$p.activity}}\r\nBid code: {{$p.reference}}\r\nReport: {{$p.link}}\r\n\r\nClosed by: {{$p.name}}', '$p[organization] - Organization name
$p[activity] - Work type
$p[reference] - Bid Code
$p[phase] - An array of Phase descriptions
$p[link] - Link to the report screen
$p[name] - Name of person who closed the phase.', NOW(), 'Y');
INSERT INTO sl_msg_text VALUES (85, 9, 'prj_prepare_invoice', 'it', 'Preparare fattura', 'Le seguenti fasi sono state chiuse e bisogna fatturare:\r\n\r\n{{foreach from=$p.phase item=ph}}Descrizione fase:{{$ph}}\r\n{{/foreach}}Organizzazione: {{$p.organization}}\r\nTipo di lavoro: {{$p.activity}}\r\nCodice offerta: {{$p.reference}}\r\nCollegamento al sito: {{$p.link}}\r\n\r\nChiusa da: {{$p.name}}', '$p[organization] - Organizzazione
$p[activity] - Tipo di lavoro
$p[reference] - Codice offerta
$p[phase] - Un array con le descrizioni delle fasi
$p[link] - Collegamento al sito
$p[name] - Nome della persona che ha chiuso la fase.', NOW(), 'Y');

INSERT INTO sl_page VALUES ('core_javaedit',1,'Y','Enhanced text file editor, sends a message to people subscribed to core_edit on save');

INSERT INTO sl_slot (id_slot, id_module, slot, comment) VALUES (44, '1', 'javaedit', 'Enhanced text file editor, sends a message to people subscribed to core_edit on save');

INSERT INTO sl_tabledesc (id_tabledesc, tablename, field, type, length, is_primary) VALUES (202, 'news_object', 'pos', 'integer', 0, 'N');
INSERT INTO sl_tabledesc (id_tabledesc, tablename, field, type, length, is_primary) VALUES (200, 'news_text', 'pos', 'integer', 0, 'Y');
INSERT INTO sl_tabledesc (id_tabledesc, tablename, field, type, length, is_primary) VALUES (201, 'news_text', 'blocklayout', 'integer', 0, 'N');
INSERT INTO sl_tabledesc (id_tabledesc, tablename, field, type, length, is_primary) VALUES (2550, 'news_cat', 'topic_images', 'text', 1, 'N');
INSERT INTO sl_tabledesc (id_tabledesc, tablename, field, type, length, is_primary) VALUES (2600, 'news_layout', 'id_news_layout', 'integer', 0, 'Y');
INSERT INTO sl_tabledesc (id_tabledesc, tablename, field, type, length, is_primary) VALUES (2601, 'news_layout', 'name', 'text', 80, 'N');
INSERT INTO sl_tabledesc (id_tabledesc, tablename, field, type, length, is_primary) VALUES (2700, 'prj_assigned', 'id_phase', 'integer', 0, 'Y');
INSERT INTO sl_tabledesc (id_tabledesc, tablename, field, type, length, is_primary) VALUES (2701, 'prj_assigned', 'id_person', 'integer', 0, 'Y');
INSERT INTO sl_tabledesc (id_tabledesc, tablename, field, type, length, is_primary) VALUES (2702, 'prj_assigned', 'id_project', 'integer', 0, 'Y');

ALTER TABLE `sl_news_object` ADD `pos` INT NOT NULL ;

ALTER TABLE `sl_news_text` ADD `pos` INT NOT NULL ,
ADD `blocklayout` INT NOT NULL ;

ALTER TABLE `sl_news_text` DROP INDEX `news_text` ;

ALTER TABLE `sl_news_text` ADD UNIQUE ( `id_news_item` , `lang` , `pos`);

ALTER TABLE `sl_news_cat` ADD `topic_images` CHAR( 1 ) NOT NULL ;

CREATE TABLE `sl_news_layout` (
  `id_news_layout` int(11) NOT NULL default '0',
  `name` char(80) NOT NULL default ''
) TYPE=MyISAM;

INSERT INTO `sl_news_layout` VALUES (1, 'Text on the left, photos on the right');
INSERT INTO `sl_news_layout` VALUES (2, 'Photos on the left, text on the right');
INSERT INTO `sl_news_layout` VALUES (3, 'Photos on top');
INSERT INTO `sl_news_layout` VALUES (4, 'Photos tiled horizontally on the left, text on the right');
    
CREATE TABLE `sl_prj_assigned` (
  `id_phase` int(11) NOT NULL default '0',
  `id_person` int(11) NOT NULL default '0',
  `id_project` int(11) NOT NULL default '0',
  UNIQUE KEY `id_prj_assigned_idx` (`id_phase`,`id_person`)
) TYPE=MyISAM;

Your upgrade should be now complete except for slot security to view the new additions. I suggest you to load some roles and assign them to groups

Upgrading Obliquid from 0.8.0 to 0.8.1

First of all overwrite all common files with the new ones. For example with unix shell cp -r obliquid-0.8.1/common/ obliquid-0.8.0/ or you can overwrite the common dir using ftp

Execute the following queries

INSERT INTO sl_security (id_security, ifq, ug, objtype) VALUES (174, 'user/userview::', 'g2', 's');

INSERT INTO sl_tabledesc (id_tabledesc, tablename, field, type, length, is_primary) VALUES (2590, 'persparms', 'id_module', 'integer', 0, 'Y');
INSERT INTO sl_tabledesc (id_tabledesc, tablename, field, type, length, is_primary) VALUES (2591, 'persparms', 'name', 'text', 100, 'Y');
INSERT INTO sl_tabledesc (id_tabledesc, tablename, field, type, length, is_primary) VALUES (2592, 'persparms', 'ug', 'text', 12, 'Y');
INSERT INTO sl_tabledesc (id_tabledesc, tablename, field, type, length, is_primary) VALUES (2593, 'persparms', 'setting', 'text', 255, 'N');
INSERT INTO sl_tabledesc (id_tabledesc, tablename, field, type, length, is_primary) VALUES (2594, 'persparms', 'priority', 'integer', 0, 'N');

CREATE TABLE `sl_persparms` (
  `id_module` int(11) NOT NULL default '0',
  `name` varchar(100) NOT NULL default '',
  `ug` varchar(12) NOT NULL default '',
  `setting` varchar(255) NOT NULL default '',
  `priority` int(11) NOT NULL default '0',
  PRIMARY KEY  (`id_module`,`name`,`ug`)
) TYPE=MyISAM;

ALTER TABLE `sl_group_group` CHANGE `id_son` `id_son` CHAR( 6 ) DEFAULT '0' NOT NULL;

UPDATE sl_tabledesc SET type='text', length=6 WHERE tablename='group_group' AND field='id_son';

At this point add security to access to user_roleadd and its slot

Upgrading Obliquid from 0.5.0 to 0.6.0

[Warning]Work in progress

This section is a work in progress, it's not complete. It's here basically for me, don't use it until it's complete

1) Overwrite all the files in the common directory tree.

2) Add the following tables, changing the prefix if needed.

CREATE TABLE _sequence_sl_module (
  sequence int(11) NOT NULL auto_increment,
  PRIMARY KEY  (sequence)
) TYPE=MyISAM;

CREATE TABLE _sequence_sl_posts_cat (
  sequence int(11) NOT NULL auto_increment,
  PRIMARY KEY  (sequence)
) TYPE=MyISAM;

CREATE TABLE _sequence_sl_posts_item (
  sequence int(11) NOT NULL auto_increment,
  PRIMARY KEY  (sequence)
) TYPE=MyISAM;

CREATE TABLE _sequence_sl_posts_object (
  sequence int(11) NOT NULL auto_increment,
  PRIMARY KEY  (sequence)
) TYPE=MyISAM;

CREATE TABLE _sequence_sl_translate (
  sequence int(11) NOT NULL auto_increment,
  PRIMARY KEY  (sequence)
) TYPE=MyISAM;

CREATE TABLE sl_country (
  name char(50) NOT NULL default '',
  code char(2) NOT NULL default '',
  UNIQUE KEY code (code)
) TYPE=MyISAM;

CREATE TABLE sl_language (
  abbrev char(5) NOT NULL default '',
  date_long char(24) NOT NULL default '',
  date_short char(24) NOT NULL default '',
  flag char(48) NOT NULL default '',
  name char(48) NOT NULL default '',
  ord int(11) NOT NULL default '0',
  enable char(1) NOT NULL default 'Y',
  UNIQUE KEY abbrev (abbrev)
) TYPE=MyISAM;

CREATE TABLE sl_posts_cat (
  id_posts_cat int(11) NOT NULL default '0',
  lang char(5) NOT NULL default 'en_US',
  title char(100) default NULL,
  description char(255) default NULL,
  restricted char(1) NOT NULL default 'n',
  autoapprove char(1) NOT NULL default 'y',
  ordby char(24) NOT NULL default 'mod_date',
  lastpost datetime default NULL,
  ord int(11) NOT NULL default '0',
  UNIQUE KEY id_posts_cat_idx (id_posts_cat,lang)
) TYPE=MyISAM;

CREATE TABLE sl_posts_item (
  id_posts_item int(11) NOT NULL default '0',
  id_posts_cat int(11) NOT NULL default '0',
  lang char(5) NOT NULL default '',
  title char(100) NOT NULL default '',
  comments char(255) NOT NULL default '',
  approved char(1) NOT NULL default 'n',
  ins_date datetime default NULL,
  mod_date datetime default NULL,
  ord int(11) NOT NULL default '0',
  UNIQUE KEY posts_item_idx (id_posts_item,id_posts_cat)
) TYPE=MyISAM;

CREATE TABLE sl_posts_object (
  id_posts_object int(11) NOT NULL default '0',
  id_posts_item int(11) NOT NULL default '0',
  id_posts_cat int(11) NOT NULL default '0',
  object_name char(255) NOT NULL default '',
  object_mime char(32) NOT NULL default '',
  object_size int(11) NOT NULL default '0',
  object_type char(1) NOT NULL default 't',
  location char(1) NOT NULL default 'c',
  ord int(11) NOT NULL default '0',
  approved char(1) NOT NULL default 'n',
  pic_width int(11) NOT NULL default '0',
  pic_height int(11) NOT NULL default '0',
  UNIQUE KEY id_posts_object_idx (id_posts_object,id_posts_item)
) TYPE=MyISAM;

CREATE TABLE sl_translate (
  id_translate int(11) NOT NULL default '0',
  msgid char(255) NOT NULL default '',
  locale char(5) NOT NULL default '',
  msgstr char(255) default NULL,
  fuzzy char(1) NOT NULL default 'N',
  UNIQUE KEY id_translate_idx (id_translate)
) TYPE=MyISAM;

3) Alter and drop table instructions

ALTER TABLE sl_page DROP ord;

ALTER TABLE sl_page DROP INDEX name_idx ,
ADD UNIQUE name_idx (name, is_common);

ALTER TABLE sl_project CHANGE name name CHAR(128) NOT NULL;

DROP TABLE sl_slotdep;

ALTER TABLE sl_module ADD is_common CHAR(1) DEFAULT 'Y' NOT NULL AFTER ord;

4) Populate the language table: select Configure site, and then Resets a table to default (FIXME: this message will be changed soon). Then choose the language table and press OK.

5) Click on Configure site and then on Resets a table to default (FIXME:). Then choose the message table and press OK. Be patient. The operation may take a minute.

Appendix C. Code snippets

Introduction

I use this section of the manual to collect code snippets that may be useful for general PHP programming, or within Obliquid framework. They are here as a convenient places to find them without having to dig in old projects source code to find the one needed.

Create a pronounceable password

It creates a prononceable password by alternating vowels and consonants. I removed j, k, w, x and y because it's tuned for Italian language that doesn't have these letters. However they can be easily added back in the array below.


/** Creates a pronounceable random password
 *  @param string $len, optional length (default 8)
 *  @retrun string the generated password
 */
function make_passwd($len=8)
{
    $letter=array(
       array("b","c","d","f","g","i","l","m","n","o","p","qu","r","s","t","v","z"),
       array("a","e","i","o","u"));
    $pass="";
    $set=round(rand(0,1));
    for ($i=0; $i<$len; $i++) {
        $set=!$set;
        $idx=floor(rand(0, count($letter[$set])-1));
        $pass.=$letter[$set][$idx];
    }
    return $pass;
}

Check an email address

Checks if an email is formally valid, note that this doesn't mean that such email address exists. There are scripts to connect to a smtp server and test if a user exist faking to send him/her an email, but in my opinion the best way to test if an address works is sending an email there and have the user click some activation url.


/** Checks is the provided email address is formally valid
 *  @param string $email email address to be checked
 *  @return true if the email is valid, false otherwise
 */
function valid_email($email) {
  $regexp="/^[a-z0-9]+([_\\.-][a-z0-9]+)*@([a-z0-9]+([\.-][a-z0-9]+)*)+\\.[a-z]{2,}$/i";
  if ( !preg_match($regexp, $email) ) {
       $_obweb->addErr("Email address is not correct\n");
       return false;
  }
  return true;
}

Automatic login


/** Automatic Login.
 *  Supposing that the user is not signed on, simply redirect to a page
 *  containing the user slot (may be also only the PHP and not the template)
 */
function login($username, $passwd) {
    $goto = & new url();
    header("Location: index.php?page=home&action=login&username=".$username."&passwd="
        .$passwd."&goto=".urlencode($goto->Build()));
    exit();
}

Profiling your PHP scripts

Before optimizing some code, test its execution time, so first you can see if it's worth and second you will see if your changes gave improvements or not. Performance can be not obvious, so don't trust your intuitions. This snippet comes from php.net website: microtime on www.php.net


function getmicrotime(){
  list($usec, $sec) = explode(" ",microtime());
  return ((float)$usec + (float)$sec);
}
$time_start = getmicrotime();

... code to be profiled here

echo (getmicrotime() - $time_start);

Renaming files

A common problem under Unix is having to make a huge number of files all lowercase. The following short program can help in this task.


$dir=opendir("/home/federico/maiuscole");
while (($file = readdir($dir)) !== false) {
   echo "mv $file ".strtolower($file)."n";
}
closedir($dir);

Appendix D. GNU LESSER GENERAL PUBLIC LICENSE

Preamble


		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 Free Software Foundation, Inc.
     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
not price.  Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

Terms and conditions


		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.

  You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.

  2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.

  If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License.  However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.

  9. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.

  11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.

If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded.  In such case, this License incorporates the limitation as if
written in the body of this License.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.

Each version is given a distinguishing version number.  If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation.  If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission.  For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this.  Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.

			    NO WARRANTY

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.

		     END OF TERMS AND CONDITIONS

How to apply the License terms


           How to Apply These Terms to Your New Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

Also add information on how to contact you by electronic and paper mail.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!