Although all activeCollab releases are important, some are just more important than the other ones. They can be divided into two groups:
Another thing makes branches important: it's the access to the files as part of your Upgrade and Support plan which is tied to them. What this means is that you get access to all releases in all branches while you are in Upgrade and Support plan. Once you get access to a branch, you can download all smaller releases within it. This lets you have access to the most stable release within a branch even when your Upgrade and Support plan expires.
Let me explain with an example: *
You decide that today, October 6th 2008. is a fine day to purchase activeCollab. Now that you have a license purchased, you get access to activeCollab 1.1 branch and its latest release, plus one year of upgrade (expires on October 6th 2009).
Spring 2009 comes and we release activeCollab 1.2. It's all great because you are within Upgrade and Support plan and you get instant access to this branch.
In September 2009. we release activeCollab 2.0 and you get access to that branch also. Because of that, you still get the access to all 2.0.x releases even when your plan expires. If we release activeCollab 2.0.2 in November 2009. you’ll be able to download it even through your Upgrade and Support plan expired a month ago and you didn’t renew.
Couple of months later we release activeCollab 2.1, but this time you don’t get access to that branch because your Upgrade and Support plan is expired - the latest release available to you is the one which belongs to 2.0 branch (precisely, to the branch that was actual at the moment when your Upgrades and Support plan expired). To get access to this new branch you’ll need to renew your subscription for another year.

Additional information about Upgrade and Support services and branches can be found in this knowledge base article. If you have any additional questions, don’t hesitate to ask.
* Dates in this example are not real. This is not activeCollab roadmap!
Persistence layer enables activeCollab to map PHP objects to persistent storage (database) where they can be saved for later use. This means that activeCollab business logic (M in MVC) can be used without the need for SQL queries to be written by developer himself. It works with regular PHP objects that are selected, inserted, updated and deleted from database by persistence layer automatically.
There are two classes that are needed by activeCollab to map an object type to a database table.
The first one is manager class. Manager class is used to work with multiple objects of the same class that are stored in the same table. Name of this class is plural name of the objects stored in the table, in camel notation. For instance, if you would like to get all book authors stored in book_authors table, you will do it using manager class like this:
$authors = BookAuthors::find();
If you need to find all authors whose names are Albert and to be sorted by date of birth, then you would need to pass a few more arguments to the find() method:
$authors = BookAuthors::find(array(
'conditions' => "name = 'Albert'",
'order' => 'birthday',
));
By default find(), returns an array of objects (we’ll talk more about data objects later on). Also, it can return a single object that matches your conditions. For example, if you would like to find a specific author by his social security number, you will do something like this:
$author = BookAuthors::find(array(
'conditions' => 'ssn = ' . db_escape( authors SSN here ),
'one' => true
));
If multiple authors are matched with the requested conditions, then only the first one will be returned. If no authors are found, this function will return NULL.
Conditions argument is basically WHERE part of the SQL query. You can use a string where you manually escape all arguments used in conditions, but you can also use an array if you would like to have WHERE part prepared by activeCollab. In that case, first element of an array is a pattern, while the other elements are variables that are going to be escaped and inserted in places marked with ? character, respectively. Here is an example:
$authors = BookAuthors::find(array(
'conditions' => array('name = ? AND birthday > ?', 'Albert', 1945),
'order' => 'birthday'
));
activeCollab is type sensitive, which means that strings, numbers, date / datetime objects, NULL values and arrays will be properly escaped based on their type when they are prepared.
The second class which activeCollab needs to map PHP objects to a database table is an object class. The instance of this class represents a single row from a table. Class name is the name of the object stored in the table, in singular camel notation (BookAuthor for example).
Row properties are accessed using get and set methods. For instance, this is how we are going to find a book if we know its ID:
$book = Books::findById(12);
if(instance_of($book, 'Book')) {
print $book->getTitle();
}
Code that loads row fields is also type sensitive, so it will properly cast values in native PHP types. Date and datetime fields are automatically converted from strings returned by MySQL into DateValue and DateTimeValue objects.
Table rows are handled by save() and delete() methods. If an object does not exist in database table already, it will be inserted in a new row. But, if you load an object from a row, set some properties and then call save() method, then it will update the original object's data. Loaded object gets deleted by calling delete() method. Here are some examples for using the methods explained above:
$book = new Book();
$book->setTitle('Anatomy of activeCollab Modules');
$book->save() // INSERT ($book is new object)
$book->setTitle('Updating book title to something else');
$book->save(); // UPDATE (because $book already exists in database)$book->delete(); // DELETE
This article explains only the basic features of persistence layer that's built in into activeCollab. in future articles we will talk more about how manager and objects classes are written, about pagination and other advanced topics. If you have any questions, please post a comment or send us an email.
Yesterday we announced on activeCollab forum that there are a couple of new activeCollab modules approaching final stages of development and that we could use some help with testing. There are three new modules that we would like to have tested:
If you would like to grab a test copies of these modules when they get available, please send an email to hi@a51dev.com and tell us more about yourself, which modules are you interested in how you'll be using them.
This is just call for testers and should not be considered as any type of promise. We have not set any exact date when these modules will be included in official activeCollab package. If you are considering to purchase activeCollab, please ignore these modules until they are published and officially supported (if ever).
Thank you!
As we described in previous article, purpose of controllers is to load data and provide it to views based on request. Everything up to that point is handled by activeCollab - mapping URL with appropriate controller and action, loading resources, preparing request data etc. When all that is done $request property is set in controller and you can use it to access request details and data. Here is an example:
class SomeController extends ApplicationController {
function some_action() {
$user = Users::findById($this->request->get('user_id'));
if(instance_of($user, 'User')) {
$this->renderText('User found and loaded');
} else {
$this->renderText('User not found');
}
}
}There are two methods that you will be using to access request variables:
Basically, there are two types of requests user can make:
For request to be recognized as submitted it needs to be a POST request with variable "submitted" set to "submitted". There is a helper method that will check this for you with a single call - isSubmitted():
<form action="/something" method="post">
<input type="text" name="input_value" />
<input type="hidden" name="submitted" value="submitted" />
<button type="submit">Submit</button>
</form>
Controller code:
class SomeController extends ApplicationController {
function some_action() {
if($this->request->isSubmitted()) {
$this->renderText('Form is submitted. Value: ' . $this->request->post('input_value'));
} else {
$this->renderText('Form is not submitted');
}
}
}Additionally, activeCollab recognizes following requests:
In the first article of Anatomy of activeCollab Modules series we explained what activeCollab modules are and which key blocks make a module. Now we'll talk more about one of the most important parts of any module - controllers.
Controllers are used to handle user request. Based on the request they decide how it will be handled, which data needs to be loaded and how it will be presented to the user. Every controller is made out of multiple actions. If action is not specified in the request default action will be used - index().
class MyController extends Controller {
function index() {
// Default action
}
function do_something() {
// Additional action
}
}Most of the time you will use controller and views to render web pages with HTML output, but controllers can also be used to serve data in other formats (CSV, iCalendar etc) or provide an interface to your application to other applications by serving data in XML and JSON format.
All view interaction is done through $smarty instance available in every controller. You can assign values to the views by calling assign method:
$this->smarty->assign('variable_name', $variable_value);or:
$this->smarty->assign(array(
'first_variable' => $value1,
'second_variable' => $value2,
));
If you don't provide an exit from the action by redirecting user or breaking execution with die() activeCollab will automatically resolve template path based on controller and action name and render it. For instance, if we have TestController and show() action in backend module view that will be automatically rendered will be located in:
activecollab/application/modules/backend/views/test/show.tpl
Notice how module, controller and view names are mapped with physical location of the template on the file system.
Views can also be rendered manually. Generated content can be directly forwarded to the browser or returned as a string:
$this->smarty->display($template_path); // directly to browser
$str = $this->smarty->fetch($template_path); // render to string
There are three important method used to redirect users to other pages:
In every controller that inherits ApplicationController you have following variables you can use:
activeCollab controllers rely a lot on inheritance. For instance, if you want to add a functionlity to a project (Events for example) best would be to inherit ProjectController. This way you'll have $active_project instance with loaded project available automatically, access permissions checked and interface prepared (tabs, bread crumbs etc):
class EventsController extends ProjectController {
function index() {
$this->renderText($this->active_project->getName());
}
}Common controller that are usually used as a foundation for additional functionality: