Skip to content

Commit

Permalink
Added support for multiple site alias. Closes #45.
Browse files Browse the repository at this point in the history
  • Loading branch information
gaspard committed Jun 18, 2013
1 parent ed2e38a commit 92f468d
Show file tree
Hide file tree
Showing 19 changed files with 105 additions and 42 deletions.
5 changes: 3 additions & 2 deletions app/controllers/sites_controller.rb
Expand Up @@ -54,8 +54,9 @@ def update
end

def clear_cache
@site = secure!(Site) { Site.first }
@site.clear_cache
secure!(Site) { Site.all }.each do |site|
site.clear_cache
end
redirect_to '/'
end

Expand Down
2 changes: 1 addition & 1 deletion app/controllers/user_sessions_controller.rb
Expand Up @@ -58,7 +58,7 @@ def set_visitor
raise ActiveRecord::RecordNotFound.new("host not found #{request.host}")
end

Thread.current[:visitor] = anonymous_visitor(site)
setup_visitor(anonymous_visitor(site), site)
end

def redirect_after_login
Expand Down
62 changes: 42 additions & 20 deletions app/models/site.rb
Expand Up @@ -6,7 +6,8 @@
The #Site model holds configuration information for a site:
+host+:: Unique host name. (teti.ch, zenadmin.org, dev.example.org, ...)
+root_id+:: Site root node id. This is the only node in the site without a parent.
+orphan_id+:: Site seed node id. This is the only node in the site without a parent.
+root_id+:: This is the apparent root of the site.
+anon_id+:: Anonymous user id. This user is the 'public' user of the site. Even if +authorize+ is set to true, this user is needed to configure the defaults for all newly created users.
+public_group_id+:: Id of the 'public' group. Every user of the site (with 'anonymous user') belongs to this group.
+site_group_id+:: Id of the 'site' group. Every user except anonymous are part of this group. This group can be seen as the 'logged in users' group.
Expand Down Expand Up @@ -55,8 +56,9 @@ class Site < ActiveRecord::Base
CACHE_PATH = Bricks.raw_config['cache_path'] || '/public'

include RubyLess
safe_method :host => String, :lang_list => [String], :default_lang => String
safe_method :root => Proc.new {|h, r, s| {:method => 'root_node', :class => VirtualClass['Project'], :nil => true}}
safe_method :host => String, :lang_list => [String], :default_lang => String, :master_host => String
safe_method :root => Proc.new {|h, r, s| {:method => 'root_node', :class => VirtualClass['Project'], :nil => true}}
safe_method :orphan => Proc.new {|h, r, s| {:method => 'orphan_node', :class => VirtualClass['Project'], :nil => true}}

validate :valid_site
validates_uniqueness_of :host
Expand All @@ -79,7 +81,7 @@ class Site < ActiveRecord::Base
}

@@alias_attributes_for_form = {
:bool => %w{},
:bool => %w{authentication auto_publish ssl_on_auth},
:text => %w{},
}

Expand Down Expand Up @@ -123,7 +125,7 @@ def create_for_host(host, su_password, opts={})
:lang => site.default_lang, :status => User::Status[:admin])
admin_user.site = site

Thread.current[:visitor] = admin_user
setup_visitor(admin_user, site)

unless admin_user.save
# rollback
Expand Down Expand Up @@ -180,6 +182,8 @@ def create_for_host(host, su_password, opts={})
raise Exception.new("Could not publish root node for site [#{host}] (site#{site[:id]})\n#{root.errors.map{|k,v| "[#{k}] #{v}"}.join("\n")}") unless (root.v_status == Zena::Status::Pub || root.publish)

site.root_id = root[:id]
site.orphan_id = root[:id]

# Make sure safe definitions on Time/Array/String are available on prop_eval validation.
Zena::Use::ZafuSafeDefinitions
# Should not be needed since we load PropEval in Node, but it does not work
Expand Down Expand Up @@ -247,7 +251,7 @@ def attributes_for_form(is_alias = false)
Site.attributes_for_form[:text] << 'usr_prototype_attributes'
Site.attributes_for_form[:bool] << 'expire_in_dev'
attr_accessible :usr_prototype_attributes, :expire_in_dev

# Return path for static/cached content served by proxy: RAILS_ROOT/sites/_host_/public
# If you need to serve from another directory, we do not store the path into the sites table
# for security reasons. The easiest way around this limitation is to symlink the 'public' directory.
Expand All @@ -264,12 +268,12 @@ def cache_path
# Return path for documents data: RAILS_ROOT/sites/_host_/data
# You can symlink the 'data' directory if you need to keep the data in some other place.
def data_path
"/#{host}/data"
"/#{master_host}/data"
end

# Return the path for zafu rendered templates: RAILS_ROOT/sites/_host_/zafu
def zafu_path
"/#{host}/zafu"
"/#{master_host}/zafu"
end

# Return the anonymous user, the one used by anonymous visitors to visit the public part
Expand All @@ -288,6 +292,11 @@ def any_admin
def root_node
@root ||= secure(Node) { Node.find(root_id) } || Node.new(:title => host)
end

# Return the orphan node.
def orphan_node
@orphan ||= secure(Node) { Node.find(orphan_id) } || Node.new(:title => host)
end

# Return the public group: the one in which every visitor belongs.
def public_group
Expand Down Expand Up @@ -315,16 +324,6 @@ def admin_user_ids
@admin_user_ids ||= secure!(User) { User.find(:all, :conditions => "status >= #{User::Status[:admin]}") }.map {|r| r[:id]}
end

# Return true if the site is configured to force authentication
def authentication?
self[:authentication]
end

# Return true if the site is configured to automatically publish redactions
def auto_publish?
self[:auto_publish]
end

# Set redit time from a string of the form "1d 4h 5s" or "4 days"
def redit_time=(val)
if val.kind_of?(String)
Expand Down Expand Up @@ -360,8 +359,28 @@ def is_alias?
!self[:master_id].blank?
end

# This is the host of the master site.
def master_host
self[:host]
end

# Host with aliasing (returns alias host if alias is loaded)
def host
@host ||= @alias && @alias[:host] || self[:host]
@alias && @alias.host || master_host
end

def ssl_on_auth
@alias && @alias.prop['ssl_on_auth'] || self.prop['ssl_on_auth']
end

# Return true if the site is configured to automatically publish redactions
def auto_publish?
@alias && @alias[:auto_publish] || self[:auto_publish]
end

# Return true if the site is configured to force authentication
def authentication?
@alias && @alias[:authentication] || self[:authentication]
end

def root_id
Expand All @@ -385,6 +404,9 @@ def create_alias(hostname)
ali = Site.new(self.attributes)
ali.host = hostname
ali.master_id = self.id
ali.orphan_id = self.orphan_id
ali.root_id = self.root_id
ali.prop = self.prop
ali.save
ali
end
Expand Down Expand Up @@ -436,7 +458,7 @@ def iformats_updated!

def clear_cache(clear_zafu = true)
paths = ["#{SITES_ROOT}#{self.cache_path}"]
aliases = Site.all(:master_id => self.id)
aliases = Site.all(:conditions => {:master_id => self.id})
aliases.each do |site|
paths << "#{SITES_ROOT}#{site.cache_path}"
end
Expand Down
4 changes: 2 additions & 2 deletions app/views/sites/_form.erb
Expand Up @@ -23,9 +23,9 @@
<tr><td class='label'><%= _('public group') %></td><td><%= @site.public_group.name %></td></tr>
<tr><td class='label'><%= _('site group') %></td><td><%= @site.site_group.name %></td></tr>
<tr><td class='label'><%= _('API group') %></td><td><%= select('site', 'api_group_id', visitor.all_groups.map{|g| [g.name, g.id]}, {:include_blank => true, :selected => @site.api_group_id} ) %></td></tr>

<tr><td class='label'><%= _('options') %></td><td>
<% end %>

<tr><td class='label'><%= _('options') %></td><td>
<% Site.attributes_for_form(@site.is_alias?)[:bool].each do |sym| -%>
<input type='hidden' name='site[<%= sym %>]' value=''/>
<input type='checkbox' name='site[<%= sym %>]' value='1'<%= @site.send(sym) ? " checked='checked'" : '' %>/> <%= _(sym.to_s) %><br/>
Expand Down
2 changes: 1 addition & 1 deletion bricks/fs_skin/zena/tasks.rb
Expand Up @@ -13,7 +13,7 @@
end

sites.each do |site|
Thread.current[:visitor] = site.any_admin
setup_visitor(site.any_admin, site)
if ENV['WORKER'] == 'false' || RAILS_ENV == 'test'
# We avoid SiteWorker.
site.rebuild_fs_skin_index
Expand Down
@@ -1,9 +1,13 @@
class AddMasterIdToSite < ActiveRecord::Migration
def self.up
add_column :sites, :master_id, :integer
# The id of the node without parent
add_column :sites, :orphan_id, :integer
execute "UPDATE sites SET orphan_id = root_id"
end

def self.down
remove_column :sites, :master_id
remove_column :sites, :orphan_id
end
end
4 changes: 2 additions & 2 deletions lib/tasks/zena.rake
Expand Up @@ -401,7 +401,7 @@ namespace :zena do
end

sites.each do |site|
Thread.current[:visitor] = site.any_admin
setup_visitor(site.any_admin, site)

if ENV['WORKER'] == 'false' || RAILS_ENV == 'test'
# We avoid SiteWorker by passing nodes.
Expand Down Expand Up @@ -440,7 +440,7 @@ namespace :zena do
end
sites.each do |site|
# We avoid SiteWorker by passing nodes.
Thread.current[:visitor] = site.any_admin
setup_visitor(site.any_admin, site)
nodes = Node.find(:all,
:conditions => ['site_id = ?', site.id]
)
Expand Down
9 changes: 5 additions & 4 deletions lib/zena/console.rb
Expand Up @@ -99,17 +99,18 @@ def change_prop(pseudo_sql, key)
def login(name, host = nil)
finder = {}
finder[:conditions] = cond = [[]]
site = nil
if host
finder[:joins] = 'INNER JOIN sites ON sites.id = users.site_id'
cond.first << 'sites.host = ?'
cond << host.to_s
site = Site.find_by_host(host)
cond.first << 'site_id = ?'
cond << site.id
end

cond.first << 'users.login = ?'
cond << name.to_s
cond[0] = cond.first.join(' AND ')
if visitor = User.find(:first, finder)
Thread.current[:visitor] = visitor
setup_visitor(visitor, site || visitor.site)
puts "Logged #{visitor.login} in #{visitor.site.host}"
else
raise ActiveRecord::RecordNotFound
Expand Down
13 changes: 13 additions & 0 deletions lib/zena/deploy.rb
Expand Up @@ -162,6 +162,19 @@ def ancestry(path)
run "chown -R www-data:www-data #{sites_root}/#{self[:host]}"
end

desc "create a new site alias [-s host='...' -s alias='...' -s pass='...']"
task :mkalias, :roles => :app do
run "#{in_current} rake zena:mksite HOST='#{self[:host]}' ALIAS='#{self[:alias]}' RAILS_ENV='production'"

# Same as mksite
self[:host] = self[:alias]
run "test -e #{sites_root}/#{self[:host]} || mkdir #{sites_root}/#{self[:host]}"
create_vhost
create_awstats
logrotate
run "chown -R www-data:www-data #{sites_root}/#{self[:host]}"
end

task :mksymlinks, :roles => :app do
run "#{in_current} rake zena:mksymlinks HOST='#{self[:host]}'"
run "chown -R www-data:www-data #{sites_root}/#{self[:host]}"
Expand Down
4 changes: 2 additions & 2 deletions lib/zena/site_worker.rb
Expand Up @@ -19,7 +19,7 @@ def self.perform(site, action, page = 1)
def perform(site = nil)
if site.nil?
site ||= Site.find(site_id)
Thread.current[:visitor] = site.any_admin
setup_visitor(site.any_admin, site)
end

if page.nil?
Expand Down Expand Up @@ -58,7 +58,7 @@ def page_count

# Return a textual description of the operation.
def info
if site_id == current_site.id
if site_id == current_site.site_id
"#{action}, #{_('page')} #{page}/#{page_count}"
else
# Do not show jobs from other sites
Expand Down
2 changes: 1 addition & 1 deletion lib/zena/use/ancestry.rb
Expand Up @@ -10,7 +10,7 @@ def title_join
TITLE_ML_JOIN = %Q{INNER JOIN idx_nodes_ml_strings AS id1 ON id1.node_id = nodes.id AND id1.key = 'title'}

# (slow). Find a node by it's path. This is used during node importation when stored as zml files or to resolve custom_base url until we have an "alias" table.
def find_by_path(path, parent_id = current_site.root_id, multilingual = false)
def find_by_path(path, parent_id = current_site.orphan_id, multilingual = false)
res = nil
path = path.split('/') unless path.kind_of?(Array)
last = path.size - 1
Expand Down
2 changes: 1 addition & 1 deletion lib/zena/use/test_helper.rb
Expand Up @@ -9,7 +9,7 @@ module TestHelper
# Set visitor for unit testing
def login(fixture)
user = users(fixture)
Thread.current[:visitor] = user
setup_visitor(user, user.site)
user.ip = '10.0.0.44'
$_test_site = user.site.name
::I18n.locale = user.lang
Expand Down
3 changes: 2 additions & 1 deletion lib/zena/use/zafu_safe_definitions.rb
Expand Up @@ -168,9 +168,10 @@ def self.join_proc
safe_method_for String, :url_name => {:class => String, :pre_processor => true, :method => :url_name}
safe_method_for String, :to_i => {:class => Number, :pre_processor => true}
safe_method_for String, :to_s => {:class => String, :pre_processor => true}
safe_method_for String, [:limit, Number] => {:class => String, :pre_processor => true}
safe_method_for String, :size => {:class => Number, :pre_processor => true}
safe_method_for String, [:limit, Number] => {:class => String, :pre_processor => true}
safe_method_for String, [:limit, Number, String] => {:class => Number, :pre_processor => true}

safe_method_for String, :to_f => {:class => Number, :pre_processor => true}
safe_method_for String, :to_json => {:class => String, :pre_processor => true}
safe_method_for String, [:split, String] => {:class => [String], :pre_processor => true}
Expand Down
1 change: 1 addition & 0 deletions lib/zena/use/zafu_templates.rb
Expand Up @@ -261,6 +261,7 @@ def template_url(opts={})
return default_template_url(opts) unless zafu_url

rel_path = current_site.zafu_path + "/#{zafu_url}/#{lang_path}/_main.erb"

path = SITES_ROOT + rel_path
if !File.exists?(path) || params[:rebuild]
if @node && klass = VirtualClass.find_by_kpath(template.tkpath)
Expand Down
2 changes: 1 addition & 1 deletion test/functional/sites_controller_test.rb
Expand Up @@ -23,7 +23,7 @@ def setup
end


test 'should clear cache with GET' do
test 'should clear cache with get' do
with_caching do
login(:anon)
@node = secure!(Node) { nodes(:status) }
Expand Down
3 changes: 2 additions & 1 deletion test/integration/multiple_hosts_test.rb
Expand Up @@ -36,7 +36,8 @@ def test_visitor_anon

def test_cache
# We need the visitor to load VirtualClass cache.
Thread.current[:visitor] = users(:anon)
anon_user = users(:anon)
setup_visitor(anon_user, anon_user.site)
node_zip = nodes(:zena, :people).zip
Thread.current[:visitor] = nil

Expand Down
2 changes: 1 addition & 1 deletion test/integration/zafu_compiler/display.yml
Expand Up @@ -331,7 +331,7 @@ string_size:
res: '<div>11</div>'

string_limit:
src: "<div do='text'><r:void do='limit(10,\"\")'/><r:if test='size > 10' do='link' href='@node' do='t' t='read more'/></div>"
src: "<div do='text'><r:void do='limit(10,\"\")'/><r:if test='size > 10' do='link' href='@node' t='read more'/></div>"
res: "<div>status tex<a href='/oo/projects-list/Clean-Water-project/page22.html'>read more</a></div>"

show_with_label_shortcut:
Expand Down
3 changes: 3 additions & 0 deletions test/sites/zena/sites.yml
@@ -1,5 +1,6 @@
zena:
host: test.host
orphan: zena
root: zena
anon: anon
public_group: public
Expand All @@ -19,6 +20,7 @@ zena:
alias:
master: zena
host: alias.host
orphan: zena
root: wiki
anon: anon
public_group: public
Expand All @@ -31,6 +33,7 @@ alias:
formats_updated_at:
api_group:
prop:
ssl_on_auth: true
recaptcha_pub: 'pub'
recaptcha_priv: 'priv'
usr_prototype_attributes: "{'klass' => 'Contact', 'address' => 'Iping', 'name' => login}"

0 comments on commit 92f468d

Please sign in to comment.