radiant-reader-extension 3.0.0.rc4 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. data/README.md +105 -38
  2. data/Rakefile +4 -4
  3. data/app/controllers/accounts_controller.rb +24 -11
  4. data/app/controllers/admin/groups_controller.rb +15 -0
  5. data/app/controllers/admin/memberships_controller.rb +13 -7
  6. data/app/controllers/admin/messages_controller.rb +1 -0
  7. data/app/controllers/admin/permissions_controller.rb +15 -8
  8. data/app/controllers/groups_controller.rb +6 -4
  9. data/app/controllers/password_resets_controller.rb +1 -1
  10. data/app/controllers/reader_action_controller.rb +7 -51
  11. data/app/controllers/reader_sessions_controller.rb +9 -11
  12. data/app/helpers/reader_helper.rb +23 -3
  13. data/app/models/group.rb +45 -16
  14. data/app/models/message.rb +4 -11
  15. data/app/models/permission.rb +4 -0
  16. data/app/models/reader.rb +61 -26
  17. data/app/models/reader_notifier.rb +2 -2
  18. data/app/models/reader_page.rb +9 -5
  19. data/app/views/accounts/{_memberships.html.haml → _choose_memberships.html.haml} +0 -0
  20. data/app/views/accounts/_form.html.haml +9 -11
  21. data/app/views/accounts/_preamble.html.haml +14 -0
  22. data/app/views/accounts/_profile_form.html.haml +13 -29
  23. data/app/views/accounts/dashboard.html.haml +0 -1
  24. data/app/views/accounts/edit.html.haml +3 -3
  25. data/app/views/accounts/edit_profile.html.haml +10 -10
  26. data/app/views/admin/groups/_chooser.html.haml +8 -0
  27. data/app/views/admin/groups/_form.html.haml +7 -5
  28. data/app/views/admin/groups/_group.html.haml +29 -0
  29. data/app/views/admin/groups/index.html.haml +2 -28
  30. data/app/views/admin/groups/show.html.haml +3 -2
  31. data/app/views/admin/memberships/_reader.html.haml +2 -6
  32. data/app/views/admin/messages/_form.html.haml +10 -11
  33. data/app/views/admin/messages/_list_function.haml +2 -3
  34. data/app/views/admin/messages/{index.haml → index.html.haml} +21 -11
  35. data/app/views/admin/pages/_page_groups.html.haml +5 -11
  36. data/app/views/admin/permissions/_page.html.haml +6 -12
  37. data/app/views/admin/reader_configuration/edit.html.haml +2 -0
  38. data/app/views/admin/reader_configuration/show.html.haml +2 -0
  39. data/app/views/groups/_group.html.haml +12 -0
  40. data/app/views/groups/show.html.haml +10 -3
  41. data/app/views/readers/_description.html.haml +3 -0
  42. data/app/views/readers/_groups.html.haml +9 -0
  43. data/app/views/{accounts → readers}/_links.html.haml +2 -2
  44. data/app/views/readers/_list.html.haml +23 -0
  45. data/app/views/{accounts/_groups.html.haml → readers/_memberships.html.haml} +0 -1
  46. data/app/views/{accounts/_reader.html.haml → readers/_mugshot.html.haml} +1 -1
  47. data/app/views/readers/_people.html.haml +6 -0
  48. data/app/views/readers/_profile.html.haml +30 -0
  49. data/app/views/readers/_reader.html.haml +28 -0
  50. data/app/views/readers/index.html.haml +20 -0
  51. data/app/views/{accounts → readers}/show.html.haml +1 -2
  52. data/app/views/shared/_standard_reader_parts.html.haml +1 -1
  53. data/app/views/shared/not_allowed.html.haml +16 -0
  54. data/config/initializers/formats.rb +1 -2
  55. data/config/initializers/radiant_config.rb +5 -2
  56. data/config/locales/en.yml +181 -140
  57. data/config/routes.rb +25 -16
  58. data/db/migrate/001_create_readers.rb +0 -1
  59. data/db/migrate/20090921125653_reader_messages.rb +0 -1
  60. data/db/migrate/20090921125654_group_messages.rb +0 -1
  61. data/db/migrate/20110812111934_groups_nested_set.rb +19 -0
  62. data/db/migrate/20110814070858_message_has_many_groups.rb +14 -0
  63. data/db/migrate/20110905194602_group_ancestry.rb +23 -0
  64. data/lib/controller_extensions.rb +49 -0
  65. data/lib/grouped_model.rb +49 -8
  66. data/lib/grouped_page.rb +17 -5
  67. data/lib/message_tags.rb +21 -3
  68. data/lib/radiant-reader-extension.rb +1 -1
  69. data/lib/reader_admin_ui.rb +7 -8
  70. data/lib/reader_tags.rb +1 -1
  71. data/lib/site_controller_extensions.rb +7 -18
  72. data/public/images/furniture/csv.png +0 -0
  73. data/public/images/furniture/csv_tiny.png +0 -0
  74. data/public/images/furniture/vcard.png +0 -0
  75. data/public/images/furniture/vcard_tiny.png +0 -0
  76. data/public/javascripts/admin/reader.js +22 -1
  77. data/public/stylesheets/sass/admin/reader_group.sass +23 -22
  78. data/public/stylesheets/sass/reader.sass +81 -17
  79. data/radiant-reader-extension.gemspec +3 -1
  80. data/reader_extension.rb +7 -9
  81. data/spec/controllers/accounts_controller_spec.rb +8 -22
  82. data/spec/controllers/admin/messages_controller_spec.rb +0 -12
  83. data/spec/datasets/readers_dataset.rb +41 -38
  84. data/spec/lib/reader_tags_spec.rb +1 -1
  85. data/spec/models/group_spec.rb +89 -22
  86. data/spec/models/message_spec.rb +1 -1
  87. data/spec/models/reader_notifier_spec.rb +1 -1
  88. data/spec/models/reader_page_spec.rb +34 -18
  89. data/spec/models/reader_spec.rb +0 -1
  90. data/spec/spec.opts +4 -3
  91. metadata +51 -28
  92. data/app/views/accounts/_contributions.html.haml +0 -2
  93. data/app/views/accounts/_description.html.haml +0 -2
  94. data/app/views/accounts/_list.html.haml +0 -17
  95. data/app/views/accounts/_profile.html.haml +0 -29
  96. data/app/views/accounts/index.html.haml +0 -23
  97. data/app/views/groups/_all.html.haml +0 -10
  98. data/app/views/site/not_allowed.html.haml +0 -4
  99. data/db/migrate/20100922152338_lock_versions.rb +0 -9
  100. data/db/migrate/20101004074945_unlock_version.rb +0 -9
data/README.md CHANGED
@@ -2,82 +2,150 @@
2
2
 
3
3
  This is a framework that takes care of all the dull bits of registering, activating, reminding, logging in and editing preferences for your site visitors.
4
4
 
5
- It uses authlogic to handle sessions and provides complete interfaces both for the administrator and the visitor. The admin interface is very basic and fits in with radiant. The visitor interface is more friendly (and incidentally includes a trick email field - so-called inverse captcha - that should prevent spam signups).
5
+ It uses authlogic to handle sessions and provides complete interfaces both for the administrator and the visitor. The admin interface is simple and fits in with radiant. The visitor interface is more friendly (and incidentally includes a trick email field - so-called inverse captcha - that should prevent spam signups).
6
6
 
7
7
  The visitors are referred to as 'readers' here. Readers never see the admin interface, but your site authors and admins are automatically given reader status.
8
8
 
9
- The purpose of this extension is to provide a common core that supports other visitor-facing machinery. See for example our [forum extension](http://github.com/spanner/radiant-forum-extension) for discussions and page/blog comments and [downloads extension](http://github.com/spanner/radiant-downloads-extension) for secure access-controlled file downloads. More will follow and I hope other people will make use of this framework.
9
+ ## Status
10
10
 
11
- ## Latest
11
+ Compatible with radiant 1, which isn't out yet. You can use radiant edge to try this out. Expect a few point releases as radiant 1 is finalised.
12
12
 
13
- This version requires edge radiant, or radiant 1 when it becomes available. We are using a lot of the new configuration and sheets code.
13
+ Multi-site compatibility is currently missing but will follow as soon as I can add a better scoping engine to radiant core.
14
14
 
15
- New ReaderPages provide flexible directory services with configurable access control. The old controller and page parts mechanism is going to be phased out gradually both here and in the forum in favour of more orthodox radiant page-types. We will always need to use the layout-wrapper approach for login and registration forms, though.
15
+ ## Note on internationalisation and customisation
16
16
 
17
- Right now we are **not compatible with multi_site or the sites extension**: that's mostly because neither is radiant edge: it will all be sorted out in time for the release of v1, which isn't far away.
17
+ The locale strings here are generally defined in a functional rather than grammatical way. That is, they have labels like `activation_required_explanation` rather than being assembled out of lexical units. This is partly because for flexibility of translation, but also because it gives you an easy way to change the text on functional pages like reader-preferences and registration forms.
18
18
 
19
- Also:
19
+ ## Requirements
20
20
 
21
- * public interface internationalized;
22
- * Uses the new configuration interface;
23
- * Messaging much simplified and now intended to be purely administrative.
24
- * ajaxable status panel returned by `reader_session_url` (ie. you just have to call /reader_session.js over xmlhttp to get a sensible welcome and control block)
21
+ Versions 3.x of reader are designed to work with radiant 1 and will not work with older versions. There's a '0.9.1' tag in the repository for the last release that will.
25
22
 
26
- ## Status
23
+ Since you now have to install reader as a gem, all of its gem-based dependencies will be taken care of for you, but you may need some system libraries. We use Sanitize to whitelist html input. Sanitize uses Nogogiri to parse html, and Nokogiri needs `libxml2` and `libxslt` to do that. If you've installed imagemagick to work with radiant assets, it's very likely that you have those libraries already. If not, you will need to install them before you can install the reader gem.
27
24
 
28
- Compatible with radiant 1, which isn't out yet. You can use radiant edge to try this out. Expect small changes in support of the new forum and group releases. Multi-site compatibility will follow soon.
25
+ ## Installation
29
26
 
30
- ## Note on internationalisation and customisation
27
+ Install the gem:
31
28
 
32
- The locale strings here are generally defined in a functional rather than grammatical way. That is, they have labels like `activation_required_explanation` rather than being assembled out of lexical units. This is partly because for flexibility of translation, but also because it gives you an easy way to change the text on functional pages like reader-preferences and registration forms.
29
+ sudo gem install radiant-reader-extension
33
30
 
34
- ## Requirements
31
+ add it to your application's Gemfile:
35
32
 
36
- Radiant 0.9.2 (or currently, edge). The [layouts](http://github.com/squaretalent/radiant-layouts-extension) and [mailer_layouts](http://github.com/spanner/radiant-mailer_layouts-extension) extensions.
33
+ gem 'authlogic', "~> 2.1.6"
34
+ gem radiant-reader-extension, '~>2.0.0'
37
35
 
38
- You also need three gems (in addition to those that radiant requires): authlogic, gravtastic and sanitize. They're declared in the extension so you should be able just to run
36
+ and then you can bring over assets and create data tables:
39
37
 
40
- sudo rake gems:install
38
+ rake radiant:extensions:update_all
39
+ rake radiant:extensions:reader:migrate
40
+ rake radiant:extensions:reader:update
41
41
 
42
- Sanitize uses nokogiri, which needs libxml2 and libxslt: you may need to go off and install those first. You will also need to put
42
+ ## Configuration
43
43
 
44
- gem 'authlogic'
44
+ All the main configuration settings can now be managed through the `settings > readers` configuration pane. THey have sensible defaults but you will need to choose a layout for reader-administration views and supply the name and email address that messages should appear to come from.
45
45
 
46
- in your environment.rb before you can migrate anything. Authlogic has to load before _anything_ else requires `ApplicationController`.
46
+ ## Usage
47
47
 
48
- ## Installation
48
+ This is primarily a framework and its main purpose is to take care of the tedious minutiae of account-management. The basic reader framework provides for:
49
49
 
50
- As a gem:
50
+ * registration
51
+ * honeypot spam trap
52
+ * activation by email confirmation
53
+ * logging in and out
54
+ * password reminders
55
+ * edit account preferences
56
+ * edit profile
57
+ * dashboard view on login
58
+ * configurable members directory with csv and vcard export
59
+ * administrative email messages for welcome, invitation, etc
60
+ * ad-hoc email messages to some or all readers
51
61
 
52
- gem install 'radiant_reader_extension'
53
-
54
- or for more control:
62
+ The extension also includes a group-based access control mechanism. You can organise your readers into groups (either by invitation or by public subscription) and any resource (eg a page) associated with one or more groups is visible only to their members. Anyone else attempting to access the page will be prompted to log in (or register, if registration is permitted).
55
63
 
56
- git submodule add git://github.com/spanner/radiant-reader-extension.git vendor/extensions/reader
64
+ You can use the group mechanism in a simple way just to create self-selected interest groups, or you can disable public registration and use the full group-hierarchical functionality to provide a very secure system of controlled access to selected resources.
57
65
 
58
- and then:
66
+ The group-scoping mechanism is easily extended to other classes:
59
67
 
60
- rake radiant:extensions:reader:migrate
61
- rake radiant:extensions:reader:update
68
+ class Widget < ActiveRecord::Base
69
+ has_groups
70
+ ...
71
+ end
62
72
 
63
- ## Configuration
73
+ So the forum extension, for example, includes the ability to make a forum visible only to members of selected groups.
64
74
 
65
- All the main configuration settings can now be managed through the 'readers' configuration pane.
75
+ For more reader-facing usefulness please see our [forum extension](http://github.com/spanner/radiant-forum-extension) for discussions and page/blog comments and [downloads extension](http://github.com/spanner/radiant-downloads-extension) for secure access-controlled file downloads. We also have extensions for public submission of calendar events and assets, which will emerge here soon.
66
76
 
67
77
  ## Layouts
68
78
 
69
- We use the share_layouts extension to wrap the layout of your public site around the pages produced by the reader extension. You can designate any layout as the 'reader layout': in a single-site installation put the name of the layout in a `reader.layout` config entry. In a multi-site installation you'll find a 'reader layout' dropdown on the 'edit site' page. Choose the one you want to use for each site.
79
+ We use the `layouts` extension to wrap the appearance of your public site around the views produced by the reader extension. You can configure this with the dropdown list on the reader configuration page.
80
+
81
+ The laying-out is achieved by defining lots of fake page parts in the reader views. All your layout has to do is include those page parts with the usual `<r:content />` calls.
82
+
83
+ * `<r:content />` on its own will hold the main page content: login form, activation form, dashboard or whatever.
84
+ * `<r:title />` is always the page title
85
+ * `<r:content part="introduction" />` will provide a separate opening paragraph for layouts that require it
86
+ * `<r:content part="sidebar" />` will (sometimes) provide relevant marginal content
87
+ * `<r:content part="breadhead" />` is a more minimal breadcrumb trail that omits the present page and is suitable for use above the page title
88
+ * `<r:content part="controls" />` will show a standard 'hello [name]' block with login and logout and so on, but only on an uncached page. If the page is cached it will render an empty div with class 'remote_controls' that you can populate with javascipt. An ajax call to `/reader_session/show` will return the same controls block.
89
+ * `<r:content part="signals" />` will render any confirmation or error flashes. Not suitable for cached pages.
90
+ * `<r:content part="person" />` will render a gravatar and link for the current reader
91
+
92
+ You don't really need anything but the main title and content tags:
70
93
 
71
- The layout of the layout is up to you: from our point of view all it has to do is call `<r:content />` at some point. Ideally it will call `<r:content part="pagetitle" />` too. There is also a `breadcrumbs` part if that's required. In many cases you can just use your existing site layout and the various forms and pages will drop into its usual compartments.
94
+ <h1><r:title /></h1>
95
+ <r:content />
96
+
97
+ will do just fine to start with.
98
+
99
+ ## CSS and Javascript
100
+
101
+ Some standard formatting and interaction is included for you to build upon.
102
+
103
+ `/javascripts/reader.js` includes some rather basic jquery code to handle retrieving remote content (such as the control block mentioned above), fading out flashes and errors and adding a bit of responsiveness to the reader-facing forms.
104
+
105
+ `/stylesheets/sass/reader.sass` includes the default formatting of reader-facing forms and lists. It could probably stand to be reorganised a bit but you should find it a useful starting point and I recommend that you override it selectively rather than replacing it completely. There are two ways to do that:
106
+
107
+ * @import 'reader.sass' at the top of your (SASS-based) stylesheet within radiant
108
+ * link to /stylesheets/reader.css in the old-fashioned way and then bring in your own stylesheets any way you like.
109
+
110
+ Or you can replace all this with your own, of course.
111
+
112
+ ## ReaderPages
113
+
114
+ You can also provide a more customised directory service by creating a ReaderPage and populating it with the many `r:readers` and `r:reader` tags that are defined here.
115
+
116
+ ## Directory Visibility
117
+
118
+ There are four levels of directory visibility, and the behaviour of your site is set by the `reader.directory_visibility` configuration entry:
119
+
120
+ * *none* is the default. No reader details are shown to anybody.
121
+ * *public* means that anyone can see the directory. Individual readers can still opt out, but this is intended for public directory services with the expectation that people want to be shown.
122
+ * *private* means that only logged in readers can see the directory. Useful for closed groups and works well as an internal directory for an organisation or team.
123
+ * *grouped* means that only logged in people can see the directory and that they can only see the people with whom they share a group. Useful for more complex authorization requirements but also for sites that have a privileged core group and unprivileged guests.
72
124
 
73
125
  ## Using readers in other extensions
74
126
 
75
- The reader admin pages are properly registered with the AdminUI as collections of parts, so you can override them in the same way as the other admin pages.
127
+ All the reader pages (both public and administrative) are sharded in the AdminUI. The public-facing administration and directory pages are all in `admin.accounts`, since the admin-facing views are already in `admin.readers`.
76
128
 
77
129
  Most of your reader-facing controllers will want to inherit from `ReaderActionController`.
78
130
 
79
131
  Marking a reader as untrusted does nothing here apart from making them go red, but we assume that in other extensions it will have some limiting effect.
80
132
 
133
+ ## Latest changes
134
+
135
+ This version requires edge radiant, or radiant 1 when it becomes available.
136
+
137
+ New ReaderPages provide flexible directory services with configurable access control. The old controller and page parts mechanism is likely to be phased out gradually both here and in the forum in favour of more orthodox radiant page-types. We will always need to use the layout-wrapper approach for login and registration forms, though.
138
+
139
+ Right now we are **not compatible with multi_site or the sites extension**: that's mostly because neither is radiant edge: it will all be sorted out in time for the release of v1, which isn't far away.
140
+
141
+ Also:
142
+
143
+ * groups hierarchical
144
+ * public interface internationalized;
145
+ * Uses the new configuration interface;
146
+ * Messaging much simplified and now intended to be purely administrative.
147
+ * ajaxable status panel returned by `reader_session_url` (ie. you just have to call /reader_session.js over xmlhttp to get a sensible welcome and control block)
148
+
81
149
  ## See also
82
150
 
83
151
  * [reader_group](http://github.com/spanner/radiant-reader_group-extension)
@@ -91,7 +159,6 @@ Marking a reader as untrusted does nothing here apart from making them go red, b
91
159
 
92
160
  ## Author and copyright
93
161
 
94
- * Copyright spanner ltd 2007-9.
162
+ * Copyright spanner ltd 2007-11.
95
163
  * Released under the same terms as Rails and/or Radiant.
96
164
  * Contact will at spanner.org
97
-
data/Rakefile CHANGED
@@ -16,11 +16,11 @@ unless defined? RADIANT_ROOT
16
16
  end
17
17
 
18
18
  require 'rake'
19
- require 'rake/rdoctask'
19
+ require 'rdoc/task'
20
20
  require 'rake/testtask'
21
21
 
22
- rspec_base = File.expand_path(RADIANT_ROOT + '/vendor/plugins/rspec/lib')
23
- $LOAD_PATH.unshift(rspec_base) if File.exist?(rspec_base)
22
+ # rspec_base = File.expand_path(RADIANT_ROOT + '/vendor/plugins/rspec/lib')
23
+ # $LOAD_PATH.unshift(rspec_base) if File.exist?(rspec_base)
24
24
  require 'spec/rake/spectask'
25
25
  require 'cucumber'
26
26
  require 'cucumber/rake/task'
@@ -101,7 +101,7 @@ namespace :spec do
101
101
  end
102
102
 
103
103
  desc 'Generate documentation for the reader extension.'
104
- Rake::RDocTask.new(:rdoc) do |rdoc|
104
+ RDoc::Task.new(:rdoc) do |rdoc|
105
105
  rdoc.rdoc_dir = 'rdoc'
106
106
  rdoc.title = 'ReaderExtension'
107
107
  rdoc.options << '--line-numbers' << '--inline-source'
@@ -2,17 +2,20 @@ class AccountsController < ReaderActionController
2
2
  helper :reader
3
3
 
4
4
  before_filter :check_registration_allowed, :only => [:new, :create, :activate]
5
+ before_filter :no_removing, :only => [:remove, :destroy]
5
6
  before_filter :i_am_me, :only => [:show, :edit, :edit_profile]
6
- before_filter :require_reader, :except => [:new, :create, :activate]
7
7
  before_filter :default_to_self, :only => [:show]
8
8
  before_filter :restrict_to_self, :only => [:edit, :edit_profile, :update, :resend_activation]
9
- before_filter :no_removing, :only => [:remove, :destroy]
9
+ before_filter :get_readers_and_groups, :only => [:index, :show]
10
+ before_filter :require_reader, :except => [:new, :create, :activate]
11
+ before_filter :require_reader_visibility
10
12
  before_filter :ensure_groups_subscribable, :only => [:update, :create]
11
13
 
12
14
  def index
13
- @readers = Reader.visible_to(current_reader)
14
15
  respond_to do |format|
15
- format.html {}
16
+ format.html {
17
+ render :template => 'readers/index'
18
+ }
16
19
  format.csv {
17
20
  send_data generate_csv(@readers), :type => 'text/csv; charset=utf-8; header=present', :filename => "everyone.csv"
18
21
  }
@@ -23,9 +26,10 @@ class AccountsController < ReaderActionController
23
26
  end
24
27
 
25
28
  def show
26
- @reader = Reader.find(params[:id])
27
29
  respond_to do |format|
28
- format.html
30
+ format.html {
31
+ render :template => 'readers/show'
32
+ }
29
33
  format.vcard {
30
34
  send_data @reader.vcard.to_s, :filename => "#{@reader.filename}.vcf"
31
35
  }
@@ -88,7 +92,7 @@ class AccountsController < ReaderActionController
88
92
  @reader.clear_password = params[:reader][:password] if params[:reader][:password]
89
93
  if @reader.save
90
94
  flash[:notice] = t('reader_extension.account_updated')
91
- redirect_to dashboard_url
95
+ redirect_to reader_dashboard_url
92
96
  else
93
97
  render :action => 'edit'
94
98
  end
@@ -105,7 +109,7 @@ protected
105
109
  end
106
110
 
107
111
  def restrict_to_self
108
- flash[:error] = t("reader_extension.cannot_edit_others") if params[:id] && params[:id] != current_reader.id
112
+ flash[:error] = t("reader_extension.cannot_edit_others") if params[:id] && params[:id] != current_reader.id.to_s
109
113
  @reader = current_reader
110
114
  end
111
115
 
@@ -136,15 +140,24 @@ protected
136
140
 
137
141
  private
138
142
 
143
+ def get_readers_and_groups
144
+ @readers = Reader.visible_to(current_reader)
145
+ @groups = Group.roots.visible_to(current_reader)
146
+ @reader = Reader.find(params[:id]) if params[:id]
147
+ end
148
+
149
+ def require_reader_visibility
150
+ # more useful than a 404 but perhaps too informative?
151
+ raise ReaderError::AccessDenied, "You do not have permission to see that person." if @reader && !@reader.visible_to?(current_reader)
152
+ end
153
+
139
154
  def ensure_groups_subscribable
140
155
  if params[:reader] && params[:reader][:group_ids]
141
156
  params[:reader][:group_ids].each do |g|
142
- raise ActiveRecord::RecordNotFound unless Group.find(g).public?
157
+ raise ReaderError::AccessDenied, "One of those groups is not public and does not accept subscriptions." unless Group.find(g).public?
143
158
  end
144
159
  end
145
160
  true
146
- rescue ActiveRecord::RecordNotFound
147
- false
148
161
  end
149
162
 
150
163
  end
@@ -1,8 +1,23 @@
1
1
  class Admin::GroupsController < Admin::ResourceController
2
+ helper :reader
3
+ paginate_models
2
4
  skip_before_filter :load_model
3
5
  before_filter :load_model, :except => :index # we want the filter to run before :show too
4
6
 
5
7
  def show
6
8
 
7
9
  end
10
+
11
+ def load_models
12
+ self.models = paginated? ? model_class.roots.paginate(pagination_parameters) : model_class.roots.all
13
+ end
14
+
15
+ def load_model
16
+ self.model = if params[:id]
17
+ model_class.find(params[:id])
18
+ else
19
+ model_class.new(:parent_id => params[:parent_id])
20
+ end
21
+ end
22
+
8
23
  end
@@ -1,14 +1,12 @@
1
1
  class Admin::MembershipsController < ApplicationController
2
2
 
3
- before_filter :find_group
3
+ before_filter :find_reader_and_group
4
4
 
5
5
  def index
6
6
  redirect_to admin_group_url(@group)
7
7
  end
8
8
 
9
9
  def create
10
- @reader = Reader.find(params[:reader_id])
11
- raise ActiveRecord::RecordNotFound unless @reader
12
10
  @membership = Membership.find_or_create_by_reader_id_and_group_id(@reader.id, @group.id)
13
11
  respond_to do |format|
14
12
  format.html {
@@ -20,7 +18,7 @@ class Admin::MembershipsController < ApplicationController
20
18
  end
21
19
 
22
20
  def destroy
23
- @membership = @group.memberships.find(params[:id])
21
+ @membership ||= @group.memberships.find(params[:id])
24
22
  @reader = @membership.reader
25
23
  @membership.delete if @membership
26
24
  respond_to do |format|
@@ -32,11 +30,19 @@ class Admin::MembershipsController < ApplicationController
32
30
  end
33
31
  end
34
32
 
33
+ def toggle
34
+ if @membership = Membership.find_by_reader_id_and_group_id(@reader.id, @group.id)
35
+ destroy
36
+ else
37
+ create
38
+ end
39
+ end
40
+
35
41
  protected
36
42
 
37
- def find_group
43
+ def find_reader_and_group
38
44
  @group = Group.find(params[:group_id])
39
- raise ActiveRecord::RecordNotFound unless @group
45
+ @reader = Reader.find(params[:reader_id]) if params[:reader_id]
40
46
  end
41
-
47
+
42
48
  end
@@ -6,6 +6,7 @@ class Admin::MessagesController < Admin::ResourceController
6
6
  before_filter :get_group, :only => :new
7
7
 
8
8
  # here :show is the preview/send page
9
+ # continue_url is extended below to redirect to show rather than index after editing.
9
10
  def show
10
11
 
11
12
  end
@@ -1,6 +1,6 @@
1
1
  class Admin::PermissionsController < ApplicationController
2
2
 
3
- before_filter :find_group
3
+ before_filter :find_page_and_group
4
4
 
5
5
  def index
6
6
  redirect_to admin_group_url(@group)
@@ -8,12 +8,11 @@ class Admin::PermissionsController < ApplicationController
8
8
 
9
9
  def create
10
10
  @page = Page.find(params[:page_id])
11
- raise ActiveRecord::RecordNotFound unless @page
12
11
  scope = @group.permissions.for(@page)
13
12
  @permission = scope.first || scope.create!
14
13
  respond_to do |format|
15
14
  format.html {
16
- flash[:notice] = "#{@page.name} bound to group #{@group.name}"
15
+ flash[:notice] = "#{@page.title} bound to group #{@group.name}"
17
16
  redirect_to admin_group_url(@group)
18
17
  }
19
18
  format.js { render :partial => 'page' }
@@ -21,23 +20,31 @@ class Admin::PermissionsController < ApplicationController
21
20
  end
22
21
 
23
22
  def destroy
24
- @permission = @group.permissions.find(params[:id])
23
+ @permission ||= @group.permissions.find(params[:id])
25
24
  @page = @permission.permitted
26
25
  @permission.delete if @permission
27
26
  respond_to do |format|
28
27
  format.html {
29
- flash[:notice] = "#{@page.name} released from group #{@group.name}"
28
+ flash[:notice] = "#{@page.title} released from group #{@group.name}"
30
29
  redirect_to admin_group_url(@group)
31
30
  }
32
31
  format.js { render :partial => 'page' }
33
32
  end
34
33
  end
35
34
 
35
+ def toggle
36
+ if @permission = @group.permission_for(@page)
37
+ destroy
38
+ else
39
+ create
40
+ end
41
+ end
42
+
36
43
  protected
37
44
 
38
- def find_group
45
+ def find_page_and_group
39
46
  @group = Group.find(params[:group_id])
40
- raise ActiveRecord::RecordNotFound unless @group
47
+ @page = Page.find(params[:page_id]) if params[:page_id]
41
48
  end
42
-
49
+
43
50
  end
@@ -16,7 +16,7 @@ class GroupsController < ReaderActionController
16
16
  send_data generate_csv(@readers), :type => 'text/csv; charset=utf-8; header=present', :filename => "#{@group.filename}.csv"
17
17
  }
18
18
  format.vcard {
19
- send_data @readers.map(&:vcard).join("\n"), :filename => "#{@group.filename}.vcf"
19
+ send_data generate_vcard(@readers), :filename => "#{@group.filename}.vcf"
20
20
  }
21
21
  end
22
22
  end
@@ -24,12 +24,14 @@ class GroupsController < ReaderActionController
24
24
  private
25
25
 
26
26
  def get_group_or_groups
27
- @groups = Group.visible_to(current_reader)
28
- @group = @groups.find(params[:id]).first if params[:id]
27
+ @groups = Group.roots.visible_to(current_reader)
28
+ @group = Group.find(params[:id]) if params[:id]
29
29
  end
30
30
 
31
31
  def require_group_visibility
32
- raise ReaderError::AccessDenied if @group && !@group.visible_to?(current_reader)
32
+ if @group && !@group.visible_to?(current_reader) # nb. @groups is a smaller set
33
+ raise ReaderError::AccessDenied, "That group is not public and you are not in it."
34
+ end
33
35
  end
34
36
 
35
37
  end