big refactoring of the locomotive config file in order to handle bushido / single / multi sites, ...etc

This commit is contained in:
did 2011-04-01 02:34:19 +02:00
parent 19b6341ace
commit 551d8a5761
32 changed files with 737 additions and 566 deletions

View File

@ -23,7 +23,7 @@ gem 'locomotive_carrierwave', '0.5.0.1.beta3', :require => 'carrierwave'
gem 'custom_fields', '1.0.0.beta.9' gem 'custom_fields', '1.0.0.beta.9'
gem 'fog', '0.3.7' gem 'fog', '0.3.7'
gem 'mimetype-fu' gem 'mimetype-fu'
gem 'actionmailer-with-request' gem 'actionmailer_with_request', :git => 'git://github.com/eric1234/actionmailer_with_request.git'
gem 'heroku', '1.18.2' gem 'heroku', '1.18.2'
gem 'bushido' gem 'bushido'
gem 'httparty', '>= 0.6.1' gem 'httparty', '>= 0.6.1'

View File

@ -1,3 +1,10 @@
GIT
remote: git://github.com/eric1234/actionmailer_with_request.git
revision: 6dd5658d44cc3624596a4fc57e6cde08b7e3bf43
specs:
actionmailer_with_request (0.0.3)
rails (>= 3)
GIT GIT
remote: git://github.com/floehopper/mocha.git remote: git://github.com/floehopper/mocha.git
revision: 2b4e868d1907859cd03f407078bd8b630f7d0dd6 revision: 2b4e868d1907859cd03f407078bd8b630f7d0dd6
@ -14,13 +21,16 @@ GIT
GEM GEM
remote: http://rubygems.org/ remote: http://rubygems.org/
specs: specs:
POpen4 (0.1.4)
Platform (>= 0.4.0)
open4
Platform (0.4.0)
RedCloth (4.2.7) RedCloth (4.2.7)
ZenTest (4.5.0) ZenTest (4.5.0)
abstract (1.0.0) abstract (1.0.0)
actionmailer (3.0.5) actionmailer (3.0.5)
actionpack (= 3.0.5) actionpack (= 3.0.5)
mail (~> 2.2.15) mail (~> 2.2.15)
actionmailer-with-request (0.1.1)
actionpack (3.0.5) actionpack (3.0.5)
activemodel (= 3.0.5) activemodel (= 3.0.5)
activesupport (= 3.0.5) activesupport (= 3.0.5)
@ -52,7 +62,7 @@ GEM
bson (1.2.4) bson (1.2.4)
bson_ext (1.2.4) bson_ext (1.2.4)
builder (2.1.2) builder (2.1.2)
bushido (0.0.10) bushido (0.0.11)
highline (>= 1.6.1) highline (>= 1.6.1)
json (>= 1.4.6) json (>= 1.4.6)
rest-client (>= 1.6.1) rest-client (>= 1.6.1)
@ -65,10 +75,10 @@ GEM
rack-test (>= 0.5.4) rack-test (>= 0.5.4)
selenium-webdriver (>= 0.0.27) selenium-webdriver (>= 0.0.27)
xpath (~> 0.1.2) xpath (~> 0.1.2)
celerity (0.8.8) celerity (0.8.9)
childprocess (0.1.8) childprocess (0.1.8)
ffi (~> 1.0.6) ffi (~> 1.0.6)
closure-compiler (1.0.0) closure-compiler (1.1.1)
columnize (0.3.2) columnize (0.3.2)
configuration (1.2.0) configuration (1.2.0)
crack (0.1.8) crack (0.1.8)
@ -85,7 +95,7 @@ GEM
activesupport (>= 3.0.4) activesupport (>= 3.0.4)
locomotive_carrierwave locomotive_carrierwave
mongoid (~> 2.0.0.rc.7) mongoid (~> 2.0.0.rc.7)
daemons (1.1.0) daemons (1.1.2)
database_cleaner (0.6.6) database_cleaner (0.6.6)
delayed_job (2.1.4) delayed_job (2.1.4)
activesupport (~> 3.0) activesupport (~> 3.0)
@ -99,7 +109,7 @@ GEM
diff-lcs (1.1.2) diff-lcs (1.1.2)
erubis (2.6.6) erubis (2.6.6)
abstract (>= 1.0.0) abstract (>= 1.0.0)
excon (0.5.7) excon (0.6.0)
factory_girl (1.3.3) factory_girl (1.3.3)
factory_girl_rails (1.0.1) factory_girl_rails (1.0.1)
factory_girl (~> 1.3) factory_girl (~> 1.3)
@ -167,20 +177,21 @@ GEM
mimetype-fu (0.1.2) mimetype-fu (0.1.2)
mongo (1.2.4) mongo (1.2.4)
bson (>= 1.2.4) bson (>= 1.2.4)
mongoid (2.0.0.rc.7) mongoid (2.0.0)
activemodel (~> 3.0) activemodel (~> 3.0)
mongo (~> 1.2) mongo (~> 1.2)
tzinfo (~> 0.3.22) tzinfo (~> 0.3.22)
will_paginate (~> 3.0.pre) will_paginate (~> 3.0.pre)
net-ssh (2.0.24) net-ssh (2.0.24)
nokogiri (1.4.3.1) nokogiri (1.4.3.1)
pickle (0.4.6) open4 (1.0.1)
pickle (0.4.7)
cucumber (>= 0.8) cucumber (>= 0.8)
rake rake
polyglot (0.3.1) polyglot (0.3.1)
proxies (0.2.1) proxies (0.2.1)
rack (1.2.2) rack (1.2.2)
rack-mount (0.6.13) rack-mount (0.6.14)
rack (>= 1.0.0) rack (>= 1.0.0)
rack-test (0.5.7) rack-test (0.5.7)
rack (>= 1.0) rack (>= 1.0)
@ -252,7 +263,8 @@ GEM
warden (0.10.7) warden (0.10.7)
rack (>= 1.0.0) rack (>= 1.0.0)
will_paginate (3.0.pre2) will_paginate (3.0.pre2)
yui-compressor (0.9.4) yui-compressor (0.9.6)
POpen4 (>= 0.1.4)
PLATFORMS PLATFORMS
ruby ruby
@ -260,7 +272,7 @@ PLATFORMS
DEPENDENCIES DEPENDENCIES
RedCloth (= 4.2.7) RedCloth (= 4.2.7)
ZenTest ZenTest
actionmailer-with-request actionmailer_with_request!
autotest autotest
bson_ext (~> 1.2.1) bson_ext (~> 1.2.1)
bushido bushido

View File

@ -1,15 +1,20 @@
module Admin::SitesHelper module Admin::SitesHelper
def application_domain def application_domain
domain = Locomotive.config.default_domain domain = Locomotive.config.domain
domain += ":#{request.port}" if request.port != 80 domain += ":#{request.port}" if request.port != 80
domain domain
end end
def main_site_url(site = current_site, options = {}) def main_site_url(site = current_site, options = {})
url = "http://#{site.subdomain}.#{Locomotive.config.default_domain}" # TODO: to be refactored
url += ":#{request.port}" if request.port != 80 if multi_sites_enabled?
url = File.join(url, controller.request.fullpath) if options.has_key?(:uri) && options[:uri] url = "http://#{site.subdomain}.#{Locomotive.config.domain}"
url += ":#{request.port}" if request.port != 80
else
url = "#{request.protocol}#{request.host_with_port}"
end
url = File.join(url, request.fullpath) if options.has_key?(:uri) && options[:uri]
url url
end end
@ -21,4 +26,16 @@ module Admin::SitesHelper
end end
end end
def manage_subdomain_or_domains?
Locomotive.config.manage_subdomain || Locomotive.config.manage_domains
end
def manage_domains?
Locomotive.config.manage_domains
end
def multi_sites_enabled?
Locomotive.multi_sites_enabled?
end
end end

View File

@ -4,7 +4,7 @@ class Asset
include Mongoid::Timestamps include Mongoid::Timestamps
## Extensions ## ## Extensions ##
include Models::Extensions::Asset::Vignette include Extensions::Asset::Vignette
include CustomFields::ProxyClassEnabler include CustomFields::ProxyClassEnabler
## fields ## ## fields ##

View File

@ -1,19 +1,17 @@
module Models module Extensions
module Extensions module Asset
module Asset module Vignette
module Vignette
def vignette_url def vignette_url
if self.image? if self.image?
if self.width < 80 && self.height < 80 if self.width < 80 && self.height < 80
self.source.url self.source.url
else else
self.source.url(:medium) self.source.url(:medium)
end
end end
end end
end end
end end
end end
end end

View File

@ -1,117 +1,115 @@
module Models module Extensions
module Extensions module Page
module Page module EditableElements
module EditableElements
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do included do
embeds_many :editable_elements embeds_many :editable_elements
after_save :remove_disabled_editable_elements after_save :remove_disabled_editable_elements
# editable file callbacks # editable file callbacks
after_save :store_file_sources! after_save :store_file_sources!
before_save :write_file_source_identifiers before_save :write_file_source_identifiers
after_destroy :remove_file_sources! after_destroy :remove_file_sources!
accepts_nested_attributes_for :editable_elements accepts_nested_attributes_for :editable_elements
end
module InstanceMethods
def disable_parent_editable_elements(block)
self.editable_elements.each { |el| el.disabled = true if el.from_parent? && el.block == block }
end end
module InstanceMethods def disable_all_editable_elements
self.editable_elements.each { |el| el.disabled = true }
end
def disable_parent_editable_elements(block) def editable_element_blocks
self.editable_elements.each { |el| el.disabled = true if el.from_parent? && el.block == block } self.editable_elements.collect(&:block)
end
def editable_elements_grouped_by_blocks
all_enabled = self.editable_elements.reject { |el| el.disabled? }
groups = all_enabled.group_by(&:block)
groups.delete_if { |block, elements| elements.empty? }
end
def find_editable_element(block, slug)
self.editable_elements.detect { |el| el.block == block && el.slug == slug }
end
def find_editable_files
self.editable_elements.find_all { |el| el.respond_to?(:source) }
end
def add_or_update_editable_element(attributes, type)
element = self.find_editable_element(attributes[:block], attributes[:slug])
if element
element.attributes = attributes
else
self.editable_elements.build(attributes, type)
end end
end
def disable_all_editable_elements def enable_editable_elements(block)
self.editable_elements.each { |el| el.disabled = true } self.editable_elements.each { |el| el.disabled = false if el.block == block }
end end
def editable_element_blocks def merge_editable_elements_from_page(source)
self.editable_elements.collect(&:block) source.editable_elements.each do |el|
end next if el.disabled? or !el.assignable?
def editable_elements_grouped_by_blocks existing_el = self.find_editable_element(el.block, el.slug)
all_enabled = self.editable_elements.reject { |el| el.disabled? }
groups = all_enabled.group_by(&:block)
groups.delete_if { |block, elements| elements.empty? }
end
def find_editable_element(block, slug) if existing_el.nil? # new one from parents
self.editable_elements.detect { |el| el.block == block && el.slug == slug } new_attributes = el.attributes.merge(:from_parent => true)
end if new_attributes['default_attribute'].present?
new_attributes['default_content'] = self.send(new_attributes['default_attribute']) || el.content
def find_editable_files
self.editable_elements.find_all { |el| el.respond_to?(:source) }
end
def add_or_update_editable_element(attributes, type)
element = self.find_editable_element(attributes[:block], attributes[:slug])
if element
element.attributes = attributes
else
self.editable_elements.build(attributes, type)
end
end
def enable_editable_elements(block)
self.editable_elements.each { |el| el.disabled = false if el.block == block }
end
def merge_editable_elements_from_page(source)
source.editable_elements.each do |el|
next if el.disabled? or !el.assignable?
existing_el = self.find_editable_element(el.block, el.slug)
if existing_el.nil? # new one from parents
new_attributes = el.attributes.merge(:from_parent => true)
if new_attributes['default_attribute'].present?
new_attributes['default_content'] = self.send(new_attributes['default_attribute']) || el.content
else
new_attributes['default_content'] = el.content
end
self.editable_elements.build(new_attributes, el.class)
elsif existing_el.default_attribute.nil?
existing_el.attributes = { :disabled => false, :default_content => el.content }
else else
existing_el.attributes = { :disabled => false } new_attributes['default_content'] = el.content
end end
self.editable_elements.build(new_attributes, el.class)
elsif existing_el.default_attribute.nil?
existing_el.attributes = { :disabled => false, :default_content => el.content }
else
existing_el.attributes = { :disabled => false }
end end
end end
end
def remove_disabled_editable_elements def remove_disabled_editable_elements
return unless self.editable_elements.any? { |el| el.disabled? } return unless self.editable_elements.any? { |el| el.disabled? }
# super fast way to remove useless elements all in once (TODO callbacks) # super fast way to remove useless elements all in once (TODO callbacks)
self.collection.update(self._selector, '$pull' => { 'editable_elements' => { 'disabled' => true } }) self.collection.update(self._selector, '$pull' => { 'editable_elements' => { 'disabled' => true } })
end end
protected protected
## callbacks for editable files ## callbacks for editable files
# equivalent to "after_save :store_source!" in EditableFile # equivalent to "after_save :store_source!" in EditableFile
def store_file_sources! def store_file_sources!
self.find_editable_files.collect(&:store_source!) self.find_editable_files.collect(&:store_source!)
end end
# equivalent to "before_save :write_source_identifier" in EditableFile # equivalent to "before_save :write_source_identifier" in EditableFile
def write_file_source_identifiers def write_file_source_identifiers
self.find_editable_files.collect(&:write_source_identifier) self.find_editable_files.collect(&:write_source_identifier)
end end
# equivalent to "after_destroy :remove_source!" in EditableFile
def remove_file_sources!
self.find_editable_files.collect(&:remove_source!)
end
# equivalent to "after_destroy :remove_source!" in EditableFile
def remove_file_sources!
self.find_editable_files.collect(&:remove_source!)
end end
end end
end end
end end
end end

View File

@ -1,19 +1,15 @@
module Models module Extensions
module Extensions module Page
module Page module Listed
module Listed
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do included do
field :listed, :type => Boolean, :default => true field :listed, :type => Boolean, :default => true
end
end end
end end
end end
end end

View File

@ -1,107 +1,105 @@
module Models module Extensions
module Extensions module Page
module Page module Parse
module Parse
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do included do
field :serialized_template, :type => Binary field :serialized_template, :type => Binary
field :template_dependencies, :type => Array, :default => [] field :template_dependencies, :type => Array, :default => []
field :snippet_dependencies, :type => Array, :default => [] field :snippet_dependencies, :type => Array, :default => []
attr_reader :template_changed attr_reader :template_changed
before_validation :serialize_template before_validation :serialize_template
after_save :update_template_descendants after_save :update_template_descendants
validate :template_must_be_valid validate :template_must_be_valid
scope :pages, lambda { |domain| { :any_in => { :domains => [*domain] } } } scope :pages, lambda { |domain| { :any_in => { :domains => [*domain] } } }
end
module InstanceMethods
def template
@template ||= Marshal.load(read_attribute(:serialized_template).to_s) rescue nil
end end
module InstanceMethods protected
def template def serialize_template
@template ||= Marshal.load(read_attribute(:serialized_template).to_s) rescue nil if self.new_record? || self.raw_template_changed?
end @template_changed = true
protected @parsing_errors = []
def serialize_template begin
if self.new_record? || self.raw_template_changed? self._parse_and_serialize_template
@template_changed = true rescue ::Liquid::SyntaxError => error
@parsing_errors << I18n.t(:liquid_syntax, :fullpath => self.fullpath, :error => error.to_s,:scope => [:errors, :messages])
@parsing_errors = [] rescue ::Locomotive::Liquid::PageNotFound => error
@parsing_errors << I18n.t(:liquid_extend, :fullpath => self.fullpath,:scope => [:errors, :messages])
begin
self._parse_and_serialize_template
rescue ::Liquid::SyntaxError => error
@parsing_errors << I18n.t(:liquid_syntax, :fullpath => self.fullpath, :error => error.to_s,:scope => [:errors, :messages])
rescue ::Locomotive::Liquid::PageNotFound => error
@parsing_errors << I18n.t(:liquid_extend, :fullpath => self.fullpath,:scope => [:errors, :messages])
end
end end
end end
end
def _parse_and_serialize_template(context = {}) def _parse_and_serialize_template(context = {})
self.parse(context) self.parse(context)
self._serialize_template self._serialize_template
end
def _serialize_template
self.serialized_template = BSON::Binary.new(Marshal.dump(@template))
end
def parse(context = {})
self.disable_all_editable_elements
default_context = { :site => self.site, :page => self, :templates => [], :snippets => [] }
context = default_context.merge(context)
@template = ::Liquid::Template.parse(self.raw_template, context)
self.template_dependencies = context[:templates]
self.snippet_dependencies = context[:snippets]
@template.root.context.clear
end
def template_must_be_valid
@parsing_errors.try(:each) { |msg| self.errors.add :template, msg }
end
def update_template_descendants
return unless @template_changed == true
# we admit at this point that the current template is up-to-date
template_descendants = self.site.pages.any_in(:template_dependencies => [self.id]).to_a
# group them by fullpath for better performance
cached = template_descendants.inject({}) { |memo, page| memo[page.fullpath] = page; memo }
self._update_direct_template_descendants(template_descendants, cached)
# finally save them all
template_descendants.map(&:save)
end
def _update_direct_template_descendants(template_descendants, cached)
direct_descendants = template_descendants.select do |page|
((page.template_dependencies || []) - (self.template_dependencies || [])).size == 1
end end
def _serialize_template direct_descendants.each do |page|
self.serialized_template = BSON::Binary.new(Marshal.dump(@template)) page.send(:_parse_and_serialize_template, { :cached_parent => self, :cached_pages => cached })
page.send(:_update_direct_template_descendants, template_descendants, cached)
end end
def parse(context = {})
self.disable_all_editable_elements
default_context = { :site => self.site, :page => self, :templates => [], :snippets => [] }
context = default_context.merge(context)
@template = ::Liquid::Template.parse(self.raw_template, context)
self.template_dependencies = context[:templates]
self.snippet_dependencies = context[:snippets]
@template.root.context.clear
end
def template_must_be_valid
@parsing_errors.try(:each) { |msg| self.errors.add :template, msg }
end
def update_template_descendants
return unless @template_changed == true
# we admit at this point that the current template is up-to-date
template_descendants = self.site.pages.any_in(:template_dependencies => [self.id]).to_a
# group them by fullpath for better performance
cached = template_descendants.inject({}) { |memo, page| memo[page.fullpath] = page; memo }
self._update_direct_template_descendants(template_descendants, cached)
# finally save them all
template_descendants.map(&:save)
end
def _update_direct_template_descendants(template_descendants, cached)
direct_descendants = template_descendants.select do |page|
((page.template_dependencies || []) - (self.template_dependencies || [])).size == 1
end
direct_descendants.each do |page|
page.send(:_parse_and_serialize_template, { :cached_parent => self, :cached_pages => cached })
page.send(:_update_direct_template_descendants, template_descendants, cached)
end
end
end end
end end
end end
end end
end end

View File

@ -1,25 +1,21 @@
module Models module Extensions
module Extensions module Page
module Page module Redirect
module Redirect
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do included do
field :redirect, :type => Boolean, :default => false field :redirect, :type => Boolean, :default => false
field :redirect_url, :type => String field :redirect_url, :type => String
validates_presence_of :redirect_url, :if => :redirect validates_presence_of :redirect_url, :if => :redirect
validates_format_of :redirect_url, :with => Locomotive::Regexps::URL, :allow_blank => true validates_format_of :redirect_url, :with => Locomotive::Regexps::URL, :allow_blank => true
end
end end
end end
end end
end end

View File

@ -1,13 +1,11 @@
module Models module Extensions
module Extensions module Page
module Page module Render
module Render
def render(context)
self.template.render(context)
end
def render(context)
self.template.render(context)
end end
end end
end end
end end

View File

@ -1,32 +1,28 @@
module Models module Extensions
module Extensions module Page
module Page module Templatized
module Templatized
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do included do
referenced_in :content_type referenced_in :content_type
field :templatized, :type => Boolean, :default => false field :templatized, :type => Boolean, :default => false
field :content_type_visible_column field :content_type_visible_column
before_validation :set_slug_if_templatized before_validation :set_slug_if_templatized
end end
module InstanceMethods module InstanceMethods
def set_slug_if_templatized
self.slug = 'content_type_template' if self.templatized?
end
def set_slug_if_templatized
self.slug = 'content_type_template' if self.templatized?
end end
end end
end end
end end
end end

View File

@ -1,155 +1,153 @@
module Models module Extensions
module Extensions module Page
module Page module Tree
module Tree
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do included do
include Mongoid::Acts::Tree include Mongoid::Acts::Tree
## fields ## ## fields ##
field :position, :type => Integer field :position, :type => Integer
## behaviours ## ## behaviours ##
acts_as_tree :order => ['position', 'asc'] acts_as_tree :order => ['position', 'asc']
## callbacks ## ## callbacks ##
before_validation :reset_parent before_validation :reset_parent
before_save { |p| p.send(:write_attribute, :parent_id, nil) if p.parent_id.blank? } before_save { |p| p.send(:write_attribute, :parent_id, nil) if p.parent_id.blank? }
before_save :change_parent before_save :change_parent
before_create { |p| p.send(:fix_position, false) } before_create { |p| p.send(:fix_position, false) }
before_create :add_to_list_bottom before_create :add_to_list_bottom
before_destroy :remove_from_list before_destroy :remove_from_list
# Fixme (Didier L.): Instances methods are defined before the include itself # Fixme (Didier L.): Instances methods are defined before the include itself
alias :fix_position :hacked_fix_position alias :fix_position :hacked_fix_position
alias :descendants :hacked_descendants alias :descendants :hacked_descendants
end
module ClassMethods
# Warning: used only in read-only
def quick_tree(site)
pages = site.pages.minimal_attributes.order_by([[:depth, :asc], [:position, :asc]]).to_a
tmp = []
while !pages.empty?
tmp << _quick_tree(pages.delete_at(0), pages)
end
tmp
end end
module ClassMethods def _quick_tree(current_page, pages)
i, children = 0, []
# Warning: used only in read-only while !pages.empty?
def quick_tree(site) page = pages[i]
pages = site.pages.minimal_attributes.order_by([[:depth, :asc], [:position, :asc]]).to_a
tmp = [] break if page.nil?
while !pages.empty? if page.parent_id == current_page.id
tmp << _quick_tree(pages.delete_at(0), pages) page = pages.delete_at(i)
children << _quick_tree(page, pages)
else
i += 1
end end
tmp
end end
def _quick_tree(current_page, pages) current_page.instance_eval do
i, children = 0, [] def children=(list); @children = list; end
def children; @children || []; end
while !pages.empty?
page = pages[i]
break if page.nil?
if page.parent_id == current_page.id
page = pages.delete_at(i)
children << _quick_tree(page, pages)
else
i += 1
end
end
current_page.instance_eval do
def children=(list); @children = list; end
def children; @children || []; end
end
current_page.children = children
current_page
end end
current_page.children = children
current_page
end end
module InstanceMethods end
def children? module InstanceMethods
self.class.where(self.parent_id_field => self.id).count
def children?
self.class.where(self.parent_id_field => self.id).count
end
def children_with_minimal_attributes
self.class.where(self.parent_id_field => self.id).
order_by(self.tree_order).
minimal_attributes
end
def sort_children!(ids)
ids.each_with_index do |id, position|
child = self.children.detect { |p| p._id == BSON::ObjectId(id) }
child.position = position
child.save
end end
end
def children_with_minimal_attributes def parent=(owner) # missing in acts_as_tree
self.class.where(self.parent_id_field => self.id). @_parent = owner
order_by(self.tree_order). self.fix_position(false)
minimal_attributes self.instance_variable_set :@_will_move, true
end end
def sort_children!(ids) def hacked_descendants
ids.each_with_index do |id, position| return [] if new_record?
child = self.children.detect { |p| p._id == BSON::ObjectId(id) } self.class.all_in(path_field => [self._id]).order_by tree_order
child.position = position end
child.save
end
end
def parent=(owner) # missing in acts_as_tree protected
@_parent = owner
def change_parent
if self.parent_id_changed?
self.fix_position(false) self.fix_position(false)
unless self.parent_id_was.nil?
self.position = nil # make it move to bottom
self.add_to_list_bottom
end
self.instance_variable_set :@_will_move, true self.instance_variable_set :@_will_move, true
end end
def hacked_descendants
return [] if new_record?
self.class.all_in(path_field => [self._id]).order_by tree_order
end
protected
def change_parent
if self.parent_id_changed?
self.fix_position(false)
unless self.parent_id_was.nil?
self.position = nil # make it move to bottom
self.add_to_list_bottom
end
self.instance_variable_set :@_will_move, true
end
end
def hacked_fix_position(perform_save = true)
if parent.nil?
self.write_attribute parent_id_field, nil
self[path_field] = []
self[depth_field] = 0
else
self.write_attribute parent_id_field, parent._id
self[path_field] = parent[path_field] + [parent._id]
self[depth_field] = parent[depth_field] + 1
self.save if perform_save
end
end
def reset_parent
if self.parent_id_changed?
@_parent = nil
end
end
def add_to_list_bottom
self.position ||= (::Page.where(:_id.ne => self._id).and(:parent_id => self.parent_id).max(:position) || 0) + 1
end
def remove_from_list
return if (self.site rescue nil).nil?
::Page.where(:parent_id => self.parent_id).and(:position.gt => self.position).each do |p|
p.position -= 1
p.save
end
end
end end
def hacked_fix_position(perform_save = true)
if parent.nil?
self.write_attribute parent_id_field, nil
self[path_field] = []
self[depth_field] = 0
else
self.write_attribute parent_id_field, parent._id
self[path_field] = parent[path_field] + [parent._id]
self[depth_field] = parent[depth_field] + 1
self.save if perform_save
end
end
def reset_parent
if self.parent_id_changed?
@_parent = nil
end
end
def add_to_list_bottom
self.position ||= (::Page.where(:_id.ne => self._id).and(:parent_id => self.parent_id).max(:position) || 0) + 1
end
def remove_from_list
return if (self.site rescue nil).nil?
::Page.where(:parent_id => self.parent_id).and(:position.gt => self.position).each do |p|
p.position -= 1
p.save
end
end
end end
end end
end end

View File

@ -0,0 +1,79 @@
module Extensions
module Site
module SubdomainDomains
unloadable
extend ActiveSupport::Concern
included do
## fields ##
field :subdomain
field :domains, :type => Array, :default => []
## indexes
index :domains
## validations ##
validates_presence_of :subdomain
validates_uniqueness_of :subdomain
validates_exclusion_of :subdomain, :in => Locomotive.config.multi_sites.reserved_subdomains
validates_format_of :subdomain, :with => Locomotive::Regexps::SUBDOMAIN, :allow_blank => true
validate :domains_must_be_valid_and_unique
## callbacks ##
before_save :add_subdomain_to_domains
## named scopes ##
scope :match_domain, lambda { |domain| { :any_in => { :domains => [*domain] } } }
scope :match_domain_with_exclusion_of, lambda { |domain, site|
{ :any_in => { :domains => [*domain] }, :where => { :_id.ne => site.id } }
}
end
module InstanceMethods
def domains=(array)
array = [] if array.blank?; super(array)
end
def add_subdomain_to_domains
self.domains ||= []
(self.domains << self.full_subdomain).uniq!
end
def domains_without_subdomain
(self.domains || []) - [self.full_subdomain]
end
def domains_with_subdomain
((self.domains || []) + [self.full_subdomain]).uniq
end
def full_subdomain
"#{self.subdomain}.#{Locomotive.config.default_domain}"
end
protected
def domains_must_be_valid_and_unique
return if self.domains.empty?
self.domains_without_subdomain.each do |domain|
if self.class.match_domain_with_exclusion_of(domain, self).any?
self.errors.add(:domains, :domain_taken, :value => domain)
end
if not domain =~ Locomotive::Regexps::DOMAIN
self.errors.add(:domains, :invalid_domain, :value => domain)
end
end
end
end
end
end
end

View File

@ -3,13 +3,13 @@ class Page
include Locomotive::Mongoid::Document include Locomotive::Mongoid::Document
## Extensions ## ## Extensions ##
include Models::Extensions::Page::Tree include Extensions::Page::Tree
include Models::Extensions::Page::EditableElements include Extensions::Page::EditableElements
include Models::Extensions::Page::Parse include Extensions::Page::Parse
include Models::Extensions::Page::Render include Extensions::Page::Render
include Models::Extensions::Page::Templatized include Extensions::Page::Templatized
include Models::Extensions::Page::Redirect include Extensions::Page::Redirect
include Models::Extensions::Page::Listed include Extensions::Page::Listed
## fields ## ## fields ##
field :title field :title

View File

@ -4,8 +4,6 @@ class Site
## fields ## ## fields ##
field :name field :name
field :subdomain
field :domains, :type => Array, :default => []
field :meta_keywords field :meta_keywords
field :meta_description field :meta_description
@ -17,37 +15,19 @@ class Site
references_many :content_types, :dependent => :destroy references_many :content_types, :dependent => :destroy
embeds_many :memberships embeds_many :memberships
## indexes
index :domains
## validations ## ## validations ##
validates_presence_of :name, :subdomain validates_presence_of :name
validates_uniqueness_of :subdomain
validates_exclusion_of :subdomain, :in => Locomotive.config.reserved_subdomains
validates_format_of :subdomain, :with => Locomotive::Regexps::SUBDOMAIN, :allow_blank => true
validate :domains_must_be_valid_and_unique
## callbacks ## ## callbacks ##
after_create :create_default_pages! after_create :create_default_pages!
before_save :add_subdomain_to_domains
after_destroy :destroy_pages after_destroy :destroy_pages
## named scopes ##
scope :match_domain, lambda { |domain| { :any_in => { :domains => [*domain] } } }
scope :match_domain_with_exclusion_of, lambda { |domain, site|
{ :any_in => { :domains => [*domain] }, :where => { :_id.ne => site.id } }
}
## methods ## ## methods ##
def all_pages_in_once def all_pages_in_once
Page.quick_tree(self) Page.quick_tree(self)
end end
def domains=(array)
array = [] if array.blank?; super(array)
end
def accounts def accounts
Account.criteria.in(:_id => self.memberships.collect(&:account_id)) Account.criteria.in(:_id => self.memberships.collect(&:account_id))
end end
@ -56,43 +36,12 @@ class Site
self.memberships.find_all { |m| m.admin? } self.memberships.find_all { |m| m.admin? }
end end
def add_subdomain_to_domains
self.domains ||= []
(self.domains << self.full_subdomain).uniq!
end
def domains_without_subdomain
(self.domains || []) - [self.full_subdomain]
end
def domains_with_subdomain
((self.domains || []) + [self.full_subdomain]).uniq
end
def full_subdomain
"#{self.subdomain}.#{Locomotive.config.default_domain}"
end
def to_liquid def to_liquid
Locomotive::Liquid::Drops::Site.new(self) Locomotive::Liquid::Drops::Site.new(self)
end end
protected protected
def domains_must_be_valid_and_unique
return if self.domains.empty?
self.domains_without_subdomain.each do |domain|
if self.class.match_domain_with_exclusion_of(domain, self).any?
self.errors.add(:domains, :domain_taken, :value => domain)
end
if not domain =~ Locomotive::Regexps::DOMAIN
self.errors.add(:domains, :invalid_domain, :value => domain)
end
end
end
def create_default_pages! def create_default_pages!
%w{index 404}.each do |slug| %w{index 404}.each do |slug|
self.pages.create({ self.pages.create({

View File

@ -9,35 +9,37 @@
= f.input :meta_keywords = f.input :meta_keywords
= f.input :meta_description = f.input :meta_description
= f.foldable_inputs :name => :access_points, :class => 'editable-list off' do - if manage_subdomain_or_domains?
= f.foldable_inputs :name => :access_points, :class => 'editable-list off' do
= f.custom_input :subdomain, :css => 'path' do = f.custom_input :subdomain, :css => 'path' do
%em
http://
= f.text_field :subdomain
\.
%em
= application_domain
- @site.domains_without_subdomain.each_with_index do |name, index|
%li{ :class => "item added #{'last' if index == @site.domains.size - 1}"}
%em %em
http:// http://
= text_field_tag 'site[domains][]', name, :class => 'string label void domain' = f.text_field :subdomain
&nbsp; \.
= error_on_domain(@site, name) %em
%span.actions = application_domain
= link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove first', :confirm => t('admin.messages.confirm')
%li.item.template - if manage_domains?
%em - @site.domains_without_subdomain.each_with_index do |name, index|
http:// %li{ :class => "item added #{'last' if index == @site.domains.size - 1}"}
= text_field_tag 'label', t('formtastic.hints.site.domain_name'), :class => 'string label void domain' %em
&nbsp; http://
%span.actions = text_field_tag 'site[domains][]', name, :class => 'string label void domain'
= link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove first', :confirm => t('admin.messages.confirm') &nbsp;
%button{ :class => 'button light add', :type => 'button' } = error_on_domain(@site, name)
%span!= t('admin.buttons.new_item') %span.actions
= link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove first', :confirm => t('admin.messages.confirm')
%li.item.template
%em
http://
= text_field_tag 'label', t('formtastic.hints.site.domain_name'), :class => 'string label void domain'
&nbsp;
%span.actions
= link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove first', :confirm => t('admin.messages.confirm')
%button{ :class => 'button light add', :type => 'button' }
%span!= t('admin.buttons.new_item')
= f.foldable_inputs :name => :memberships, :class => 'memberships' do = f.foldable_inputs :name => :memberships, :class => 'memberships' do
- @site.memberships.each_with_index do |membership, index| - @site.memberships.each_with_index do |membership, index|

View File

@ -6,8 +6,9 @@
- content_for :submenu do - content_for :submenu do
= render 'admin/shared/menu/settings' = render 'admin/shared/menu/settings'
- content_for :buttons do - if multi_sites_enabled?
= admin_button_tag t('.new_site'), new_admin_site_url, :class => 'new' - content_for :buttons do
= admin_button_tag t('.new_site'), new_admin_site_url, :class => 'new'
%p= t('.help') %p= t('.help')

View File

@ -4,32 +4,34 @@
= f.foldable_inputs :name => :information, :style => "#{'display: none' unless @site.new_record?}" do = f.foldable_inputs :name => :information, :style => "#{'display: none' unless @site.new_record?}" do
= f.input :name, :required => false = f.input :name, :required => false
= f.foldable_inputs :name => :access_points, :class => 'editable-list off' do - if manage_subdomain_or_domains?
= f.foldable_inputs :name => :access_points, :class => 'editable-list off' do
= f.custom_input :subdomain, :css => 'path' do = f.custom_input :subdomain, :css => 'path' do
%em
http://
= f.text_field :subdomain
\.
%em
= application_domain
- @site.domains_without_subdomain.each_with_index do |name, index|
%li{ :class => "item added #{'last' if index == @site.domains.size - 1}"}
%em %em
http:// http://
= text_field_tag 'site[domains][]', name, :class => 'string label void domain' = f.text_field :subdomain
&nbsp; \.
= error_on_domain(@site, name) %em
%span.actions = application_domain
= link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove first', :confirm => t('admin.messages.confirm')
%li.item.template - if manage_domains?
%em - @site.domains_without_subdomain.each_with_index do |name, index|
http:// %li{ :class => "item added #{'last' if index == @site.domains.size - 1}" }
= text_field_tag 'label', t('formtastic.hints.site.domain_name'), :class => 'string label void domain' %em
&nbsp; http://
%span.actions = text_field_tag 'site[domains][]', name, :class => 'string label void domain'
= link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove first', :confirm => t('admin.messages.confirm') &nbsp;
%button{ :class => 'button light add', :type => 'button' } = error_on_domain(@site, name)
%span!= t('admin.buttons.new_item') %span.actions
= link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove first', :confirm => t('admin.messages.confirm')
%li.item.template
%em
http://
= text_field_tag 'label', t('formtastic.hints.site.domain_name'), :class => 'string label void domain'
&nbsp;
%span.actions
= link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove first', :confirm => t('admin.messages.confirm')
%button{ :class => 'button light add', :type => 'button' }
%span!= t('admin.buttons.new_item')

View File

@ -16,6 +16,8 @@ module Locomotive
# Add additional load paths for your own custom dirs # Add additional load paths for your own custom dirs
# config.load_paths += %W( #{config.root}/extras ) # config.load_paths += %W( #{config.root}/extras )
# config.autoload_paths += %W( #{config.root}/app/models/extensions #{config.root}/app/models/extensions/site #{config.root}/app/models/extensions/page #{config.root}/app/models/extensions/asset)
# config.autoload_paths += %W( #{config.root}/app/models/extensions/site )
# Only load the plugins named here, in the order given (default is alphabetical). # Only load the plugins named here, in the order given (default is alphabetical).
# :all can be used as a placeholder for all plugins not explicitly named # :all can be used as a placeholder for all plugins not explicitly named

View File

@ -1,36 +1,107 @@
require File.dirname(__FILE__) + '/../../lib/locomotive.rb' require File.dirname(__FILE__) + '/../../lib/locomotive.rb'
# require_or_load 'extensions/site/subdomain_domains'
Locomotive.configure do |config| Locomotive.configure do |config|
# if not defined, locomotive will use example.com as main domain name. Remove prefix www from your domain name.
# Ex: # A single locomotive instance can serve one single site or many.
# config.default_domain = Rails.env.production? ? 'mydomain.com' : 'example.com' # If you want to run many different websites, you will have to specify
# your own domain name (ex: locomotivehosting.com).
# #
# If you use locomotive for a single site in Heroku, use "heroku.com" as default domain name.
# Your heroku app name (<app_name>.heroku.name) will be used as the sub domain name in Locomotive
# during the installation wizzard.
# Ex: # Ex:
# config.default_domain = Rails.env.production? ? 'heroku.com' : 'example.com' config.multi_sites do |multi_sites|
config.default_domain = ENV["APP_TLD"] || 'example.com' # each new website you add will have a default entry based on a subdomain
# and the multi_site_domain value (ex: website_1.locomotivehosting.com).
multi_sites.domain = 'example.com' #'myhostingplatform.com'
# define the reserved subdomains
# Ex:
multi_sites.reserved_subdomains = %w(www admin email blog webmail mail support help site sites)
end
# config.multi_sites = false
# configure the hosting target for the production environment. Locomotive can be installed in:
# - your own server
# - Heroku (you need to create an account in this case)
# - Bushi.do (you need to create an account in this case)
#
# the possible options are: server, heroku, bushido or auto (default)
# if you select 'auto', Locomotive will look after specific ENV variables to check
# the matching platform (Heroku and Bushido set their own ENV variable).
#
config.hosting = :auto
# In case you host Locomotive in Heroku, the engine uses the 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
# config.heroku = {
# :login => '<your_heroku_login>'
# :password => '<your_heroku_password>'
# }
# Locomotive uses the DelayedJob gem for the theme import module.
# In case you want to deploy to Heroku, you will have to pay for an extra dyno.
# If you do not mind about importing theme without DelayedJob, disable it.
config.delayed_job = false
# configure how many items we display in sub menu in the "Contents" section. # configure how many items we display in sub menu in the "Contents" section.
config.lastest_items_nb = 5 config.lastest_items_nb = 5
# default locale (for now, only en, de, fr and pt-BR are supported)
config.default_locale = :en
# tell if logs are enabled. Useful for debug purpose. # tell if logs are enabled. Useful for debug purpose.
config.enable_logs = true config.enable_logs = true
# tell if the application is hosted on Heroku. # Configure the e-mail address which will be shown in the DeviseMailer, NotificationMailer, ...etc
# Locomotive uses heroku api to add / remove domains. # if you do not put the domain name in the email, Locomotive will take the default domain name depending
# there are 2 ways of passing heroku credentials to Locomotive # on your deployment target (server, Heroku, Bushido, ...etc)
# - 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: # Ex:
# config.heroku = { :name => '<my heroku app name>', :login => 'john@doe.net', :password => 'easy' } # config.mailer_sender = 'support'
config.heroku = false # # => 'support@heroku.com' (Heroku), 'support@bushi.do' (Bushido), 'support@example.com' (Dev) or 'support@<your_hosting_platform>' (Multi-sites)
config.mailer_sender = 'support'
# ===================================================
# do |deployment|
#
# # the possible options are: server, heroku, bushido or auto (default)
# # if you select 'auto', Locomotive will look after specific ENV variables to check
# # the matching platform (Heroku and Bushido set their own ENV variable).
# deployment.target = :auto
#
# # in case you select heroku, it requires your Heroku credentials in order to manage domains.
# # If you do not want to store the credentials here, you can add them directly into the Heroku ENV
# # (see Heroku documentation)
# # deployment.heroku = {
# # :login => '<your_heroku_login>'
# # :password => '<your_heroku_password>'
# # }
# end
#
# # define the domain name used in production.cn
# deployment.domain = 'example.com'
# set the default domain name used in development/test. By default, 'example.com' is used.
# config.local_domain = 'example.com'
# Locomotive can serve one single site or many. The "many sites" option requires more constraints for
# the domain name (see config.domain for more explanation)
#
# Ex:
# config.site_mode = :single
# or
# config.site_mode = :many
# config.site_mode = :single
# 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'
#
# If you use locomotive for a single site in Heroku, use "heroku.com" as default domain name.bzc,
# tell if the application is hosted on Bushido. # tell if the application is hosted on Bushido.
# If enabled, there's no further configuration needed. # If enabled, there's no further configuration needed.
@ -38,16 +109,16 @@ Locomotive.configure do |config|
# #
# Ex: # Ex:
# config.bushido = true # config.bushido = true
config.bushido = ENV['HOSTING_PLATFORM'] == 'bushido' # config.bushido = ENV['HOSTING_PLATFORM'] == 'bushido'
# Locomotive uses the DelayedJob gem for the theme import module. # Locomotive uses the DelayedJob gem for the theme import module.
# In case you want to deploy to Heroku, you will have to pay for an extra dyno. # In case you want to deploy to Heroku, you will have to pay for an extra dyno.
# If you do not mind about importing theme without DelayedJob, disable it. # If you do not mind about importing theme without DelayedJob, disable it.
config.delayed_job = false # config.delayed_job = false
#
# default locale (for now, only en, de, fr and pt-BR are supported) # # default locale (for now, only en, de, fr and pt-BR are supported)
config.default_locale = :en # config.default_locale = :en
#
# Configure the e-mail address which will be shown in the DeviseMailer, NotificationMailer, ...etc # # Configure the e-mail address which will be shown in the DeviseMailer, NotificationMailer, ...etc
config.mailer_sender = ENV['BUSHIDO_DOMAIN'] ? "support@#{ENV['BUSHIDO_DOMAIN']}" : 'support@example.com' # config.mailer_sender = ENV['BUSHIDO_DOMAIN'] ? "support@#{ENV['BUSHIDO_DOMAIN']}" : 'support@example.com'
end end

View File

@ -9,6 +9,7 @@ x bugs:
- 2 different sites on the same main domain (one in www, the other one in something else) (Raphael Costa) - 2 different sites on the same main domain (one in www, the other one in something else) (Raphael Costa)
- seo section for the page form: seo title, seo keywords, seo description - seo section for the page form: seo title, seo keywords, seo description
- icon for redirection page in the pages section (back-office) - icon for redirection page in the pages section (back-office)
- write my first tutorial about locomotive
BACKLOG: BACKLOG:
@ -20,7 +21,6 @@ BACKLOG:
- editable elements should wrap a tag: div, h1, ...etc (default span) - editable elements should wrap a tag: div, h1, ...etc (default span)
- edit images (upload new ones, ...etc) => wait for aloha or send them an email ? - edit images (upload new ones, ...etc) => wait for aloha or send them an email ?
- global regions: keyword in editable element (http://www.mongodb.org/display/DOCS/Updating) - global regions: keyword in editable element (http://www.mongodb.org/display/DOCS/Updating)
- write my first tutorial about locomotive
- cucumber features for admin pages (in progress) - cucumber features for admin pages (in progress)
REFACTORING: REFACTORING:

View File

@ -8,7 +8,7 @@ require 'locomotive/logger'
require 'locomotive/liquid' require 'locomotive/liquid'
require 'locomotive/mongoid' require 'locomotive/mongoid'
require 'locomotive/carrierwave' require 'locomotive/carrierwave'
require 'locomotive/deployment' require 'locomotive/hosting'
require 'locomotive/custom_fields' require 'locomotive/custom_fields'
require 'locomotive/httparty' require 'locomotive/httparty'
require 'locomotive/inherited_resources' require 'locomotive/inherited_resources'
@ -23,8 +23,8 @@ require 'locomotive/session_store'
module Locomotive module Locomotive
include Locomotive::Deployment::Heroku include Locomotive::Hosting::Heroku
include Locomotive::Deployment::Bushido include Locomotive::Hosting::Bushido
class << self class << self
attr_accessor :config attr_accessor :config
@ -37,29 +37,31 @@ module Locomotive
def self.configure def self.configure
self.config ||= Configuration.new self.config ||= Configuration.new
yield(self.config) yield(self.config)
after_configure after_configure
end end
def self.after_configure def self.after_configure
raise '[Error] Locomotive needs a default domain name' if Locomotive.config.default_domain.blank? # ActionMailer::Base.default_url_options[:host] = self.config.default_domain + (Rails.env.development? ? ':3000' : '')
ActionMailer::Base.default_url_options[:host] = self.config.default_domain + (Rails.env.development? ? ':3000' : '') # multi sites support
self.configure_multi_sites
# hosting platform
self.configure_hosting
# Devise
mail_address = self.config.mailer_sender
Devise.mailer_sender = mail_address =~ /.+@.+/ ? mail_address : "#{mail_address}@#{Locomotive.config.domain}"
# cookies stored in mongodb (mongoid_store) # cookies stored in mongodb (mongoid_store)
Rails.application.config.session_store :mongoid_store, { Rails.application.config.session_store :mongoid_store, {
:key => Locomotive.config.cookie_key :key => self.config.cookie_key
} }
# Hosting-platform support self.define_various_helpers
self.enable_heroku if self.heroku?
# Bushido support
self.enable_bushido if self.bushido?
# Devise
Devise.mailer_sender = self.config.mailer_sender
# Load all the dynamic classes (custom fields) # Load all the dynamic classes (custom fields)
begin begin
@ -70,8 +72,44 @@ module Locomotive
end end
end end
def self.configure_multi_sites
if self.multi_sites_enabled?
domain_name = self.config.multi_sites.domain
raise '[Error] Locomotive needs a domain name when used as a multi sites platform' if domain_name.blank?
# Site.send :include, Extensions::Site::SubdomainDomains
self.config.domain = domain_name
end
end
def self.configure_hosting
if Rails.env.production?
# Heroku support
self.enable_heroku if self.heroku?
# Bushido support
self.enable_bushido if self.bushido?
end
end
def self.define_various_helpers
if self.multi_sites_enabled?
self.config.manage_subdomain = self.config.manage_domains = true
else
# FIXME: (Did) modify the code below if Locomotive handles a new hosting solution
self.config.manage_domains = self.heroku? || self.bushido?
self.config.manage_subdomain = self.bushido?
end
end
def self.multi_sites_enabled?
self.config.multi_sites != false
end
def self.logger(message) def self.logger(message)
if Locomotive.config.enable_logs == true if self.config.enable_logs == true
Rails.logger.info(message) Rails.logger.info(message)
end end
end end

View File

@ -3,18 +3,20 @@ module Locomotive
@@defaults = { @@defaults = {
:name => 'LocomotiveApp', :name => 'LocomotiveApp',
:default_domain => 'example.com', :domain => 'example.com',
:reserved_subdomains => %w{www admin email blog webmail mail support help site sites}, # :multi_sites => false,
# :default_domain => 'example.com',
# :reserved_subdomains => %w{www admin email blog webmail mail support help site sites},
# :forbidden_paths => %w{layouts snippets stylesheets javascripts assets admin system api}, # :forbidden_paths => %w{layouts snippets stylesheets javascripts assets admin system api},
:reserved_slugs => %w{stylesheets javascripts assets admin images api pages edit}, :reserved_slugs => %w{stylesheets javascripts assets admin images api pages edit},
:locales => %w{en de fr pt-BR}, :locales => %w{en de fr pt-BR},
:cookie_key => '_locomotive_session', :cookie_key => '_locomotive_session',
:enable_logs => false, :enable_logs => false,
:heroku => false, # :heroku => false,
:bushido => false, # :bushido => false,
:delayed_job => true, :delayed_job => true,
:default_locale => :en, :default_locale => :en,
:mailer_sender => 'support@example.com' :mailer_sender => 'support' #support@example.com'
} }
cattr_accessor :settings cattr_accessor :settings

View File

@ -1 +0,0 @@
Dir[File.join(File.dirname(__FILE__), 'deployment/*.rb')].each { |lib| require lib }

View File

@ -0,0 +1 @@
Dir[File.join(File.dirname(__FILE__), 'hosting/*.rb')].each { |lib| require lib }

View File

@ -1,8 +1,8 @@
require 'bushido' require 'bushido'
require 'locomotive/deployment/bushido/custom_domain' require 'locomotive/hosting/bushido/custom_domain'
module Locomotive module Locomotive
module Deployment module Hosting
module Bushido module Bushido
extend ActiveSupport::Concern extend ActiveSupport::Concern
@ -17,19 +17,22 @@ module Locomotive
module ClassMethods module ClassMethods
def bushido? def bushido?
ENV["HOSTING_PLATFORM"] == 'bushido' self.config.hosting == :bushido ||
(self.config.hosting == :auto && ENV['HOSTING_PLATFORM'] == 'bushido')
end end
def enable_bushido def enable_bushido
self.config.domain = ENV['APP_TLD']
self.enhance_site_model self.enhance_site_model
self.bushido_domains = ::Bushido::App.domains self.bushido_domains = ::Bushido::App.domains
self.bushido_subdomain = ::Bushido::App.subdomain self.bushido_subdomain = ::Bushido::App.subdomain
end end
def enhance_site_model def enhance_site_model
Site.send :include, Locomotive::Deployment::Bushido::CustomDomain Site.send :include, Extensions::Site::SubdomainDomains
Site.send :include, Locomotive::Hosting::Bushido::CustomDomain
end end
# manage domains # manage domains

View File

@ -1,5 +1,5 @@
module Locomotive module Locomotive
module Deployment module Hosting
module Bushido module Bushido
module CustomDomain module CustomDomain
@ -20,14 +20,14 @@ module Locomotive
protected protected
def subdomain_availability def subdomain_availability
unless ::Bushido::App.check_subdomain_availability?(self.subdomain) unless ::Bushido::App.subdomain_available?(self.subdomain)
self.errors.add(:subdomain, :exclusion) self.errors.add(:subdomain, :exclusion)
end end
end end
def add_subdomain_to_domains_with_bushido def add_subdomain_to_domains_with_bushido
unless self.domains_change.nil? unless self.domains_change.nil?
full_subdomain = "#{self.subdomain}.#{Locomotive.config.default_domain}" full_subdomain = "#{self.subdomain}.#{Locomotive.config.domain}"
@bushido_domains_change = { @bushido_domains_change = {
:added => self.domains_change.last - self.domains_change.first - [full_subdomain], :added => self.domains_change.last - self.domains_change.first - [full_subdomain],
:removed => self.domains_change.first - self.domains_change.last - [full_subdomain] :removed => self.domains_change.first - self.domains_change.last - [full_subdomain]

View File

@ -1,9 +1,9 @@
require 'heroku' require 'heroku'
require 'heroku/client' require 'heroku/client'
require 'locomotive/deployment/heroku/custom_domain' require 'locomotive/hosting/heroku/custom_domain'
module Locomotive module Locomotive
module Deployment module Hosting
module Heroku module Heroku
extend ActiveSupport::Concern extend ActiveSupport::Concern
@ -18,11 +18,16 @@ module Locomotive
module ClassMethods module ClassMethods
def heroku? def heroku?
!self.config.heroku.nil? && self.config.heroku.respond_to?(:[]) self.config.hosting == :heroku ||
(self.config.hosting == :auto && ENV['HEROKU_SLUG'].present?)
end end
def enable_heroku def enable_heroku
raise 'Heroku application name is mandatory' if self.config.heroku[:name].blank? # raise 'Heroku application name is mandatory' if self.config.heroku[:name].blank?
self.config.domain = 'heroku.com'
self.config.heroku ||= {}
self.config.heroku[:name] = ENV['APP_NAME']
self.open_heroku_connection self.open_heroku_connection
self.enhance_site_model self.enhance_site_model
@ -33,13 +38,14 @@ module Locomotive
def open_heroku_connection def open_heroku_connection
login = self.config.heroku[:login] || ENV['HEROKU_LOGIN'] login = self.config.heroku[:login] || ENV['HEROKU_LOGIN']
password = self.config.heroku[:password] || ENV['HEROKU_PASSWORD'] password = self.config.heroku[:password] rescue ENV['HEROKU_PASSWORD']
self.heroku_connection = ::Heroku::Client.new(login, password) self.heroku_connection = ::Heroku::Client.new(login, password)
end end
def enhance_site_model def enhance_site_model
Site.send :include, Locomotive::Deployment::Heroku::CustomDomain Site.send :include, Extensions::Site::SubdomainDomains
Site.send :include, Locomotive::Hosting::Heroku::CustomDomain
end end
# manage domains # manage domains

View File

@ -1,5 +1,5 @@
module Locomotive module Locomotive
module Deployment module Hosting
module Heroku module Heroku
module CustomDomain module CustomDomain
@ -19,7 +19,7 @@ module Locomotive
def add_subdomain_to_domains_with_heroku def add_subdomain_to_domains_with_heroku
unless self.domains_change.nil? unless self.domains_change.nil?
full_subdomain = "#{self.subdomain}.#{Locomotive.config.default_domain}" full_subdomain = "#{self.subdomain}.#{Locomotive.config.domain}"
@heroku_domains_change = { @heroku_domains_change = {
:added => self.domains_change.last - self.domains_change.first - [full_subdomain], :added => self.domains_change.last - self.domains_change.first - [full_subdomain],
:removed => self.domains_change.first - self.domains_change.last - [full_subdomain] :removed => self.domains_change.first - self.domains_change.last - [full_subdomain]

View File

@ -3,10 +3,14 @@ module Locomotive
class DefaultConstraint class DefaultConstraint
def self.matches?(request) def self.matches?(request)
domain, subdomain = domain_and_subdomain(request) if Locomotive.multi_sites_enabled?
subdomain = 'www' if subdomain.blank? domain, subdomain = domain_and_subdomain(request)
subdomain = 'www' if subdomain.blank?
domain == Locomotive.config.default_domain && Locomotive.config.reserved_subdomains.include?(subdomain) domain == Locomotive.config.domain && Locomotive.config.multi_sites.reserved_subdomains.include?(subdomain)
else
false
end
end end
# see actionpack/lib/action_dispatch/http/url.rb for more information # see actionpack/lib/action_dispatch/http/url.rb for more information

View File

@ -18,7 +18,12 @@ module Locomotive
def fetch_site def fetch_site
Locomotive.logger "[fetch site] host = #{request.host} / #{request.env['HTTP_HOST']}" Locomotive.logger "[fetch site] host = #{request.host} / #{request.env['HTTP_HOST']}"
@current_site ||= Site.match_domain(request.host).first
if Locomotive.multi_sites_enabled?
@current_site ||= Site.match_domain(request.host).first
else
@current_site ||= Site.first
end
end end
def current_site def current_site
@ -34,7 +39,7 @@ module Locomotive
end end
def render_no_site_error def render_no_site_error
render :template => "/admin/errors/no_site", :layout => false render :template => '/admin/errors/no_site', :layout => false
end end
def validate_site_membership def validate_site_membership