Monday, September 29, 2008

Upload Manager: A Radiant CMS extension for batch uploads

Starting to work with Radiant, I found out that it is a very powerful CMS. However, it lacks an important administrative feature: file upload. A common requirement for any site admin is to be able to upload files and link for them in the site pages. So, starting to add this feature to Radiant, I thought I should extract it as a generic extension for uploading multiple files easily.

Radiant Upload Manager is an extension for Radiant CMS that enables the admin to upload multiple files at once in a handy way. It is built on the SWFUpload Flash library, enabling selecting multiple files at a time for upload, and preserving the same HTML/CSS interface as the rest of the admin layout.

The idea of the uploader is pretty simple. A hidden Flash object is wrapped in a JavaScript wrapper. The wrapper initializes the flash open file dialog, which has the feature of multiple file selection. When the user chooses the files to upload, the flash object starts uploading them, giving the wrapper a set of useful callbacks (uploadStart, uploadProgress, ..etc) to update the HTML view.

You can download the extension at its GitHub page. For installation and usage, refer to the README



Thursday, September 25, 2008

Extending FCKeditor Radiant extension

FCKeditor is one of the most powerful rich text editors out there. However, FCKeditor extension for Radiant lacks a very important feature; that is enabling the site admin to change the editor's interface language. That feature was a requirement in a project I was working on, so I decided to dive into the extension's code to see what's going on in there, trying to figure out a way to hack that feature in.

Initially, what I had already known is that FCKeditor Radiant extension allows changing the default language, text direction, and other configuration through editing /vendor/extensions/fckeditor/app/views/fckeditor/config.js.erb. The following line was of my concern:

FCKConfig.DefaultLanguage = 'en' ;

This is fine for default configuration, but how could I change these configurations at runtime? Taking a look at the resulting html of the editor, I noticed that the editor is loaded in an iframe element with id 'part_0_content___Frame'. What jumped first to mind is that I can figure out a way to reload the iframe, passing an extra language parameter to the page it loads. Then, receiving this parameter by javascript, I could change the configuration that I want before the iframe reloads.

So, the solution was to extend the extension. I needed to add a button to the page-editing page to fire the switch-language functionality. Editing the extension file /vendor/extensions/fckeditor/fckeditor_extension.rb, I added this line to the extension's activate method:

admin.page.edit.add :part_controls, "language_btn"

This line adds the partial /vendor/extensions/fckeditor/app/views/admin/page/_language_btn.html.erb to the part-controls region og the page-editing page. Then adding that partial, adding to it a button whose onclick implementation reloads the iframe with extra parameter. It could roughly look like this:

frame = $('part_0_content___Frame');
frame.src = frame.src + "&language=ar"

The remaining job is to intercept that extra parameter; editing config.js.erb:

//getting params from the url
params = window.location.href.split('?')[1].split('&');

//checking each param
for (var i=0; i<params.length; i++){
pair = params[i].split('=');
name = pair[0];
value = pair[1];
if (name == 'language'){
FCKConfig.DefaultLanguage = value ;
}
}


So when the button we added gets clicked, the iframe will reload. This forces reloading of its internal script tags, among which is our code snippet added to config.js.erb. This snippet checks for the language parameter and acts accordingly. When the editor is reloaded, it reloads with the new default language.



Tuesday, September 23, 2008

Rails plugins: Metaprogramming vs Generators

One of the most powerful features of Rails is plugins. Plugins enable developers to make generic extensions to Rails applications that others can benifit from. One could use two different approaches to add new logic/aspects to a Rails application through a plugin: metaprogramming and generators. This entry is not a tutorial about writing Rails plugins. Assuming that you know the basics of metaprogramming in Ruby, and using a very basic example, we'll try to conclude a simple comparison between the two approaches that can be generalized.

Assume that your plugin relies on adding some logic as a before_filter to all controllers. You have that logic in a module that you want to mix in ApplicationController. Also you want to declare that filter. Using the first approach, Ruby metaprogramming, you can apply virtually any changes to the existing classes, modules and even objects. The following two lines can help in our example:
ApplicationController.class_eval "include MyModule"
ApplicationController.class_eval "before_filter :my_filter"

Basically, what we have just done is that we told the interpreter to dynamically evaluate those two lines in the context of the ApplicationController. Thos two lines are NOT lexically added to the definition of the class. It's like they're added at runtime.

Another approach we could use is a Rails generator. A generator, in a nutshell, lexically adds generated code to the existing files. We could do the same job in the example using a generator as follows:


class MyGenerator < Rails::Generator::Base

def manifest
record do |m|
m.gsub_file 'app/controllers/application.rb', /(#{Regexp.escape("class ApplicationController < ActionController::Base")})/mi do |match|
"#{match}\n include MyModule\n before_filter :my_filter\n"
end
end
end

end

Basically, what we have just done is that we searched for the line class ApplicationController < ActionController::Base and LEXICALLY added the two lines next to it. Of course, this generator has to be run to apply its changes.
ruby script/generate my_generator


The tradeoff between generation and metaprogramming is simply the tradeoff between being mixed but explicit and being isolated but subtle. Generation may result in mixing some logic, but has a major advantage of being explicit. The resulting code is explicitly added to the project files, and can even be modified by the developer using the plugin. Metaprogramming forces separation of concerns, but is done in a subtle way that could lead in a time waste for the developer investigating what happened behind the scenes.

Of course, there are situations where only generation could be used, like adding migrations, routes and other stuff. Take a look at the features Rails generators give.

Rails/Generator/Commands/Base
Rails/Generator/Commands/Create
Rails/Generator/Commands/Destroy



Wednesday, September 17, 2008

Rails: RESTful namespaces shall set you free

Being one of the its admirers, I try to conform to REST almost all the time, thanks to Rails support. One of the problems in following the REST model is the problem of resource naming. In its simplest forms, REST is about mapping each resource to a unique name with a set of conventional urls. The problem appears in a case when you need more than one representation to some/all of your application resources (in the same format), or in other words, if some resources need to have colliding names.

A famous example of such a case is application with administration modules. Regularly, most of the resources are needed to be duplicated in another representation for the admin. For example, you already have a resource named 'Item' and you need another one with the same name and different representation for the admin.

Rails solves this problem with a handy feature: RESTful namespaces. RESTful routes could be managed in different namespaces to avoid names collision. What's elegant about it is that when you nest a resource in a namespace, it's mapped directly to an expected controller class that's a member in a module whose name is the same as the namespace. Also, the namespace contributes to the url helpers just as nested resources.

For example, while the configuration:
map.resources :items

maps to ItemsController and generates a set of helpers/routes like:

items_path() => /items
item_path(id) => /items/id
new_item_path() => /items/new
edit_item_path(id) => /items/id/edit

, The configuration:

map.namespace :admin do |admin|
admin.resources :items
end

maps to Admin::ItemsController and generates a set of helpers/routes like:

admin_items_path() => /admin/items
admin_item_path(id) => /admin/items/id
new_admin_item_path() => /admin/items/new
edit_admin_item_path(id) => /admin/items/id/edit


So, essentially what happens is a total collision removal(resource names, controller names, helpers, urls) with a minimal effort. I gotta admit, I love Ruby in Rails.


Monday, September 15, 2008

HTTP authentication and Rails

There are situations where a Rails application is almost complete, only lacking the users-authentication aspect. In such situations, sometimes elegant/complex authentication schemes are not really needed. For example, consider a simple administration panel that is developed for managing another application. In such a case, HTTP basic authentication could be very handy.

HTTP basic authentication doesn't rely on cookies like the casual session scheme. It simply depends on the fact that the client will send all its requests accompanied by an authorization header that represents the username and password. Web browsers implement HTTP basic authentication in a comfortable way. When a user requests a page that requires HTTP authentication, the response status is 401 unauthorized. the browser automatically detects this status and prompts the user for a username and password. The browser then repeats the request after adding the authorization header, and remembers adding it to all subsequent requests to the same domain name. Hence, a session is emulated.

Rails provides a very handy way for implementing HTTP authentication as a new aspect. The method authenticate_or_request_with_http_basic extracts the username and password from the authorization header and passes to the given block as parameters. By applying a before filter as the following, a simple HTTP authentication can be added to a complete Rails application as a new isolated aspect:


def authenticate_admin
authenticate_or_request_with_http_basic do |username, password|
admin = Administrator.authenticate(username, password)
if admin.nil?
render :text => "", :status => :unauthorized
return false
end
true
end
end


where Administrator.authenticate returns an admin from the database if username and password match any, and nil otherwise.


Wednesday, September 3, 2008

webistrano_privileges: a Rails plugin for Webistrano

Webistrano is a widely used tool for automated deployment of Rails applications. It makes Rails people's life much easier. However, one of its most outstanding flaws is the lack for user-access control. All registered users can control all projects.

webistrano_privileges is a simple Rails plugin that I developed, introducing access control to Webistrano-1.3. After applying the plugin to your working webistrano project copy, and running two shell commands, Webistrano will be accomodating a simple access control scheme. Admins can manipulate all projects. Non-admins can manipulate only THEIR projects. Admins can add and remove users to projects.

What the plugin basically does is:
- it generates a migration for a many-to-many relation between users and projects.
- it generates a route and a controller for adding and removing users from projects
- it replaces some views to present the added functionality.
- it introduces some logic to secure unauthorized access to projects from non-related users.

You can get the plugin from its github page. After downloading, only two steps are required:

- run the generate command, accept whenever prompted for overwriting existing files:
ruby script/generate privileges_extensions

- migrate
rake db:migrate RAILS_ENV=production



Monday, September 1, 2008

Rails: simple_localization hates observers

One of the most helpful and simple-to-use Rails plugins is simple_localization. It makes the job of localizing a Rails application a lot easy and straight forward. It implements localization through a set of features. One of the most commonly used features is localized ActiveRecord error messages. Its use is for localizing the standard error messages generated upon ActiveRecord validations ("can't be blank", "is too short", ..etc).

Working on a Rails project, after localizing the whole application, everything was working just fine in the development environment. However, when trying it in production environment, the locaized-error-messages feature didn't seem to work properly. Error messages for some (not all) of the models were not translated. For my surprise, Those models were exactly those being observed by declared observers.

Now, what seems to be the problem? The problem is that the implementation of localized-error-messages feature is just as simple as follows; it just overrides ActiveRecord::Errors.default_error_messages with the translated version. (simple_localization/lib/features/localized_error_messages.rb - line 26):

ActiveRecord::Errors.default_error_messages = ArkanisDevelopment::SimpleLocalization::LangSectionProxy.new :sections => [:active_record_messages],
:orginal_receiver => ActiveRecord::Errors.default_error_messages do |localized, orginal|
orginal.merge localized.symbolize_keys
end


The problem is that, during Rails initialization, observers declared in the standard way in the environment's configuration (and their associated models) are loaded before plugins. (config/environment.rb):
Rails::Initializer.run do |config|
config.active_record.observers = :user_observer
end

Using this line, we are not starting the observers; we are just telling Rails what observers to load when initializing. We don't control its loading order with respect to the loading plugins. By default, observers load first, loading their models accordingly. Now those models are completely loaded, including their default_error_messages way before the plugin is allowed to override them. This problem occures only in production environment, becuse in development environment, models are reloaded with each request, inheriting the overridden default_error_messages.

To work around this problem, we need to deliberately postpone loading the observers until initialization is done. This can be done by explicitly setting the observers in the after_initialize block instead of the above standard scheme:
Rails::Initializer.run do |config|
config.after_initialize do do
ActiveRecord::Base.observers = :user_observer
end
end

Like this, observers will load after initialization is finished, and all models will inherit the overridden default_error_messages.