sitemap + use inherited_sources gem to refactor controllers + fix bugs + clean stuff + save some forms with CMD + S (in progress)

This commit is contained in:
dinedine 2010-07-13 02:46:17 +02:00
parent a36dd1bf8b
commit 0671a55ef8
62 changed files with 602 additions and 594 deletions

View File

@ -23,6 +23,7 @@ gem 'actionmailer-with-request', :require => 'actionmailer_with_request'
gem 'heroku'
gem 'httparty', '0.6.0'
gem 'RedCloth'
gem 'inherited_resources', '1.1.2'
# Development environment
group :development do

View File

@ -8,17 +8,11 @@ module Admin
end
def create
@account = Account.new(params[:account])
if @account.save
current_site.memberships.create(:account => @account)
flash_success!
redirect_to edit_admin_current_site_url
else
flash_error!
render :action => 'new'
end
end
@account = Account.create(params[:account])
current_site.memberships.create(:account => @account) if @account.errors.empty?
respond_with @account, :location => edit_admin_current_site_url
end
end
end

View File

@ -4,63 +4,22 @@ module Admin
sections 'assets'
before_filter :set_collections
def index
if not @collections.empty?
redirect_to(edit_admin_asset_collection_url(@collections.first)) and return
if not @asset_collections.empty?
redirect_to(edit_admin_asset_collection_url(@asset_collections.first)) and return
end
end
def show
@collection = current_site.asset_collections.find(params[:id])
@asset_collection = current_site.asset_collections.find(params[:id])
render :action => 'edit'
end
def new
@collection = current_site.asset_collections.build
end
def edit
@collection = current_site.asset_collections.find(params[:id])
end
def create
@collection = current_site.asset_collections.build(params[:asset_collection])
if @collection.save
flash_success!
redirect_to edit_admin_asset_collection_url(@collection)
else
flash_error!
render :action => 'new'
end
end
def update
@collection = current_site.asset_collections.find(params[:id])
if @collection.update_attributes(params[:asset_collection])
flash_success!
redirect_to edit_admin_asset_collection_url(@collection)
else
flash_error!
render :action => 'edit'
end
end
def destroy
@collection = current_site.asset_collections.find(params[:id])
@collection.destroy
flash_success!
redirect_to admin_asset_collections_url
end
protected
def set_collections
@collections = current_site.asset_collections.order_by([[:name, :asc]])
@asset_collections = current_site.asset_collections.order_by([[:name, :asc]])
end
end
end

View File

@ -5,43 +5,23 @@ module Admin
before_filter :set_collections_and_current_collection
def new
@asset = @collection.assets.build
end
def edit
@asset = @collection.assets.find(params[:id])
end
def create
@asset = @collection.assets.build(params[:asset])
if @asset.save
flash_success!
redirect_to edit_admin_asset_collection_url(@collection)
else
flash_error!
render :action => 'new'
end
create! { edit_admin_asset_collection_url(@asset_collection) }
end
def update
@asset = @collection.assets.find(params[:id])
if @asset.update_attributes(params[:asset])
flash_success!
redirect_to edit_admin_asset_collection_url(@collection)
else
flash_error!
render :action => 'edit'
end
update! { edit_admin_asset_collection_url(@asset_collection) }
end
protected
def begin_of_association_chain
@asset_collection
end
def set_collections_and_current_collection
@collections = current_site.asset_collections
@collection = @collections.find(params[:collection_id])
@asset_collections = current_site.asset_collections
@asset_collection = @asset_collections.find(params[:collection_id])
end
end

View File

@ -1,6 +1,9 @@
require 'locomotive/admin_responder'
module Admin
class BaseController < ::ApplicationController
# class BaseController < ::ApplicationController
class BaseController < InheritedResources::Base
include Locomotive::Routing::SiteDispatcher
layout 'admin/application'
@ -14,27 +17,23 @@ module Admin
before_filter :set_locale
helper_method :sections
# https://rails.lighthouseapp.com/projects/8994/tickets/1905-apphelpers-within-plugin-not-being-mixed-in
Dir[File.dirname(__FILE__) + "/../../helpers/**/*_helper.rb"].each do |file|
helper "admin/#{File.basename(file, '.rb').gsub(/_helper$/, '')}"
end
self.responder = Locomotive::AdminResponder # custom responder
defaults :route_prefix => 'admin'
respond_to :html
protected
def flash_success!(options = {})
msg = translate_flash_msg(:successful)
(options.has_key?(:now) && options[:now] ? flash.now : flash)[:success] = msg
end
def flash_error!(options = { :now => true })
msg = translate_flash_msg(:failed)
(options.has_key?(:now) && options[:now] ? flash.now : flash)[:error] = msg
end
def translate_flash_msg(kind)
t("#{kind.to_s}_#{action_name}", :scope => [:admin, controller_name.underscore.gsub('/', '.'), :messages])
end
def begin_of_association_chain
current_site
end
def self.sections(main, sub = nil)
before_filter do |c|
@ -55,6 +54,6 @@ module Admin
def set_locale
I18n.locale = current_admin.locale
end
end
end

View File

@ -2,51 +2,6 @@ module Admin
class ContentTypesController < BaseController
sections 'contents'
def new
@content_type = current_site.content_types.build
end
def edit
@content_type = current_site.content_types.find(params[:id])
end
def create
@content_type = current_site.content_types.build(params[:content_type])
if @content_type.save
flash_success!
redirect_to edit_admin_content_type_url(@content_type)
else
flash_error!
render :action => 'new'
end
end
def update
@content_type = current_site.content_types.find(params[:id])
if @content_type.update_attributes(params[:content_type])
flash_success!
redirect_to edit_admin_content_type_url(@content_type)
else
flash_error!
render :action => "edit"
end
end
def destroy
@content_type = current_site.content_types.find(params[:id])
begin
@content_type.destroy
flash_success!
rescue Exception => e
flash[:error] = e.to_s
end
redirect_to admin_pages_url
end
end
end

View File

@ -3,70 +3,41 @@ module Admin
sections 'contents'
before_filter :set_content_type
before_filter :set_content_type
def index
@contents = @content_type.list_or_group_contents
end
def new
@content = @content_type.contents.build
end
def edit
@content = @content_type.contents.find(params[:id])
end
def create
@content = @content_type.contents.build(params[:content_instance])
if @content.save
flash_success!
redirect_to edit_admin_content_url(@content_type.slug, @content)
else
flash_error!
render :action => 'new'
create! do |success, failure|
success.html { redirect_to edit_admin_content_url(@content_type.slug, @content) }
end
end
def update
@content = @content_type.contents.find(params[:id])
if @content.update_attributes(params[:content_instance])
flash_success!
redirect_to edit_admin_content_url(@content_type.slug, @content)
else
flash_error!
render :action => "edit"
end
def update
update! { edit_admin_content_url(@content_type.slug, @content) }
end
def sort
@content_type.sort_contents!(params[:order])
flash_success!
redirect_to admin_contents_url(@content_type.slug)
respond_with(@content_type, :location => admin_contents_url(@content_type.slug))
end
def destroy
@content = @content_type.contents.find(params[:id])
begin
@content.destroy
flash_success!
rescue Exception => e
flash[:error] = e.to_s
end
redirect_to admin_contents_url(@content_type.slug)
destroy! { admin_contents_url(@content_type.slug) }
end
protected
def set_content_type
@content_type = current_site.content_types.where(:slug => params[:slug]).first
@content_type ||= current_site.content_types.where(:slug => params[:slug]).first
end
def begin_of_association_chain
set_content_type
end
end
end

View File

@ -2,24 +2,21 @@ module Admin
class CurrentSitesController < BaseController
sections 'settings', 'site'
def edit
@site = current_site
end
actions :edit, :update
def update
@site = current_site
if @site.update_attributes(params[:site])
flash_success!
redirect_to edit_admin_current_site_url(new_host_if_subdomain_changed)
else
flash_error!
render :action => :edit
update! do |success, failure|
success.html { redirect_to edit_admin_current_site_url(new_host_if_subdomain_changed) }
end
end
protected
def resource
@site = current_site
end
def new_host_if_subdomain_changed
if @site.domains.include?(request.host)
{}

View File

@ -19,7 +19,7 @@ module Admin
if @field.update_attributes(params[:custom_field])
render :json => @field.attributes
else
render :json => { :error => translate_flash_msg(:successful) }
render :json => { :error => t('flash.admin.custom_fields.update.alert') }
end
end

View File

@ -6,52 +6,6 @@ module Admin
def index
@layouts = current_site.layouts.order_by([[:name, :asc]])
end
def new
@layout = current_site.layouts.build
end
def edit
@layout = current_site.layouts.find(params[:id])
end
def create
@layout = current_site.layouts.build(params[:layout])
if @layout.save
flash_success!
redirect_to edit_admin_layout_url(@layout)
else
logger.debug "===> #{@layout.errors.inspect}"
flash_error!
render :action => 'new'
end
end
def update
@layout = current_site.layouts.find(params[:id])
if @layout.update_attributes(params[:layout])
flash_success!
redirect_to edit_admin_layout_url(@layout)
else
flash_error!
render :action => "edit"
end
end
def destroy
@layout = current_site.layouts.find(params[:id])
begin
@layout.destroy
flash_success!
rescue Exception => e
flash[:error] = e.to_s
end
redirect_to admin_layouts_url
end
end
end

View File

@ -3,36 +3,23 @@ module Admin
sections 'settings'
def new
@membership = current_site.memberships.build
end
def create
@membership = current_site.memberships.build(params[:membership])
case @membership.action_to_take
case @membership.process!
when :create_account
redirect_to new_admin_account_url(:email => @membership.email)
when :save_it
current_site.save
flash_success!
redirect_to edit_admin_site_url
respond_with @membership, :location => edit_admin_current_site_url
when :error
flash_error! :now => true
render :action => 'new'
respond_with @membership, :flash => true
when :nothing
flash[:error] = translate_flash_msg(:already_saved)
redirect_to edit_admin_site_url
respond_with @membership, :alert => t('flash.admin.memberships.create.already_created'), :location => edit_admin_current_site_url
end
end
def destroy
current_site.memberships.find(params[:id]).destroy
current_site.save
flash_success!
redirect_to edit_admin_site_url
destroy! { edit_admin_current_site_url }
end
end

View File

@ -3,19 +3,19 @@ module Admin
sections 'settings', 'account'
def edit
actions :edit, :update
def update
update! { edit_admin_my_account_url }
end
protected
def resource
@account = current_admin
end
def update
@account = current_admin
if @account.update_attributes(params[:account])
flash_success!
redirect_to edit_admin_my_account_url
else
render :action => :edit
end
end
def begin_of_association_chain; nil; end # not related directly to current_site
end
end

View File

@ -2,6 +2,8 @@ module Admin
class PagesController < BaseController
sections 'contents'
respond_to :json, :only => [:update, :sort]
def index
@pages = current_site.pages.roots
@ -11,60 +13,45 @@ module Admin
@page = current_site.pages.build
@page.parts << PagePart.build_body_part
end
def edit
@page = current_site.pages.find(params[:id])
end
def create
@page = current_site.pages.build(params[:page])
if @page.save
flash_success!
redirect_to edit_admin_page_url(@page)
else
flash_error!
render :action => 'new'
end
end
def update
@page = current_site.pages.find(params[:id])
if @page.update_attributes(params[:page])
flash_success!
redirect_to edit_admin_page_url(@page)
else
flash_error!
render :action => "edit"
end
end
def sort
@page = current_site.pages.find(params[:id])
@page.sort_children!(params[:children])
render :json => { :message => translate_flash_msg(:successful) }
respond_with @page
end
def get_path
page = current_site.pages.build(:parent => current_site.pages.find(params[:parent_id]), :slug => params[:slug].slugify)
render :json => { :url => page.url, :slug => page.slug }
end
def destroy
@page = current_site.pages.find(params[:id])
begin
@page.destroy
flash_success!
rescue Exception => e
flash[:error] = e.to_s
end
redirect_to admin_pages_url
end
end
end
end
# DEPRECATED
# def edit
# @page = current_site.pages.find(params[:id])
# end
#
# def create
# @page = current_site.pages.create(params[:page])
#
# respond_with(@page, :location => (edit_admin_page_url(@page) rescue nil))
# end
#
# def update
# @page = current_site.pages.find(params[:id])
# @page.update_attributes(params[:page])
#
# respond_with(@page, :location => edit_admin_page_url(@page))
# end
#
# def destroy
# @page = current_site.pages.find(params[:id])
# @page.destroy
#
# respond_with(@page, :alert => @page.errors.full_messages.first, :location => admin_pages_url)
# end

View File

@ -0,0 +1,16 @@
module Admin
class SitemapsController < ActionController::Base
include Locomotive::Routing::SiteDispatcher
before_filter :require_site
respond_to :xml
def show
@pages = current_site.pages.published
@host = request.host
end
end
end

View File

@ -3,35 +3,28 @@ module Admin
sections 'settings'
def new
@site = Site.new
end
def create
@site = Site.new(params[:site])
if @site.save
@site.memberships.create :account => @current_admin, :admin => true
flash_success!
redirect_to edit_admin_my_account_url
else
flash_error!
render :action => 'new'
end
@site.memberships.build :account => @current_admin, :admin => true
create! { edit_admin_my_account_url }
end
def destroy
@site = current_admin.sites.detect { |s| s._id == params[:id] }
if @site != current_site
@site.destroy
flash_success!
else
flash_error!
@site.errors.add(:base, 'Can not destroy the site you are logging in now')
end
redirect_to edit_admin_my_account_url
respond_with @site, :location => edit_admin_my_account_url
end
protected
def begin_of_association_chain; nil; end # not related directly to current_site
end
end

View File

@ -7,50 +7,5 @@ module Admin
@snippets = current_site.snippets.order_by([[:name, :asc]])
end
def new
@snippet = current_site.snippets.build
end
def edit
@snippet = current_site.snippets.find(params[:id])
end
def create
@snippet = current_site.snippets.build(params[:snippet])
if @snippet.save
flash_success!
redirect_to edit_admin_snippet_url(@snippet)
else
flash_error!
render :action => 'new'
end
end
def update
@snippet = current_site.snippets.find(params[:id])
if @snippet.update_attributes(params[:snippet])
flash_success!
redirect_to edit_admin_snippet_url(@snippet)
else
flash_error!
render :action => "edit"
end
end
def destroy
@snippet = current_site.snippets.find(params[:id])
begin
@snippet.destroy
flash_success!
rescue Exception => e
flash[:error] = e.to_s
end
redirect_to admin_snippets_url
end
end
end

View File

@ -5,6 +5,8 @@ module Admin
sections 'settings', 'theme_assets'
respond_to :json, :only => :create
def index
assets = current_site.theme_assets.all
@non_image_assets = assets.find_all { |a| a.stylesheet? || a.javascript? }
@ -12,69 +14,26 @@ module Admin
@flash_assets = assets.find_all { |a| a.movie? }
if request.xhr?
render :action => 'images', :layout => false
render :action => 'images', :layout => false and return
end
end
def new
@asset = current_site.theme_assets.build
end
def edit
@asset = current_site.theme_assets.find(params[:id])
end
def create
params[:theme_asset] = { :source => params[:file] } if params[:file]
@asset = current_site.theme_assets.build(params[:theme_asset])
respond_to do |format|
if @asset.save
format.html do
flash_success!
redirect_to edit_admin_theme_asset_url(@asset)
end
format.json do
render :json => {
:status => 'success',
:name => truncate(@asset.slug, :length => 22),
:url => @asset.source.url,
:vignette_url => @asset.vignette_url
}
end
else
format.html do
flash_error!
render :action => 'new'
end
format.json do
render :json => { :status => 'error' }
end
create! do |success, failure|
success.json do
render :json => {
:status => 'success',
:name => truncate(@theme_asset.slug, :length => 22),
:url => @theme_asset.source.url,
:vignette_url => @theme_asset.vignette_url
}
end
failure.json { render :json => { :status => 'error' } }
end
end
def update
@asset = current_site.theme_assets.find(params[:id])
if @asset.update_attributes(params[:theme_asset])
flash_success!
redirect_to edit_admin_theme_asset_url(@asset)
else
flash_error!
render :action => 'edit'
end
end
def destroy
@asset = current_site.theme_assets.find(params[:id])
@asset.destroy
flash_success!
redirect_to admin_theme_assets_url
end
end
end

View File

@ -5,7 +5,7 @@ module Admin::AssetsHelper
end
def image_dimensions_and_size(asset)
content_tag(:small, "#{asset.width}px x #{@asset.height}px | #{number_to_human_size(asset.size)}")
content_tag(:small, "#{asset.width}px x #{asset.height}px | #{number_to_human_size(asset.size)}")
end
def allow_plain_text_editing?(asset)

View File

@ -50,5 +50,5 @@ module Admin::BaseHelper
def nocoffee_tag
link_to 'noCoffee', 'http://www.nocoffee.fr', :id => 'nocoffee'
end
end

View File

@ -1,5 +1,18 @@
module Admin::PagesHelper
def page_main_url(page)
url = ''
if page.site.domains.empty?
url = main_site_url(page.site)
else
url = "http://#{current_site.domains.first}"
url += ":#{request.port}" if request.port != 80
end
File.join(url, page.fullpath)
end
def parent_pages_options
roots = current_site.pages.roots.where(:slug.ne => '404').and(:_id.ne => @page.id)

View File

@ -21,14 +21,17 @@ class Membership
self.account = Account.where(:email => email).first
end
def action_to_take
def process!
if @email.blank?
self.errors.add_on_blank(:email)
:error
elsif self.account.nil?
:create_account
elsif self.site.memberships.find_all { |m| m.account_id == self.account_id }.size > 1
self.errors.add(:base, 'Already created')
:nothing
else
self.save
:save_it
end
end

View File

@ -33,6 +33,7 @@ class Page
named_scope :latest_updated, :order_by => [[:updated_at, :desc]], :limit => Locomotive.config.lastest_items_nb
named_scope :index, :where => { :slug => 'index', :depth => 0, :published => true }
named_scope :not_found, :where => { :slug => '404', :depth => 0, :published => true }
named_scope :published, :where => { :published => true }
## behaviours ##
liquify_template :joined_parts
@ -47,6 +48,10 @@ class Page
self.slug == '404' && self.depth.to_i == 0
end
def index_or_not_found?
self.index? || self.not_found?
end
def fullpath(force = false)
if read_attribute(:fullpath).present? && !force
return read_attribute(:fullpath)
@ -79,8 +84,10 @@ class Page
return if (self.site rescue nil).nil?
if self.index? || self.not_found?
raise I18n.t('errors.messages.protected_page')
self.errors[:base] << I18n.t('errors.messages.protected_page')
end
self.errors.empty?
end
def normalize_slug

View File

@ -11,10 +11,7 @@ class PagePart
## associations ##
embedded_in :page, :inverse_of => :parts
## callbacks ##
# before_validate { |p| p.slug ||= p.name.slugify if p.name.present? }
## validations ##
validates_presence_of :name, :slug

View File

@ -1,5 +1,5 @@
%li{ :id => "asset-#{asset.id}", :class => "asset #{'last' if (asset_counter + 1) % 6 == 0}"}
%h4= link_to truncate(asset.name, :length => 22), edit_admin_asset_path(@collection, asset)
%h4= link_to truncate(asset.name, :length => 22), edit_admin_asset_path(@asset_collection, asset)
.image
.inside
= vignette_tag(asset)

View File

@ -1,4 +1,4 @@
- title link_to(@collection.name.blank? ? @collection.name_was : @collection.name, '#', :rel => 'asset_collection_name', :title => t('.ask_for_name'), :class => 'editable')
- title link_to(@asset_collection.name.blank? ? @asset_collection.name_was : @asset_collection.name, '#', :rel => 'asset_collection_name', :title => t('.ask_for_name'), :class => 'editable')
- content_for :head do
= javascript_include_tag 'admin/plugins/fancybox', 'admin/asset_collections', 'admin/custom_fields'
@ -10,16 +10,16 @@
%p= t('.help')
- content_for :buttons do
= admin_button_tag :add_asset, new_admin_asset_url(@collection), :class => 'add'
= admin_button_tag :add_asset, new_admin_asset_url(@asset_collection), :class => 'add'
%p.no-items{ :style => "#{'display: none' unless @collection.assets.empty? }" }
= t('.no_items', :url => new_admin_asset_url(@collection))
%p.no-items{ :style => "#{'display: none' unless @asset_collection.assets.empty? }" }
= t('.no_items', :url => new_admin_asset_url(@asset_collection))
%ul#assets.assets.sortable
= render :partial => 'asset', :collection => @collection.ordered_assets
= render :partial => 'asset', :collection => @asset_collection.ordered_assets
%li.clear
= semantic_form_for @collection, :url => admin_asset_collection_url(@collection), :html => { :multipart => true } do |f|
= semantic_form_for @asset_collection, :url => admin_asset_collection_url(@asset_collection), :html => { :multipart => true } do |f|
= f.hidden_field :assets_order
= f.foldable_inputs :name => :options do
@ -28,6 +28,6 @@
= render 'admin/custom_fields/index', :form => f, :collection_name => 'assets'
= render 'admin/shared/form_actions', :delete_button => link_to(content_tag(:span, t('.destroy')), admin_asset_collection_url(@collection), :confirm => t('admin.messages.confirm'), :method => :delete, :class => 'button small remove'), :button_label => :update
= render 'admin/shared/form_actions', :delete_button => link_to(content_tag(:span, t('.destroy')), admin_asset_collection_url(@asset_collection), :confirm => t('admin.messages.confirm'), :method => :delete, :class => 'button small remove'), :button_label => :update
= render 'admin/custom_fields/edit_field'

View File

@ -8,7 +8,7 @@
%p= t('.help')
= semantic_form_for @collection, :url => admin_asset_collections_url do |f|
= semantic_form_for @asset_collection, :url => admin_asset_collections_url do |f|
= f.inputs :name => :information do
= f.input :name

View File

@ -7,7 +7,7 @@
= f.input :source
- unless @asset.custom_fields.empty?
= render 'admin/custom_fields/custom_form', :form => f, :title => :other_fields, :parent => @collection
= render 'admin/custom_fields/custom_form', :form => f, :title => :other_fields, :parent => @asset_collection
- if @asset.image? && @asset.valid?
= f.foldable_inputs :name => "#{t('formtastic.titles.preview')} #{image_dimensions_and_size(@asset)}", :class => 'preview' do

View File

@ -4,12 +4,12 @@
= render 'admin/shared/menu/assets'
- content_for :buttons do
= admin_button_tag t('admin.asset_collections.edit.add_asset'), new_admin_asset_url(@collection), :class => 'add'
= admin_button_tag t('admin.asset_collections.edit.add_asset'), new_admin_asset_url(@asset_collection), :class => 'add'
%p= t('.help')
= semantic_form_for @asset, :url => admin_asset_url(@collection, @asset), :html => { :multipart => true } do |form|
= semantic_form_for @asset, :url => admin_asset_url(@asset_collection, @asset), :html => { :multipart => true } do |form|
= render 'form', :f => form
= render 'admin/shared/form_actions', :back_url => edit_admin_asset_collection_url(@collection), :button_label => :update
= render 'admin/shared/form_actions', :back_url => edit_admin_asset_collection_url(@asset_collection), :button_label => :update

View File

@ -5,8 +5,8 @@
%p= t('.help')
= semantic_form_for @asset, :url => admin_assets_url(@collection), :html => { :multipart => true } do |form|
= semantic_form_for @asset, :url => admin_assets_url(@asset_collection), :html => { :multipart => true } do |form|
= render 'form', :f => form
= render 'admin/shared/form_actions', :back_url => edit_admin_asset_collection_url(@collection), :button_label => :create
= render 'admin/shared/form_actions', :back_url => edit_admin_asset_collection_url(@asset_collection), :button_label => :create

View File

@ -9,7 +9,7 @@
%p= @content_type.description
= semantic_form_for @content, :url => admin_content_url(@content_type.slug, @content), :html => { :multipart => true } do |form|
= semantic_form_for @content, :as => :content, :url => admin_content_url(@content_type.slug, @content), :html => { :multipart => true } do |form|
= render 'form', :f => form

View File

@ -8,7 +8,7 @@
%p= @content_type.description
= semantic_form_for @content, :url => admin_contents_url(@content_type.slug), :html => { :multipart => true } do |form|
= semantic_form_for @content, :as => :content, :url => admin_contents_url(@content_type.slug), :html => { :multipart => true } do |form|
= render 'form', :f => form

View File

@ -8,7 +8,7 @@
%p= t('.help')
= semantic_form_for @account, :url => admin_my_account_url do |f|
= semantic_form_for @account, :as => :my_account, :url => admin_my_account_url do |f|
= f.foldable_inputs :name => :information, :style => 'display: none' do
= f.input :name

View File

@ -8,7 +8,7 @@
%p= t('.help')
= semantic_form_for @page, :url => admin_page_url(@page) do |form|
= semantic_form_for @page, :url => admin_page_url(@page), :html => { :class => 'save-with-shortcut' } do |form|
= render 'form', :f => form

View File

@ -10,7 +10,7 @@
= stylesheet_link_tag 'admin/layout', 'admin/jquery/ui', 'admin/plugins/toggle', 'admin/menu', 'admin/buttons', 'admin/formtastic', 'admin/formtastic_changes', 'admin/application', :media => 'screen', :cache => Rails.env.production? && !Locomotive.heroku?
= javascript_include_tag 'admin/jquery', 'admin/jquery.ui', 'admin/rails', 'admin/utils', 'admin/plugins/toggle', 'admin/plugins/growl', 'admin/plugins/cookie', 'admin/application', 'admin/locales/datepicker_fr', :cache => Rails.env.production? && !Locomotive.heroku?
= javascript_include_tag 'admin/jquery', 'admin/jquery.ui', 'admin/rails', 'admin/utils', 'admin/plugins/shortcut', 'admin/plugins/toggle', 'admin/plugins/growl', 'admin/plugins/cookie', 'admin/application', 'admin/locales/datepicker_fr', :cache => Rails.env.production? && !Locomotive.heroku?
%script{ :type => 'text/javascript' }
= find_and_preserve(growl_message)

View File

@ -1,6 +1,6 @@
%ul
- @collections.each do |c|
%li{ :class => "#{'on' if @collection.id == c.id}" }
- @asset_collections.each do |c|
%li{ :class => "#{'on' if @asset_collection.id == c.id}" }
= link_to content_tag(:span, truncate(c.name, :length => 20)), edit_admin_asset_collection_url(c)
.action

View File

@ -0,0 +1,18 @@
xml.instruct!
xml.urlset "xmlns" => "http://www.sitemaps.org/schemas/sitemap/0.9" do
xml.url do
xml.loc "http://#{@host}"
xml.priority 1.0
end
@pages.each do |page|
if not page.index_or_not_found?
xml.url do
xml.loc page_main_url(page)
xml.priority 0.9
end
end
end
end

View File

@ -4,26 +4,26 @@
= f.hidden_field :performing_plain_text
#file-selector{ :class => "selector #{'hidden' if @asset.performing_plain_text?}" }
#file-selector{ :class => "selector #{'hidden' if @theme_asset.performing_plain_text?}" }
= f.inputs :name => :information do
= f.input :source
- if @asset.new_record? || !@asset.image?
- if @theme_asset.new_record? || !@theme_asset.image?
%span.alt
= t('admin.theme_assets.form.choose_plain_text')
- if allow_plain_text_editing?(@asset)
#text-selector{ :class => "selector #{'hidden' if !@asset.performing_plain_text?}", :style => "#{'display: none' if !@asset.performing_plain_text?}" }
- if allow_plain_text_editing?(@theme_asset)
#text-selector{ :class => "selector #{'hidden' if !@theme_asset.performing_plain_text?}", :style => "#{'display: none' if !@theme_asset.performing_plain_text?}" }
= f.inputs :name => :code, :class => 'inputs code' do
- if @asset.new_record?
- if @theme_asset.new_record?
= f.input :slug
= f.custom_input :content_type do
= f.select :content_type, ["stylesheet", "javascript"]
= f.custom_input :plain_text, :css => 'full', :with_label => false do
%code{ :class => (@asset.size && @asset.size > 40000 ? 'nude' : (@asset.content_type || 'stylesheet')) }
%code{ :class => (@theme_asset.size && @theme_asset.size > 40000 ? 'nude' : (@theme_asset.content_type || 'stylesheet')) }
= f.text_area :plain_text
.more
= link_to t('.picker_link'), admin_theme_assets_path, :id => 'asset-picker-link'
@ -31,9 +31,9 @@
%span.alt
= t('admin.theme_assets.form.choose_file')
- if @asset.image?
= f.foldable_inputs :name => "#{t('formtastic.titles.preview')} #{image_dimensions_and_size(@asset)}", :class => 'preview' do
- if @theme_asset.image?
= f.foldable_inputs :name => "#{t('formtastic.titles.preview')} #{image_dimensions_and_size(@theme_asset)}", :class => 'preview' do
%li
.image
.inside
= image_tag(@asset.source.url(:preview))
= image_tag(@theme_asset.source.url(:preview))

View File

@ -1,4 +1,4 @@
- title t('.title', :file => @asset.source_filename)
- title t('.title', :file => @theme_asset.source_filename)
- content_for :submenu do
= render 'admin/shared/menu/settings'
@ -6,9 +6,9 @@
- content_for :buttons do
= admin_button_tag t('admin.theme_assets.index.new'), new_admin_theme_asset_url, :class => 'add'
%p= t('.help', :url => @asset.source.url)
%p= t('.help', :url => @theme_asset.source.url)
= semantic_form_for @asset, :url => admin_theme_asset_url(@asset), :html => { :multipart => true } do |form|
= semantic_form_for @theme_asset, :url => admin_theme_asset_url(@theme_asset), :html => { :multipart => true } do |form|
= render 'form', :f => form

View File

@ -5,7 +5,7 @@
%p= t('.help')
= semantic_form_for @asset, :url => admin_theme_assets_url, :html => { :multipart => true } do |form|
= semantic_form_for @theme_asset, :url => admin_theme_assets_url, :html => { :multipart => true } do |form|
= render 'form', :f => form

View File

@ -2,7 +2,28 @@ require File.dirname(__FILE__) + '/../../lib/locomotive.rb'
require File.dirname(__FILE__) + '/../../lib/core_ext.rb'
Locomotive.configure do |config|
# if not defined, locomotive will use example.com as main domain name. Remove prefix www from your domain name.
# Ex:
# config.default_domain = Rails.env.production? ? 'mydomain.com' : 'example.com'
config.default_domain = 'example.com'
# configure how many items we display in sub menu in the "Contents" section.
config.lastest_items_nb = 5
# tell if logs are enabled. Useful for debug purpose.
config.enable_logs = true
# tell if the application is hosted on Heroku.
# Locomotive uses heroku api to add / remove domains.
# there are 2 ways of passing heroku credentials to Locomotive
# - from ENV variables: HEROKU_LOGIN & HEROKU_PASSWORD
# - from this file
#
# Notes:
# - IMPORTANT: behaviours related to this option will only be applied in production
# - credentials coming from this file take precedence over ENV variables
#
# Ex:
# config.heroku = { :name => '<my heroku app name>', :login => 'john@doe.net', :password => 'easy' }
config.heroku = false
end

View File

@ -88,13 +88,6 @@ en:
show: show
help: "The page title can be updated by clicking it."
ask_for_title: "Please type the new page title"
messages:
successful_create: "Page was successfully created."
successful_update: "Page was successfully updated."
successful_destroy: "Page was successfully deleted."
successful_sort: "Pages were successfully sorted."
failed_create: "Page was not created."
failed_update: "Page was not updated."
form:
cache_strategy:
none: None
@ -119,12 +112,6 @@ en:
new: new layout
layout:
updated_at: Updated at
messages:
successful_create: "Layout was successfully created."
successful_update: "Layout was successfully updated."
successful_destroy: "Layout was successfully deleted."
failed_create: "Layout was not created."
failed_update: "Layout was not updated."
snippets:
index:
@ -140,45 +127,27 @@ en:
help: "Fill in the form below to update your snippet."
snippet:
updated_at: Updated at
messages:
successful_create: "Snippet was successfully created."
successful_update: "Snippet was successfully updated."
successful_destroy: "Snippet was successfully deleted."
failed_create: "Snippet was not created."
failed_update: "Snippet was not updated."
sites:
new:
title: New site
help: "Fill in the form below to create your new site."
messages:
successful_create: "Site was successfully created."
failed_create: "Site was not created."
current_sites:
edit:
new_membership: add account
help: "The site name can be updated by clicking it."
ask_for_name: "Please type the new site name"
messages:
successful_update: "My site was successfully updated."
failed_update: "My site was not updated."
memberships:
new:
title: New membership
help: "Please give the account email to add. If it does not exist, you will be redirected to the account creation form."
messages:
successful_create: "Membership was successfully created."
failed_create: "Membership was not created."
accounts:
new:
title: New account
help: "Fill in the form below to add a new account."
messages:
successful_create: "Account was successfully created."
failed_create: "Account was not created."
my_accounts:
edit:
@ -187,9 +156,6 @@ en:
en: English
fr: French
ask_for_name: "Please type your new name"
messages:
successful_update: "My account was successfully updated."
failed_update: "My account was not updated."
theme_assets:
index:
@ -212,12 +178,6 @@ en:
images:
title: Listing images
no_items: "There are no files for now."
messages:
successful_create: "File was successfully created."
successful_update: "File was successfully updated."
successful_destroy: "File was successfully deleted."
failed_create: "File was not created."
failed_update: "File was not updated."
asset_collections:
index:
@ -234,12 +194,6 @@ en:
destroy: remove collection
no_items: "There are no assets for now. Just click <a href=\"{{url}}\">here</a> to create the first one."
ask_for_name: "Please type the new name"
messages:
successful_create: "Collection was successfully created."
successful_update: "Collection was successfully updated."
successful_destroy: "Collection was successfully deleted."
failed_create: "Collection was not created."
failed_update: "Collection was not updated."
assets:
new:
@ -248,12 +202,6 @@ en:
edit:
title: Edit asset
help: "Fill in the form below to update your asset."
messages:
successful_create: "Asset was successfully created."
successful_update: "Asset was successfully updated."
successful_destroy: "Asset was successfully deleted."
failed_create: "Asset was not created."
failed_update: "Asset was not updated."
content_types:
index:
@ -270,12 +218,6 @@ en:
order_by:
updated_at: 'By "updated at" date'
position_in_list: Manually
messages:
successful_create: "Model was successfully created."
successful_update: "Model was successfully updated."
successful_destroy: "Model was successfully deleted."
failed_create: "Model was not created."
failed_update: "Model was not updated."
contents:
index:
@ -293,13 +235,7 @@ en:
title: '{{type}} &mdash; new item'
edit:
title: '{{type}} &mdash; editing item'
messages:
successful_create: "Content was successfully created."
successful_update: "Content was successfully updated."
successful_destroy: "Content was successfully deleted."
failed_create: "Content was not created."
failed_update: "Content was not updated."
formtastic:
titles:
information: General information

116
config/locales/flash.en.yml Normal file
View File

@ -0,0 +1,116 @@
en:
flash:
admin:
pages:
create:
notice: "Page was successfully created"
alert: "Page was not created."
update:
notice: "Page was successfully updated"
alert: "Page was not updated."
sort:
notice: "Pages were successfully sorted."
destroy:
notice: "Page was successfully deleted."
contents:
create:
notice: "Content was successfully created."
alert: "Content was not created."
update:
notice: "Content was successfully updated."
alert: "Content was not updated."
sort:
notice: "Contents were successfully sorted."
destroy:
notice: "Content was successfully deleted."
content_types:
create:
notice: "Model was successfully created."
alert: "Model was not created."
update:
notice: "Model was successfully updated."
alert: "Model was not updated."
destroy:
notice: "Model was successfully deleted."
current_sites:
update:
notice: "My site was successfully updated."
alert: "My site was not updated."
layouts:
create:
notice: "Layout was successfully created."
alert: "Layout was not created."
update:
notice: "Layout was successfully updated."
alert: "Layout was not updated."
destroy:
notice: "Layout was successfully deleted."
snippets:
create:
notice: "Snippet was successfully created."
alert: "Snippet was not created."
update:
notice: "Snippet was successfully updated."
alert: "Snippet was not updated."
destroy:
notice: "Snippet was successfully deleted."
accounts:
create:
notice: "Account was successfully created."
alert: "Account was not created."
my_accounts:
update:
notice: "My account was successfully updated."
alert: "My account was not updated."
sites:
create:
notice: "Site was successfully created."
alert: "Site was not created."
destroy:
notice: "Site was successfully deleted."
memberships:
create:
notice: "Membership was successfully created."
alert: "Membership was not created."
already_created: "Account was already added the current site."
asset_collections:
create:
notice: "Collection was successfully created."
alert: "Collection was not created."
update:
notice: "Collection was successfully updated."
alert: "Collection was not updated."
destroy:
notice: "Collection was successfully deleted."
assets:
create:
notice: "Asset was successfully created."
alert: "Asset was not created."
update:
notice: "Asset was successfully updated."
alert: "Asset was not updated."
theme_assets:
create:
notice: "File was successfully created."
alert: "File was not created."
update:
notice: "File was successfully updated."
alert: "File was not updated."
destroy:
notice: "File was successfully deleted."
custom_fields:
update:
alert: "Field not updated"

View File

@ -49,6 +49,9 @@ Rails.application.routes.draw do |map|
resources :custom_fields, :path => 'custom/:parent/:slug/fields'
end
# sitemap
match '/sitemap.xml' => 'admin/sitemaps#show', :format => 'xml'
# magic urls
match '/' => 'admin/rendering#show'
match '*path' => 'admin/rendering#show'

View File

@ -1,5 +1,9 @@
account = Account.create! :name => 'Admin', :email => 'admin@locomotiveapp.org', :password => 'locomotive', :password_confirmation => 'locomotive'
account = Account.create! :name => 'Admin', :email => 'admin@example.com', :password => 'locomotive', :password_confirmation => 'locomotive'
site = Site.new :name => 'Locomotive test website', :subdomain => 'test'
site.memberships.build :account => account, :admin => true
site.save!
puts "Your first website has been created !"
puts "Admin url: http://test.example.com/admin"
puts "Crendetials: admin@example.com / locomotive"

View File

@ -1,13 +1,12 @@
BOARD:
- refactoring admin crud (pages + layouts + snippets)
- refactor slugify method (use parameterize + create a module)
- save layout / snippet / page / stylesheet / javascript with CMD + S (ajax)
- change action icons according to the right action [Sacha]
- page redirection (option)
- send email when new content added thru api
- sitemap
- save layout / snippet / page / stylesheet / javascript with CMD + S (ajax)
- flash messages in French
BACKLOG:
@ -58,4 +57,6 @@ x change credits in the admin footer
x license
x textile filter
x [bug] varnish can not be refreshed in heroku so "max-age" has to be disabled => modify cache strategy
x "remember me" should always be enabled
x "remember me" should always be enabled
x sitemap
x refactoring admin crud (pages + layouts + snippets)

View File

@ -1,16 +0,0 @@
module Locomotive
class AssetsGenerator < Rails::Generators::Base
def self.source_root
puts "(before) source_root = #{@_locomotive_source_root}"
@_locomotive_source_root ||= File.expand_path("../../../../../", __FILE__)
puts "(after) source_root = #{@_locomotive_source_root}"
@_locomotive_source_root
end
def copy_public_files
directory "public", "public", :recursive => true
end
end
end

View File

@ -0,0 +1,14 @@
module Locomotive
class CopyAssetsGenerator < Rails::Generators::Base
def self.source_root
@_locomotive_source_root ||= File.expand_path('../../../../../', __FILE__)
@_locomotive_source_root
end
def copy_public_files
directory 'public', 'public', :recursive => true
end
end
end

View File

@ -2,11 +2,24 @@ module Locomotive
class InstallGenerator < Rails::Generators::Base
def self.source_root
@_locomotive_source_root ||= File.expand_path("../templates", __FILE__)
@_locomotive_source_root ||= File.expand_path('../templates', __FILE__)
end
def copy_initializer
template "locomotive.rb", "config/initializers/locomotive.rb"
template 'locomotive.rb', 'config/initializers/locomotive.rb'
end
def seed_db
append_file 'db/seeds.rb', %{
# Uncomment the following lines if you want to create the first website / account
#account = Account.create! :name => 'Admin', :email => 'admin@example.com', :password => 'locomotive', :password_confirmation => 'locomotive'
#site = Site.new :name => 'Locomotive test website', :subdomain => 'test'
#site.memberships.build :account => account, :admin => true
#site.save!}
end
def show_readme
readme 'README'
end
end

View File

@ -0,0 +1,17 @@
===============================================================================
Locomotive initializer has been added to your application. Your db/seeds.rb file has also been updated.
1. Take a look at this to match your environment.
2. Uncomment added code in db/seeds.rb if necessary
Note: do not forget to run rake db:seed if you modify your db/seeds.rb file.
Once it is done, the next step is to install assets:
bundle exec rails g locomotive:copy_assets
Fire your application server and launch a browser !
===============================================================================

View File

@ -7,6 +7,7 @@ require 'locomotive/carrierwave'
require 'locomotive/heroku'
require 'locomotive/custom_fields'
require 'locomotive/httparty'
require 'locomotive/inherited_resources'
require 'mongo_session_store/mongoid'

View File

@ -0,0 +1,26 @@
module Locomotive
class AdminResponder < ::ActionController::Responder
include Responders::FlashResponder
def api_behavior(error)
raise error unless resourceful?
# generate flash messages
set_flash_message!
if get?
display resource
elsif has_errors?
display({ :errors => resource.errors, :alert => flash[:alert] }, :status => :unprocessable_entity)
elsif post?
display resource, :status => :created, :location => resource_location
else
display({ :notice => controller.flash[:notice] })
end
controller.flash.discard # reset flash messages !
end
end
end

View File

@ -0,0 +1,42 @@
module InheritedResources
# redirect to edit_resource_url instead of resource_url
module Actions
def create(options={}, &block)
object = build_resource
if create_resource(object)
options[:location] ||= edit_resource_url rescue nil # change here
end
respond_with_dual_blocks(object, options, &block)
end
alias :create! :create
# PUT /resources/1
def update(options={}, &block)
object = resource
if update_resource(object, params[resource_instance_name])
options[:location] ||= edit_resource_url rescue nil # change here
end
respond_with_dual_blocks(object, options, &block)
end
alias :update! :update
# DELETE /resources/1
def destroy(options={}, &block)
object = resource
options[:location] ||= collection_url rescue nil
destroy_resource(object)
options[:alert] = object.errors.full_messages.first # display the first error if present
respond_with_dual_blocks(object, options, &block)
end
alias :destroy! :destroy
end
end

View File

@ -3,7 +3,7 @@ module Locomotive
module Drops
class Page < Base
liquid_attributes << :title
liquid_attributes << :title << :slug
def children
@children ||= liquify(*@source.children)

View File

@ -4,7 +4,7 @@ module Locomotive
module Text
def textile(input)
RedCloth.new(input).to_html
::RedCloth.new(input).to_html
end
end

View File

@ -15,8 +15,10 @@ module Locomotive
def initialize(tag_name, markup, tokens)
if markup =~ Syntax
@site_or_page = $1 || 'page'
@options = {}
markup.scan(::Liquid::TagAttributes) { |key, value| @options[key.to_sym] = value }
else
raise ::Liquid::SyntaxError.new("Syntax Error in 'nav' - Valid syntax: nav <page|site>")
raise ::Liquid::SyntaxError.new("Syntax Error in 'nav' - Valid syntax: nav <page|site> <options>")
end
super
@ -27,8 +29,6 @@ module Locomotive
source = context.registers[@site_or_page.to_sym]
# puts "[Nav] source = #{source.inspect}"
if source.respond_to?(:name) # site ?
source = source.pages.first # start from home page
else
@ -46,9 +46,12 @@ module Locomotive
def render_child_link(page)
selected = @current_page._id == page._id ? ' on' : ''
icon = @options[:icon] ? '<span></span>' : ''
label = %{#{icon if @options[:icon] != 'after' }#{page.title}#{icon if @options[:icon] == 'after' }}
%{
<li id="#{page.slug.dasherize}" class="link#{selected}">
<a href="/#{page.fullpath}">#{page.title}</a>
<a href="/#{page.fullpath}">#{label}</a>
</li>
}.strip
end

View File

@ -80,6 +80,9 @@ $(document).ready(function() {
});
$('.formtastic li.error input').eq(0).focus();
// save form in AJAX
$('form.save-with-shortcut').saveWithShortcut();
// editable title (page, ...etc)
$('#content h2 a.editable').each(function() {
var target = $('#' + $(this).attr('rel')),

View File

@ -26,7 +26,7 @@ $(document).ready(function() {
params += '&_method=put';
$.post($(this).attr('data_url'), params, function(data) {
$.growl('success', data.message);
$.growl('success', data.notice);
}, 'json');
}
});

View File

@ -0,0 +1,68 @@
// Save -> Command S (need a form)
jQuery.fn.saveWithShortcut = function() {
var resetFormErrors = function(form) {
jQuery('div.form-errors').remove();
jQuery('div.formError').remove();
jQuery('p.inline-errors').remove();
form.find('li.error').removeClass('error');
}
// var updateFromWyMeditor = function() {
// if (jQuery.wymeditors == undefined)
// return;
// var i = 0;
// while (jQuery.wymeditors(i) != undefined) {
// var editor = jQuery.wymeditors(i);
// editor.box().prev().val(editor.html());
// i++;
// }
// };
var updateFromCodeMirror = function() {
if (CodeMirror == undefined)
return;
jQuery.each(CodeMirrorEditors, function() {
this.el.val(this.editor.getCode());
});
}
var save = function(form) {
$.post(form.attr('action'), form.serializeArray(), function(data) {
onSaveCallback(form, data)
}, 'json');
};
var onSaveCallback = function(form, data) {
resetFormErrors(form);
if (data.alert != undefined) {
$.growl('error', data.alert);
for (var i = 0; i < data.errors.length; i++) {
var type = data.errors[i][0], value = data.errors[i][1];
var node = form.find('li:has(#' + data.model + '_' + type + ')');
node.addClass('error');
node.append("<p class='inline-errors'>" + value + "</p>");
}
form.find('li.error input').eq(0).focus();
} else {
$.growl('success', data.notice);
// $.publish('form.saved.success', [data]);
}
};
return this.each(function() {
var form = jQuery(this);
jQuery(document).bind('keypress.shortcut', function(event) {
if (!(event.which == 115 && (event.ctrlKey || event.metaKey))) return true;
// updateFromWyMeditor();
updateFromCodeMirror();
save(form);
event.preventDefault();
return false;
});
});
};

View File

@ -7,7 +7,7 @@ div.notice {
height: 90px;
}
div.notice.error {
div.notice.error, div.notice.alert {
background-image: url(/images/admin/form/growl-error.png);
}

View File

@ -45,7 +45,7 @@ Factory.define :layout do |l|
</head>
<body>
<div id="sidebar">\{\{ content_for_left_sidebar \}\}</div>
<div id="main">\{\{ content_for_layout \}\}</div>
<div id="main">\{\{ content_for_layout | textile \}\}</div>
</body>
</html>}
end

View File

@ -28,13 +28,22 @@ describe Locomotive::Liquid::Tags::Nav do
output.should == '<ul id="nav"><li id="sub-child-1" class="link on"><a href="/child_2/sub_child_1">Child #2.1</a></li><li id="sub-child-2" class="link"><a href="/child_2/sub_child_2">Child #2.2</a></li></ul>'
end
it 'adds an icon before the link' do
render_nav('site', {}, 'icon: true').should match /<li id="child-1" class="link"><a href="\/child_1"><span><\/span>Child #1<\/a>/
render_nav('site', {}, 'icon: before').should match /<li id="child-1" class="link"><a href="\/child_1"><span><\/span>Child #1<\/a>/
end
it 'adds an icon after the link' do
render_nav('site', {}, 'icon: after').should match /<li id="child-1" class="link"><a href="\/child_1">Child #1<span><\/span><\/a><\/li>/
end
end
def render_nav(source = 'site', registers = {})
def render_nav(source = 'site', registers = {}, template_option = '')
registers = { :site => @site, :page => @home }.merge(registers)
liquid_context = ::Liquid::Context.new({}, registers)
output = Liquid::Template.parse("{% nav #{source} %}").render(liquid_context)
output = Liquid::Template.parse("{% nav #{source} #{template_option} %}").render(liquid_context)
output.gsub(/\n\s{0,}/, '')
end

View File

@ -81,24 +81,26 @@ describe Page do
end
describe 'delete' do
describe '#deleting' do
before(:each) do
@page = Factory.build(:page)
end
it 'should delete index page' do
it 'does not delete the index page' do
@page.stubs(:index?).returns(true)
lambda {
@page.destroy
}.should raise_error(Exception, 'You can not remove index or 404 pages')
@page.destroy.should be_false
@page.errors.first == 'You can not remove index or 404 pages'
}.should_not change(Page, :count)
end
it 'should delete 404 page' do
it 'does not delete the 404 page' do
@page.stubs(:not_found?).returns(true)
lambda {
@page.destroy
}.should raise_error(Exception, 'You can not remove index or 404 pages')
@page.destroy.should be_false
@page.errors.first == 'You can not remove index or 404 pages'
}.should_not change(Page, :count)
end
end
@ -323,7 +325,7 @@ describe Page do
</head>
<body>
<div id="sidebar">A sidebar...</div>
<div id="main">Hello world !</div>
<div id="main"><p>Hello world !</p></div>
</body>
</html>}
end