radiant-vhost-extension 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitmodules +3 -0
- data/README +64 -0
- data/Rakefile +125 -0
- data/VERSION +1 -0
- data/app/controllers/admin/sites_controller.rb +16 -0
- data/app/models/site.rb +26 -0
- data/app/models/site_association_observer.rb +8 -0
- data/app/views/admin/sites/_form.html.haml +19 -0
- data/app/views/admin/sites/edit.html.haml +5 -0
- data/app/views/admin/sites/index.html.haml +24 -0
- data/app/views/admin/sites/new.html.haml +5 -0
- data/app/views/admin/sites/remove.html.haml +19 -0
- data/app/views/admin/users/_edit_sites.html.haml +4 -0
- data/app/views/admin/users/_site_admin_roles.html.haml +5 -0
- data/app/views/admin/users/_sites_td.html.haml +2 -0
- data/app/views/admin/users/_sites_th.html.haml +2 -0
- data/config/routes.rb +5 -0
- data/db/migrate/001_create_sites.rb +11 -0
- data/db/migrate/002_add_sites_users.rb +18 -0
- data/db/migrate/003_replace_snippet_name_unique_index.rb +10 -0
- data/db/migrate/004_add_site_admin_to_users.rb +9 -0
- data/db/templates/empty.yml +7 -0
- data/db/templates/simple-blog.yml +213 -0
- data/lib/bootstrap_with_site_id.rb +50 -0
- data/lib/radiant-vhost-extension.rb +0 -0
- data/lib/site_scope.rb +65 -0
- data/lib/tasks/add_site_columns.rb +44 -0
- data/lib/tasks/vhost_extension_tasks.rake +115 -0
- data/lib/vhost/admin_users_controller_extensions.rb +22 -0
- data/lib/vhost/admin_users_helper_extensions.rb +17 -0
- data/lib/vhost/application_controller_extensions.rb +36 -0
- data/lib/vhost/application_helper_extensions.rb +15 -0
- data/lib/vhost/controller_access_extensions.rb +16 -0
- data/lib/vhost/pages_controller_extensions.rb +11 -0
- data/lib/vhost/radiant_cache_extensions.rb +53 -0
- data/lib/vhost/site_scoped_model_extensions.rb +46 -0
- data/lib/vhost_default_config.yml +22 -0
- data/spec/controllers/admin/pages_controller_spec.rb +173 -0
- data/spec/controllers/admin/sites_controller_spec.rb +33 -0
- data/spec/controllers/site_controller_spec.rb +33 -0
- data/spec/datasets/site_home_pages_dataset.rb +76 -0
- data/spec/datasets/site_pages_dataset.rb +31 -0
- data/spec/datasets/site_users_dataset.rb +50 -0
- data/spec/datasets/sites_dataset.rb +10 -0
- data/spec/datasets/sites_site_users_and_site_pages_dataset.rb +8 -0
- data/spec/datasets/sites_site_users_dataset.rb +13 -0
- data/spec/fixtures/page_parts.yml +11 -0
- data/spec/fixtures/pages.yml +19 -0
- data/spec/fixtures/sites.yml +7 -0
- data/spec/fixtures/sites_users.yml +6 -0
- data/spec/fixtures/users.yml +35 -0
- data/spec/models/page_spec.rb +22 -0
- data/spec/models/site_spec.rb +19 -0
- data/spec/models/user_spec.rb +16 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +42 -0
- data/test/fixtures/page_parts.yml +11 -0
- data/test/fixtures/pages.yml +19 -0
- data/test/fixtures/sites.yml +7 -0
- data/test/fixtures/sites_users.yml +6 -0
- data/test/fixtures/users.yml +35 -0
- data/test/functional/admin/pages_controller_test.rb +142 -0
- data/test/functional/admin/site_controller_test.rb +53 -0
- data/test/functional/vhost_extension_test.rb +37 -0
- data/test/helpers/page_part_test_helper.rb +49 -0
- data/test/test_helper.rb +17 -0
- data/test/unit/site_test.rb +26 -0
- data/vhost_extension.rb +154 -0
- metadata +167 -0
@@ -0,0 +1,50 @@
|
|
1
|
+
module BootstrapWithSiteId
|
2
|
+
def load_database_template_with_site_id(filename)
|
3
|
+
template = nil
|
4
|
+
if filename
|
5
|
+
name = find_template_in_path(filename)
|
6
|
+
unless name
|
7
|
+
announce "Invalid template name: #{filename}"
|
8
|
+
filename = nil
|
9
|
+
else
|
10
|
+
template = load_template_file(name)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
unless filename
|
14
|
+
templates = find_and_load_templates("#{RAILS_ROOT}/vendor/extensions/vhost/db/templates/*.yml")
|
15
|
+
templates.concat find_and_load_templates("#{RADIANT_ROOT}/config/extensions/vhost/db/templates/*.yml")
|
16
|
+
templates.concat find_and_load_templates("#{RAILS_ROOT}/db/templates/*.yml") if RADIANT_ROOT != RAILS_ROOT
|
17
|
+
choose do |menu|
|
18
|
+
menu.header = "\nSelect a database template"
|
19
|
+
menu.prompt = "[1-#{templates.size}]: "
|
20
|
+
menu.select_by = :index
|
21
|
+
templates.each { |t| menu.choice(t['name']) { template = t } }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
# If there are sites defined create them first as there are many to many
|
25
|
+
# relationships from users to sites that need to be created and will fail
|
26
|
+
# if the site isn't created first.
|
27
|
+
if !template['records']['Sites'].nil?
|
28
|
+
site_template = {}
|
29
|
+
site_template['records'] = {}
|
30
|
+
site_template['records']['Sites'] = template['records']['Sites']
|
31
|
+
create_records(site_template)
|
32
|
+
site_template['records'].delete('Sites')
|
33
|
+
end
|
34
|
+
create_records(template)
|
35
|
+
end
|
36
|
+
|
37
|
+
def find_template_in_path_with_site_id(filename)
|
38
|
+
[
|
39
|
+
filename,
|
40
|
+
"#{RAILS_ROOT}/vendor/extensions/vhost/db/templates/#{filename}",
|
41
|
+
"#{RADIANT_ROOT}/config/extensions/vhost/db/templates/#{filename}",
|
42
|
+
"#{RADIANT_ROOT}/#{filename}",
|
43
|
+
"#{RADIANT_ROOT}/db/templates/#{filename}",
|
44
|
+
"#{RAILS_ROOT}/#{filename}",
|
45
|
+
"#{RAILS_ROOT}/db/templates/#{filename}",
|
46
|
+
"#{Dir.pwd}/#{filename}",
|
47
|
+
"#{Dir.pwd}/db/templates/#{filename}",
|
48
|
+
].find { |name| File.file?(name) }
|
49
|
+
end
|
50
|
+
end
|
File without changes
|
data/lib/site_scope.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
module SiteScope
|
2
|
+
|
3
|
+
def self.included(base)
|
4
|
+
base.send :helper_method, :current_site
|
5
|
+
end
|
6
|
+
|
7
|
+
def current_site
|
8
|
+
return @current_site unless @current_site.nil?
|
9
|
+
# For testing we won't have a request.host so we're going to use a class
|
10
|
+
# variable (VhostExtension.HOST) in those cases.
|
11
|
+
host ||= VhostExtension.HOST || request.host
|
12
|
+
# Remove the 'www.' from the site so we don't have to always include a www.
|
13
|
+
# in addition to the regular domain name.
|
14
|
+
host.gsub!(/^www\./, '')
|
15
|
+
@current_site ||= Site.find_by_hostname(host) || Site.find_by_hostname('*') || Site.find(:first, :conditions => ["hostname LIKE ?", "%#{host}%"])
|
16
|
+
raise "No site found to match #{host}." unless @current_site
|
17
|
+
Thread.current[:current_site_id] = @current_site.id
|
18
|
+
@current_site
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
# This is the key method that forks in the additional conditions that will
|
23
|
+
# be used to fetch the site-scoped models. It also defines how the site-scoped
|
24
|
+
# models will be saved with a site_id.
|
25
|
+
def site_scope
|
26
|
+
@site_scope ||= {
|
27
|
+
:find => { :conditions => ["site_id = ?", current_site.id]},
|
28
|
+
:create => { :site_id => current_site.id }
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def users_site_scope
|
33
|
+
@users_site_scope = {}
|
34
|
+
# Only do the user site scoping if it's a site_admin. We don't want the admin to be restricted.
|
35
|
+
if current_user && current_user.site_admin?
|
36
|
+
@users_site_scope = {
|
37
|
+
:find => { :joins => "JOIN sites_users AS scoped_sites_users ON scoped_sites_users.user_id = id", :conditions => ["scoped_sites_users.site_id = ?", current_site.id]},
|
38
|
+
# Make sure admin is always false - wouldn't want someone trying to set it to true through some html magic
|
39
|
+
:create => { :site_ids => [current_site.id], :site_admin => false }
|
40
|
+
}
|
41
|
+
end
|
42
|
+
return @users_site_scope
|
43
|
+
end
|
44
|
+
|
45
|
+
# Should this really be here? Shouldn't we be calling this regardless of if we go
|
46
|
+
# through the ApplicationControllers :before_filter?
|
47
|
+
def set_site_scope_in_models
|
48
|
+
set_model_current_site = lambda {|model|
|
49
|
+
model.constantize.send :cattr_accessor, :current_site
|
50
|
+
model.constantize.current_site = self.current_site
|
51
|
+
}
|
52
|
+
VhostExtension.MODELS.each do |model|
|
53
|
+
model_config = VhostExtension.read_config[:model_uniqueness_validations][model]
|
54
|
+
if model_config['sti_classes']
|
55
|
+
model_config['sti_classes'].each do |klass|
|
56
|
+
set_model_current_site.call(klass)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
set_model_current_site.call(model)
|
60
|
+
end
|
61
|
+
Site.send :cattr_accessor, :current_site
|
62
|
+
Site.current_site = self.current_site
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
class AddSiteColumns < ActiveRecord::Migration
|
2
|
+
|
3
|
+
config = VhostExtension.read_config
|
4
|
+
|
5
|
+
MODELS = config[:models]
|
6
|
+
|
7
|
+
# Declare the models so we can use them.
|
8
|
+
MODELS.each do |model|
|
9
|
+
eval "class #{model} < ActiveRecord::Base; end"
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.up
|
13
|
+
MODELS.each do |model|
|
14
|
+
begin
|
15
|
+
add_column model.tableize, :site_id, :integer
|
16
|
+
model.constantize.update_all "site_id = 1"
|
17
|
+
# Special case for Snippets to add a proper index
|
18
|
+
if model == 'Snippet'
|
19
|
+
add_index :snippets, [:name, :site_id], :unique => true
|
20
|
+
end
|
21
|
+
rescue
|
22
|
+
# Ignore errors here, they're going to happen when the user
|
23
|
+
# does a 'remigrate'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
add_index :snippets, [:name, :site_id] rescue nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.down
|
30
|
+
MODELS.each do |model|
|
31
|
+
begin
|
32
|
+
# Special case for Snippets to remove index
|
33
|
+
if model == 'Snippet'
|
34
|
+
remove_index :snippets, [:name, :site_id]
|
35
|
+
end
|
36
|
+
remove_column model.tableize, :site_id
|
37
|
+
rescue
|
38
|
+
# Ignore errors here, they're going to happen when the user
|
39
|
+
# does a 'remigrate'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
remove_index :snippets, :column => [:name, :site_id] rescue nil
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
namespace :radiant do
|
2
|
+
namespace :extensions do
|
3
|
+
namespace :vhost do
|
4
|
+
|
5
|
+
desc "Prepares your database for Vhost"
|
6
|
+
task :install => [:environment, :migrate, :apply_site_scoping]
|
7
|
+
|
8
|
+
desc "Runs the migration of the Vhost extension"
|
9
|
+
task :migrate => :environment do
|
10
|
+
require 'radiant/extension_migrator'
|
11
|
+
if ENV["VERSION"]
|
12
|
+
VhostExtension.migrator.migrate(ENV["VERSION"].to_i)
|
13
|
+
else
|
14
|
+
VhostExtension.migrator.migrate
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
# @todo the following method should accept a direction. We should also
|
20
|
+
# rename it something like 'apply_site_scoping' and 'reset_site_scoping'
|
21
|
+
# or something...
|
22
|
+
desc "Initializes site scoping. "
|
23
|
+
task :apply_site_scoping => :environment do
|
24
|
+
require "#{File.dirname(__FILE__)}/add_site_columns"
|
25
|
+
AddSiteColumns.up
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "Reinitializes site scoping in the event a new model needs to be site scoped."
|
29
|
+
task :destroy_site_scoping => :environment do
|
30
|
+
require 'highline/import'
|
31
|
+
if agree("This task will destroy any model to site relationships in the database. Are you sure \nyou want to continue? [yn] ")
|
32
|
+
AddSiteColumns.up
|
33
|
+
AddSiteColumns.down
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
Rake::TaskManager.class_eval do
|
42
|
+
def remove_task(task_name)
|
43
|
+
@tasks.delete(task_name.to_s)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def remove_task(task_name)
|
48
|
+
Rake.application.remove_task(task_name)
|
49
|
+
end
|
50
|
+
|
51
|
+
# We need the bootstrap task to use site_ids
|
52
|
+
remove_task "db:bootstrap"
|
53
|
+
remove_task "db:remigrate"
|
54
|
+
namespace :db do
|
55
|
+
desc "Bootstrap your database for Radiant."
|
56
|
+
task :bootstrap => :remigrate do
|
57
|
+
require 'radiant/setup'
|
58
|
+
require File.join(File.dirname(__FILE__), '../bootstrap_with_site_id')
|
59
|
+
Radiant::Setup.send :include, BootstrapWithSiteId
|
60
|
+
Radiant::Setup.send :alias_method_chain, :load_database_template, :site_id
|
61
|
+
Radiant::Setup.send :alias_method_chain, :find_template_in_path, :site_id
|
62
|
+
|
63
|
+
Radiant::Setup.bootstrap(
|
64
|
+
:admin_name => ENV['ADMIN_NAME'],
|
65
|
+
:admin_username => ENV['ADMIN_USERNAME'],
|
66
|
+
:admin_password => ENV['ADMIN_PASSWORD'],
|
67
|
+
:database_template => ENV['DATABASE_TEMPLATE']
|
68
|
+
)
|
69
|
+
|
70
|
+
Rake::Task["radiant:extensions:vhost:apply_site_scoping"].invoke
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
desc "Migrate schema to version 0 and back up again. WARNING: Destroys all data in tables!!"
|
75
|
+
task :remigrate => :environment do
|
76
|
+
require 'highline/import'
|
77
|
+
require 'radiant/extension_migrator'
|
78
|
+
if ENV['OVERWRITE'].to_s.downcase == 'true' or
|
79
|
+
agree("This task will destroy any data in the database. Are you sure you want to \ncontinue? [yn] ")
|
80
|
+
|
81
|
+
# Migrate extensions downward
|
82
|
+
Radiant::ExtensionLoader.instance.extensions.each do |ext|
|
83
|
+
# The first time you bootstrap you'll always encounter exceptions
|
84
|
+
# so be sure to ignore them here.
|
85
|
+
begin
|
86
|
+
ext.migrator.migrate(0)
|
87
|
+
rescue
|
88
|
+
puts "An error occurred while migrating the #{ext} extension downward: #{$!}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Migrate downward
|
93
|
+
ActiveRecord::Migrator.migrate("#{RADIANT_ROOT}/db/migrate/", 0)
|
94
|
+
|
95
|
+
# Migrate upward
|
96
|
+
Rake::Task["db:migrate"].invoke
|
97
|
+
|
98
|
+
# Migrate extensions upward
|
99
|
+
Radiant::ExtensionLoader.instance.extensions.each do |ext|
|
100
|
+
# The first time you bootstrap you'll always encounter exceptions
|
101
|
+
# so be sure to ignore them here.
|
102
|
+
ext.migrator.migrate
|
103
|
+
end
|
104
|
+
|
105
|
+
# Remigrate the extensions to catch any new site scoped extensions added
|
106
|
+
Rake::Task["radiant:extensions:vhost:apply_site_scoping"].invoke
|
107
|
+
|
108
|
+
# Dump the schema
|
109
|
+
Rake::Task["db:schema:dump"].invoke
|
110
|
+
else
|
111
|
+
say "Task cancelled."
|
112
|
+
exit
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Vhost::AdminUsersControllerExtensions
|
2
|
+
def self.included(receiver)
|
3
|
+
receiver.send :only_allow_access_to, :index, :show, :new, :create, :edit, :update, :remove, :destroy,
|
4
|
+
:when => [:admin, :site_admin],
|
5
|
+
:denied_url => { :controller => 'pages', :action => 'index' },
|
6
|
+
:denied_message => 'You must have administrative privileges to perform this action.'
|
7
|
+
|
8
|
+
receiver.class_eval {
|
9
|
+
def load_model
|
10
|
+
self.model = if params[:id]
|
11
|
+
model_class.find(params[:id], :readonly => false)
|
12
|
+
else
|
13
|
+
model_class.new
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def load_models
|
18
|
+
self.models = current_site.users.paginate(pagination_parameters)
|
19
|
+
end
|
20
|
+
}
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Vhost::AdminUsersHelperExtensions
|
2
|
+
def self.included(receiver)
|
3
|
+
receiver.send :alias_method_chain, :roles, :site_admin
|
4
|
+
receiver.send :define_method, :sites do |user|
|
5
|
+
sites = user.sites.collect{|site| site.hostname}
|
6
|
+
sites.join("<br/>")
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def roles_with_site_admin(user)
|
11
|
+
roles = []
|
12
|
+
roles << 'Admin' if user.admin?
|
13
|
+
roles << 'Site Admin' if user.site_admin?
|
14
|
+
roles << 'Designer' if user.designer?
|
15
|
+
roles.join(', ')
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Vhost::ApplicationControllerExtensions
|
2
|
+
def self.included(base)
|
3
|
+
base.class_eval {
|
4
|
+
prepend_before_filter :redirect_to_primary_site
|
5
|
+
|
6
|
+
helper_method :primary_site_url
|
7
|
+
|
8
|
+
def redirect_to_primary_site
|
9
|
+
if VhostExtension.REDIRECT_TO_PRIMARY_SITE
|
10
|
+
site = current_site
|
11
|
+
return if site.nil? || site.hostname.include?("*")
|
12
|
+
primary_host = site.hostname.split(',')[0].strip
|
13
|
+
redirect_to(primary_site_url + request.request_uri) if request.host != primary_host
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def primary_site_url
|
18
|
+
site = current_site
|
19
|
+
return nil if site.nil? || site.hostname.include?("*")
|
20
|
+
|
21
|
+
# Rebuild the current URL. Check if it matches the URL of the
|
22
|
+
# primary site and redirect if it does not.
|
23
|
+
prefix = request.ssl? ? "https://" : "http://"
|
24
|
+
host = request.host
|
25
|
+
port = request.port_string
|
26
|
+
|
27
|
+
# Primary site is the first site
|
28
|
+
primary_host = site.hostname.split(',')[0].strip
|
29
|
+
|
30
|
+
# Return the concatenation
|
31
|
+
prefix+primary_host+port
|
32
|
+
end
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Vhost::ApplicationHelperExtensions
|
2
|
+
def self.included(receiver)
|
3
|
+
# This swaps out the 'subtitle' method for the 'site_hostname'
|
4
|
+
# method to show the hostname in the subtitle in admin...
|
5
|
+
receiver.send :alias_method_chain, :subtitle, :site_hostname
|
6
|
+
|
7
|
+
receiver.send :define_method, :site_admin? do
|
8
|
+
current_user and current_user.site_admin?
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def subtitle_with_site_hostname
|
13
|
+
current_site.hostname
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Vhost::ControllerAccessExtensions
|
2
|
+
def self.included(receiver)
|
3
|
+
receiver.send :before_filter, :ensure_user_has_site_access
|
4
|
+
end
|
5
|
+
|
6
|
+
def ensure_user_has_site_access
|
7
|
+
unless current_site.allow_access_for(current_user)
|
8
|
+
cookies[:session_token] = { :expires => 1.day.ago }
|
9
|
+
self.current_user.forget_me if self.current_user
|
10
|
+
self.current_user = nil
|
11
|
+
flash[:error] = 'Access denied.'
|
12
|
+
redirect_to login_url
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Vhost::PagesControllerExtensions
|
2
|
+
def self.included(receiver)
|
3
|
+
receiver.send :alias_method_chain, :clear_model_cache, :site_specificity
|
4
|
+
end
|
5
|
+
|
6
|
+
def clear_model_cache_with_site_specificity
|
7
|
+
url_to_expire = "#{request.host}#{@page.url}"
|
8
|
+
Radiant::Cache.clear(url_to_expire) if defined?(Radiant::Cache)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Alter Radiant's site controller to add the domain to the cache files.
|
2
|
+
# Also store requests for later use.
|
3
|
+
# See radiant/app/controllers/site_controller.rb
|
4
|
+
module Vhost::RadiantCacheExtensions
|
5
|
+
module RadiantCache
|
6
|
+
def self.included(base)
|
7
|
+
base.class_eval {
|
8
|
+
# This sets up the cache - the entitystore and metastore 'cache/entity' and 'cache/meta' sets up the folder
|
9
|
+
# structure for storing the cache items.
|
10
|
+
def self.new(app, options={})
|
11
|
+
self.use_x_sendfile = options.delete(:use_x_sendfile) if options[:use_x_sendfile]
|
12
|
+
self.use_x_accel_redirect = options.delete(:use_x_accel_redirect) if options[:use_x_accel_redirect]
|
13
|
+
Rack::Cache.new(app, {
|
14
|
+
:entitystore => "radiant:tmp/cache/entity",
|
15
|
+
:metastore => "radiant:tmp/cache/meta",
|
16
|
+
:verbose => false}.merge(options))
|
17
|
+
end
|
18
|
+
def self.clear(host_and_url = nil)
|
19
|
+
meta_stores.each {|ms| ms.clear(host_and_url) }
|
20
|
+
entity_stores.each {|es| es.clear }
|
21
|
+
end
|
22
|
+
}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module MetaStore
|
27
|
+
def self.included(base)
|
28
|
+
base.class_eval {
|
29
|
+
def initialize(root="#{Rails.root}/tmp/cache/meta")
|
30
|
+
super
|
31
|
+
Radiant::Cache.meta_stores << self
|
32
|
+
end
|
33
|
+
|
34
|
+
def clear(host_and_url = nil)
|
35
|
+
if host_and_url.nil?
|
36
|
+
Dir[File.join(self.root, "*")].each {|file| FileUtils.rm_rf(file) }
|
37
|
+
else
|
38
|
+
FileUtils.rm_rf(key_path("#{host_and_url}"))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# cache_key should, by default, include the host as well the query string
|
43
|
+
# def cache_key(request)
|
44
|
+
# "#{request.host}#{request.path_info}"
|
45
|
+
# end
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# FIXME - Add support for EntityStore - MetaStore is the most important caching
|
51
|
+
# mechanism to be able to clear by site but EntityStore would be great too.
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
module Vhost::SiteScopedModelExtensions
|
3
|
+
module InstanceMethods
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval {
|
6
|
+
Rails.logger.debug("Applying SiteScope to '"+self.name+"'")
|
7
|
+
|
8
|
+
self.clear_callbacks_by_calling_method_name(:validate, :validates_uniqueness_of)
|
9
|
+
validates_presence_of :site_id
|
10
|
+
belongs_to :site
|
11
|
+
|
12
|
+
# Parse the model_uniqueness_validations config and set any necessary validations
|
13
|
+
# If the current class name matches an entry in the config then process it
|
14
|
+
config = VhostExtension.MODEL_UNIQUENESS_VALIDATIONS[self.name]
|
15
|
+
unless config.nil?
|
16
|
+
config.each_pair do |attr, params|
|
17
|
+
unless attr == 'sti_classes'
|
18
|
+
validates_uniqueness_of attr.to_sym, params.symbolize_keys
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
module ClassMethods
|
26
|
+
def clear_callbacks_by_calling_method_name(kind, calling_method_name)
|
27
|
+
calling_method_name = calling_method_name.to_s
|
28
|
+
# Callbacks are stored by kind as instance variables named @<kind>_callbacks.
|
29
|
+
# Fetch them so we can kick out the matching items.
|
30
|
+
callback_chain = eval("@#{kind.to_s}_callbacks")
|
31
|
+
unless callback_chain.nil?
|
32
|
+
callback_chain.reject! do |callback|
|
33
|
+
method = callback.method
|
34
|
+
if method.is_a?(Proc)
|
35
|
+
# Returns the symbol for the method the proc was declared in
|
36
|
+
current_calling_method_name = eval("caller[0] =~ /`([^']*)'/ and $1", method.binding).to_s rescue nil
|
37
|
+
current_calling_method_name == calling_method_name
|
38
|
+
else
|
39
|
+
false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
redirect_to_primary_site: false
|
2
|
+
models:
|
3
|
+
Layout:
|
4
|
+
name:
|
5
|
+
message:
|
6
|
+
'name already in use'
|
7
|
+
scope:
|
8
|
+
site_id
|
9
|
+
Page:
|
10
|
+
slug:
|
11
|
+
scope:
|
12
|
+
- parent_id
|
13
|
+
- site_id
|
14
|
+
message:
|
15
|
+
'slug already in use for child of parent'
|
16
|
+
Snippet:
|
17
|
+
name:
|
18
|
+
message:
|
19
|
+
'name already in use'
|
20
|
+
scope:
|
21
|
+
site_id
|
22
|
+
|