Archive for the ‘Lussumo’ Category

The Road

Tuesday, June 9th, 2009

Vanilla 2

It is long overdue that I post an update to the Garden & Vanilla 2 development. First: if you find that I’ve not been posting on my blog, you can take it as a good sign meaning that I’m busy doing other things.

Myself and a great friend of mine (Todd Burry) have been working full-time on the project for the last four weeks.

Next week we will be moving our current issue tracker over to GitHub and simultaneously releasing the Garden and Vanilla 2 code there. The code is still under heavy development and is absolutely not ready for production use. However, it does work and we need your help getting it finished.

Although Vanilla 1 is an open source project, I have always been the bottleneck that keeps it from really taking off. I refuse to let this happen with Garden and Vanilla 2. So, we will be doing our best to allow everyone in the 10,000-strong Lussumo community to help us out this time around, and the release of the code on GitHub is just the first of many steps in this direction.

Following the GitHub move, I will be contacting the authors of the most popular plugins for Vanilla 1 and helping them to get their plugins ported to Vanilla 2 so they can be fully functional and compatible for the Vanilla 2 release. If, for some reason, the authors are uninterested in porting their plugins to Vanilla 2, I will be making announcements here to find anyone out there who is interested in picking up where the previous authors left off, so keep your eyes peeled. If you have a new plugin idea that you want implemented for Vanilla 2 and you want a hand with getting Garden, Vanilla 2, and understanding how things are done: feel free to contact me here on this blog, via twitter @navvywavvy, or directly via email at mark [at] lussumodotcom.

The future starts here!

Survey Says?

Monday, May 25th, 2009

Pat Sajak

In an effort to better understand the every day Vanilla user & administrator, I’ve put a short survey together and linked it from the Vanilla download and the Addon download pages. If you are a Vanilla user, were a Vanilla user, or will be a Vanilla user, please take a few minutes to help us out with this survey so we can make Vanilla better for everyone: Survey!

And Or Alternatives

Saturday, May 16th, 2009

Apples and Oranges

I’m in the process of adding some methods to the database object to help in writing chained queries for Garden. One of the annoying problems I’ve run into is that the words “or” and “and” are reserved and won’t work as method names. So, for example, imagine I wanted to write the following chained query:

$Database
     ->Select('FieldName')
     ->From('TableName')
     ->Where('FieldName', 'value')
     ->Or()
     ->Where('FieldName', 'othervalue')
     ->And()
     ->Where('FieldName', 'Yet Another Value')
     ->Get();

I realize that this query really doesn’t make much sense, but that’s beside the point. The “or” and the “and” methods can’t exist in PHP, so what should I use instead?

I’ve played with things like AndOp and OrOp, implying that they are operators. I’ve tried going for shorter versions like Nd and R, but those are just stupid and don’t make much sense. I’ve also considered having OrWhere and AndWhere methods, but that would mean adding a whole bunch of methods like that (think: OrWhere, AndWhere, OrLike, AndLike, OrWhereIn, AndWhereIn, etc).

I’m looking for community input here. What would you prefer to type?

Rebranding

Tuesday, May 5th, 2009

In recent weeks I’ve been working closely with Jon Contino of Onetwentysix to overhaul the branding of Lussumo products. Recently we settled on the new logos for Vanilla, Conversations, and Garden. Thanks Jon!

Vanilla, Conversations, Garden

Update
Jon posted a walk-through of how all of these logos came to life. Check it out!

Vanilla 1.1.6 Released

Tuesday, April 14th, 2009

Vanilla 1.1.6 has been released and is now available at getvanilla.com.

If you are upgrading from a previous installation of Vanilla, you’ll want to read the upgrade instructions.

If you’re curious like me, you’ll want to read the release notes.

A big thank you to all of the contributors!

dinoboff
edavis
hst
izaak
Reed Loden
[-Stash-]
sirlancelot
SubJunk
Wallphone

Community Help: Lussumo.com Connectivity Issues [RESOLVED]

Tuesday, February 17th, 2009

Today you may have begun to notice database errors when attempting to load any of my websites. Particularly lussumo.com/community and markosullivan.ca/blog have been showing intermittent errors.

These errors have come at a particularly inopportune time (is there ever a good time?) because I am extremely busy with a new contract, development of the Garden framework, Vanilla 2, and I also manage to have a life in there somewhere (sometimes :).

When I began to notice the slow page-loading times on my server and then the errors that followed, I contacted my hosting company to find out what was going wrong. I am hosted at rackspace.com, and they are well known for their fanatical support. True to form, they got back to me quickly with a diagnosis of the problem:

Good Afternoon,

I have made some adjustments to the my.cnf configuration file in /etc

skip-bdb

query_cache_size=64M
query_cache_limit=12M

interactive_timeout=300
wait_timeout=300

tmp_table_size=128M
max_heap_table_size=128M

in order to decrease the high amount of disk I/O occuring on this server.  This should help with the query building by allocating more memory to this resource.  I have also disabled persistent MySQL connections from PHP:

mysql.allow_persistent = Off

It appears you are reaching your maximum connections limit for MySQL.  The above adjustments are conservative due to the low amount of physical memory you have on this server.

When your server runs out of physical memory, it resorts to using disk space (SWAP memory).  This swapping can and will cause your server to become unresponsive.

You may also consider increasing the amount of physical memory on this server with a RAM upgrade.  If you are interested in proceeding, I can send this ticket to a BDC who can assist you with this upgrade and update you on pricing for this component.

Besides processes in "sleep" status, indicating the use of persistent MySQL connections, it appears most of the connections are due to table locking occuring:

+-----+---------+-----------+-----------+---------+------+-------------------------------+------------------------------------------------------------------------------------------------------+
| Id  | User    | Host      | db        | Command | Time | State                         | Info                                                                                                 |
+-----+---------+-----------+-----------+---------+------+-------------------------------+------------------------------------------------------------------------------------------------------+
| 573 | xxxx | localhost | community | Query   |    9 | Locked                        | SELECT t.DiscussionID  AS DiscussionID, t.FirstCommentID  AS FirstCommentID, t.AuthUserID  AS AuthUs | 
| 574 | xxxx | localhost | community | Query   |   10 | Locked                        | SELECT t.DiscussionID  AS DiscussionID, t.FirstCommentID  AS FirstCommentID, t.AuthUserID  AS AuthUs | 
| 583 | xxxx | localhost | community | Query   |   10 | Locked                        | SELECT t.DiscussionID  AS DiscussionID, t.FirstCommentID  AS FirstCommentID, t.AuthUserID  AS AuthUs | 
| 584 | xxxx | localhost | community | Query   |    9 | Locked                        | SELECT t.DiscussionID  AS DiscussionID, t.FirstCommentID  AS FirstCommentID, t.AuthUserID  AS AuthUs | 
| 591 | xxxx | localhost | community | Query   |   10 | Locked                        | SELECT t.DiscussionID  AS DiscussionID, t.FirstCommentID  AS FirstCommentID, t.AuthUserID  AS AuthUs | 
| 593 | xxxx | localhost | community | Query   |   10 | Locked                        | SELECT t.DiscussionID  AS DiscussionID, t.FirstCommentID  AS FirstCommentID, t.AuthUserID  AS AuthUs | 
| 728 | xxxx | localhost | community | Query   |    5 | Locked                        | SELECT t.DiscussionID  AS DiscussionID, t.FirstCommentID  AS FirstCommentID, t.AuthUserID  AS AuthUs | 
| 729 | xxxx | localhost | community | Query   |    4 | Locked                        | select a.AddOnID  as AddOnID, a.AddOnTypeID  as AddOnTypeID, a.ApplicationID  as ApplicationID, a.Au | 
| 733 | xxxx | localhost | community | Query   |    3 | Locked                        | SELECT t.DiscussionID  AS DiscussionID, t.FirstCommentID  AS FirstCommentID, t.AuthUserID  AS AuthUs | 
| 734 | xxxx | localhost | community | Query   |    3 | Locked                        | SELECT t.DiscussionID  AS DiscussionID, t.FirstCommentID  AS FirstCommentID, t.AuthUserID  AS AuthUs | 
| 730 | xxxx | localhost | community | Query   |    3 | Locked                        | SELECT t.DiscussionID  AS DiscussionID, t.FirstCommentID  AS FirstCommentID, t.AuthUserID  AS AuthUs | 
| 735 | xxxx | localhost | community | Query   |    2 | Locked                        | SELECT t.DiscussionID  AS DiscussionID, t.FirstCommentID  AS FirstCommentID, t.AuthUserID  AS AuthUs | 
| 736 | xxxx | localhost | community | Query   |    2 | Locked                        | SELECT t.DiscussionID  AS DiscussionID, t.FirstCommentID  AS FirstCommentID, t.AuthUserID  AS AuthUs | 
| 737 | xxxx | localhost | community | Query   |    2 | Locked                        | SELECT t.DiscussionID  AS DiscussionID, t.FirstCommentID  AS FirstCommentID, t.AuthUserID  AS AuthUs | 
| 738 | xxxx | localhost | community | Query   |    0 | Locked                        | SELECT t.DiscussionID  AS DiscussionID, t.FirstCommentID  AS FirstCommentID, t.AuthUserID  AS AuthUs | 
| 739 | xxxx | localhost | community | Query   |    0 | Locked                        | SELECT t.DiscussionID  AS DiscussionID, t.FirstCommentID  AS FirstCommentID, t.AuthUserID  AS AuthUs | 
| 740 | xxxx | localhost | community | Query   |    0 | Locked                        | SELECT t.DiscussionID  AS DiscussionID, t.FirstCommentID  AS FirstCommentID, t.AuthUserID  AS AuthUs |
+-----+---------+-----------+-----------+---------+------+-------------------------------+------------------------------------------------------------------------------------------------------+
as these queries are locking the table, subsequent queries are having to wait and thus stacking up taking available connections.  You may find that changing this table type to Innodb may help with this table locking issue.  You may need to discuss with your developers if this change would have an inverse affect to your applications.

As well, I have enabled slow query logging in:

/var/lib/mysqllogs/slow-log

which will log queries taking over 5 seconds to complete.  This information will help your developers to optimize any SQL queries and/or apply indexing where appropriate.

I have also put in the option in Apache:

MaxRequestsPerChild  1000

which will help to reduce the memory footprint of this service.

While it appears that the above changes helped with the non availabilty of MySQL, the server is still highly loaded.

Now, I always knew that the Vanilla 1 queries were hairy and could cause problems. I didn’t think it was going to happen any time soon, and I was hoping to get Vanilla 2 in place before this became an issue (Vanilla 2’s queries are much simpler and faster) – but it looks like that is not going to happen. Regardless, it would seem that my traffic has slowly and steadily been increasing at lussumo.com over the years. In December we peaked at 2.5 million page views for that month at lussumo.com alone, and we’ve maintained that amount of traffic almost every day since.

Obviously I could throw more RAM at the server as the Rackspace support person suggested – this seems to be a common answer to problems of this sort (we currently only have 1G of ram on the server), but I don’t know if that is the answer I should be looking for – especially considering that I’m already paying a lot of money for the server.

So, I am hoping that all of those who use Vanilla can step up to the plate and offer your expertise on how to resolve this issue. I am opening the doors and accepting any and all advice, questions, ideas on how to fix the problem.

Here is what I have tried so far:

* I reviewed the slow queries that mysql logged and found that 99% of them were Vanilla’s “comments page” and “discussions page” queries. I’ve uploaded a sample of the slow query log so you can see what queries are causing problems.

* I downloaded a copy of the Lussumo Community database to my local dev machine so I could get a good look at the tables, indexes, etc.

* I found that none of the indexes that are included with the current release of Vanilla 1 were applied on the tables (other than primary keys). This is probably due to the fact that I’ve just added columns as development has continued and never had a problem before now.

* I added the indexes that are shipped with the current release of Vanilla 1 to the community database. I found that this had little-to-no effect on the speed of the page-load (it might have even made the queries slower).

* I’ve created a script that converts all of the tables in the community db to innodb tables (as suggested by the rackspace tech). I’ve done some googling that has detailed both good and bad results of this type of change. It could start to throw fatal errors when data is being inserted (rather than while it’s being selected, as it is now). I have not yet run this script as I want to hear back from the community first.

* I’ve taken the community forums offline and enabled wp-cache on this blog so that everyone can have access to this blog post and be fully aware of the issue.

Help!

So, I am reaching out to you for help. No question is a dumb one. Any idea is welcome. Please share your expertise and help us to get this convoy back on the road…

Update

It turns out that I had forgotten to apply all of the indexes & optimizations to this database through the years that we’ve been online. The growth of our community, combined with poor indexing caused a couple of the tables to begin to lock. The LUM_User and LUM_UserDiscussionWatch tables in particular were locking. These tables are updated frequently with login information and discussion tracking information respectively. Because the tables were MyISAM type, all records would be locked when an update was applied to just a single row – this meant that all 9000+ user records would get locked whenever anyone’s “DateLastActive” field was updated, and all 90,000+ records in the LUM_UserDiscussionWatch table would get locked whenever anyone even looked at a single discussion (and the record of their view of that discussion was recorded).

To fix both of these issues, I changed their table types to InnoDB so that only the affected row should become locked when updates are applied.

I also analyzed the Discussions & Comments queries, which are (obviously) the most actively run queries in the application. The comments query was extremely slow. After running EXPLAIN on the query, I found that it was indexed incorrectly. For some reason the LUM_Comment table was using both the CommentID and the DiscussionID columns as it’s primary key. I removed the DiscussionID as a primary key and added it as a simple index. This allows the query to not scan the entire LUM_Comment table when performing the join to LUM_Discussion. I also found that the LUM_UserBlock table had no indexes at all, so I added those and was able to further reduce the query time. Here is a list of the changes that I made to the database for anyone who might be interested:

ALTER TABLE `community`.`LUM_Comment` DROP PRIMARY KEY,
 ADD PRIMARY KEY  USING BTREE(`CommentID`),
 ADD INDEX `comment_discussion`(`DiscussionID`);

ALTER TABLE LUM_UserBlock ADD INDEX (BlockingUserID);
ALTER TABLE LUM_UserBlock ADD INDEX (BlockedUserID);

ALTER TABLE LUM_User ENGINE=InnoDB;
ALTER TABLE LUM_UserDiscussionWatch ENGINE=InnoDB;

Thanks to Damien (Dinoboff) and Dave (Wallphone) for jumping in and offering some assistance.

Garden Preview Part IV: Views

Tuesday, December 2nd, 2008

Garden Preview Part I: Overview
Garden Preview Part II: Filesystem
Garden Preview Part III: Anatomy of a Request
Garden Preview Part IV: Views
Garden Preview Part V: Data
Garden Preview Part VI: Models & Forms
Garden Preview Part VII: Ajax
Garden Preview Part VIII: Plugins
Garden Preview Part IX: Roles & Permissions
Garden Preview Part X: User Registration
Garden Preview Part XI: Structure

Last week I described how requests to Garden are handled by the dispatcher. This week I’m going to explain how those requests are rendered by the requested controller.

Once a controller method is called to handle the request, how is the xhtml of the page put together? Views.

There are two types of views in Garden: “Views” and “Master Views”. A view relates directly to the controller method that called it and handles rendering content related to that request. You can typically think of a view as the content for that page. For example, if a Vanilla->Discussion->All() method is called, the view for that method would handle rendering all of the discussions. Everything that is rendered around the discussions is handled by the Master View.

The Master View allows you to create a consistent layout for the pages in the application. A single master view defines the look and feel and standard behavior for all of the pages (or a group of pages) in the application.

Let’s go back to the filesystem so you can get a better picture. In the following example, the request in the url would have been the following:

http://myserver.com/garden/profile/index/mark

So, this means that Garden’s “Profile” controller was requested, the “index” method was requested from the profile controller, and the first argument into the index method is “mark”. In other words, the request was: $ProfileController->Index('mark');

Let’s take a look at the profile controller’s index method:

public function Index($UserReference = '') {
   $UserModel = new UserModel();
   $this->User = $UserModel->GetByReference($UserReference);
   $this->Render();
}

Simply put, the index method grabs some data that can be later used in the profile controller’s “index” view, and then calls the Render() method. The Render method is defined on the base Controller class; the class from which all controllers are extended. The Render method performs the following tasks:

1. Finds and fetches the view.
2. Finds and fetches the css for the view.
3. Finds and renders the master view.

In the most simple example, the view will be as it appears in the image above: in a “profile” folder within the “views” folder, and named after the method that was called: /views/profile/index.php.

By default, the controller will fetch that view from the context of the FetchView method of the base controller class. In plain-English: the view file is included within a method on the Controller class called “FetchView”. That is why I placed the user data within a $this->User property in my $ProfileController->Index() method, above; so it could be accessible from within my view file. The view file likely will contain something as simple as this:

<h3>Basic Information</h3>
<dl class="Info">
   <dt>Joined</dt>
   <dd><?php echo $this->User->DateFirstVisit; ?></dd>
   <dt>Visits</dt>
   <dd><?php echo $this->User->CountVisits; ?></dd>
   <dt>Last Active</dt>
   <dd><?php echo $this->User->DateLastActive; ?></dd>
</dl>

It is important to note that I have not used a custom templating engine like smarty. This is because PHP already is a templating engine. Further to that point, I am only resorting to switching to PHP when echoing data to the screen, and I am not building large concatenated echo statements. All of these efforts are taken in order to make the theme author’s job easier.

Once the view is fetched, it is added to an asset collection. By default it is added to the “Content” asset collection. The rest of the page (the “Frame” of the page) is handled by the master view. By default, the “default.master” master view is used unless another master view is specified. In the image above you can see that there are a few different master views available in the Garden application: default, email, error, and setup. The css files for each of those master views are named accordingly, as well. So, the default.master view has a default.screen.css file, the error.master view has an error.screen.css file, etc. Again, in the most simple of examples, the master view is located in the “view” folder of the application, and the related css files are located in the “design” folder of that application.

Here is what the default.master view looks like:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-ca">
<head>
	<?php $this->RenderAsset('Head'); ?>
</head>
<body<?php echo Attribute(array('id' => $BodyIdentifier, 'class' => $this->CssClass)); ?>>
   <div id="Frame">
      <div id="Title"><a href="<?php Link('garden/user/browse'); ?>"><span><?php echo $this->Head->TitlePrefix; ?></span></a></div>
      <?php $this->RenderAsset('Menu'); ?>
      <div id="Content"><?php $this->RenderAsset('Content'); ?></div>
      <div id="Foot"><?php $this->RenderAsset('Foot'); ?></div>
   </div>
</body>
</html>

As you can see, the master view handles the basic structure of a page. As I mentioned above, by default the view that is fetched by the controller is added to an asset collection called “Content”, and that asset collection is rendered in this master view when the $this->RenderAsset('Content') method is called – right there in the middle of the master view.

As you can see in the master view above, there are a number of different asset collections available for application and plugin authors (Head, Menu, Content, and Foot). An application author has the ability to add as many of these as he/she desires and add to them as necessary. The idea behind these asset collections is that while the basic request can be handled and placed into the “Content” asset (ie. the profile information being displayed from the example above), there are still a lot of other elements a person may want on a page. Plugin authors may want to add assets to the head, menu, or foot. They may even want to add assets to the content collection. Garden itself has other UI components that get rendered in the head and menu asset collections (which will be discussed in a later preview).

So, in the most basic example, the “index” view is requested from it’s controller’s view folder, the default.screen.css file is requested from the application’s design folder, and the master view is requested from the root of the application’s “view” folder. What other ways could the views and css have been retrieved?

The controller uses Garden’s FileSystem object to search the application for the appropriate file to handle the request. Whatever the filesystem object finds first, it will use.

Views are retrieved with the following precedence:

1. An explicitly defined path to a view.
ie. a plugin’s custom view location
2. Application-specific theme view.
ie. /path/to/garden/themes/theme_name/app_name/views/controller_name/view.php
3. Garden-wide theme view.
ie. /path/to/garden/themes/theme_name/views/controller_name/view.php
4. Application default.
ie. /path/to/garden/applications/app_name/views/controller_name/view.php

Master views are retrieved with the following precedence:

1. Application-specific theme view.
ie. /path/to/garden/themes/theme_name/app_name/views/default.master
2. Garden-wide theme view.
ie. /path/to/garden/themes/theme_name/views/default.master
3. Application default.
ie. /path/to/garden/applications/app_name/views/default.master
4. Garden default.
ie. /path/to/garden/applications/garden/views/default.master

CSS files are retrieved with the following precedence:

1. Application-specific css.
ie. /path/to/garden/themes/theme_name/app_name/design/default.screen.css
2. Garden-wide theme view.
ie. /path/to/garden/themes/theme_name/design/default.screen.css
3. Application default.
ie. /path/to/garden/applications/app_name/design/default.screen.css
4. Garden default.
ie. /path/to/garden/applications/garden/design/default.screen.css

You might be wondering things like: can I change the view or master view that handles my request on the fly? Of course you can! Everything in Garden was written so that you have total control over what comes out on the other end. In my example above, I could change my views on the fly with something like this:

public function Index($UserReference = '') {
   $UserModel = new UserModel();
   $this->User = $UserModel->GetByReference($UserReference);
   $this->View = "somethingelse"; // Use /views/profile/somethingelse.php to handle the content
   // ... or ...
   $this->View = "/some/other/view.php"; // Use some custom view to handle the content
   $this->MasterView = "setup"; // Use the setup master view to render my contents
   $this->Render();
}

Views are a lot to take in, and they certainly might be daunting upon first read. But I can guarantee that I have found them to be utterly simple and flexible to use. Think of the possibilities you can accomplish with views! You could create a master view that handles rendering of RSS feeds. You could port your request method to use your RSS master and render all of your contents with a custom RSS view. You could grab the email master view and send out your content to some email address(es) before rendering. You could bypass the rendering altogether and deliver json data. The possibilities are endless, and that is the real power of views.

jQuery Expression Reference

Thursday, November 27th, 2008

Plans

Does anyone know if there is a way to get the expression that returned an object in jQuery from within it’s handler?

Example:

$('body > a').click(function() {
   // Is there any way I can alert 'body > a' in here?
   // Perhaps something like: alert($(this).expression); ?
});

Garden Preview Part III: Anatomy of a Request

Monday, November 24th, 2008

Garden Preview Part I: Overview
Garden Preview Part II: Filesystem
Garden Preview Part III: Anatomy of a Request
Garden Preview Part IV: Views
Garden Preview Part V: Data
Garden Preview Part VI: Models & Forms
Garden Preview Part VII: Ajax
Garden Preview Part VIII: Plugins
Garden Preview Part IX: Roles & Permissions
Garden Preview Part X: User Registration
Garden Preview Part XI: Structure

mod_rewrite was an afterthought in Vanilla 1. As a result, I’ve always felt that the mod_rewrite mappings were sloppy and really just didn’t make much sense. In Garden, the way pages are accessed is completely different, and definitely deserves explaining. Let’s start by looking at a typical url request in Garden without mod_rewrite enabled:

http://localhost/garden/default.php/garden/settings/configure

Let’s look closer at each part of the request:

http://localhost/garden/default.php: On my development server, I’ve created a folder called garden, and placed all of the garden files I discussed in last week’s preview in it. As you can see, the default.php file that handles all requests is sitting within that folder’s root.

/garden: The next part of the url is the application that is being requested. In this case, we are requesting the “garden” application within the garden framework’s application folder.

/settings: The next part of the url is the controller that is being requested within the garden application. In this example, we are requesting a controller called “Settings”.

/configure: the final part of the url in this example is the method within the “Settings” controller that we are calling. In this example, we are calling $SettingsController->Configure();.

If you were to map this request to the filesystem, it would be like this:

Request

I could take the same request and add information to the end, like this:

http://localhost/garden/default.php/garden/settings/configure/arg1/arg2/argn

And it would take any other parameters after the controller’s configure method as if they are arguments being passed into that method. In other words, the above request would be essentially the same as calling that method like so:

$SettingsController->Configure('arg1', 'arg2', 'argn');

This can get pretty handy when doing things like paging through records, or specifying which user to load in a page. For example, to edit a user, the url would be:

http://localhost/garden/default.php/garden/user/edit/mark

Which would map to:

$UserController->Edit('mark');

Analyzing the Request
All requests are handled through the Dispatcher class. The dispatcher class looks at the request (everything after default.php) and tries to figure out the best way to handle it. As far as the dispatcher is concerned, in a perfect world the request would always include the application name, the controller name, and the method name. But in reality you might not always want all three items in your url. For example, you might want your application invisible to the user – so that, for example, a request to vanilla’s discussion list goes to

http://yourwebsite.com/default.php/discussions/all

… instead of …

http://yourwebsite.com/default.php/vanilla/discussions/all

Furthermore, you might want your controller’s method hidden as well. So you could end up with something like:

http://yourwebsite.com/default.php/discussions

The dispatcher can handle all of these and a lot more. First of all, when no method is defined in the request, the dispatcher assumes that you are calling the “index” method of the controller. So, a request to:

http://yourwebsite.com/default.php/vanilla/discussions

… is the same as calling …

$DiscussionsController->Index();

Furthermore, when the dispatcher gets a request that doesn’t include the application name, it starts to look through all of the enabled applications for the requested controller name. As soon as it finds one, it records it’s mapping in the cache folder and calls it appropriately. There is the possibility that two different applications could have the same controller name, and in that case, it would return the first application’s controller that it found.

Probably the nicest thing about this method of handling requests is that a 6-line .htaccess file:

<IfModule mod_rewrite.c>
   RewriteEngine On
   RewriteCond %{REQUEST_FILENAME} !-d
   RewriteCond %{REQUEST_FILENAME} !-f
   RewriteRule ^(.*)$ default.php/$1 [QSA,L]
</IfModule>

… allows you to remove the default.php from the url and makes all requests look like this:

http://yourdomain.com/application/controller/method

Or, in it’s simplest form:

http://yourdomain.com/controller

Next
In the next garden preview, I’ll be getting into how controllers work, how views are found and rendered, and how master views can let you customize layouts between applications and themes.

Garden Preview Part I

Tuesday, November 11th, 2008

Garden Preview Part I: Overview
Garden Preview Part II: Filesystem
Garden Preview Part III: Anatomy of a Request
Garden Preview Part IV: Views
Garden Preview Part V: Data
Garden Preview Part VI: Models & Forms
Garden Preview Part VII: Ajax
Garden Preview Part VIII: Plugins
Garden Preview Part IX: Roles & Permissions
Garden Preview Part X: User Registration
Garden Preview Part XI: Structure

What's growing in your Garden?

Almost two years ago I started thinking about rewriting the Lussumo Framework, the framework upon which Vanilla is built. After a year of heavy contemplation, I finally started development on the new version, which I have come to call “Garden”.

My concept for the Garden framework is not to be competition for the likes of The Zend Framework, CakePHP, or even CodeIgniter (to name a few of the other great PHP frameworks), but something both a little less and a little more. My idea was to create a framework that didn’t solve every problem, but just most of the common problems we face when developing simple web applications. I wanted to build a framework that had user and permission management built right in. I wanted to build a framework with which you could easily create new applications and throw them alongside any others – and all users, roles, permissions, preferences, plugins, and themes could play nice together.

I can safely say that I have accomplished that task.

Recently I had an active member of the Lussumo Community contact me and ask if his plugins for Vanilla 1 would work with Vanilla 2. It was hard to give him the answer that, no, none of the (over 400) plugins for Vanilla 1 would work with Vanilla 2. I had to explain to him that developing in Garden will be easier, faster, and take far less code than it used to. His questions made me realize that I need to step up and explain Garden to all of the great Lussumo Developers out there.

In this part of the Garden preview, I am going to give a brief overview of some of the main goals of Garden. In the parts to follow, I will go into great detail of each part of the framework, explaining the theories behind them and how they can be used by developers to build new applications, plugins and themes.

Extending Garden
In Vanilla 1 there were a number of ways that developers could add to the application. I called these Add-ons and they fell into the categories of: extensions, themes, styles, and languages.

When I started thinking about how to add to Garden, I realized that I was missing a key ingredient: applications. Vanilla is just one application, and I wanted to be able to use Garden to do more than just Vanilla. I also realized that almost no-one ever worked with themes (Hell, I started to do one and found it too difficult), so I’ve taken the concept of themes, simplified it and munged it together with styles. Finally, I realized that the lay-person doesn’t really know what an extension is, but everyone knows what a plugin is.

So, in Garden, add-ons will fall into the following categories: applications, plugins, and themes. If you’re wondering what happened to languages, you’ll read about that in a little bit.

Speed
Probably the biggest criticism of Vanilla 1 was that it slows down under heavy load. I absolutely refuse to give up on object-oriented programming in PHP, so I’ve been exclusively working with the latest releases of PHP; taking advantage of it’s speed with OO programming. I’ve also done a ton of research on code optimization for PHP. Rest assured, Garden is very fast. But application speed isn’t where the conversation should end.

I’ve also worked very hard to produce libraries that are fast and easy to work with. I wanted it to take fewer lines of code to accomplish both difficult and common tasks. For example, I’ve completely rewritten the database libraries so that building queries is easier, and it integrates with the MVC code so that models are instantly created simply by referring to the tables they represent. This means that data validation can be automated based on the properties of columns in database tables – just one of the many places that Garden will speed up your development time.

Models, Views, and Controllers
When I was writing Vanilla 1, I didn’t know what an MVC pattern was. Little did I know that I was re-inventing the wheel at the time. Vanilla 1 used a bloated, red-headed step-child of the MVC design pattern. Garden is still based on the MVC design pattern, but it is done with far less code and in a much more elegant way. When I began re-thinking the page delivery model for Garden, I decided to go out there and research what the competition was doing. I read books on design patterns, read blog entries about everything under the sun by developers from all around the world. I looked into every framework I could get my hands on.

I ended up being a little wowed by the simplicity of CakePHP and CodeIgniter’s approaches to MVC. In the end I’ve borrowed ideas from both and added a few of my own. The result is views that can be delivered in-page or via ajax popups. Dynamic content delivered in a myriad of ways. Fully integrated protection from exploits and CSRF attacks. Master/container views that allow theme authors to quickly and easily put Garden applications right into their own applications. Simple models that can be generated on the fly. And custom routing so that pointing users to different parts of your applications is dead simple.

Membership, Roles & Permissions
While I had done some very different things with roles & permissions in Vanilla 1, I had also painted myself into a corner that made expansion difficult. In Garden, adding new permissions is dead easy, and users can be assigned to more than just one role. There are many different ways to set up registration and add new users. Four out-of-the box options are: simple (apply and you’re in), captcha, administrative approval, and by invitation.

Localization
While I believe that localization was one of the things I got right in Vanilla 1, it didn’t have any follow through. The result of this lack of follow-through is that Vanilla 1 is still released with just one language available. Another problem is that whenever the application changed, or new extensions were added, there would be the need for new translations to be in place – but no way of notifying the translation authors that these translations were needed.

In Garden, the localization files are roughly the same. One difference is that the folder name for a set of localization files is based on the already known and followed internationalization codes used everywhere on the internet. So, for example, the locale folder I’ve been working with is labeled “en-CA” for “Canadian English”. The major difference is that localization will no longer be represented on the add-ons site. Instead, the Lussumo community will be in charge of localization.

One of the many applications I am building with Garden (alongside Vanilla, of course) is a localization application that monitors any translation that is made by Garden and records it in a database. Any member of the community can then work on any set of locale definitions they want to. It will be a wiki-like handling of localization. Whenever a full set of translations is finished and approved by a group of members, it will be added to the appropriate application or plugin and be included in the downloadable application package.

So Much More
With literally years of deep pontification transformed into code, there is so much to tell you. I still haven’t even touched upon Garden’s error-handling, out-of-the-box friendly urls, new licensing model (still open source and free), database engine integrations, cache management, jQuery! I haven’t even mentioned menu organization, plugin management, enforced plugin and application requirements, automated setup and installation. And I have only hinted at the other applications that are coming along with garden besides Vanilla. I never even told you about the hosted solution I’ll be offering when Garden and Vanilla are released!

The long and short of it is that the hard work is done. Aside from cleanup and debugging, the Garden framework is up and running and I am now into application, plugin, and theme development. My hope is to have an entirely new lussumo.com launched within the first quarter of the new year. In the meantime, keep checking back here for more in-depth previews of Garden.