-
Notifications
You must be signed in to change notification settings - Fork 23
Adding a page widget
Want to add a page widget to Rucksack? Well here’s how you do it.
Widgets are essentially the content in a Rucksack page. While they are linked to the page by a belongs_to relationship, they are positioned via another model, the PageSlot.
In order to add a Widget to RuckSack, you’ll need to implement the following:
- A model for the Widget
- A RESTful controller for the Widget
- JavaScript code to handle insertion and editing
Then to link it into the system:
- Add a route for it in “config/routes.rb”
- Add it to “self.widgets” in the Page model
There are a few important things to include in the model. These are:
- The associations with the page and page_slot
- The associations with the application_logs
- Handlers for create, update, and destroy to add application logs
- object_name to display the objects name
- view_partial which is used to display the object
- form_partial which is used to display the objects form on the InsertionBar tablet (set to nil if you want to create the object as-is)
- duplicate method which is called when the page is being duplicated
- The permissions handlers (technically controller-specific, so not necessarily needed)
class Cake < ActiveRecord::Base
belongs_to :page
has_one :page_slot, :as => :rel_object
has_many :application_logs, :as => :rel_object, :dependent => :destroy
belongs_to :created_by, :class_name => 'User', :foreign_key => 'created_by_id'
belongs_to :updated_by, :class_name => 'User', :foreign_key => 'updated_by_id'
after_create :process_create
before_update :process_update_params
before_destroy :process_destroy
def process_create
ApplicationLog.new_log(self, self.created_by, :add)
end
def process_update_params
ApplicationLog.new_log(self, self.updated_by, :edit)
end
def process_destroy
ApplicationLog.new_log(self, self.updated_by, :delete)
end
def object_name
"A delicious cake"
end
def view_partial
"cake/show"
end
def self.form_partial
nil
end
def duplicate(new_page)
new_cake = self.clone
new_cake.created_by = new_page.created_by
new_cake.page = new_page
new_cake.save!
new_cake
end
# Common permissions
def self.can_be_created_by(user, page)
page.can_add_widget(user, Cake)
end
def can_be_edited_by(user)
self.page.can_be_edited_by(user)
end
def can_be_deleted_by(user)
self.page.can_be_edited_by(user)
end
def can_be_seen_by(user)
self.page.can_be_seen_by(user)
end
end
To start off with, you’ll need a standard RESTful rails controller. There are a few specifics which need to be addressed
- JavaScript needs to be returned on js requests for edit, create, update and destroy
- Permissions need to be enforced with a suitable rejection mechanism
For edit, make sure you return something like this:
page.replace_html "page_slot_#{@cake.page_slot.id}", :partial => 'form'
For create, you’ll first need to make sure you create your widget with a slot assigned using something like this:
# Calculate target position
calculate_position
# Make the darn cake
@cake = @cake.separators.build(params[:separator])
@cake.created_by = @logged_user
saved = @cake.save
# And the slot, don't forget the slot
save_slot(@cake) if saved
@cake.save # ...
Then for the js handler, something like this should suffice:
page.insert_html((@insert_before ? :before : :after),
@insert_element,
"<div class=\"pageSlot\" id=\"page_slot_#{@slot.id}\" slot=\"#{@slot.id}\"></div>")
page.replace_html("page_slot_#{@slot.id}",
:partial => 'pages/slot',
:locals => {:object => @cake,
:slot => @cake.page_slot})
page.call "Page.makeSortable"
Note the call to “Page.makeSortable”, which makes sure that the new page widget can be sorted.
For update, this should suffice for the js request:
page.replace_html "page_slot_#{@cake.page_slot.id}",
:partial => 'pages/slot',
:locals => {
:object => @cake,
:slot => @cake.page_slot
}
page.call "Page.makeSortable"
For destroy, you can simply return this:
page.remove "page_slot_#{@slot_id}"
For the show partial, the only special thing you will need is to assign a hoverhandler attribute to any element for which you want the page slot’s hover handle to pop up.
- hhandle = "page_slot_handle_#{object.page_slot.id}"
.pageCake{:hover_handle => hhandle}
%img{:src => 'cake.png', :alt => 'A lie. A lie.'}
For the bare essentials, all you need is a click handler registering for ‘.add_(Insert class of your widget here)’. Make sure you add the bind to the bind function in Page, and the unbind to the unbind function in Page.
var Page =
{
...
bind: function(){
...
$('.add_Cake').click(function(evt) {
// Set to top of page if on top toolbar
if ($(this).hasClass('atTop'))
InsertionMarker.set(null, false);
// Use if you have a form in InsertionBar's tablet
//var form = $('#add_CakeForm');
//InsertionBar.setWidgetForm(form);
//form.autofocus();
// Otherwise send a request
Page.insertWidget('cake');
InsertionBar.hide();
InsertionMarker.setEnabled(true);
HoverHandle.setEnabled(true);
return false;
});
...
}
...
function unbind() {
...
$('.add_Note').unbind();
...
}
...
}
When adding the route to “config/routes.rb”, make sure it is nested in the :pages route.
map.resources :pages do |page|
page.resources :lists, :member => {:reorder => :post, :transfer => :put} do |list|
list.resources :list_items, :as => 'items', :member => {:status => :put}
end
page.resources :notes
page.resources :separators
page.resources :cake # Cake widget
end
For a widget to be listed in the “Add to page:” list, it needs to be in the “Page.widgets” list. This is located in “app/models/page.rb”
def self.widgets
[List, Note, Separator, Cake] # Add Cake widget
end