radiant-reader_group-extension 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/README.markdown +44 -0
- data/Rakefile +137 -0
- data/VERSION +1 -0
- data/app/controllers/admin/group_invitations_controller.rb +84 -0
- data/app/controllers/admin/groups_controller.rb +4 -0
- data/app/controllers/admin/memberships_controller.rb +42 -0
- data/app/controllers/admin/permissions_controller.rb +42 -0
- data/app/helpers/admin/groups_helper.rb +36 -0
- data/app/models/group.rb +43 -0
- data/app/models/membership.rb +13 -0
- data/app/models/permission.rb +13 -0
- data/app/views/admin/group_invitations/new.html.haml +45 -0
- data/app/views/admin/group_invitations/preview.html.haml +63 -0
- data/app/views/admin/groups/_add_readers.html.haml +0 -0
- data/app/views/admin/groups/_form.html.haml +61 -0
- data/app/views/admin/groups/_list_head.html.haml +12 -0
- data/app/views/admin/groups/_listed.html.haml +25 -0
- data/app/views/admin/groups/edit.html.haml +8 -0
- data/app/views/admin/groups/index.html.haml +19 -0
- data/app/views/admin/groups/new.html.haml +6 -0
- data/app/views/admin/groups/remove.html.haml +31 -0
- data/app/views/admin/groups/show.html.haml +41 -0
- data/app/views/admin/memberships/_reader.html.haml +9 -0
- data/app/views/admin/messages/_list_notes.html.haml +9 -0
- data/app/views/admin/messages/_message_description.html.haml +7 -0
- data/app/views/admin/messages/_message_group.html.haml +3 -0
- data/app/views/admin/pages/_listed.html.haml +16 -0
- data/app/views/admin/pages/_page_groups.html.haml +17 -0
- data/app/views/admin/permissions/_page.html.haml +24 -0
- data/app/views/admin/reader_settings/_group_welcomes.html.haml +11 -0
- data/app/views/admin/readers/_reader_groups.html.haml +7 -0
- data/app/views/messages/show.html.haml +11 -0
- data/app/views/reader_activations/_on_activation.html.haml +10 -0
- data/app/views/readers/_memberships.html.haml +11 -0
- data/app/views/site/not_allowed.html.haml +4 -0
- data/config/routes.rb +8 -0
- data/db/migrate/001_create_groups.rb +32 -0
- data/db/migrate/20090921125654_group_messages.rb +9 -0
- data/db/migrate/20091120083119_groups_public.rb +11 -0
- data/lib/admin_messages_controller_extensions.rb +15 -0
- data/lib/group_message_tags.rb +82 -0
- data/lib/group_ui.rb +37 -0
- data/lib/grouped_message.rb +38 -0
- data/lib/grouped_model.rb +100 -0
- data/lib/grouped_page.rb +59 -0
- data/lib/grouped_reader.rb +63 -0
- data/lib/reader_activations_controller_extensions.rb +21 -0
- data/lib/reader_notifier_extensions.rb +14 -0
- data/lib/reader_sessions_controller_extensions.rb +21 -0
- data/lib/readers_controller_extensions.rb +22 -0
- data/lib/site_controller_extensions.rb +37 -0
- data/lib/tasks/reader_group_extension_tasks.rake +28 -0
- data/pkg/radiant-reader_group-extension-0.9.0.gem +0 -0
- data/public/images/admin/chk_auto.png +0 -0
- data/public/images/admin/chk_off.png +0 -0
- data/public/images/admin/chk_on.png +0 -0
- data/public/images/admin/edit.png +0 -0
- data/public/images/admin/error.png +0 -0
- data/public/images/admin/message.png +0 -0
- data/public/images/admin/new-group.png +0 -0
- data/public/images/admin/populate.png +0 -0
- data/public/images/admin/rdo_off.png +0 -0
- data/public/images/admin/rdo_on.png +0 -0
- data/public/stylesheets/sass/admin/group.sass +66 -0
- data/radiant-reader_group-extension.gemspec +134 -0
- data/reader_group_extension.rb +53 -0
- data/spec/controllers/readers_controller_spec.rb +44 -0
- data/spec/controllers/site_controller_spec.rb +64 -0
- data/spec/datasets/group_messages_dataset.rb +32 -0
- data/spec/datasets/group_readers_dataset.rb +49 -0
- data/spec/datasets/group_sites_dataset.rb +11 -0
- data/spec/datasets/groups_dataset.rb +48 -0
- data/spec/models/group_spec.rb +45 -0
- data/spec/models/message_spec.rb +42 -0
- data/spec/models/page_spec.rb +53 -0
- data/spec/models/reader_spec.rb +16 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +36 -0
- metadata +184 -0
data/.gitignore
ADDED
data/README.markdown
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# Reader Group Extension
|
2
|
+
|
3
|
+
The basic purpose of this extension is to control access to pages in your public site. It allows you to create a group of readers, and to make pages 9and other site elements) visible only to selected groups. It also provides a useful - and now quite shiny - group-messaging interface and a batch-import function for inviting readers.
|
4
|
+
|
5
|
+
In combination with our other extensions it also lets you control access to forums, downloads and assets. I hope other people will find the framework useful and apply it to their own requirements.
|
6
|
+
|
7
|
+
This extension doesn't group your users or affect the admin interface at all, apart from adding (quite a lot of) machinery for looking after groups. As always, readers have their own self-management interface that looks like the rest of your site. They never see the admin pages.
|
8
|
+
|
9
|
+
This works with multi_site. If you use [our fork](https://github.com/spanner/radiant-paperclipped_multisite-extension/tree) then readers and groups are automatically site-scoped.
|
10
|
+
|
11
|
+
## Latest
|
12
|
+
|
13
|
+
* groups can be marked subscribable, which puts a checkbox to subscribe or unsubscribe on the readers' registration and preference forms. Migration required.
|
14
|
+
* messages can each have a different layout
|
15
|
+
* group-mailer moved into Reader as general purpose reader-mailer, now using radiant layouts for styled group mailouts
|
16
|
+
* group access control consolidated in `is_grouped` function to simplify maintenance of other extensions
|
17
|
+
* invitations factored out, made reasonably restful
|
18
|
+
|
19
|
+
## Status
|
20
|
+
|
21
|
+
This has been brought across from a previous version that grouped users instead of working in the reader framework, so it's a mixture of the well-used and the just-invented, but the tests are fairly comprehensive. It's in use on a couple of biggish sites and seems fairly robust.
|
22
|
+
|
23
|
+
## Requirements
|
24
|
+
|
25
|
+
The [reader](https://github.com/spanner/radiant-reader-extension/tree) and [submenu](https://github.com/spanner/radiant-submenu-extension/tree) extensions.
|
26
|
+
|
27
|
+
## Installation
|
28
|
+
|
29
|
+
Once you've got the reader extension in, the rest is easy:
|
30
|
+
|
31
|
+
git submodule add git://github.com/spanner/radiant-reader_group-extension.git vendor/extensions/reader_group
|
32
|
+
rake radiant:extensions:reader_group:migrate
|
33
|
+
rake radiant:extensions:reader_group:update
|
34
|
+
|
35
|
+
## Bugs and comments
|
36
|
+
|
37
|
+
In [lighthouse](http://spanner.lighthouseapp.com/projects/26912-radiant-extensions), please, or for little things an email or github message is always welcome.
|
38
|
+
|
39
|
+
## Author and copyright
|
40
|
+
|
41
|
+
* Copyright spanner ltd 2007-9.
|
42
|
+
* Released under the same terms as Rails and/or Radiant.
|
43
|
+
* Contact will at spanner.org
|
44
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
begin
|
2
|
+
require 'jeweler'
|
3
|
+
Jeweler::Tasks.new do |gem|
|
4
|
+
gem.name = "radiant-reader_group-extension"
|
5
|
+
gem.summary = %Q{Group-based access control for the radiant CMS}
|
6
|
+
gem.description = %Q{Adds group-based page access control to radiant.}
|
7
|
+
gem.email = "will@spanner.org"
|
8
|
+
gem.homepage = "http://github.com/spanner/radiant-reader_group-extension"
|
9
|
+
gem.authors = ["spanner"]
|
10
|
+
gem.add_dependency "radiant", ">= 0.9.0"
|
11
|
+
gem.add_dependency 'radiant-reader-extension'
|
12
|
+
end
|
13
|
+
rescue LoadError
|
14
|
+
puts "Jeweler (or a dependency) not available. This is only required if you plan to package reader_group as a gem."
|
15
|
+
end
|
16
|
+
|
17
|
+
# In rails 1.2, plugins aren't available in the path until they're loaded.
|
18
|
+
# Check to see if the rspec plugin is installed first and require
|
19
|
+
# it if it is. If not, use the gem version.
|
20
|
+
|
21
|
+
# Determine where the RSpec plugin is by loading the boot
|
22
|
+
unless defined? RADIANT_ROOT
|
23
|
+
ENV["RAILS_ENV"] = "test"
|
24
|
+
case
|
25
|
+
when ENV["RADIANT_ENV_FILE"]
|
26
|
+
require File.dirname(ENV["RADIANT_ENV_FILE"]) + "/boot"
|
27
|
+
when File.dirname(__FILE__) =~ %r{vendor/radiant/vendor/extensions}
|
28
|
+
require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../../")}/config/boot"
|
29
|
+
else
|
30
|
+
require "#{File.expand_path(File.dirname(__FILE__) + "/../../../")}/config/boot"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
require 'rake'
|
35
|
+
require 'rake/rdoctask'
|
36
|
+
require 'rake/testtask'
|
37
|
+
|
38
|
+
rspec_base = File.expand_path(RADIANT_ROOT + '/vendor/plugins/rspec/lib')
|
39
|
+
$LOAD_PATH.unshift(rspec_base) if File.exist?(rspec_base)
|
40
|
+
require 'spec/rake/spectask'
|
41
|
+
require 'cucumber'
|
42
|
+
require 'cucumber/rake/task'
|
43
|
+
|
44
|
+
# Cleanup the RADIANT_ROOT constant so specs will load the environment
|
45
|
+
Object.send(:remove_const, :RADIANT_ROOT)
|
46
|
+
|
47
|
+
extension_root = File.expand_path(File.dirname(__FILE__))
|
48
|
+
|
49
|
+
task :default => :spec
|
50
|
+
task :stats => "spec:statsetup"
|
51
|
+
|
52
|
+
desc "Run all specs in spec directory"
|
53
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
54
|
+
t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
|
55
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
56
|
+
end
|
57
|
+
|
58
|
+
task :features => 'spec:integration'
|
59
|
+
|
60
|
+
namespace :spec do
|
61
|
+
desc "Run all specs in spec directory with RCov"
|
62
|
+
Spec::Rake::SpecTask.new(:rcov) do |t|
|
63
|
+
t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
|
64
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
65
|
+
t.rcov = true
|
66
|
+
t.rcov_opts = ['--exclude', 'spec', '--rails']
|
67
|
+
end
|
68
|
+
|
69
|
+
desc "Print Specdoc for all specs"
|
70
|
+
Spec::Rake::SpecTask.new(:doc) do |t|
|
71
|
+
t.spec_opts = ["--format", "specdoc", "--dry-run"]
|
72
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
73
|
+
end
|
74
|
+
|
75
|
+
[:models, :controllers, :views, :helpers].each do |sub|
|
76
|
+
desc "Run the specs under spec/#{sub}"
|
77
|
+
Spec::Rake::SpecTask.new(sub) do |t|
|
78
|
+
t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
|
79
|
+
t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
desc "Run the Cucumber features"
|
84
|
+
Cucumber::Rake::Task.new(:integration) do |t|
|
85
|
+
t.fork = true
|
86
|
+
t.cucumber_opts = ['--format', (ENV['CUCUMBER_FORMAT'] || 'pretty')]
|
87
|
+
# t.feature_pattern = "#{extension_root}/features/**/*.feature"
|
88
|
+
t.profile = "default"
|
89
|
+
end
|
90
|
+
|
91
|
+
# Setup specs for stats
|
92
|
+
task :statsetup do
|
93
|
+
require 'code_statistics'
|
94
|
+
::STATS_DIRECTORIES << %w(Model\ specs spec/models)
|
95
|
+
::STATS_DIRECTORIES << %w(View\ specs spec/views)
|
96
|
+
::STATS_DIRECTORIES << %w(Controller\ specs spec/controllers)
|
97
|
+
::STATS_DIRECTORIES << %w(Helper\ specs spec/views)
|
98
|
+
::CodeStatistics::TEST_TYPES << "Model specs"
|
99
|
+
::CodeStatistics::TEST_TYPES << "View specs"
|
100
|
+
::CodeStatistics::TEST_TYPES << "Controller specs"
|
101
|
+
::CodeStatistics::TEST_TYPES << "Helper specs"
|
102
|
+
::STATS_DIRECTORIES.delete_if {|a| a[0] =~ /test/}
|
103
|
+
end
|
104
|
+
|
105
|
+
namespace :db do
|
106
|
+
namespace :fixtures do
|
107
|
+
desc "Load fixtures (from spec/fixtures) into the current environment's database. Load specific fixtures using FIXTURES=x,y"
|
108
|
+
task :load => :environment do
|
109
|
+
require 'active_record/fixtures'
|
110
|
+
ActiveRecord::Base.establish_connection(RAILS_ENV.to_sym)
|
111
|
+
(ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir.glob(File.join(RAILS_ROOT, 'spec', 'fixtures', '*.{yml,csv}'))).each do |fixture_file|
|
112
|
+
Fixtures.create_fixtures('spec/fixtures', File.basename(fixture_file, '.*'))
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
desc 'Generate documentation for the reader_group extension.'
|
120
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
121
|
+
rdoc.rdoc_dir = 'rdoc'
|
122
|
+
rdoc.title = 'ReaderGroupExtension'
|
123
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
124
|
+
rdoc.rdoc_files.include('README')
|
125
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
126
|
+
end
|
127
|
+
|
128
|
+
# For extensions that are in transition
|
129
|
+
desc 'Test the reader_group extension.'
|
130
|
+
Rake::TestTask.new(:test) do |t|
|
131
|
+
t.libs << 'lib'
|
132
|
+
t.pattern = 'test/**/*_test.rb'
|
133
|
+
t.verbose = true
|
134
|
+
end
|
135
|
+
|
136
|
+
# Load any custom rakefiles for extension
|
137
|
+
Dir[File.dirname(__FILE__) + '/tasks/*.rake'].sort.each { |f| require f }
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.9.0
|
@@ -0,0 +1,84 @@
|
|
1
|
+
class Admin::GroupInvitationsController < ApplicationController
|
2
|
+
require 'csv'
|
3
|
+
|
4
|
+
before_filter :find_group, :only => [:new, :create]
|
5
|
+
|
6
|
+
def new
|
7
|
+
|
8
|
+
end
|
9
|
+
|
10
|
+
def create
|
11
|
+
if params[:invite_reader] || params[:import_reader]
|
12
|
+
notice = []
|
13
|
+
if invites = params[:invite_reader]
|
14
|
+
invite_counter = 0
|
15
|
+
invites.each do |i|
|
16
|
+
if reader = Reader.find_by_id(i)
|
17
|
+
reader.groups << @group unless reader.is_in?(@group)
|
18
|
+
@group.send_welcome_to(reader)
|
19
|
+
invite_counter += 1
|
20
|
+
end
|
21
|
+
end
|
22
|
+
notice << "#{invite_counter} existing"
|
23
|
+
end
|
24
|
+
if imports = params[:import_reader]
|
25
|
+
import_counter = 0
|
26
|
+
imports.each do |i|
|
27
|
+
r = params["reader_#{i}".to_sym]
|
28
|
+
r[:password] = r[:password_confirmation] = generate_password
|
29
|
+
reader = Reader.new(r)
|
30
|
+
reader.clear_password = r[:password]
|
31
|
+
if reader.save!
|
32
|
+
reader.groups << @group
|
33
|
+
reader.send_group_invitation_message(@group)
|
34
|
+
import_counter += 1
|
35
|
+
end
|
36
|
+
notice << "#{import_counter} new"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
flash[:notice] = notice.join(' and ') + " readers invited into the #{@group.name} group"
|
40
|
+
redirect_to admin_group_url(@group)
|
41
|
+
else
|
42
|
+
if params[:readerlist] && @readers = readers_from_csv(params[:readerlist])
|
43
|
+
render :action => 'preview'
|
44
|
+
else
|
45
|
+
render :action => 'new'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def find_group
|
53
|
+
@group = Group.find(params[:group_id])
|
54
|
+
end
|
55
|
+
|
56
|
+
def readers_from_csv(readerdata)
|
57
|
+
readers = []
|
58
|
+
CSV::StringReader.parse(readerdata).each do |line|
|
59
|
+
csv = line.collect {|value| value ? value.gsub(/^ */, '').chomp : ''}
|
60
|
+
input = {}
|
61
|
+
input[:honorific] = csv.shift if Radiant::Config['reader.use_honorifics?']
|
62
|
+
input[:password] = input[:password_confirmation] = generate_password
|
63
|
+
[:name, :email, :login, :phone].each {|field| input[field] = csv.shift}
|
64
|
+
r = Reader.find_by_email(input[:email]) || Reader.new(input)
|
65
|
+
r.login = generate_login(input[:name]) if r.login.blank?
|
66
|
+
r.valid? # so that errors can be shown on the confirmation form
|
67
|
+
readers << r
|
68
|
+
end
|
69
|
+
readers
|
70
|
+
end
|
71
|
+
|
72
|
+
def generate_login(name)
|
73
|
+
names = name.split
|
74
|
+
initials = names.map {|n| n.slice(0,1)}
|
75
|
+
initials.pop
|
76
|
+
initials.push(names.last).join('_').downcase
|
77
|
+
end
|
78
|
+
|
79
|
+
def generate_password(length=12)
|
80
|
+
chars = ("a".."z").to_a + ("A".."Z").to_a + ("1".."9").to_a
|
81
|
+
Array.new(length, '').collect{chars[rand(chars.size)]}.join
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class Admin::MembershipsController < ApplicationController
|
2
|
+
|
3
|
+
before_filter :find_group
|
4
|
+
|
5
|
+
def index
|
6
|
+
redirect_to admin_group_url(@group)
|
7
|
+
end
|
8
|
+
|
9
|
+
def create
|
10
|
+
@reader = Reader.find(params[:reader_id])
|
11
|
+
raise ActiveRecord::RecordNotFound unless @reader
|
12
|
+
@membership = Membership.find_or_create_by_reader_id_and_group_id(@reader.id, @group.id)
|
13
|
+
respond_to do |format|
|
14
|
+
format.html {
|
15
|
+
flash[:notice] = "#{@reader.name} added to group #{@group.name}"
|
16
|
+
redirect_to admin_group_url(@group)
|
17
|
+
}
|
18
|
+
format.js { render :partial => 'reader' }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def destroy
|
23
|
+
@membership = @group.memberships.find(params[:id])
|
24
|
+
@reader = @membership.reader
|
25
|
+
@membership.delete if @membership
|
26
|
+
respond_to do |format|
|
27
|
+
format.html {
|
28
|
+
flash[:notice] = "#{@reader.name} removed from group #{@group.name}" if @membership
|
29
|
+
redirect_to admin_group_url(@group)
|
30
|
+
}
|
31
|
+
format.js { render :partial => 'reader' }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
|
37
|
+
def find_group
|
38
|
+
@group = Group.find(params[:group_id])
|
39
|
+
raise ActiveRecord::RecordNotFound unless @group
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class Admin::PermissionsController < ApplicationController
|
2
|
+
|
3
|
+
before_filter :find_group
|
4
|
+
|
5
|
+
def index
|
6
|
+
redirect_to admin_group_url(@group)
|
7
|
+
end
|
8
|
+
|
9
|
+
def create
|
10
|
+
@page = Page.find(params[:page_id])
|
11
|
+
raise ActiveRecord::RecordNotFound unless @page
|
12
|
+
@permission = Permission.find_or_create_by_page_id_and_group_id(@page.id, @group.id)
|
13
|
+
respond_to do |format|
|
14
|
+
format.html {
|
15
|
+
flash[:notice] = "#{@page.name} bound to group #{@group.name}"
|
16
|
+
redirect_to admin_group_url(@group)
|
17
|
+
}
|
18
|
+
format.js { render :partial => 'page' }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def destroy
|
23
|
+
@permission = @group.permissions.find(params[:id])
|
24
|
+
@page = @permission.page
|
25
|
+
@permission.delete if @permission
|
26
|
+
respond_to do |format|
|
27
|
+
format.html {
|
28
|
+
flash[:notice] = "#{@page.name} released from group #{@group.name}"
|
29
|
+
redirect_to admin_group_url(@group)
|
30
|
+
}
|
31
|
+
format.js { render :partial => 'page' }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
|
37
|
+
def find_group
|
38
|
+
@group = Group.find(params[:group_id])
|
39
|
+
raise ActiveRecord::RecordNotFound unless @group
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Admin::GroupsHelper
|
2
|
+
|
3
|
+
def message_preview(subject, body, reader)
|
4
|
+
preview = <<EOM
|
5
|
+
From: #{current_user.name} <#{current_user.email}>
|
6
|
+
To: #{reader.name} <#{reader.email}>
|
7
|
+
Date: #{Time.now.to_date.to_s :long}
|
8
|
+
<strong>Subject: #{subject}</strong>
|
9
|
+
|
10
|
+
Dear #{reader.name},
|
11
|
+
|
12
|
+
#{body}
|
13
|
+
|
14
|
+
EOM
|
15
|
+
simple_format(preview)
|
16
|
+
end
|
17
|
+
|
18
|
+
def choose_page(object, field, select_options={})
|
19
|
+
root = Page.respond_to?(:homepage) ? Page.homepage : Page.find_by_parent_id(nil)
|
20
|
+
options = page_option_branch(root)
|
21
|
+
options.unshift ['<none>', nil]
|
22
|
+
select object, field, options, select_options
|
23
|
+
end
|
24
|
+
|
25
|
+
def page_option_branch(page, depth=0)
|
26
|
+
options = []
|
27
|
+
unless page.title.first == '_'
|
28
|
+
options << ["#{". " * depth}#{h(page.title)}", page.id]
|
29
|
+
page.children.each do |child|
|
30
|
+
options += page_option_branch(child, depth + 1)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
options
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
data/app/models/group.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
class Group < ActiveRecord::Base
|
2
|
+
|
3
|
+
is_site_scoped if defined? ActiveRecord::SiteNotFound
|
4
|
+
default_scope :order => 'name'
|
5
|
+
|
6
|
+
belongs_to :created_by, :class_name => 'User'
|
7
|
+
belongs_to :updated_by, :class_name => 'User'
|
8
|
+
belongs_to :homepage, :class_name => 'Page'
|
9
|
+
|
10
|
+
has_many :messages
|
11
|
+
has_many :permissions
|
12
|
+
has_many :pages, :through => :permissions
|
13
|
+
has_many :memberships
|
14
|
+
has_many :readers, :through => :memberships
|
15
|
+
|
16
|
+
validates_presence_of :name
|
17
|
+
validates_uniqueness_of :name
|
18
|
+
|
19
|
+
named_scope :with_home_page, { :conditions => "homepage_id IS NOT NULL", :include => :homepage }
|
20
|
+
named_scope :subscribable, { :conditions => "public = 1" }
|
21
|
+
named_scope :unsubscribable, { :conditions => "public = 0" }
|
22
|
+
|
23
|
+
def url
|
24
|
+
homepage.url if homepage
|
25
|
+
end
|
26
|
+
|
27
|
+
def send_welcome_to(reader)
|
28
|
+
if reader.activated? # welcomes will be triggered again on activation
|
29
|
+
message = messages.for_function('group_welcome').first # only if a group_welcome message exists *belonging to this group*
|
30
|
+
message.deliver_to(reader) if message # (the belonging also allows us to mention the group in the message)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def permission_for(page)
|
35
|
+
self.permissions.for(page).first
|
36
|
+
end
|
37
|
+
|
38
|
+
def membership_for(reader)
|
39
|
+
self.memberships.for(reader).first
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
- include_stylesheet('admin/group')
|
2
|
+
|
3
|
+
%h1
|
4
|
+
Invite people into
|
5
|
+
= @group.name
|
6
|
+
|
7
|
+
%p
|
8
|
+
This is a quick way to bring a group of people into the system in one go.
|
9
|
+
Enter a comma-separated list of names and email addresses below (one person per line)
|
10
|
+
and each of those people will be invited into the system and issued an account with a vaguely adequate username
|
11
|
+
and entirely random password. Anyone who is already here will just be added to the group.
|
12
|
+
|
13
|
+
- if message = @group.messages.for_function('invitation').first
|
14
|
+
%p
|
15
|
+
%strong
|
16
|
+
You may want to check the group's
|
17
|
+
= link_to "invitation message", admin_message_url(message)
|
18
|
+
before going any further.
|
19
|
+
|
20
|
+
- form_for :group, @group, :url => admin_group_group_invitations_url(@group), :html => {:id => 'preview_form', :method => 'post'} do
|
21
|
+
.form-area
|
22
|
+
%p
|
23
|
+
%label{:for => "readerlist"}
|
24
|
+
List of people
|
25
|
+
- if Radiant::Config['reader.use_honorifics?']
|
26
|
+
title or rank,
|
27
|
+
name, email, [login], [phone]
|
28
|
+
%br
|
29
|
+
= text_area_tag "readerlist", params[:readerlist], :class => "textarea", :style => "width: 100%; height: 240px;"
|
30
|
+
|
31
|
+
%p.buttons
|
32
|
+
= submit_tag 'preview import', :class => 'button'
|
33
|
+
or
|
34
|
+
= link_to "Cancel", admin_group_url(@group)
|
35
|
+
|
36
|
+
%p
|
37
|
+
When you press the 'preview import' button, the list will be repeated back to you to show exactly what reader accounts we are going to create and/or invite into this group.
|
38
|
+
You can make changes at that stage, but please check the list formatting carefully: be sure there are no commas other than where you want them and that the email addresses are correct.
|
39
|
+
|
40
|
+
- else
|
41
|
+
%p.haserror
|
42
|
+
Before you can proceed, you will need to
|
43
|
+
= link_to("create an invitation message", new_admin_group_message_url(@group, :function => 'invitation')) + '.'
|
44
|
+
for this group.
|
45
|
+
|
@@ -0,0 +1,63 @@
|
|
1
|
+
- include_stylesheet('admin/group')
|
2
|
+
|
3
|
+
%h1
|
4
|
+
Populate:
|
5
|
+
= link_to @group.name, admin_group_url(@group)
|
6
|
+
= render :partial => 'admin/groups/actions', :locals => {:group => @group}
|
7
|
+
|
8
|
+
- form_for :group, @group, :url => admin_group_group_invitations_url(@group), :html => {:id => 'confirmation_form', :method => 'post'} do
|
9
|
+
.form-area
|
10
|
+
%h2 Please check the invitation list
|
11
|
+
%ul
|
12
|
+
%li Fields in red have validation problems. That usually means an email address or login needs checking. Hover your mouse pointer over the affected field to find out more.
|
13
|
+
%li Fields in grey show that we already have that person here (based on their email address). You can still bring them into the group but you can't edit their details here.
|
14
|
+
%li Edit anything that needs it, uncheck the box next to anyone that you don't want to invite after all, and hit the 'invite these people' button to send out messages.
|
15
|
+
%table#import
|
16
|
+
%thead
|
17
|
+
%tr
|
18
|
+
%th
|
19
|
+
- if Radiant::Config['reader.use_honorifics?']
|
20
|
+
%th Title or rank
|
21
|
+
%th Full name
|
22
|
+
%th Email
|
23
|
+
%th Login
|
24
|
+
%th Phone
|
25
|
+
%tbody
|
26
|
+
- i = 0
|
27
|
+
- @readers.each do |reader|
|
28
|
+
- if reader.new_record?
|
29
|
+
%tr.import
|
30
|
+
%td
|
31
|
+
= check_box_tag "import_reader[]", i, reader.valid?
|
32
|
+
- if Radiant::Config['reader.use_honorifics?']
|
33
|
+
%td
|
34
|
+
= text_field_tag "reader_#{i}[honorific]", reader.honorific, :class => "preview#{ ' with_error' if reader.errors.on(:honorific)}", :title => reader.errors.on(:honorific)
|
35
|
+
%td
|
36
|
+
= text_field_tag "reader_#{i}[name]", reader.name, :class => "preview#{ ' with_error' if reader.errors.on(:name)}", :title => reader.errors.on(:name)
|
37
|
+
%td
|
38
|
+
= text_field_tag "reader_#{i}[email]", reader.email, :class => "preview#{ ' with_error' if reader.errors.on(:email)}", :title => reader.errors.on(:email)
|
39
|
+
%td
|
40
|
+
= text_field_tag "reader_#{i}[login]", reader.login, :class => "preview#{ ' with_error' if reader.errors.on(:login)}", :title => reader.errors.on(:login)
|
41
|
+
%td
|
42
|
+
= text_field_tag "reader_#{i}[phone]", reader.phone, :class => "preview#{ ' with_error' if reader.errors.on(:phone)}", :title => reader.errors.on(:phone)
|
43
|
+
- else
|
44
|
+
%tr.invite
|
45
|
+
%td
|
46
|
+
= check_box_tag "invite_reader[]", reader.id, {:checked => true}
|
47
|
+
- if Radiant::Config['reader.use_honorifics?']
|
48
|
+
%td
|
49
|
+
= text_field_tag "reader_#{i}[honorific]", reader.honorific, :class => "preview#{ ' with_error' if reader.errors.on(:honorific)}", :title => reader.errors.on(:honorific), :disabled => true
|
50
|
+
%td
|
51
|
+
= text_field_tag "reader_#{i}[name]", reader.name, :class => "preview#{ ' with_error' if reader.errors.on(:name)}", :title => reader.errors.on(:name), :disabled => true
|
52
|
+
%td
|
53
|
+
= text_field_tag "reader_#{i}[email]", reader.email, :class => "preview#{ ' with_error' if reader.errors.on(:email)}", :title => reader.errors.on(:email), :disabled => true
|
54
|
+
%td
|
55
|
+
= text_field_tag "reader_#{i}[login]", reader.login, :class => "preview#{ ' with_error' if reader.errors.on(:login)}", :title => reader.errors.on(:login), :disabled => true
|
56
|
+
%td
|
57
|
+
= text_field_tag "reader_#{i}[phone]", reader.phone, :class => "preview#{ ' with_error' if reader.errors.on(:phone)}", :title => reader.errors.on(:phone)
|
58
|
+
- i = i + 1
|
59
|
+
|
60
|
+
%p.buttons
|
61
|
+
= submit_tag 'Invite these people into the group', :name => 'confirm', :class => 'button'
|
62
|
+
or
|
63
|
+
= link_to 'start again', new_admin_group_group_invitation_url(@group)
|
File without changes
|
@@ -0,0 +1,61 @@
|
|
1
|
+
- form_for [:admin, @group] do |f|
|
2
|
+
.form-area
|
3
|
+
= render_region :form_top
|
4
|
+
= hidden_field 'group', 'lock_version'
|
5
|
+
|
6
|
+
- render_region :form do |form|
|
7
|
+
- form.edit_group do
|
8
|
+
#group
|
9
|
+
%p.title
|
10
|
+
= f.label :name
|
11
|
+
= f.text_field 'name', :maxlength => 100, :class => "textbox"
|
12
|
+
%p.homepage
|
13
|
+
= f.label :homepage_id, "Group home page:"
|
14
|
+
= choose_page 'group', 'homepage_id', {:selected =>@group.homepage_id}
|
15
|
+
|
16
|
+
- form.edit_timestamp do
|
17
|
+
= updated_stamp @group
|
18
|
+
- form.edit_buttons do
|
19
|
+
%p.buttons
|
20
|
+
= save_model_button @group
|
21
|
+
= save_model_and_continue_editing_button @group
|
22
|
+
or
|
23
|
+
= link_to "Cancel", admin_groups_url
|
24
|
+
|
25
|
+
- form.edit_membership do
|
26
|
+
- unless @group.new_record?
|
27
|
+
#group_people.box.narrow
|
28
|
+
%h3
|
29
|
+
Group members
|
30
|
+
- readers = Reader.find(:all)
|
31
|
+
- total = readers.count
|
32
|
+
- column_length = (readers.count-1) / 2
|
33
|
+
- columns = [readers[0..column_length], readers[column_length+1..readers.count]]
|
34
|
+
- columns.each do |column|
|
35
|
+
%ul.column
|
36
|
+
- column.each do |reader|
|
37
|
+
%div{:id => "reader_holder_#{reader.id}"}
|
38
|
+
= render :partial => 'admin/memberships/reader', :object => reader
|
39
|
+
|
40
|
+
- form.edit_pages do
|
41
|
+
- unless @group.new_record?
|
42
|
+
#group_pages.box.narrow
|
43
|
+
%h3
|
44
|
+
Private pages
|
45
|
+
%ul
|
46
|
+
- page = Page.respond_to?(:homepage) ? Page.homepage : Page.find_by_parent_id(nil)
|
47
|
+
%div{:id => "page_holder_#{page.id}"}
|
48
|
+
= render :partial => 'admin/permissions/page', :object => page
|
49
|
+
|
50
|
+
:javascript
|
51
|
+
var h1 = $('group_pages').getHeight();
|
52
|
+
var h2 = $('group_people').getHeight();
|
53
|
+
var h = (h1 > h2) ? h1 : h2
|
54
|
+
$('group_people').setStyle({'height': h + 'px'});
|
55
|
+
$('group_pages').setStyle({'height': h + 'px'});
|
56
|
+
|
57
|
+
#footnotes
|
58
|
+
%p
|
59
|
+
The pages selected on the left are only visible to the people selected on the right.
|
60
|
+
|
61
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
%tr
|
2
|
+
- render_region :thead do |thead|
|
3
|
+
- thead.name_header do
|
4
|
+
%th.group Name
|
5
|
+
- thead.home_header do
|
6
|
+
%th.home Home page
|
7
|
+
- thead.members_header do
|
8
|
+
%th.members Members
|
9
|
+
- thead.pages_header do
|
10
|
+
%th.pages Pages
|
11
|
+
- thead.modify_header do
|
12
|
+
%th.modify Modify
|