bullet_train 1.0.1 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ebd16b30c620c7fd848abef684f55e40fd585ad3904050d38ef628b07e871794
4
- data.tar.gz: 3c462799a93a477eaa92bae1d9272c17d3a32462e531d1d726f22ecdf2a46fd5
3
+ metadata.gz: 633b451afdfeee512883e8536928cc2dd247f15139d59e7bfd24cc4d637b735d
4
+ data.tar.gz: c2a59912f00000e2b72ae5193b6d5cd0ebcd34a736f01260f5abfa1a5c170c94
5
5
  SHA512:
6
- metadata.gz: 90ec50fd0de2e4a657ebcf3dcdcfa642d893270126cfe1e00a6955f0d7b2b8789a28c80eb5afb36c14dfccf236bea74e3168d873fdf9d5fbc95e86500259d847
7
- data.tar.gz: 55c3fb8efc58ab6a09f87e67d065935af839136d696ebc48ab1bbbd45d89731c4c466de07eb7e52dbc55b57f1292577317b0d1ed435dde3dbe04f15036ecb886
6
+ metadata.gz: 6cacd920c5c13800b167a1d1c3e9ae8fa6d6bc1cea28d1128f7404ce8bd9e0615957aad91b4cb7363bb10ac88823294511344bf9103febe2b0ed830508472cba
7
+ data.tar.gz: 1bc0e30c37ebd765df530a9d25b65a591736be8420949c68651569347280d7403d3889cdd59933384825c19d5727a25c7d16569a42e9baececfeb2970938b85a
@@ -1,146 +1,3 @@
1
1
  class Account::InvitationsController < Account::ApplicationController
2
- # the 'accept' action isn't covered by cancancan, because simply having the
3
- # link is authorization enough to claim membership on a team. see `#accept`
4
- # for more information.
5
- account_load_and_authorize_resource :invitation, :team, except: [:show]
6
-
7
- # we skip the onboarding requirement for users claiming and invitation.
8
- # this way the invitation gets accepted after they complete the devise
9
- # workflow, but before they're prompted to complete their onboarding.
10
- skip_before_action :ensure_onboarding_is_complete_and_set_next_step, only: :accept
11
- skip_before_action :authenticate_user!, only: :accept
12
-
13
- # GET /invitations
14
- # GET /invitations.json
15
- def index
16
- redirect_to [:account, @team, :memberships]
17
- end
18
-
19
- # GET /invitations/1
20
- # GET /invitations/1.json
21
- def show
22
- # it's important that we only allow invitations to be shown via their uuid,
23
- # otherwise team members can just step the id in the url to claim an
24
- # invitation that would escalate their privileges.
25
- @invitation = Invitation.find_by(uuid: params[:id])
26
- unless @invitation
27
- raise I18n.t("global.notifications.not_found")
28
- end
29
- @team = @invitation.team
30
-
31
- # backfill these objects for the locale magic, since we didn't use `account_load_and_authorize_resource`.
32
- @child_object = @invitation
33
- @parent_object = @team
34
-
35
- render layout: "devise"
36
- end
37
-
38
- # POST /invitations/1/accept
39
- # POST /invitations/1/accept.json
40
- def accept
41
- # unless the user is signed in.
42
- if !current_user.present?
43
-
44
- # keep track of the uuid of the invitation so we can reload it
45
- # after they sign up. at this point we don't even know if it's
46
- # valid, but that's fine.
47
- session[:invitation_uuid] = params[:id]
48
-
49
- # also, we'll queue devise up to return to the invitation url after a sign in.
50
- session["user_return_to"] = request.path
51
-
52
- # assume the user needs to create an account.
53
- # this is not the default for devise, but a sensible default here.
54
- redirect_to new_user_registration_path
55
-
56
- else
57
-
58
- @invitation = Invitation.find_by(uuid: params[:id])
59
-
60
- if @invitation
61
- @team = @invitation.team
62
- if @invitation.is_for?(current_user) || request.post?
63
- @invitation.accept_for(current_user)
64
- redirect_to account_dashboard_path, notice: I18n.t("invitations.notifications.welcome", team_name: @team.name)
65
- else
66
- redirect_to account_invitation_path(@invitation.uuid)
67
- end
68
- else
69
- redirect_to account_dashboard_path
70
- end
71
-
72
- end
73
- end
74
-
75
- # GET /invitations/new
76
- def new
77
- @invitation.build_membership
78
- @cancel_path = only_allow_path(params[:cancel_path])
79
- end
80
-
81
- # POST /invitations
82
- # POST /invitations.json
83
- def create
84
- @invitation.membership.team = current_team
85
- # this allows notifications to be sent to a user before they've accepted their invitation.
86
- @invitation.membership.user_email = @invitation.email
87
- @invitation.from_membership = current_membership
88
- respond_to do |format|
89
- if @invitation.save
90
- format.html { redirect_to account_team_invitations_path(@team), notice: I18n.t("invitations.notifications.created") }
91
- format.json { render :show, status: :created, location: [:account, @team, @invitation] }
92
- else
93
- format.html { render :new, status: :unprocessable_entity }
94
- format.json { render json: @invitation.errors, status: :unprocessable_entity }
95
- end
96
- end
97
- end
98
-
99
- # DELETE /invitations/1
100
- # DELETE /invitations/1.json
101
- def destroy
102
- @invitation.destroy
103
- respond_to do |format|
104
- format.html { redirect_to account_team_invitations_path(@team), notice: I18n.t("invitations.notifications.destroyed") }
105
- format.json { head :no_content }
106
- end
107
- end
108
-
109
- private
110
-
111
- def manageable_role_keys
112
- helpers.current_membership.manageable_roles.map(&:key)
113
- end
114
-
115
- # NOTE this method is only designed to work in the context of creating a invitation.
116
- # we don't provide any support for updating invitations.
117
- def invitation_params
118
- # we use strong params first.
119
- strong_params = params.require(:invitation).permit(
120
- :email,
121
- # 🚅 super scaffolding will insert new fields above this line.
122
- # 🚅 super scaffolding will insert new arrays above this line.
123
- membership_attributes: [
124
- :user_first_name,
125
- :user_last_name,
126
- role_ids: []
127
- ],
128
- )
129
-
130
- # after that, we have to be more careful how we assign the roles.
131
- # we can't let users assign roles to an invitation that they don't have permission
132
- # to assign, but they do have permission to assign some to other team members.
133
- if params[:invitation] && params[:invitation][:role_ids].present?
134
-
135
- # ensure the list of role keys from the form only includes keys that they're allowed to assign.
136
- assignable_role_keys_from_the_form = params[:invitation][:role_ids].map(&:to_i) & manageable_role_keys
137
-
138
- strong_params[:role_ids] = assignable_role_keys_from_the_form
139
-
140
- end
141
-
142
- # 🚅 super scaffolding will insert processing for new fields above this line.
143
-
144
- strong_params
145
- end
2
+ include Invitations::ControllerBase
146
3
  end
@@ -1,132 +1,3 @@
1
1
  class Account::MembershipsController < Account::ApplicationController
2
- account_load_and_authorize_resource :membership, :team, member_actions: [:demote, :promote, :reinvite], collection_actions: [:search]
3
-
4
- def index
5
- unless @memberships.count > 0
6
- redirect_to account_team_invitations_path(@team), notice: I18n.t("memberships.notifications.no_members")
7
- end
8
- end
9
-
10
- def search
11
- # TODO This is a particularly crazy example where we're doing the search logic ourselves in SQL.
12
- # In the future, I could see us replacing this with a recommended example using Elasticsearch and the `searchkick` Ruby Gem.
13
- limit = params[:limit] || 100
14
- page = [params[:page].to_i, 1].max # Ensure we never have a negative or zero page value
15
- search_term = "%#{params[:search]&.upcase}%"
16
- offset = (page - 1) * limit
17
- # Currently we're only searching on user.first_name, user.last_name, memberships.user_first_name and memberships.user_last_name. Should we also search on the email address?
18
- # This query could use impromement. Currently if you search for "Ad Pal" you wouldn't find a user "Adam Pallozzi"
19
- query = "UPPER(first_name) LIKE :search_term OR UPPER(last_name) LIKE :search_term OR UPPER(user_first_name) LIKE :search_term OR UPPER(user_last_name) LIKE :search_term"
20
- # We're using left outer join here because we may get memberships that don't belong to a membership yet
21
- memberships = @team.memberships.accessible_by(current_ability, :show).left_outer_joins(:user).where(query, search_term: search_term)
22
- total_results = memberships.size
23
- # the Areal.sql(LOWER(COALESCE...)) part means that if the user record doesn't exist or if there are records that start with a lower case letter, we will still sort everything correctly using the user.first_name instead.
24
- memberships_array = memberships.limit(limit).offset(offset).order(Arel.sql("LOWER(COALESCE(first_name, user_first_name) )")).map { |membership| {id: membership.id, text: membership.label_string.to_s} }
25
- results = {results: memberships_array, pagination: {more: (total_results > page * limit)}}
26
- render json: results.to_json
27
- end
28
-
29
- def show
30
- end
31
-
32
- def edit
33
- end
34
-
35
- # PATCH/PUT /account/memberships/:id
36
- # PATCH/PUT /account/memberships/:id.json
37
- def update
38
- respond_to do |format|
39
- if @membership.update(membership_params)
40
- format.html { redirect_to [:account, @membership], notice: I18n.t("memberships.notifications.updated") }
41
- format.json { render :show, status: :ok, location: [:account, @membership] }
42
- else
43
- format.html { render :edit, status: :unprocessable_entity }
44
- format.json { render json: @membership.errors, status: :unprocessable_entity }
45
- end
46
- rescue RemovingLastTeamAdminException => _
47
- format.html { redirect_to [:account, @team, :memberships], alert: I18n.t("memberships.notifications.cant_demote") }
48
- format.json { render json: {exception: I18n.t("memberships.notifications.cant_demote")}, status: :unprocessable_entity }
49
- end
50
- end
51
-
52
- def demote
53
- @membership.roles.delete Role.admin
54
- redirect_to account_team_memberships_path(@team)
55
- rescue RemovingLastTeamAdminException => _
56
- redirect_to account_team_memberships_path(@team), alert: I18n.t("memberships.notifications.cant_demote")
57
- end
58
-
59
- def promote
60
- @membership.roles << Role.admin unless @membership.roles.include?(Role.admin)
61
- redirect_to account_team_memberships_path(@team)
62
- end
63
-
64
- def destroy
65
- # Instead of destroying the membership, we nullify the user_id and use the membership record as a 'Tombstone' for referencing past associations (eg message at-mentions and Scaffolding::CompletelyConcrete::TangibleThings::Assignment)
66
-
67
- user_was = @membership.user
68
- @membership.nullify_user
69
-
70
- if user_was == current_user
71
- # if a user removes themselves from a team, we'll have to send them to their dashboard.
72
- redirect_to account_dashboard_path, notice: I18n.t("memberships.notifications.you_removed_yourself", team_name: @team.name)
73
- else
74
- redirect_to [:account, @team, :memberships], notice: I18n.t("memberships.notifications.destroyed")
75
- end
76
- rescue RemovingLastTeamAdminException
77
- redirect_to account_team_memberships_path(@team), alert: I18n.t("memberships.notifications.cant_remove")
78
- end
79
-
80
- def reinvite
81
- @invitation = Invitation.new(membership: @membership, team: @team, email: @membership.user_email, from_membership: current_membership)
82
- if @invitation.save
83
- redirect_to [:account, @team, :memberships], notice: I18n.t("account.memberships.notifications.reinvited")
84
- else
85
- redirect_to [:account, @team, :memberships], notice: "There was an error creating the invitation (#{@invitation.errors.full_messages.to_sentence})"
86
- end
87
- end
88
-
89
- private
90
-
91
- def manageable_role_keys
92
- helpers.current_membership.manageable_roles.map(&:key)
93
- end
94
-
95
- # NOTE this method is only designed to work in the context of updating a membership.
96
- # we don't provide any support for creating memberships other than by an invitation.
97
- def membership_params
98
- # we use strong params first.
99
- strong_params = params.require(:membership).permit(
100
- :user_first_name,
101
- :user_last_name,
102
- :user_profile_photo_id,
103
- # 🚅 super scaffolding will insert new fields above this line.
104
- # 🚅 super scaffolding will insert new arrays above this line.
105
- )
106
-
107
- # after that, we have to be more careful how we update the roles.
108
- # we can't let users remove roles from a membership that they don't have permission
109
- # to remove, but we want to allow them to add or remove other roles they do have
110
- # permission to assign to other team members.
111
- if params[:membership] && params[:membership][:role_ids].present?
112
-
113
- # first, start with the list of role keys already assigned to this membership.
114
- existing_role_keys = @membership.role_ids
115
-
116
- # generate a list of role keys we can't allow the current user to remove from this membership.
117
- existing_role_keys_that_are_unmanageable = existing_role_keys - manageable_role_keys
118
-
119
- # now let's ensure the list of role keys from the form only includes keys that they're allowed to assign.
120
- assignable_role_keys_from_the_form = params[:membership][:role_ids].map(&:to_s) & manageable_role_keys
121
-
122
- # any role keys that are manageable by the current user have to then come from the form data,
123
- # otherwise we can assume they were removed by being unchecked.
124
- strong_params[:role_ids] = existing_role_keys_that_are_unmanageable + assignable_role_keys_from_the_form
125
-
126
- end
127
-
128
- # 🚅 super scaffolding will insert processing for new fields above this line.
129
-
130
- strong_params
131
- end
2
+ include Account::Memberships::ControllerBase
132
3
  end
@@ -1,63 +1,3 @@
1
1
  class Account::Onboarding::UserDetailsController < Account::ApplicationController
2
- layout "devise"
3
- load_and_authorize_resource class: "User"
4
-
5
- # this is because cancancan doesn't let us set the instance variable name above.
6
- before_action do
7
- @user = @user_detail
8
- end
9
-
10
- # GET /users/1/edit
11
- def edit
12
- flash[:notice] = nil
13
- end
14
-
15
- # PATCH/PUT /users/1
16
- # PATCH/PUT /users/1.json
17
- def update
18
- respond_to do |format|
19
- if @user.update(user_params)
20
- # if you update your own user account, devise will normally kick you out, so we do this instead.
21
- bypass_sign_in current_user.reload
22
-
23
- if @user.details_provided?
24
- format.html { redirect_to account_team_path(@user.teams.first), notice: "" }
25
- else
26
- format.html {
27
- flash[:error] = I18n.t("global.notifications.all_fields_required")
28
- redirect_to edit_account_onboarding_user_detail_path(@user)
29
- }
30
- end
31
-
32
- format.json { render :show, status: :ok, location: [:account, @user] }
33
- else
34
- format.html { render :edit, status: :unprocessable_entity }
35
- format.json { render json: @user.errors, status: :unprocessable_entity }
36
- end
37
- end
38
- end
39
-
40
- private
41
-
42
- # Never trust parameters from the scary internet, only allow the white list through.
43
- def user_params
44
- permitted_attributes = [
45
- :first_name,
46
- :last_name,
47
- :time_zone,
48
- # 🚅 super scaffolding will insert new fields above this line.
49
- ]
50
-
51
- permitted_hash = {
52
- # 🚅 super scaffolding will insert new arrays above this line.
53
- }
54
-
55
- if can? :edit, @user.current_team
56
- permitted_hash[:current_team_attributes] = [:id, :name]
57
- end
58
-
59
- params.require(:user).permit(permitted_attributes, permitted_hash)
60
-
61
- # 🚅 super scaffolding will insert processing for new fields above this line.
62
- end
2
+ include Account::Onboarding::UserDetails::ControllerBase
63
3
  end
@@ -1,65 +1,3 @@
1
1
  class Account::Onboarding::UserEmailController < Account::ApplicationController
2
- layout "devise"
3
- load_and_authorize_resource class: "User"
4
-
5
- # this is because cancancan doesn't let us set the instance variable name above.
6
- before_action do
7
- @user = @user_email
8
- end
9
-
10
- # GET /users/1/edit
11
- def edit
12
- flash[:notice] = nil
13
- if @user.email_is_oauth_placeholder?
14
- @user.email = nil
15
- end
16
- end
17
-
18
- # PATCH/PUT /users/1
19
- # PATCH/PUT /users/1.json
20
- def update
21
- respond_to do |format|
22
- if @user.update(user_params)
23
- # if you update your own user account, devise will normally kick you out, so we do this instead.
24
- bypass_sign_in current_user.reload
25
-
26
- if !@user.email_is_oauth_placeholder?
27
- @user.send_welcome_email
28
- format.html { redirect_to account_team_path(@user.teams.first), notice: "" }
29
- else
30
- format.html {
31
- flash[:error] = I18n.t("global.notifications.all_fields_required")
32
- redirect_to edit_account_onboarding_user_detail_path(@user)
33
- }
34
- end
35
-
36
- format.json { render :show, status: :ok, location: [:account, @user] }
37
- else
38
-
39
- # this is just checking whether the error on the email field is taking the email
40
- # address is already taken.
41
- @email_taken = begin
42
- @user.errors.details[:email].select { |error| error[:error] == :taken }.any?
43
- rescue
44
- false
45
- end
46
-
47
- format.html { render :edit, status: :unprocessable_entity }
48
- format.json { render json: @user.errors, status: :unprocessable_entity }
49
- end
50
- end
51
- end
52
-
53
- private
54
-
55
- # Never trust parameters from the scary internet, only allow the white list through.
56
- def user_params
57
- params.require(:user).permit(
58
- :email,
59
- # 🚅 super scaffolding will insert new fields above this line.
60
- # 🚅 super scaffolding will insert new arrays above this line.
61
- )
62
-
63
- # 🚅 super scaffolding will insert processing for new fields above this line.
64
- end
2
+ include Account::Onboarding::UserEmail::ControllerBase
65
3
  end
@@ -1,122 +1,3 @@
1
1
  class Account::TeamsController < Account::ApplicationController
2
- load_and_authorize_resource :team, class: "Team", prepend: true
3
-
4
- prepend_before_action do
5
- if params["action"] == "new"
6
- current_user.current_team = nil
7
- end
8
- end
9
-
10
- before_action :enforce_invitation_only, only: [:create]
11
-
12
- before_action do
13
- # for magic locales.
14
- @child_object = @team
15
- end
16
-
17
- # GET /teams
18
- # GET /teams.json
19
- def index
20
- # if a user doesn't have multiple teams, we try to simplify the team ui/ux
21
- # as much as possible. links to this page should go to the current team
22
- # dashboard. however, some other links to this page are actually in branch
23
- # logic and will not display at all. instead, users will be linked to the
24
- # "new team" page. (see the main account sidebar menu for an example of
25
- # this.)
26
- unless current_user.multiple_teams?
27
- redirect_to account_team_path(current_team)
28
- end
29
- end
30
-
31
- # POST /teams/1/switch
32
- def switch_to
33
- current_user.current_team = @team
34
- current_user.save
35
- redirect_to account_dashboard_path
36
- end
37
-
38
- # GET /teams/1
39
- # GET /teams/1.json
40
- def show
41
- # I don't think this is the best place to close the loop on the onboarding process, but practically speaking it's
42
- # the easiest place to implement this at the moment, because all the onboarding steps redirect here on success.
43
- if session[:after_onboarding_url].present?
44
- redirect_to session.delete(:after_onboarding_url)
45
- end
46
-
47
- current_user.current_team = @team
48
- current_user.save
49
- end
50
-
51
- # GET /teams/new
52
- def new
53
- render :new, layout: "devise"
54
- end
55
-
56
- # GET /teams/1/edit
57
- def edit
58
- end
59
-
60
- # POST /teams
61
- # POST /teams.json
62
- def create
63
- @team = Team.new(team_params)
64
-
65
- respond_to do |format|
66
- if @team.save
67
-
68
- # also make the creator of the team the default admin.
69
- @team.memberships.create(user: current_user, roles: [Role.admin])
70
-
71
- current_user.current_team = @team
72
- current_user.former_user = false
73
- current_user.save
74
-
75
- format.html { redirect_to [:account, @team], notice: I18n.t("teams.notifications.created") }
76
- format.json { render :show, status: :created, location: [:account, @team] }
77
- else
78
- format.html { render :new, layout: "devise" }
79
- format.json { render json: @team.errors, status: :unprocessable_entity }
80
- end
81
- end
82
- end
83
-
84
- # PATCH/PUT /teams/1
85
- # PATCH/PUT /teams/1.json
86
- def update
87
- respond_to do |format|
88
- if @team.update(team_params)
89
- format.html { redirect_to [:account, @team], notice: I18n.t("teams.notifications.updated") }
90
- format.json { render :show, status: :ok, location: [:account, @team] }
91
- else
92
- format.html { render :edit, status: :unprocessable_entity }
93
- format.json { render json: @team.errors, status: :unprocessable_entity }
94
- end
95
- end
96
- end
97
-
98
- # # DELETE /teams/1
99
- # # DELETE /teams/1.json
100
- # def destroy
101
- # @team.destroy
102
- # respond_to do |format|
103
- # format.html { redirect_to account_teams_url, notice: 'Team was successfully destroyed.' }
104
- # format.json { head :no_content }
105
- # end
106
- # end
107
-
108
- private
109
-
110
- # Never trust parameters from the scary internet, only allow the white list through.
111
- def team_params
112
- params.require(:team).permit(
113
- :name,
114
- :time_zone,
115
- :locale,
116
- # 🚅 super scaffolding will insert new fields above this line.
117
- # 🚅 super scaffolding will insert new arrays above this line.
118
- )
119
-
120
- # 🚅 super scaffolding will insert processing for new fields above this line.
121
- end
2
+ include Account::Teams::ControllerBase
122
3
  end
@@ -1,59 +1,3 @@
1
1
  class Account::UsersController < Account::ApplicationController
2
- load_and_authorize_resource
3
-
4
- before_action do
5
- # for magic locales.
6
- @child_object = @user
7
- end
8
-
9
- # GET /account/users/1/edit
10
- def edit
11
- end
12
-
13
- # GET /account/users/1
14
- def show
15
- end
16
-
17
- def updating_password?
18
- params[:user].key?(:password)
19
- end
20
-
21
- # PATCH/PUT /account/users/1
22
- # PATCH/PUT /account/users/1.json
23
- def update
24
- respond_to do |format|
25
- if updating_password? ? @user.update_with_password(user_params) : @user.update_without_password(user_params)
26
- # if you update your own user account, devise will normally kick you out, so we do this instead.
27
- bypass_sign_in current_user.reload
28
- format.html { redirect_to [:edit, :account, @user], notice: t("users.notifications.updated") }
29
- format.json { render :show, status: :ok, location: [:account, @user] }
30
- else
31
- format.html { render :edit, status: :unprocessable_entity }
32
- format.json { render json: @user.errors, status: :unprocessable_entity }
33
- end
34
- end
35
- end
36
-
37
- private
38
-
39
- # Never trust parameters from the scary internet, only allow the white list through.
40
- def user_params
41
- # TODO enforce permissions on updating the user's team name.
42
- params.require(:user).permit(
43
- :email,
44
- :first_name,
45
- :last_name,
46
- :time_zone,
47
- :current_password,
48
- :password,
49
- :password_confirmation,
50
- :profile_photo_id,
51
- :locale,
52
- # 🚅 super scaffolding will insert new fields above this line.
53
- current_team_attributes: [:name],
54
- # 🚅 super scaffolding will insert new arrays above this line.
55
- )
56
-
57
- # 🚅 super scaffolding will insert processing for new fields above this line.
58
- end
2
+ include Account::Users::ControllerBase
59
3
  end