Tuesday, July 19, 2011

Renaming Capabilities in Moodle 1.9.x

On the off chance you need to rename moodle capabilities

eg.
block/mycustomblock:viewpages
to
block/mycustomblock:viewadminpages

Here is what you have to do:

1) edit upgrade.php and add two create sql statement

eg.
$sql = "UPDATE {$CFG->prefix}capabilities
SET name = 'block/mycustomblock:viewadminpages'
WHERE name = 'block/mycustomblock:viewpages'";

$sql =
"UPDATE {$CFG->prefix}role_capabilities
SET capability = 'block/mycustomblock:viewadminpages'
WHERE capability = 'block/mycustomblock:viewpages'";

2) execute_sql($sql) both of them

3) The last part is that you MUST also change the mycustomblock/db/access.php to include the new capability type. This is because Moodle will compare capabilities in access.php to the ones stored in the database.
a) Moodle will search for new capabilities.
b) Moodle will delete them from the mdl_capabilities table, then add them as a new row.
c) Moodle will then delete the capability from mdl_role_capabilities. (I believe this is to preserve the referential integrity of foreign keys between mdl_role_capabilities.capability and mdl_capabilities.name)

In access.php

Add :
'block/mycustomblock:viewadminpages' => array(
        'riskbitmask' => RISK_PERSONAL,
        'captype' => 'read',
        'contextlevel' => CONTEXT_MODULE,
        'legacy' => array(
        )
    ),


Look at upgrade_blocks_plugins() in blocklib.php for more info.

Tuesday, July 12, 2011

Make Rails Devise Routes Look Better

AKA remap the routes in Rails Devise Authentication

Anyone who explores Devise in their rails app will find that all the generated routes fall under a single path. Since most people choose the User model for saving user information, devise will put all these methods under /users/

rake routes
        new_user_session GET    /users/sign_in(.:format)       {:action=>"new", :controller=>"devise/sessions"}
            user_session POST   /users/sign_in(.:format)       {:action=>"create", :controller=>"devise/sessions"}
    destroy_user_session DELETE /users/sign_out(.:format)      {:action=>"destroy", :controller=>"devise/sessions"}
           user_password POST   /users/password(.:format)      {:action=>"create", :controller=>"devise/passwords"}
       new_user_password GET    /users/password/new(.:format)  {:action=>"new", :controller=>"devise/passwords"}
      edit_user_password GET    /users/password/edit(.:format) {:action=>"edit", :controller=>"devise/passwords"}
                         PUT    /users/password(.:format)      {:action=>"update", :controller=>"devise/passwords"}
cancel_user_registration GET    /users/cancel(.:format)        {:action=>"cancel", :controller=>"devise/registrations"}
       user_registration POST   /users(.:format)               {:action=>"create", :controller=>"devise/registrations"}
   new_user_registration GET    /users/sign_up(.:format)       {:action=>"new", :controller=>"devise/registrations"}
  edit_user_registration GET    /users/edit(.:format)          {:action=>"edit", :controller=>"devise/registrations"}
                         PUT    /users(.:format)               {:action=>"update", :controller=>"devise/registrations"}
                         DELETE /users(.:format)               {:action=>"destroy", :controller=>"devise/registrations"}
             user_unlock POST   /users/unlock(.:format)        {:action=>"create", :controller=>"devise/unlocks"}
         new_user_unlock GET    /users/unlock/new(.:format)    {:action=>"new", :controller=>"devise/unlocks"}
                         GET    /users/unlock(.:format)        {:action=>"show", :controller=>"devise/unlocks"}

Looking at this, I wanted to customize some of the routes so they are located elsewhere in my application. eg. Move the /users/sign_in to /login and /users/sign_up to /signup

Devise and rails routes offers a few methods of doing this which are mentioned on the Devise wiki pages, but the problem I ran into was how the registration controller was mapping over top of the users controller routes.

user_registration POST   /users(.:format)               {:action=>"create", :controller=>"devise/registrations"}
   new_user_registration GET    /users/sign_up(.:format)       {:action=>"new", :controller=>"devise/registrations"}
  edit_user_registration GET    /users/edit(.:format)          {:action=>"edit", :controller=>"devise/registrations"}
                         PUT    /users(.:format)               {:action=>"update", :controller=>"devise/registrations"}
                         DELETE /users(.:format)               {:action=>"destroy", :controller=>"devise/registrations"}
             user_unlock POST   /users/unlock(.:format)        {:action=>"create", :controller=>"devise/unlocks"}

I would like new_user_registration to point to /signup, and user_registration to /signup too. This is cause when the form is submitted, and an error occurs we want the use to remain on the /signup URL. After some help from this post on google groups:

http://groups.google.com/group/plataformatec-devise/browse_thread/thread/cfa98fd217d558e6

I ended up with these devise routes: /login, /logout, and /signup and it puts some of the registration routes under /register, thereby leaving the user actions for my users controller and not for devise. Pretty now.

devise_for :user, :path => '', :path_names => { :sign_in => 'login', :sign_out => 'logout'}, :skip => [:registration] do
    scope :controller => 'devise/registrations' do      
      get :cancel, :path => 'users/cancel', :as => :cancel_user_registration
      post :create,  :path => 'signup', :as => :user_registration
      get  :new,     :path => 'signup' , :as => :new_user_registration
      get :edit,    :path => 'users/edit', :as => :edit_user_registration
      put :update, :path => 'users/edit', :as => :update_user_registration
      delete :destroy, :path => 'users'
    end
  end 
 
 
rake routes
cancel_user_registration GET    /users/cancel(.:format)   {:action=>"cancel", :controller=>"devise/registrations"}
       user_registration POST   /signup(.:format)         {:action=>"create", :controller=>"devise/registrations"}
   new_user_registration GET    /signup(.:format)         {:action=>"new", :controller=>"devise/registrations"}
  edit_user_registration GET    /users/edit(.:format)     {:action=>"edit", :controller=>"devise/registrations"}
update_user_registration PUT    /users/edit(.:format)     {:action=>"update", :controller=>"devise/registrations"}
                 destroy DELETE /users(.:format)          {:action=>"destroy", :controller=>"devise/registrations"}
        new_user_session GET    /login(.:format)          {:action=>"new", :controller=>"devise/sessions"}
            user_session POST   /login(.:format)          {:action=>"create", :controller=>"devise/sessions"}
    destroy_user_session DELETE /logout(.:format)         {:action=>"destroy", :controller=>"devise/sessions"}
           user_password POST   /password(.:format)       {:action=>"create", :controller=>"devise/passwords"}
       new_user_password GET    /password/new(.:format)   {:action=>"new", :controller=>"devise/passwords"}
      edit_user_password GET    /password/edit(.:format)  {:action=>"edit", :controller=>"devise/passwords"}
                         PUT    /password(.:format)       {:action=>"update", :controller=>"devise/passwords"}
             user_unlock POST   /unlock(.:format)         {:action=>"create", :controller=>"devise/unlocks"}
         new_user_unlock GET    /unlock/new(.:format)     {:action=>"new", :controller=>"devise/unlocks"}
                         GET    /unlock(.:format)         {:action=>"show", :controller=>"devise/unlocks"}  


Edit: A quirk with devise is that the update_user_registration uses the same action="{URL}" as user_registration no matter what is defined in the routes. This causes the action for update_user_registration to send the put to /signup when we want it to go to /user/edit. The solution is to edit the registration/edit.html.erb and change
:url => registration_path(resource_name)
to
:url => :update_user_registration

.