trusty-multi-site-extension 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/README.markdown +116 -0
  2. data/Rakefile +120 -0
  3. data/app/assets/images/admin/move_higher.png +0 -0
  4. data/app/assets/images/admin/move_lower.png +0 -0
  5. data/app/assets/images/admin/move_to_bottom.png +0 -0
  6. data/app/assets/images/admin/move_to_top.png +0 -0
  7. data/app/assets/images/admin/remove.png +0 -0
  8. data/app/assets/stylesheets/admin/multi_site.scss +121 -0
  9. data/app/assets/stylesheets/admin/site_chooser.scss +0 -0
  10. data/app/controllers/admin/sites_controller.rb +14 -0
  11. data/app/helpers/scoped_helper.rb +19 -0
  12. data/app/helpers/sites_helper.rb +10 -0
  13. data/app/models/site.rb +86 -0
  14. data/app/views/admin/layouts/_choose_site.html.haml +8 -0
  15. data/app/views/admin/layouts/_site_chooser.html.haml +2 -0
  16. data/app/views/admin/sites/_form.haml +43 -0
  17. data/app/views/admin/sites/edit.haml +7 -0
  18. data/app/views/admin/sites/index.haml +43 -0
  19. data/app/views/admin/sites/new.haml +7 -0
  20. data/app/views/admin/snippets/_choose_site.html.haml +6 -0
  21. data/app/views/admin/users/_choose_site.html.haml +8 -0
  22. data/app/views/site/not_configured.html.haml +7 -0
  23. data/config/initializers/radiant_config.rb +5 -0
  24. data/config/routes.rb +13 -0
  25. data/db/migrate/001_create_sites.rb +13 -0
  26. data/db/migrate/002_add_order_to_sites.rb +9 -0
  27. data/db/migrate/003_add_base_domain_to_sites.rb +9 -0
  28. data/db/migrate/004_add_admin_fields_to_sites.rb +17 -0
  29. data/db/migrate/005_add_sites.rb +13 -0
  30. data/db/migrate/006_remove_user_login_index.rb +8 -0
  31. data/db/migrate/20090810145840_site_abbreviation.rb +9 -0
  32. data/db/migrate/201411031415046078_recreate_non_unique_index_on_snippets_name.rb +11 -0
  33. data/lib/multi_site/admin_ui.rb +46 -0
  34. data/lib/multi_site/application_controller_extensions.rb +40 -0
  35. data/lib/multi_site/application_controller_filter_extensions.rb +21 -0
  36. data/lib/multi_site/engine.rb +5 -0
  37. data/lib/multi_site/page_extensions.rb +43 -0
  38. data/lib/multi_site/pages_controller_extensions.rb +48 -0
  39. data/lib/multi_site/resource_controller_extensions.rb +41 -0
  40. data/lib/multi_site/route_extensions.rb +21 -0
  41. data/lib/multi_site/route_set_extensions.rb +16 -0
  42. data/lib/multi_site/scoped_model.rb +145 -0
  43. data/lib/multi_site/scoped_validation.rb +32 -0
  44. data/lib/multi_site/site_chooser_helper.rb +14 -0
  45. data/lib/multi_site/site_controller_extensions.rb +12 -0
  46. data/lib/tasks/multi_site_extension_tasks.rake +28 -0
  47. data/lib/tasks/scoped_admin_extension_tasks.rake +28 -0
  48. data/lib/trusty-multi-site-extension.rb +1 -0
  49. data/multi_site_extension.rb +66 -0
  50. data/spec/controllers/extended_site_controller_spec.rb +61 -0
  51. data/spec/datasets/site_pages_dataset.rb +13 -0
  52. data/spec/datasets/sites_dataset.rb +10 -0
  53. data/spec/functional/multi_site_routing_spec.rb +37 -0
  54. data/spec/lib/multi_site/admin_ui_spec.rb +35 -0
  55. data/spec/lib/multi_site/scoped_model_spec.rb +182 -0
  56. data/spec/matchers/route_matcher.rb +38 -0
  57. data/spec/models/extended_page_spec.rb +48 -0
  58. data/spec/models/site_spec.rb +83 -0
  59. data/spec/spec.opts +6 -0
  60. data/spec/spec_helper.rb +36 -0
  61. data/trusty-multi-site-extension.gemspec +28 -0
  62. metadata +164 -0
data/README.markdown ADDED
@@ -0,0 +1,116 @@
1
+ # Multi Site #
2
+
3
+ Created by Sean Cribbs, November 2007. Inspired by the original virtual_domain behavior.
4
+
5
+ Multi Site allows you to host multiple websites on a single Radiant installation.
6
+
7
+ ## (Forked) ##
8
+
9
+ This fork adds a flexible but robust way to scope model classes to the current site. It's just a framework - nothing is scoped by default - but very easy to apply. See under scoped resources below.
10
+
11
+ ### Status ###
12
+
13
+ Fairly solid now and quite thoroughly tested. Should be a drop-in replacement for the standard multi_site. The interface is about to change, but the present one will still be supported.
14
+
15
+ ### Warning ###
16
+
17
+ I've just changed the site-finding logic so that Site.default is called in any circumstances. It makes life much easier in tests and console and should let me take out a lot of conditional code. It shouldn't affect normal use, but you know. Please let me know if anything goes wrong.
18
+
19
+ ### Requirements ###
20
+
21
+ There are no absolute requirements but you will need to install our submenu extension since that has taken the job of showing the site-chooser above any site-scoped index page.
22
+
23
+ ### Installation ###
24
+
25
+ $ git submodule add git://github.com/spanner/radiant-multi-site-extension.git vendor/extensions/multi_site
26
+ $ rake trusty:extensions:multi_site:migrate
27
+ $ rake trusty:extensions:multi_site:update
28
+
29
+ ### Compatibility ###
30
+
31
+ This differs from the original in that it will create a default site if none exists, but this should happen invisibly.
32
+
33
+ This version of multi_site does cause failures in radiant's main tests, usually when a site is required but the tests don't supply it. I will probably add a 'lax mode' at some point that doesn't mind if no site is defined.
34
+
35
+ ### Scoped resources ###
36
+
37
+ If you want to site-scope a model class (let's say you want your assets to be site-specific as well as your pages), all you have to do is add a line to the top of the class:
38
+
39
+ is_site_scoped
40
+
41
+ If you want the option to share some instances between sites (say you want some of your users to be confined to one site but a few admin users to see all of them):
42
+
43
+ is_site_scoped :shareable => true
44
+
45
+ The scoping takes effect at the ActiveRecord level - it wraps `with_scope` round every call to find (actually, to find_every) and a few other methods. If an object is out of site scope it is as though it didn't exist. This usually means your controller and view code hardly need to change at all: they just see fewer objects. You can fine-tune the scoping by specifying the `site_scope_condition` method in each scoped class.
46
+
47
+ If a site-scoped class includes any calls to `validates_uniqueness_of`, those too will be scoped to the site. There's a hack there, though: the validations are defined with the model and saved as [procs](http://casperfabricius.com/site/2008/12/06/removing-rails-validations-with-metaprogramming/) which causes all sorts of misery when you want to change them. Instead we've alias_chained the `validates_uniqueness_of` method to apply scope from the start. This has to happen very early in the initialisation procedure, when we don't really have much configuration information, so the uniqueness validation scope is applied to every model with a `site_id` column. I hope to find a better solution but it does work.
48
+
49
+ **Please Note:** a `site_scoped` class must be watched by the `UserActionObserver` in order to get the before_validation hook that sets the site id.
50
+
51
+ There is, or will soon be, more about this [in the wiki](http://wiki.github.com/spanner/radiant-multi-site-extension) and one day I'll get round to posting some [proper documentation](http://spanner.org/radiant/multi_site).
52
+
53
+
54
+
55
+ ### Examples ###
56
+
57
+ The [scoped_admin](http://github.com/spanner/radiant-scoped-admin-extension) extension uses this method to confine layouts, snippets and (some)
58
+ users to sites. It only takes four lines of code and two partials.
59
+
60
+ We've also shrunk the [paperclipped_multi_site](http://github.com/spanner/radiant-paperclipped_multisite-extension) extension to a one-liner.
61
+
62
+ Our [reader](http://github.com/spanner/radiant-reader-extension) extension - which handles the mechanics of site membership - is site scoped if this extension is present. It includes a useful `fake_site_scope` class that drops a warning in the log if site-scoping is not possible but otherwise lets the extension work in a single-site installation.
63
+
64
+ ### Security ###
65
+
66
+ Is one of the main goals. A couple of our clients are very security-conscious and we needed something in which there was no risk at all of the wrong person seeing a page. This will make more sense when I publish the [reader-groups](http://github.com/spanner/radiant-reader-groups-extension) extension), which is next. If you see a loophole we'll be __very__ glad to know of it.
67
+
68
+ ### Questions and comments ###
69
+
70
+ Would be very welcome. Contact Will on will at spanner.org or drop [something into lighthouse](http://spanner.lighthouseapp.com/projects/26912-radiant-extensions). Github messages also fine.
71
+
72
+ - - -
73
+
74
+ ## Original multi_site ##
75
+
76
+ Each site has its own independent
77
+ sitemap/page-tree and these attributes:
78
+
79
+ * name: Whatever you want to call the site
80
+ * domain: A Ruby regular expression (without the //) to match the request against
81
+ * base_domain: A canonical domain name for doing quicker matches and for generating absolute URLs against
82
+ * homepage_id: The numerical database ID of the root page (usually you can just leave this alone).
83
+
84
+ Included images are slightly modified from FamFamFam Silk Icons by Mark James: http://www.famfamfam.com/lab/icons/silk/
85
+
86
+ ### Installation ###
87
+
88
+ 1) Unpack/checkout/export the extension into vendor/extensions of your
89
+ project.
90
+
91
+ 2) Run the extension migrations.
92
+
93
+ $ rake production db:migrate:extensions
94
+
95
+ 3) Run the extension update task.
96
+
97
+ $ rake production radiant:extensions:multi_site:update
98
+
99
+ 4) Restart your server
100
+
101
+ ### Other Extensions ###
102
+
103
+ Multi Site allows you to customize routes within your other extensions. To
104
+ restrict a route to a particular site, pass the site's name into the
105
+ conditions hash:
106
+
107
+ map.resources :things, :conditions => { :site => 'My Site' }
108
+
109
+ You can also scope a route to multiple sites with an array:
110
+
111
+ map.resources :things, :conditions => { :site => ['My Site', 'Your Site'] }
112
+
113
+ ### Acknowledgments ###
114
+
115
+ Thanks to Digital Pulp, Inc. for funding the initial development of this
116
+ extension as part of the Redken.com project.
data/Rakefile ADDED
@@ -0,0 +1,120 @@
1
+ # I think this is the one that should be moved to the extension Rakefile template
2
+
3
+ # In rails 1.2, plugins aren't available in the path until they're loaded.
4
+ # Check to see if the rspec plugin is installed first and require
5
+ # it if it is. If not, use the gem version.
6
+
7
+ # Determine where the RSpec plugin is by loading the boot
8
+ unless defined? TRUSTY_CMS_ROOT
9
+ ENV["RAILS_ENV"] = "test"
10
+ case
11
+ when ENV["TRUSTY_ENV_FILE"]
12
+ require File.dirname(ENV["TRUSTY_ENV_FILE"]) + "/boot"
13
+ when File.dirname(__FILE__) =~ %r{vendor/trusty_cms/vendor/extensions}
14
+ require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../../")}/config/boot"
15
+ else
16
+ require "#{File.expand_path(File.dirname(__FILE__) + "/../../../")}/config/boot"
17
+ end
18
+ end
19
+
20
+ require 'rake'
21
+ require 'rake/rdoctask'
22
+ require 'rake/testtask'
23
+
24
+ rspec_base = File.expand_path(TRUSTY_CMS_ROOT + '/vendor/plugins/rspec/lib')
25
+ $LOAD_PATH.unshift(rspec_base) if File.exist?(rspec_base)
26
+ require 'spec/rake/spectask'
27
+ # require 'spec/translator'
28
+
29
+ # Cleanup the TRUSTY_CMS_ROOT constant so specs will load the environment
30
+ Object.send(:remove_const, :TRUSTY_CMS_ROOT)
31
+
32
+ extension_root = File.expand_path(File.dirname(__FILE__))
33
+
34
+ task :default => :spec
35
+ task :stats => "spec:statsetup"
36
+
37
+ desc "Run all specs in spec directory"
38
+ Spec::Rake::SpecTask.new(:spec) do |t|
39
+ t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
40
+ t.spec_files = FileList['spec/**/*_spec.rb']
41
+ end
42
+
43
+ namespace :spec do
44
+ desc "Run all specs in spec directory with RCov"
45
+ Spec::Rake::SpecTask.new(:rcov) do |t|
46
+ t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
47
+ t.spec_files = FileList['spec/**/*_spec.rb']
48
+ t.rcov = true
49
+ t.rcov_opts = ['--exclude', 'spec', '--rails']
50
+ end
51
+
52
+ desc "Print Specdoc for all specs"
53
+ Spec::Rake::SpecTask.new(:doc) do |t|
54
+ t.spec_opts = ["--format", "specdoc", "--dry-run"]
55
+ t.spec_files = FileList['spec/**/*_spec.rb']
56
+ end
57
+
58
+ [:models, :controllers, :views, :helpers].each do |sub|
59
+ desc "Run the specs under spec/#{sub}"
60
+ Spec::Rake::SpecTask.new(sub) do |t|
61
+ t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
62
+ t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"]
63
+ end
64
+ end
65
+
66
+ # Hopefully no one has written their extensions in pre-0.9 style
67
+ # desc "Translate specs from pre-0.9 to 0.9 style"
68
+ # task :translate do
69
+ # translator = ::Spec::Translator.new
70
+ # dir = RAILS_ROOT + '/spec'
71
+ # translator.translate(dir, dir)
72
+ # end
73
+
74
+ # Setup specs for stats
75
+ task :statsetup do
76
+ require 'code_statistics'
77
+ ::STATS_DIRECTORIES << %w(Model\ specs spec/models)
78
+ ::STATS_DIRECTORIES << %w(View\ specs spec/views)
79
+ ::STATS_DIRECTORIES << %w(Controller\ specs spec/controllers)
80
+ ::STATS_DIRECTORIES << %w(Helper\ specs spec/views)
81
+ ::CodeStatistics::TEST_TYPES << "Model specs"
82
+ ::CodeStatistics::TEST_TYPES << "View specs"
83
+ ::CodeStatistics::TEST_TYPES << "Controller specs"
84
+ ::CodeStatistics::TEST_TYPES << "Helper specs"
85
+ ::STATS_DIRECTORIES.delete_if {|a| a[0] =~ /test/}
86
+ end
87
+
88
+ namespace :db do
89
+ namespace :fixtures do
90
+ desc "Load fixtures (from spec/fixtures) into the current environment's database. Load specific fixtures using FIXTURES=x,y"
91
+ task :load => :environment do
92
+ require 'active_record/fixtures'
93
+ ActiveRecord::Base.establish_connection(RAILS_ENV.to_sym)
94
+ (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir.glob(File.join(RAILS_ROOT, 'spec', 'fixtures', '*.{yml,csv}'))).each do |fixture_file|
95
+ Fixtures.create_fixtures('spec/fixtures', File.basename(fixture_file, '.*'))
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ desc 'Generate documentation for the <%= file_name %> extension.'
103
+ Rake::RDocTask.new(:rdoc) do |rdoc|
104
+ rdoc.rdoc_dir = 'rdoc'
105
+ rdoc.title = '<%= class_name %>'
106
+ rdoc.options << '--line-numbers' << '--inline-source'
107
+ rdoc.rdoc_files.include('README')
108
+ rdoc.rdoc_files.include('lib/**/*.rb')
109
+ end
110
+
111
+ # For extensions that are in transition
112
+ desc 'Test the <%= file_name %> extension.'
113
+ Rake::TestTask.new(:test) do |t|
114
+ t.libs << 'lib'
115
+ t.pattern = 'test/**/*_test.rb'
116
+ t.verbose = true
117
+ end
118
+
119
+ # Load any custom rakefiles for extension
120
+ Dir[File.dirname(__FILE__) + '/tasks/*.rake'].sort.each { |f| require f }
Binary file
@@ -0,0 +1,121 @@
1
+ #content table.index tr.node td {
2
+ padding: .5em;
3
+ }
4
+
5
+ #content table.index .node .site small {
6
+ color: #666;
7
+ font-size: 90%;
8
+ font-style: italic;
9
+ font-weight: normal;
10
+ margin-left: .5em;
11
+ }
12
+
13
+ #content table.index .node .site a, #content table.index .node .site a:visited {
14
+ font-size: 115%;
15
+ font-weight: bold;
16
+ color: black;
17
+ text-decoration: none;
18
+ }
19
+
20
+ #content .form-area p.domain {
21
+ float: left;
22
+ width: 40%;
23
+ margin: 1em 1em 0 0;
24
+ overflow: hidden;
25
+ }
26
+
27
+ #content .form-area p.subtitle {
28
+ margin: 1em 0 0 0;
29
+ }
30
+
31
+ #content .form-area p.domain label, #content .form-area p.homepage label {
32
+ display: block;
33
+ position: relative;
34
+ }
35
+
36
+ #content div.form-area p.domain .textbox, #content div.form-area p.homepage .textbox, #content div.form-area p.subtitle .textbox, #content div.form-area p.subtitle select {
37
+ font-family: Georgia,Palatino,"Times New Roman",Times,serif;
38
+ font-size: 140%;
39
+ width: 100%;
40
+ }
41
+
42
+ #content div.form-area p.homepage {
43
+ float: right;
44
+ width: 16%;
45
+ margin: 1em 0 0 0;
46
+ overflow: hidden;
47
+ }
48
+
49
+ p.formnote {
50
+ clear: both;
51
+ padding: 2em 0;
52
+ }
53
+
54
+
55
+ /* BE SURE TO INCLUDE THE CSS RESET FOUND IN THE DEMO PAGE'S CSS */
56
+ /*------------------------------------*\
57
+ NAV
58
+ \*------------------------------------*/
59
+ #nav{
60
+ list-style:none;
61
+ font-weight:bold;
62
+ margin-bottom:10px;
63
+ /* Clear floats */
64
+ width:100%;
65
+ padding-top: 10px;
66
+ padding-bottom: 10px;
67
+ padding-left: 5px;
68
+ margin: 0px;
69
+ /* Bring the nav above everything else--uncomment if needed.
70
+ position:relative;
71
+ z-index:5;
72
+ */
73
+ }
74
+ #nav li{
75
+ margin-right:10px;
76
+ position:relative;
77
+ }
78
+ #nav a{
79
+ display:block;
80
+ padding:5px;
81
+ color:#fff;
82
+ background:#333;
83
+ text-decoration:none;
84
+ }
85
+ #nav a:hover{
86
+ color:#fff;
87
+ background:#CC494C;
88
+ text-decoration:underline;
89
+ }
90
+
91
+ /*--- DROPDOWN ---*/
92
+
93
+ #nav ul{
94
+ background:#fff; /* Adding a background makes the dropdown work properly in IE7+. Make this as close to your page's background as possible (i.e. white page == white background). */
95
+ background:rgba(255,255,255,0); /* But! Let's make the background fully transparent where we can, we don't actually want to see it if we can help it... */
96
+ list-style:none;
97
+ position:absolute;
98
+ left:-9999px; /* Hide off-screen when not needed (this is more accessible than display:none;) */
99
+ }
100
+ #nav ul li{
101
+ padding-top:1px; /* Introducing a padding between the li and the a give the illusion spaced items */
102
+ float:none;
103
+ }
104
+ #nav ul a{
105
+ white-space:nowrap; /* Stop text wrapping and creating multi-line dropdown items */
106
+ }
107
+ #nav li:hover ul{ /* Display the dropdown on hover */
108
+ left:0; /* Bring back on-screen when needed */
109
+ z-index: 99999;
110
+
111
+ }
112
+ #nav li:hover a{ /* These create persistent hover states, meaning the top-most link stays 'hovered' even when your cursor has moved down the list. */
113
+ background:#CC494C;
114
+ text-decoration:underline;
115
+ }
116
+ #nav li:hover ul a{ /* The persistent hover state does however create a global style for links even before they're hovered. Here we undo these effects. */
117
+ text-decoration:none;
118
+ }
119
+ #nav li:hover ul li a:hover{ /* Here we define the most explicit hover states--what happens when you hover each individual link. */
120
+ background:#333;
121
+ }
File without changes
@@ -0,0 +1,14 @@
1
+ class Admin::SitesController < Admin::ResourceController
2
+ helper :sites
3
+ only_allow_access_to :index, :show, :new, :create, :edit, :update, :remove, :destroy,
4
+ :when => :admin,
5
+ :denied_url => { :controller => 'pages', :action => 'index' },
6
+ :denied_message => 'You must have administrative privileges to perform this action.'
7
+
8
+ %w(move_higher move_lower move_to_top move_to_bottom).each do |action|
9
+ define_method action do
10
+ model.send(action)
11
+ response_for :update
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,19 @@
1
+ module ScopedHelper
2
+ def self.included(base)
3
+
4
+ base.module_eval do
5
+ def title
6
+ t = current_site.name
7
+ t = TrustyCms::Config['admin.title'] || 'Radiant CMS' if t.blank?
8
+ t
9
+ end
10
+
11
+ def subtitle
12
+ st = current_site.subtitle
13
+ st = TrustyCms::Config['admin.subtitle'] || 'publishing for small teams' if st.blank?
14
+ st
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,10 @@
1
+ module SitesHelper
2
+ def order_links(site)
3
+ String.new.tap do |output|
4
+ output << link_to(image("move_to_top.png", :alt => "Move to top"), move_to_top_admin_site_path(site), :method => :put)
5
+ output << link_to(image("move_higher.png", :alt => "Move up"), move_higher_admin_site_path(site), :method => :post)
6
+ output << link_to(image("move_lower.png", :alt => "Move down"), move_lower_admin_site_path(site), :method => :post)
7
+ output << link_to(image("move_to_bottom.png", :alt => "Move to bottom"), move_to_bottom_admin_site_path(site), :method => :put)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,86 @@
1
+ # The site class includes - in find_for_host - some key retrieval and creation logic that is called from ApplicationController to set the current site context.
2
+ # Otherwise it's just another radiant data model.
3
+
4
+ class Site < ActiveRecord::Base
5
+ acts_as_list
6
+ belongs_to :created_by, :class_name => 'User'
7
+ belongs_to :updated_by, :class_name => 'User'
8
+ belongs_to :production_homepage , :class_name => 'ProductionPage'
9
+
10
+ default_scope :order => 'position ASC'
11
+
12
+ class << self
13
+ attr_accessor :several
14
+
15
+ # I've added one or two sql queries here for the sake of a separate default method
16
+
17
+ def find_for_host(hostname = '')
18
+ return default if hostname.blank?
19
+ sites = all(:conditions => "domain IS NOT NULL and domain != ''")
20
+ site = sites.find { |site| hostname == site.base_domain || hostname =~ Regexp.compile(site.domain) }
21
+ site || default
22
+ end
23
+
24
+ # Site.default returns the the first site it can find with an empty domain pattern.
25
+
26
+ def default
27
+ find_by_domain('') || find_by_domain(nil) || catchall
28
+ end
29
+
30
+ # If none is found, we are probably brand new, so a workable default site is created.
31
+
32
+ def catchall
33
+ create({
34
+ :domain => '',
35
+ :name => 'default_site',
36
+ :base_domain => 'localhost',
37
+ :homepage => Page.find_by_parent_id(nil)
38
+ })
39
+ end
40
+
41
+ # Returns true if more than one site is present. This is normally only used to make interface decisions, eg whether to show the site-chooser dropdown.
42
+
43
+ def several?
44
+ several = (count > 1) if several.nil?
45
+ end
46
+ end
47
+
48
+ belongs_to :homepage, :class_name => "Page", :foreign_key => "homepage_id"
49
+ validates_presence_of :name, :base_domain
50
+ validates_uniqueness_of :domain
51
+
52
+ after_create :create_homepage
53
+ after_save :reload_routes
54
+
55
+ # Returns the fully specified web address for the supplied path, or the root of this site if no path is given.
56
+
57
+ def url(path = "/")
58
+ uri = URI.join("http://#{self.base_domain}", path)
59
+ uri.to_s
60
+ end
61
+
62
+ # Returns the fully specified web address for the development version of this site and the supplied path, or the root of this site if no path is given.
63
+
64
+ def dev_url(path = "/")
65
+ uri = URI.join("http://#{TrustyCms::Config['dev.host'] || 'dev'}.#{self.base_domain}", path)
66
+ uri.to_s
67
+ end
68
+
69
+ def create_homepage
70
+ if self.homepage_id.blank?
71
+ self.homepage = self.build_homepage(:title => "#{self.name} Homepage",
72
+ :slug => "#{self.name.to_slug}", :breadcrumb => "Home")
73
+ default_status = TrustyCms::Config['defaults.page.status']
74
+ self.homepage.status = Status[default_status] if default_status
75
+ default_parts = TrustyCms::Config['defaults.page.parts'].to_s.strip.split(/\s*,\s*/)
76
+ default_parts.each do |name|
77
+ self.homepage.parts << PagePart.new(:name => name, :filter_id => TrustyCms::Config['defaults.page.filter'])
78
+ end
79
+ save
80
+ end
81
+ end
82
+
83
+ def reload_routes
84
+ TrustyCms::Application.reload_routes!
85
+ end
86
+ end
@@ -0,0 +1,8 @@
1
+ - unless current_user.site
2
+ %p{:style => 'float: left; margin-right: 32px'}
3
+ %label{:for=>"layout_site_id", :Class => "admin_only"}
4
+ Site
5
+ - sites = Site.find(:all).map { |s| [s.name, s.id] }
6
+ - selection = {:include_blank => Layout.is_shareable?}
7
+ - selection[:selected] = current_site.id if @layout.new_record? && ! Layout.is_shareable?
8
+ = select :layout, :site_id, sites, selection
@@ -0,0 +1,2 @@
1
+ = stylesheet_link_tag 'admin/multi_site'
2
+ = sites_chooser_thing.html_safe
@@ -0,0 +1,43 @@
1
+ = render_region :form_top
2
+ .form-area
3
+ - render_region :form do |form|
4
+
5
+ - form.edit_name do
6
+ %p.title
7
+ %label{:for => "site_name"} Site title
8
+ = f.text_field :name, :class => "textbox"
9
+
10
+ %p.subtitle
11
+ %label{:for => "site_subtitle"} and subtitle
12
+ = f.text_field :subtitle, :class => "textbox"
13
+
14
+
15
+ - form.edit_domain do
16
+ %p.domain
17
+ %label{:for => "site_domain"}
18
+ Domain pattern
19
+ = f.text_field :domain, :class => "textbox"
20
+ %p.domain
21
+ %label{:for => "site_base_domain"}
22
+ Base domain name
23
+ = f.text_field :base_domain, :class => "textbox"
24
+
25
+ - form.edit_homepage do
26
+ %p.homepage
27
+ %label{:for => "site_homepage_id"} Homepage ID
28
+ = f.text_field :homepage_id, :class => "textbox"
29
+
30
+ %p.formnote
31
+ - if f.object.new_record?
32
+ A new homepage will be created automatically if none is specified.
33
+ - else
34
+ If you change the site homepage ID, be sure it is is to a page with no parents.
35
+
36
+ - render_region :form_bottom do |form_bottom|
37
+ - form_bottom.edit_timestamp do
38
+ = updated_stamp @site
39
+ - form_bottom.edit_buttons do
40
+ %p.buttons
41
+ = save_model_button(@site)
42
+ or
43
+ = link_to "cancel", admin_sites_path
@@ -0,0 +1,7 @@
1
+ = stylesheet_link_tag 'admin/multi_site'
2
+ - render_region :main do |main|
3
+ - main.edit_header do
4
+ %h1 Edit Site
5
+ - main.edit_form do
6
+ = form_for @site, :url => admin_site_path(@site), :html => {:method => :put} do |f|
7
+ = render :partial => "form", :locals => {:f => f}
@@ -0,0 +1,43 @@
1
+ = stylesheet_link_tag 'admin/multi_site'
2
+ = render_region :top
3
+
4
+ %h1 Sites
5
+
6
+ %table{:class => "index", :cellspacing => "0"}
7
+ %thead
8
+ %tr
9
+ - render_region :thead do |thead|
10
+ - thead.title_header do
11
+ %th.site Name
12
+ - thead.domain_header do
13
+ %th.domain Domain match
14
+ - thead.basedomain_header do
15
+ %th.basedomain Base domain
16
+ - thead.modify_header do
17
+ %th.remove Delete
18
+ - thead.order_header do
19
+ %th.order Order
20
+ %tbody
21
+ - @sites.each do |site|
22
+ %tr.node.level-1
23
+ - render_region :tbody do |tbody|
24
+ - tbody.title_cell do
25
+ %td.site
26
+ = link_to site.name, edit_admin_site_path(site)
27
+ - tbody.domain_cell do
28
+ %td.domain
29
+ = h(site.domain)
30
+ - tbody.basedomain_cell do
31
+ %td.basedomain
32
+ = h(site.base_domain)
33
+ - tbody.modify_cell do
34
+ %td.remove
35
+ = link_to image("remove.png", :alt => "Remove"), admin_site_path(site), :method => :delete, :confirm => "Are you sure you want to permanently remove #{site.name}?"
36
+ - tbody.order_cell do
37
+ %td.order
38
+ = order_links(site).html_safe
39
+
40
+ - render_region :bottom do |bottom|
41
+ - bottom.new_button do
42
+ %p
43
+ = link_to "New Site", new_admin_site_path
@@ -0,0 +1,7 @@
1
+ = stylesheet_link_tag 'admin/multi_site'
2
+ - render_region :main do |main|
3
+ - main.edit_header do
4
+ %h1 New Site
5
+ - main.edit_form do
6
+ = form_for @site, :url => admin_sites_path do |f|
7
+ = render :partial => "form", :locals => {:f => f}
@@ -0,0 +1,6 @@
1
+ - unless current_user.site
2
+ %p{:style => 'float: left; margin-right: 32px'}
3
+ %label{:for=>"snippet_site_id", :Class => "admin_only"}
4
+ Site
5
+ - sites = Site.find(:all).map { |s| [s.name, s.id] }
6
+ = select :snippet, :site_id, sites, :include_blank => Snippet.is_shareable?, :selected => @snippet.site_id || current_site.id
@@ -0,0 +1,8 @@
1
+ - unless current_user.site
2
+ %tr
3
+ %th.label
4
+ %label{:for=>"user_admin"} Can edit site
5
+ %td.field{:style => 'text-align: left;'}
6
+ = select :user, :site_id, [['<any>', '']] + Site.find(:all).map { |s| [s.name, s.id] }
7
+ %td.help
8
+ A user with no site is able to act (to whatever extent their status allows) on any site.
@@ -0,0 +1,7 @@
1
+ - @page_title = 'Site Not Configured'
2
+ %h1=@page_title
3
+ %p
4
+ This installation is not configured correctly: please
5
+ = link_to "log in", login_url
6
+ and make sure that a site is configured to respond either to all requests or to the host
7
+ = request.host
@@ -0,0 +1,5 @@
1
+ require 'multi_site/engine'
2
+ TrustyCms.config do |config|
3
+ # config.define "setting.name", :default => 'value', :select_from => ['foo', 'bar']
4
+ require 'multi_site/scoped_validation'
5
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,13 @@
1
+
2
+ TrustyCms::Application.routes.draw do
3
+ namespace :admin do
4
+ resources :sites do
5
+ get :remove, on: :member
6
+ post :move_higher, on: :member
7
+ post :move_lower, on: :member
8
+ put :move_to_top, on: :member
9
+ put :move_to_bottom, on: :member
10
+ end
11
+ end
12
+ end
13
+