saucy 0.15.2 → 0.16.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/CHANGELOG.md +3 -7
- data/Gemfile +1 -10
- data/Gemfile.lock +42 -3
- data/README.md +5 -0
- data/Rakefile +12 -3
- data/app/controllers/accounts_controller.rb +2 -1
- data/app/mailers/billing_mailer.rb +8 -0
- data/app/models/canceled_account.rb +12 -7
- data/app/views/accounts/_projects.html.erb +1 -1
- data/app/views/billing_mailer/cancelation_notification.text.erb +3 -0
- data/app/views/invitation_mailer/invitation.text.erb +1 -1
- data/app/views/invitations/show.html.erb +1 -1
- data/app/views/memberships/edit.html.erb +2 -2
- data/app/views/memberships/index.html.erb +1 -1
- data/app/views/projects/_form.html.erb +5 -5
- data/app/views/projects/edit.html.erb +2 -2
- data/app/views/projects/index.html.erb +3 -3
- data/app/views/projects/new.html.erb +1 -1
- data/app/views/shared/_project_dropdown.html.erb +3 -3
- data/config/routes.rb +2 -6
- data/cucumber.yml +1 -0
- data/{lib/generators/saucy/features/templates/features → features/engine}/admin_reporting.feature +0 -0
- data/{lib/generators/saucy/features/templates/features → features/engine}/edit_profile.feature +0 -0
- data/{lib/generators/saucy/features/templates/features → features/engine}/edit_project_permissions.feature +0 -0
- data/{lib/generators/saucy/features/templates/features → features/engine}/edit_user_permissions.feature +0 -0
- data/{lib/generators/saucy/features/templates/features → features/engine}/manage_account.feature +0 -0
- data/{lib/generators/saucy/features/templates/features → features/engine}/manage_billing.feature +1 -1
- data/{lib/generators/saucy/features/templates/features → features/engine}/manage_plan.feature +0 -0
- data/{lib/generators/saucy/features/templates/features → features/engine}/manage_projects.feature +0 -0
- data/{lib/generators/saucy/features/templates/features → features/engine}/manage_users.feature +0 -0
- data/{lib/generators/saucy/features/templates/features → features/engine}/new_account.feature +0 -0
- data/{lib/generators/saucy/features/templates/features → features/engine}/project_dropdown.feature +0 -0
- data/{lib/generators/saucy/features/templates/features → features/engine}/sign_up.feature +0 -0
- data/{lib/generators/saucy/features/templates/features → features/engine}/sign_up_coupon.feature +0 -0
- data/{lib/generators/saucy/features/templates/features → features/engine}/sign_up_paid.feature +0 -0
- data/{lib/generators/saucy/features/templates/features → features/engine}/trial_plans.feature +0 -0
- data/features/integration/run_features.feature +46 -0
- data/features/step_definitions/clearance_steps.rb +123 -0
- data/{lib/generators/saucy/features/templates/step_definitions → features/step_definitions/engine}/account_steps.rb +0 -0
- data/features/step_definitions/engine/braintree_steps.rb +24 -0
- data/{lib/generators/saucy/features/templates/step_definitions → features/step_definitions/engine}/country_steps.rb +0 -0
- data/{lib/generators/saucy/features/templates/step_definitions → features/step_definitions/engine}/cron_steps.rb +3 -5
- data/{lib/generators/saucy/features/templates/step_definitions → features/step_definitions/engine}/email_steps.rb +0 -0
- data/{lib/generators/saucy/features/templates/step_definitions → features/step_definitions/engine}/factory_girl_steps.rb +0 -0
- data/{lib/generators/saucy/features/templates/step_definitions → features/step_definitions/engine}/html_steps.rb +0 -0
- data/{lib/generators/saucy/features/templates/step_definitions → features/step_definitions/engine}/plan_steps.rb +0 -0
- data/{lib/generators/saucy/features/templates/step_definitions → features/step_definitions/engine}/project_steps.rb +0 -0
- data/{lib/generators/saucy/features/templates/step_definitions → features/step_definitions/engine}/session_steps.rb +0 -0
- data/{lib/generators/saucy/features/templates/step_definitions → features/step_definitions/engine}/user_steps.rb +0 -0
- data/features/step_definitions/rails_steps.rb +26 -8
- data/features/step_definitions/saucy_steps.rb +5 -0
- data/features/step_definitions/web_steps.rb +253 -0
- data/features/support/engine/braintree.rb +5 -0
- data/features/support/env.rb +18 -0
- data/features/support/factory_girl.rb +2 -0
- data/features/support/paths.rb +68 -0
- data/features/support/rails.rb +4 -0
- data/features/support/selectors.rb +11 -0
- data/lib/generators/saucy/features/features_generator.rb +7 -3
- data/lib/saucy/account.rb +55 -57
- data/lib/saucy/account_authorization.rb +39 -42
- data/lib/saucy/plan.rb +14 -16
- data/lib/saucy/project.rb +42 -44
- data/lib/saucy/projects_controller.rb +53 -55
- data/lib/saucy/subscription.rb +189 -191
- data/lib/saucy/user.rb +5 -7
- data/lib/saucy/version.rb +1 -1
- data/saucy.gemspec +21 -5
- data/spec/controllers/accounts_controller_spec.rb +2 -1
- data/spec/controllers/projects_controller_spec.rb +1 -1
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +9 -0
- data/spec/dummy/app/assets/stylesheets/application.css +7 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/controllers/homes_controller.rb +2 -0
- data/spec/dummy/app/controllers/projects_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.gitkeep +0 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/models/account.rb +3 -0
- data/spec/dummy/app/models/plan.rb +3 -0
- data/spec/dummy/app/models/project.rb +3 -0
- data/spec/dummy/app/models/user.rb +4 -0
- data/spec/dummy/app/views/homes/show.html.erb +1 -0
- data/spec/dummy/app/views/layouts/application.html.erb +24 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +48 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +30 -0
- data/spec/dummy/config/environments/production.rb +60 -0
- data/spec/dummy/config/environments/test.rb +42 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/clearance.rb +3 -0
- data/spec/dummy/config/initializers/inflections.rb +10 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +59 -0
- data/spec/dummy/db/migrate/20120201000751_create_diesel_clearance_users.rb +19 -0
- data/spec/dummy/db/migrate/20120201001546_create_saucy_tables.rb +122 -0
- data/spec/dummy/db/migrate/20120201001548_create_canceled_accounts.rb +14 -0
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +26 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/mailers/billing_mailer_spec.rb +12 -2
- data/spec/models/canceled_account_spec.rb +8 -6
- data/spec/models/subscription_spec.rb +47 -53
- data/spec/spec_helper.rb +8 -11
- data/spec/support/braintree.rb +2 -4
- data/spec/support/factories.rb +1 -0
- metadata +447 -125
- data/db/migrate/20120206155222_add_subscription_token_to_canceled_accounts.rb +0 -5
- data/features/run_features.feature +0 -84
- data/lib/generators/saucy/features/templates/step_definitions/braintree_steps.rb +0 -25
- data/lib/generators/saucy/features/templates/support/braintree.rb +0 -5
- data/lib/generators/saucy/specs/specs_generator.rb +0 -20
- data/lib/generators/saucy/specs/templates/support/braintree.rb +0 -5
- data/lib/saucy/fake_braintree.rb +0 -134
- data/lib/saucy/routing_extensions.rb +0 -116
- data/spec/dummy/db/migrate/20120206155222_add_subscription_token_to_canceled_accounts.rb +0 -5
- data/spec/environment.rb +0 -98
- data/spec/route_extensions_spec.rb +0 -51
- data/spec/scaffold/config/database.yml +0 -6
- data/spec/scaffold/config/routes.rb +0 -5
- data/spec/scaffold/views/layouts/application.html.erb +0 -1
data/features/support/env.rb
CHANGED
@@ -1,7 +1,25 @@
|
|
1
|
+
ENV["RAILS_ROOT"] = File.expand_path("../../../spec/dummy", __FILE__)
|
2
|
+
$LOAD_PATH << ENV["RAILS_ROOT"]
|
3
|
+
|
4
|
+
require 'rails/all'
|
5
|
+
require 'formtastic'
|
6
|
+
require 'jquery-rails'
|
7
|
+
require 'timecop'
|
8
|
+
require 'factory_girl'
|
9
|
+
require 'capybara'
|
1
10
|
require 'aruba/cucumber'
|
11
|
+
require 'cucumber/rails'
|
2
12
|
|
3
13
|
Before do
|
4
14
|
@aruba_timeout_seconds = 140
|
5
15
|
end
|
6
16
|
|
7
17
|
PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), "..", ".."))
|
18
|
+
|
19
|
+
Capybara.save_and_open_page_path = 'tmp'
|
20
|
+
Capybara.javascript_driver = :selenium
|
21
|
+
Capybara.default_selector = :css
|
22
|
+
|
23
|
+
ActionController::Base.allow_rescue = false
|
24
|
+
|
25
|
+
DatabaseCleaner.strategy = :truncation
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module NavigationHelpers
|
2
|
+
# Maps a name to a path. Used by the
|
3
|
+
#
|
4
|
+
# When /^I go to (.+)$/ do |page_name|
|
5
|
+
#
|
6
|
+
# step definition in web_steps.rb
|
7
|
+
#
|
8
|
+
def path_to(page_name)
|
9
|
+
case page_name
|
10
|
+
when 'the admin page'
|
11
|
+
admin_path
|
12
|
+
when 'the list of accounts'
|
13
|
+
accounts_path
|
14
|
+
when 'the list of plans page'
|
15
|
+
plans_path
|
16
|
+
when /^the memberships page for the "([^"]+)" account$/
|
17
|
+
account = Account.find_by_name!($1)
|
18
|
+
account_memberships_path(account)
|
19
|
+
when /^the projects page for the "([^"]+)" account$/
|
20
|
+
account = Account.find_by_name!($1)
|
21
|
+
account_projects_path(account)
|
22
|
+
when /settings page for the "([^"]+)" account$/i
|
23
|
+
account = Account.find_by_name!($1)
|
24
|
+
edit_account_path(account)
|
25
|
+
when /settings page$/
|
26
|
+
edit_profile_path
|
27
|
+
when /dashboard page$/
|
28
|
+
accounts_path
|
29
|
+
when /sign up page for the "([^"]+)" plan$/i
|
30
|
+
plan = Plan.find_by_name!($1)
|
31
|
+
new_plan_account_path(plan)
|
32
|
+
when /^the billing page for the "([^"]+)" account$/
|
33
|
+
account = Account.find_by_name!($1)
|
34
|
+
account_billing_path(account)
|
35
|
+
when /^the upgrade plan page for the "([^"]+)" account$/
|
36
|
+
account = Account.find_by_name!($1)
|
37
|
+
edit_account_plan_path(account)
|
38
|
+
when /^the new project page for the newest account by "([^"]*)"$/
|
39
|
+
user = User.find_by_email!($1)
|
40
|
+
account = user.accounts.order("id desc").first
|
41
|
+
new_account_project_path(account)
|
42
|
+
when /^the "([^"]*)" project page$/
|
43
|
+
project = Project.find_by_name!($1)
|
44
|
+
account_project_path(project.account, project)
|
45
|
+
when /^the new project page for the "([^"]+)" account$/
|
46
|
+
account = Account.find_by_name!($1)
|
47
|
+
new_account_project_path(account)
|
48
|
+
|
49
|
+
|
50
|
+
# Here is an example that pulls values out of the Regexp:
|
51
|
+
#
|
52
|
+
# when /^(.*)'s profile page$/i
|
53
|
+
# user_profile_path(User.find_by_login($1))
|
54
|
+
|
55
|
+
else
|
56
|
+
begin
|
57
|
+
page_name =~ /^the (.*) page$/
|
58
|
+
path_components = $1.split(/\s+/)
|
59
|
+
self.send(path_components.push('path').join('_').to_sym)
|
60
|
+
rescue NoMethodError, ArgumentError
|
61
|
+
raise "Can't find mapping from \"#{page_name}\" to a path.\n" +
|
62
|
+
"Now, go and add a mapping in #{__FILE__}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
World(NavigationHelpers)
|
@@ -10,9 +10,9 @@ Description:
|
|
10
10
|
DESC
|
11
11
|
|
12
12
|
def copy_feature_files
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
engine_directory "features"
|
14
|
+
engine_directory "features/step_definitions"
|
15
|
+
engine_directory "features/support"
|
16
16
|
template "README", "features/saucy/README"
|
17
17
|
template "README", "features/step_definitions/saucy/README"
|
18
18
|
empty_directory "spec"
|
@@ -86,6 +86,10 @@ DESC
|
|
86
86
|
File.open(path, "w") { |file| file.write(contents) }
|
87
87
|
end
|
88
88
|
|
89
|
+
def engine_directory(prefix)
|
90
|
+
directory File.expand_path("../../../#{prefix}/engine"), "#{prefix}/saucy"
|
91
|
+
end
|
92
|
+
|
89
93
|
end
|
90
94
|
end
|
91
95
|
end
|
data/lib/saucy/account.rb
CHANGED
@@ -37,79 +37,77 @@ module Saucy
|
|
37
37
|
before_destroy :create_canceled_account
|
38
38
|
end
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
end
|
40
|
+
def to_param
|
41
|
+
keyword
|
42
|
+
end
|
44
43
|
|
45
|
-
|
46
|
-
|
47
|
-
|
44
|
+
def has_member?(user)
|
45
|
+
memberships.exists?(:user_id => user.id)
|
46
|
+
end
|
48
47
|
|
49
|
-
|
50
|
-
|
51
|
-
|
48
|
+
def users_by_name
|
49
|
+
users.by_name
|
50
|
+
end
|
52
51
|
|
53
|
-
|
54
|
-
|
55
|
-
|
52
|
+
def projects_by_name
|
53
|
+
projects.by_name
|
54
|
+
end
|
56
55
|
|
57
|
-
|
58
|
-
|
59
|
-
|
56
|
+
def projects_visible_to(user)
|
57
|
+
projects.visible_to(user)
|
58
|
+
end
|
60
59
|
|
61
|
-
|
62
|
-
|
63
|
-
|
60
|
+
def memberships_by_name
|
61
|
+
memberships.by_name
|
62
|
+
end
|
64
63
|
|
65
|
-
|
66
|
-
|
67
|
-
|
64
|
+
def expired?
|
65
|
+
trial? && past_trial?
|
66
|
+
end
|
68
67
|
|
69
|
-
|
70
|
-
|
71
|
-
|
68
|
+
def past_trial?
|
69
|
+
trial_expires_at && trial_expires_at < Time.now
|
70
|
+
end
|
72
71
|
|
73
|
-
|
74
|
-
|
75
|
-
|
72
|
+
def admin_emails
|
73
|
+
admins.map(&:email)
|
74
|
+
end
|
76
75
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
76
|
+
def set_trial_expiration
|
77
|
+
number_free_days = if coupon
|
78
|
+
30 * coupon.free_months
|
79
|
+
else
|
80
|
+
Saucy::Configuration.trial_length
|
81
|
+
end
|
82
|
+
self.trial_expires_at = number_free_days.days.from_now(created_at || Time.zone.now)
|
83
|
+
end
|
85
84
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
85
|
+
def reset_trial_expiration(length_in_days = nil)
|
86
|
+
new_length = length_in_days || Saucy::Configuration.trial_length
|
87
|
+
new_trial_start_date = [trial_expires_at, Time.zone.now].max
|
88
|
+
self.trial_expires_at = new_length.days.from_now(new_trial_start_date)
|
89
|
+
self.notified_of_completed_trial = false
|
90
|
+
self.notified_of_expiration = false
|
91
|
+
end
|
93
92
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
end
|
93
|
+
def record_new_activations
|
94
|
+
if activated_changed? && activated?
|
95
|
+
Saucy::Notifications.notify_observers("activated", :account => self)
|
98
96
|
end
|
97
|
+
end
|
99
98
|
|
100
|
-
|
101
|
-
|
102
|
-
|
99
|
+
def users_count
|
100
|
+
users.count
|
101
|
+
end
|
103
102
|
|
104
|
-
|
105
|
-
|
106
|
-
|
103
|
+
def projects_count
|
104
|
+
projects.active.count
|
105
|
+
end
|
107
106
|
|
108
|
-
|
107
|
+
private
|
109
108
|
|
110
|
-
|
111
|
-
|
112
|
-
end
|
109
|
+
def create_canceled_account
|
110
|
+
CanceledAccount.create(:account => self)
|
113
111
|
end
|
114
112
|
|
115
113
|
module ClassMethods
|
@@ -4,63 +4,60 @@ module Saucy
|
|
4
4
|
|
5
5
|
included do
|
6
6
|
helper_method :current_account, :current_project, :current_account?, :current_project?
|
7
|
-
include InstanceMethods
|
8
7
|
end
|
9
8
|
|
10
|
-
|
11
|
-
protected
|
9
|
+
protected
|
12
10
|
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
def current_account
|
12
|
+
::Account.find_by_keyword!(params[:account_id])
|
13
|
+
end
|
16
14
|
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
def current_project
|
16
|
+
current_account.projects.find_by_keyword!(params[:project_id])
|
17
|
+
end
|
20
18
|
|
21
|
-
|
22
|
-
|
23
|
-
|
19
|
+
def current_project?
|
20
|
+
params[:project_id].present?
|
21
|
+
end
|
24
22
|
|
25
|
-
|
26
|
-
|
27
|
-
|
23
|
+
def current_account?
|
24
|
+
params[:account_id].present?
|
25
|
+
end
|
28
26
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
end
|
27
|
+
def authorize_admin
|
28
|
+
unless current_user.admin_of?(current_account)
|
29
|
+
deny_access("You must be an admin to access that page.")
|
33
30
|
end
|
31
|
+
end
|
34
32
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
end
|
33
|
+
def authorize_member
|
34
|
+
unless current_user.member_of?(current_project)
|
35
|
+
deny_access("You do not have permission for this project.")
|
39
36
|
end
|
37
|
+
end
|
40
38
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
end
|
39
|
+
def ensure_active_account
|
40
|
+
if current_account?
|
41
|
+
if current_account.past_due?
|
42
|
+
redirect_unusable_account account_billing_path(current_account),
|
43
|
+
"past_due"
|
44
|
+
end
|
45
|
+
if current_account.expired?
|
46
|
+
redirect_unusable_account edit_account_plan_path(current_account),
|
47
|
+
"expired"
|
51
48
|
end
|
52
49
|
end
|
50
|
+
end
|
53
51
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
52
|
+
def redirect_unusable_account(path, failure)
|
53
|
+
role = current_user.admin_of?(current_account) ? 'admin' : 'user'
|
54
|
+
flash[:alert] = t("saucy.errors.#{failure}.#{role}")
|
55
|
+
redirect_to path
|
56
|
+
end
|
59
57
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
end
|
58
|
+
def ensure_account_within_limit(limit_name)
|
59
|
+
if !Limit.can_add_one?(limit_name, current_account)
|
60
|
+
redirect_to edit_account_plan_path(current_account), :alert => t("saucy.errors.limited", :default => "You are at your limit of %{name} for your current plan.", :name => limit_name)
|
64
61
|
end
|
65
62
|
end
|
66
63
|
end
|
data/lib/saucy/plan.rb
CHANGED
@@ -29,26 +29,24 @@ module Saucy
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
end
|
32
|
+
def free?
|
33
|
+
price.zero?
|
34
|
+
end
|
36
35
|
|
37
|
-
|
38
|
-
|
39
|
-
|
36
|
+
def billed?
|
37
|
+
!free?
|
38
|
+
end
|
40
39
|
|
41
|
-
|
42
|
-
|
43
|
-
|
40
|
+
def can_add_more?(limit, amount)
|
41
|
+
limits.numbered.named(limit).value > amount
|
42
|
+
end
|
44
43
|
|
45
|
-
|
46
|
-
|
47
|
-
|
44
|
+
def allows?(limit)
|
45
|
+
limits.boolean.named(limit).allowed?
|
46
|
+
end
|
48
47
|
|
49
|
-
|
50
|
-
|
51
|
-
end
|
48
|
+
def limit(limit_name)
|
49
|
+
limits.named(limit_name)
|
52
50
|
end
|
53
51
|
end
|
54
52
|
end
|
data/lib/saucy/project.rb
CHANGED
@@ -65,60 +65,58 @@ module Saucy
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
end
|
68
|
+
def to_param
|
69
|
+
keyword
|
70
|
+
end
|
72
71
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
72
|
+
def has_member?(user)
|
73
|
+
permissions.
|
74
|
+
joins(:membership).
|
75
|
+
exists?(:memberships => { :user_id => user.id })
|
76
|
+
end
|
78
77
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
end
|
83
|
-
self
|
78
|
+
def assign_default_permissions
|
79
|
+
account.memberships.where(:admin => true).each do |membership|
|
80
|
+
self.permissions.build(:membership => membership)
|
84
81
|
end
|
82
|
+
self
|
83
|
+
end
|
85
84
|
|
86
|
-
|
85
|
+
private
|
87
86
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
87
|
+
def setup_memberships
|
88
|
+
@new_user_ids ||= []
|
89
|
+
@new_user_ids += admin_user_ids
|
90
|
+
removed_user_ids = self.user_ids - @new_user_ids
|
91
|
+
added_user_ids = @new_user_ids - self.user_ids
|
93
92
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
end
|
93
|
+
permissions.where(:user_id => removed_user_ids).destroy_all
|
94
|
+
added_user_ids.each do |added_user_id|
|
95
|
+
membership =
|
96
|
+
account.memberships.where(:user_id => added_user_id).first
|
97
|
+
permissions.create!(:membership => membership)
|
100
98
|
end
|
99
|
+
end
|
101
100
|
|
102
|
-
|
103
|
-
|
104
|
-
|
101
|
+
def update_memberships
|
102
|
+
setup_memberships if @new_user_ids
|
103
|
+
end
|
105
104
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
105
|
+
def admin_user_ids
|
106
|
+
account.
|
107
|
+
memberships.
|
108
|
+
where(:admin => true).
|
109
|
+
select(:user_id).
|
110
|
+
map(&:user_id)
|
111
|
+
end
|
113
112
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
end
|
113
|
+
def ensure_account_within_limit
|
114
|
+
message = "You are at your limit of %{name} for your current plan."
|
115
|
+
if archived_changed? && !archived? && !Limit.can_add_one?("projects", account)
|
116
|
+
errors.add(:archived, I18n.t("saucy.errors.limited", :default => message, :name => 'projects'))
|
117
|
+
end
|
118
|
+
if account_id_changed? && !Limit.can_add_one?("projects", account)
|
119
|
+
errors.add(:account_id, I18n.t("saucy.errors.limited", :default => message, :name => 'projects'))
|
122
120
|
end
|
123
121
|
end
|
124
122
|
end
|