bullet_train 1.1.10 ā 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/concerns/account/invitations/controller_base.rb +3 -11
- data/app/controllers/concerns/account/users/controller_base.rb +4 -25
- data/app/controllers/concerns/registrations/controller_base.rb +9 -6
- data/app/controllers/sessions_controller.rb +9 -0
- data/app/helpers/account/teams_helper.rb +1 -1
- data/app/helpers/concerns/helpers/base.rb +0 -9
- data/app/models/concerns/memberships/base.rb +3 -7
- data/app/models/concerns/users/base.rb +3 -0
- data/app/views/account/memberships/_index.html.erb +4 -35
- data/app/views/account/memberships/_membership.html.erb +29 -0
- data/app/views/account/memberships/index.html.erb +6 -1
- data/app/views/account/memberships/show.html.erb +1 -1
- data/app/views/account/onboarding/user_details/edit.html.erb +6 -6
- data/app/views/account/teams/_index.html.erb +1 -25
- data/app/views/account/teams/_team.html.erb +23 -0
- data/app/views/account/teams/index.html.erb +3 -1
- data/app/views/account/teams/show.html.erb +1 -1
- data/app/views/devise/registrations/new.html.erb +2 -2
- data/app/views/devise/sessions/new.html.erb +1 -1
- data/app/views/layouts/docs.html.erb +12 -0
- data/config/locales/en/base.yml +1 -0
- data/config/locales/en/framework_packages.yml +4 -4
- data/docs/application-options.md +29 -0
- data/docs/authentication.md +9 -0
- data/docs/field-partials/file-field.md +25 -0
- data/docs/field-partials.md +3 -1
- data/docs/i18n.md +28 -0
- data/docs/index.md +2 -0
- data/docs/invitation_only.md +1 -1
- data/docs/themes.md +13 -40
- data/docs/trademark.md +25 -0
- data/docs/two-factor-authentication.md +16 -0
- data/lib/bullet_train/resolver.rb +47 -0
- data/lib/bullet_train/version.rb +1 -1
- data/lib/bullet_train.rb +6 -1
- data/lib/tasks/bullet_train_tasks.rake +59 -1
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8a3d6ac8297af9001838de1bd2ccfcf442374dac5c09853323d44ea4dee1b688
|
4
|
+
data.tar.gz: 976f96ec27bfe2c2c6015bd4aac68886b0cbeadeae121cde7de4ba0d2c5df882
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7e384a07bf8d9c65887714cb7efb4f825df1eaec95af4d773196a8a0310c5cf133f43abdca338280b037c11bd961824fbbeab01677bf6d5b31c4a3913cba6c75
|
7
|
+
data.tar.gz: 8ed14efacde3f0cea832a046ae5a7067cf6586fe24d5054ba07f47f5087fd9233051ba308a401277bee3cf12f226c27c1499d4de7b647cef64a7a6a56f0b1e30
|
@@ -49,17 +49,9 @@ module Account::Invitations::ControllerBase
|
|
49
49
|
|
50
50
|
# unless the user is signed in.
|
51
51
|
if !current_user.present?
|
52
|
-
#
|
53
|
-
#
|
54
|
-
|
55
|
-
session[:invitation_uuid] = params[:id]
|
56
|
-
|
57
|
-
# also, we'll queue devise up to return to the invitation url after a sign in.
|
58
|
-
session["user_return_to"] = request.path
|
59
|
-
|
60
|
-
# assume the user needs to create an account.
|
61
|
-
# this is not the default for devise, but a sensible default here.
|
62
|
-
redirect_to new_user_registration_path
|
52
|
+
# We need them to register.
|
53
|
+
# We have to send `invitation_uuid` via params, not session, because Safari doesn't set cookies on redirect.
|
54
|
+
redirect_to new_user_registration_path(invitation_uuid: @invitation&.uuid)
|
63
55
|
|
64
56
|
# session[:invitation_uuid] should only be present if the user is registering for the first time.
|
65
57
|
elsif (@invitation = Invitation.find_by(uuid: session[:invitation_uuid] || params[:id]))
|
@@ -10,6 +10,10 @@ module Account::Users::ControllerBase
|
|
10
10
|
# for magic locales.
|
11
11
|
@child_object = @user
|
12
12
|
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
include strong_parameters_from_api
|
13
17
|
end
|
14
18
|
|
15
19
|
# GET /account/users/1/edit
|
@@ -53,29 +57,4 @@ module Account::Users::ControllerBase
|
|
53
57
|
def process_params(strong_params)
|
54
58
|
raise "It looks like you've removed `process_params` from your controller. This will break Super Scaffolding."
|
55
59
|
end
|
56
|
-
|
57
|
-
# Never trust parameters from the scary internet, only allow the white list through.
|
58
|
-
# TODO Update this to use `include strong_parameters_from_api`.
|
59
|
-
def user_params
|
60
|
-
# TODO enforce permissions on updating the user's team name.
|
61
|
-
strong_params = params.require(:user).permit(
|
62
|
-
*([
|
63
|
-
:email,
|
64
|
-
:first_name,
|
65
|
-
:last_name,
|
66
|
-
:time_zone,
|
67
|
-
:current_password,
|
68
|
-
:password,
|
69
|
-
:password_confirmation,
|
70
|
-
:profile_photo_id,
|
71
|
-
:locale,
|
72
|
-
] + permitted_fields + [
|
73
|
-
{
|
74
|
-
current_team_attributes: [:name]
|
75
|
-
}.merge(permitted_arrays)
|
76
|
-
])
|
77
|
-
)
|
78
|
-
|
79
|
-
process_params(strong_params)
|
80
|
-
end
|
81
60
|
end
|
@@ -3,6 +3,12 @@ module Registrations::ControllerBase
|
|
3
3
|
|
4
4
|
included do
|
5
5
|
def new
|
6
|
+
# We have to set the session here because Safari wouldn't save it on a redirect to this URL.
|
7
|
+
if params[:invitation_uuid]
|
8
|
+
session[:invitation_uuid] = params[:invitation_uuid]
|
9
|
+
session["user_return_to"] = accept_account_invitation_path(params[:invitation_uuid])
|
10
|
+
end
|
11
|
+
|
6
12
|
if invitation_only?
|
7
13
|
unless session[:invitation_uuid] || session[:invitation_key]
|
8
14
|
return redirect_to root_path
|
@@ -19,17 +25,14 @@ module Registrations::ControllerBase
|
|
19
25
|
|
20
26
|
# if current_user is defined, that means they were successful registering.
|
21
27
|
if current_user
|
22
|
-
#
|
23
|
-
#
|
24
|
-
|
25
|
-
# so we don't have to create a default team for them here.
|
26
|
-
unless current_user.teams.any? || session[:invitation_uuid].present?
|
28
|
+
# Don't create a default team if they're being invited to another team.
|
29
|
+
# Don't create a default team if they have another one for any reason.
|
30
|
+
unless session[:invitation_uuid].present? || current_user.teams.any?
|
27
31
|
current_user.create_default_team
|
28
32
|
end
|
29
33
|
|
30
34
|
# send the welcome email.
|
31
35
|
current_user.send_welcome_email unless current_user.email_is_oauth_placeholder?
|
32
|
-
|
33
36
|
end
|
34
37
|
end
|
35
38
|
end
|
@@ -1,6 +1,15 @@
|
|
1
1
|
class SessionsController < Devise::SessionsController
|
2
2
|
include Sessions::ControllerBase
|
3
3
|
|
4
|
+
# If user_return_to points to an oauth path we disable Turbo on the sign in form.
|
5
|
+
# This makes it work when we need to redirect to external sites and/or custom protocols.
|
6
|
+
# With Turbo enabled the browser will block those redirects with a CORS error.
|
7
|
+
# https://github.com/bullet-train-co/bullet_train/issues/384
|
8
|
+
def user_return_to_is_oauth
|
9
|
+
session["user_return_to"]&.match(/^\/oauth/)
|
10
|
+
end
|
11
|
+
helper_method :user_return_to_is_oauth
|
12
|
+
|
4
13
|
def destroy
|
5
14
|
if params.include?(:onboard_logout)
|
6
15
|
signed_out = (Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name))
|
@@ -2,7 +2,7 @@ module Account::TeamsHelper
|
|
2
2
|
def current_team
|
3
3
|
# TODO We do not want this to be based on the `current_team_id`.
|
4
4
|
# TODO We want this to be based on the current resource being loaded.
|
5
|
-
current_user&.current_team
|
5
|
+
@team || current_user&.current_team
|
6
6
|
end
|
7
7
|
|
8
8
|
def other_teams
|
@@ -6,13 +6,4 @@ module Helpers::Base
|
|
6
6
|
# This scope has an order if the SQL changes when we remove any order clause.
|
7
7
|
scope.to_sql != scope.reorder("").to_sql
|
8
8
|
end
|
9
|
-
|
10
|
-
# TODO This should really be in the API package and included from there.
|
11
|
-
if defined?(BulletTrain::Api)
|
12
|
-
def render_pagination(json)
|
13
|
-
if @pagy
|
14
|
-
json.has_more @pagy.has_more
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
9
|
end
|
@@ -27,7 +27,7 @@ module Memberships::Base
|
|
27
27
|
|
28
28
|
scope :current_and_invited, -> { includes(:invitation).where("user_id IS NOT NULL OR invitations.id IS NOT NULL").references(:invitation) }
|
29
29
|
scope :current, -> { where("user_id IS NOT NULL") }
|
30
|
-
scope :tombstones, -> { includes(:invitation).where("user_id IS NULL AND invitations.id IS NULL").references(:invitation) }
|
30
|
+
scope :tombstones, -> { includes(:invitation).where("user_id IS NULL AND invitations.id IS NULL AND platform_agent IS FALSE").references(:invitation) }
|
31
31
|
|
32
32
|
# TODO Probably we can provide a way for gem packages to define these kinds of extensions.
|
33
33
|
if billing_enabled?
|
@@ -62,7 +62,7 @@ module Memberships::Base
|
|
62
62
|
end
|
63
63
|
|
64
64
|
def tombstone?
|
65
|
-
user.nil? && invitation.nil? && !platform_agent
|
65
|
+
user.nil? && invitation.nil? && !platform_agent
|
66
66
|
end
|
67
67
|
|
68
68
|
def last_admin?
|
@@ -130,7 +130,7 @@ module Memberships::Base
|
|
130
130
|
end
|
131
131
|
|
132
132
|
def first_name_last_initial
|
133
|
-
[first_name, last_initial].
|
133
|
+
[first_name, last_initial].select(&:present?).join(" ")
|
134
134
|
end
|
135
135
|
|
136
136
|
# TODO utilize this.
|
@@ -138,8 +138,4 @@ module Memberships::Base
|
|
138
138
|
def should_receive_notifications?
|
139
139
|
invitation.present? || user.present?
|
140
140
|
end
|
141
|
-
|
142
|
-
def platform_agent?
|
143
|
-
platform_agent_of_id.present?
|
144
|
-
end
|
145
141
|
end
|
@@ -109,10 +109,12 @@ module Users::Base
|
|
109
109
|
end
|
110
110
|
end
|
111
111
|
|
112
|
+
# TODO https://github.com/bullet-train-co/bullet_train-base/pull/121 should have removed this, but it caused errors.
|
112
113
|
def administrating_team_ids
|
113
114
|
parent_ids_for(Role.admin, :memberships, :team)
|
114
115
|
end
|
115
116
|
|
117
|
+
# TODO https://github.com/bullet-train-co/bullet_train-base/pull/121 should have removed this, but it caused errors.
|
116
118
|
def parent_ids_for(role, through, parent)
|
117
119
|
parent_id_column = "#{parent}_id"
|
118
120
|
key = "#{role.key}_#{through}_#{parent_id_column}s"
|
@@ -125,6 +127,7 @@ module Users::Base
|
|
125
127
|
value
|
126
128
|
end
|
127
129
|
|
130
|
+
# TODO https://github.com/bullet-train-co/bullet_train-base/pull/121 should have removed this, but it caused errors.
|
128
131
|
def invalidate_ability_cache
|
129
132
|
update_column(:ability_cache, {})
|
130
133
|
end
|
@@ -6,12 +6,12 @@
|
|
6
6
|
<%= render 'account/shared/box' do |p| %>
|
7
7
|
<% p.content_for :title, t(".contexts.#{context.class.name.underscore}.header") %>
|
8
8
|
<% p.content_for :description do %>
|
9
|
-
<%= raw t(".contexts.#{context.class.name.underscore}.#{memberships.any? ? 'description' : 'description_empty'}") %>
|
10
|
-
<%= render "shared/limits/index", model: memberships.model %>
|
9
|
+
<%= raw t(".contexts.#{context.class.name.underscore}.#{@memberships.any? ? 'description' : 'description_empty'}") %>
|
10
|
+
<%= render "shared/limits/index", model: @memberships.model %>
|
11
11
|
<% end %>
|
12
12
|
|
13
13
|
<% p.content_for :table do %>
|
14
|
-
<% if memberships.any? %>
|
14
|
+
<% if @memberships.any? %>
|
15
15
|
<table class="table">
|
16
16
|
<thead>
|
17
17
|
<tr>
|
@@ -22,38 +22,7 @@
|
|
22
22
|
</tr>
|
23
23
|
</thead>
|
24
24
|
<tbody data-model="Membership" data-scope="current">
|
25
|
-
|
26
|
-
<tr data-id="<%= membership.id %>">
|
27
|
-
|
28
|
-
<td class="px-6 py-4 whitespace-nowrap">
|
29
|
-
<%= link_to [:account, membership], class: 'block flex items-center group hover:no-underline no-underline' do %>
|
30
|
-
<div class="flex-shrink-0 h-10 w-10">
|
31
|
-
<%= image_tag membership_profile_photo_url(membership), title: membership.label_string, class: 'h-10 w-10 rounded-full' %>
|
32
|
-
</div>
|
33
|
-
|
34
|
-
<div class="ml-3">
|
35
|
-
<span class="group-hover:underline"><%= membership.label_string %></span>
|
36
|
-
<% if membership.unclaimed? %>
|
37
|
-
<span class="ml-1.5 px-2 inline-flex text-xs text-green-dark bg-green-light border border-green-dark rounded-md">
|
38
|
-
Invited
|
39
|
-
</span>
|
40
|
-
<% end %>
|
41
|
-
</div>
|
42
|
-
<% end %>
|
43
|
-
</td>
|
44
|
-
|
45
|
-
<td>
|
46
|
-
<% if membership.roles_without_defaults.any? %>
|
47
|
-
<%= membership.roles_without_defaults.map { |role| t("memberships.fields.role_ids.options.#{role.key}.label") }.to_sentence %>
|
48
|
-
<% else %>
|
49
|
-
<%= t("memberships.fields.role_ids.options.default.label") %>
|
50
|
-
<% end %>
|
51
|
-
</td>
|
52
|
-
<td class="text-right">
|
53
|
-
<%= link_to t('.buttons.show'), [:account, membership], class: 'button-secondary button-smaller' %>
|
54
|
-
</td>
|
55
|
-
</tr>
|
56
|
-
<% end %>
|
25
|
+
<%= yield %>
|
57
26
|
</tbody>
|
58
27
|
</table>
|
59
28
|
<% end %>
|
@@ -0,0 +1,29 @@
|
|
1
|
+
<tr data-id="<%= membership.id %>">
|
2
|
+
<td class="px-6 py-4 whitespace-nowrap">
|
3
|
+
<%= link_to [:account, membership], class: 'block flex items-center group hover:no-underline no-underline' do %>
|
4
|
+
<div class="flex-shrink-0 h-10 w-10">
|
5
|
+
<%= image_tag membership_profile_photo_url(membership), title: membership.label_string, class: 'h-10 w-10 rounded-full' %>
|
6
|
+
</div>
|
7
|
+
|
8
|
+
<div class="ml-3">
|
9
|
+
<span class="group-hover:underline"><%= membership.label_string %></span>
|
10
|
+
<% if membership.unclaimed? %>
|
11
|
+
<span class="ml-1.5 px-2 inline-flex text-xs text-green-dark bg-green-light border border-green-dark rounded-md">
|
12
|
+
Invited
|
13
|
+
</span>
|
14
|
+
<% end %>
|
15
|
+
</div>
|
16
|
+
<% end %>
|
17
|
+
</td>
|
18
|
+
|
19
|
+
<td>
|
20
|
+
<% if membership.roles_without_defaults.any? %>
|
21
|
+
<%= membership.roles_without_defaults.map { |role| t("memberships.fields.role_ids.options.#{role.key}.label") }.to_sentence %>
|
22
|
+
<% else %>
|
23
|
+
<%= t("memberships.fields.role_ids.options.default.label") %>
|
24
|
+
<% end %>
|
25
|
+
</td>
|
26
|
+
<td class="text-right">
|
27
|
+
<%= link_to t('.buttons.show'), [:account, membership], class: 'button-secondary button-smaller' %>
|
28
|
+
</td>
|
29
|
+
</tr>
|
@@ -1,7 +1,12 @@
|
|
1
1
|
<%= render 'account/shared/page' do |p| %>
|
2
2
|
<% p.content_for :title, t('.section') %>
|
3
3
|
<% p.content_for :body do %>
|
4
|
-
|
4
|
+
<% if @memberships.current_and_invited.any? %>
|
5
|
+
<%= render 'index' do %>
|
6
|
+
<%= render @memberships.current_and_invited.includes(:user) %>
|
7
|
+
<% end %>
|
8
|
+
<% end %>
|
9
|
+
|
5
10
|
<%= render 'tombstones', memberships: @memberships.tombstones.includes(:user) if @memberships.tombstones.any? %>
|
6
11
|
<% end %>
|
7
12
|
<% end %>
|
@@ -46,7 +46,7 @@
|
|
46
46
|
<% else %>
|
47
47
|
<%= link_to t('.buttons.promote'), [:promote, :account, @membership], method: :post, data: { confirm: t('global.confirm_message') }, class: first_button_primary if can? :promote, @membership %>
|
48
48
|
<% end %>
|
49
|
-
<% if (can? :destroy, @membership) && (!@membership.platform_agent
|
49
|
+
<% if (can? :destroy, @membership) && (!@membership.platform_agent) %>
|
50
50
|
<%= button_to t(".buttons.#{membership_destroy_locale_key(@membership)}"), [:account, @membership], method: :delete, data: { confirm: t(".buttons.confirmations.#{membership_destroy_locale_key(@membership)}", model_locales(@membership)) }, class: first_button_primary %>
|
51
51
|
<% end %>
|
52
52
|
<% end %>
|
@@ -16,15 +16,15 @@
|
|
16
16
|
<%= render 'shared/fields/text_field', form: f, method: :last_name %>
|
17
17
|
</div>
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
<% # only edit the team name if this user is an admin and they are the first user. %>
|
20
|
+
<% # yes, that's redundant. %>
|
21
|
+
<% if can?(:edit, f.object.current_team) && f.object.current_team.users.count == 1 %>
|
22
|
+
<div class="sm:col-span-2">
|
23
23
|
<%= f.fields_for :current_team do |tf| %>
|
24
24
|
<%= render 'shared/fields/text_field', form: tf, method: :name %>
|
25
25
|
<% end %>
|
26
|
-
|
27
|
-
|
26
|
+
</div>
|
27
|
+
<% end %>
|
28
28
|
|
29
29
|
<div class="sm:col-span-2">
|
30
30
|
<%= render 'shared/fields/super_select', form: f, method: :time_zone,
|
@@ -1,29 +1,5 @@
|
|
1
1
|
<ul class="space-y">
|
2
|
-
|
3
|
-
<li class="bg-white shadow overflow-hidden sm:rounded-md dark:bg-sealBlue-400">
|
4
|
-
<%= link_to [:account, team], class: "group block hover:bg-gray-50 dark:hover:bg-sealBlue-400 dark:text-sealBlue-800" do %>
|
5
|
-
<div class="px-4 py-4 flex items-center sm:pl-8 sm:pr-6">
|
6
|
-
<div class="min-w-0 flex-1 sm:flex sm:items-center sm:justify-between">
|
7
|
-
<div>
|
8
|
-
<div class="flex text-xl font-semibold text-blue uppercase group-hover:text-blue-dark tracking-widest dark:text-white">
|
9
|
-
<%= team.name %>
|
10
|
-
</div>
|
11
|
-
</div>
|
12
|
-
<div class="mt-4 flex-shrink-0 sm:mt-0">
|
13
|
-
<div class="flex overflow-hidden">
|
14
|
-
<%= render 'account/shared/memberships/photos', memberships: team.memberships.current_and_invited.first(10) %>
|
15
|
-
</div>
|
16
|
-
</div>
|
17
|
-
</div>
|
18
|
-
<div class="ml-5 flex-shrink-0">
|
19
|
-
<svg class="h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
20
|
-
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
|
21
|
-
</svg>
|
22
|
-
</div>
|
23
|
-
</div>
|
24
|
-
<% end %>
|
25
|
-
</li>
|
26
|
-
<% end %>
|
2
|
+
<%= yield %>
|
27
3
|
</ul>
|
28
4
|
|
29
5
|
<% if show_sign_up_options? && can?(:create, Team.new) %>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<li class="bg-white shadow overflow-hidden sm:rounded-md dark:bg-sealBlue-400">
|
2
|
+
<%= link_to [:account, team], class: "group block hover:bg-gray-50 dark:hover:bg-sealBlue-400 dark:text-sealBlue-800" do %>
|
3
|
+
<div class="px-4 py-4 flex items-center sm:pl-8 sm:pr-6">
|
4
|
+
<div class="min-w-0 flex-1 sm:flex sm:items-center sm:justify-between">
|
5
|
+
<div>
|
6
|
+
<div class="flex text-xl font-semibold text-blue uppercase group-hover:text-blue-dark tracking-widest dark:text-white">
|
7
|
+
<%= team.name %>
|
8
|
+
</div>
|
9
|
+
</div>
|
10
|
+
<div class="mt-4 flex-shrink-0 sm:mt-0">
|
11
|
+
<div class="flex overflow-hidden">
|
12
|
+
<%= render 'account/shared/memberships/photos', memberships: team.memberships.current_and_invited.first(10) %>
|
13
|
+
</div>
|
14
|
+
</div>
|
15
|
+
</div>
|
16
|
+
<div class="ml-5 flex-shrink-0">
|
17
|
+
<svg class="h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
18
|
+
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
|
19
|
+
</svg>
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
<% end %>
|
23
|
+
</li>
|
@@ -1,6 +1,8 @@
|
|
1
1
|
<%= render 'account/shared/page' do |p| %>
|
2
2
|
<% p.content_for :title, t('.section') %>
|
3
3
|
<% p.content_for :body do %>
|
4
|
-
<%= render 'index', creative_concepts: @creative_concepts %>
|
4
|
+
<%= render 'index', creative_concepts: @creative_concepts do %>
|
5
|
+
<%= render @teams %>
|
6
|
+
<% end %>
|
5
7
|
<% end %>
|
6
8
|
<% end %>
|
@@ -13,7 +13,7 @@
|
|
13
13
|
<% unless scaffolding_things_disabled? %>
|
14
14
|
<%= render 'account/shared/commentary/box' do |p| %>
|
15
15
|
<% p.content_for :content do %>
|
16
|
-
<%= render 'account/scaffolding/absolutely_abstract/creative_concepts/index', creative_concepts: @team.scaffolding_absolutely_abstract_creative_concepts
|
16
|
+
<%= render 'account/scaffolding/absolutely_abstract/creative_concepts/index', creative_concepts: @team.scaffolding_absolutely_abstract_creative_concepts, hide_back: true unless scaffolding_things_disabled? %>
|
17
17
|
<% end %>
|
18
18
|
|
19
19
|
<% p.content_for :commentary do %>
|
@@ -2,7 +2,7 @@
|
|
2
2
|
<% p.content_for :title, t('devise.headers.sign_in') %>
|
3
3
|
<% p.content_for :body do %>
|
4
4
|
<% within_fields_namespace(:self) do %>
|
5
|
-
<%= form_for resource, as: resource_name, url: two_factor_authentication_enabled? ? users_pre_otp_path : session_path(resource_name), remote: two_factor_authentication_enabled?, html: {class: 'form'}, authenticity_token: true do |form| %>
|
5
|
+
<%= form_for resource, as: resource_name, url: two_factor_authentication_enabled? ? users_pre_otp_path : session_path(resource_name), remote: two_factor_authentication_enabled?, html: {class: 'form'}, authenticity_token: true, data: {turbo: !user_return_to_is_oauth} do |form| %>
|
6
6
|
<% with_field_settings form: form do %>
|
7
7
|
<%= render 'account/shared/notices', form: form %>
|
8
8
|
<%= render 'account/shared/forms/errors', form: form %>
|
@@ -108,6 +108,12 @@
|
|
108
108
|
<i class="fa-brands fa-js ti ti-pulse"></i>
|
109
109
|
<% end %>
|
110
110
|
<% end %>
|
111
|
+
|
112
|
+
<%= render 'account/shared/menu/item', url: '/docs/i18n', label: 'Internationalzation' do |p| %>
|
113
|
+
<% p.content_for :icon do %>
|
114
|
+
<i class="fa-brands fa-js ti ti-world"></i>
|
115
|
+
<% end %>
|
116
|
+
<% end %>
|
111
117
|
<% end %>
|
112
118
|
|
113
119
|
<%= render 'account/shared/menu/section', title: 'Developer Tools' do %>
|
@@ -140,6 +146,12 @@
|
|
140
146
|
<i class="fal fa-check ti ti-video-camera"></i>
|
141
147
|
<% end %>
|
142
148
|
<% end %>
|
149
|
+
|
150
|
+
<%= render 'account/shared/menu/item', url: 'docs/application-options.md', label: 'Application Options' do |p| %>
|
151
|
+
<% p.content_for :icon do %>
|
152
|
+
<i class="fal fa-gear ti ti-settings"></i>
|
153
|
+
<% end %>
|
154
|
+
<% end %>
|
143
155
|
<% end %>
|
144
156
|
|
145
157
|
<%= render 'account/shared/menu/section', title: 'Accounts & Teams' do %>
|
data/config/locales/en/base.yml
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
en:
|
2
2
|
framework_packages:
|
3
3
|
bullet_train:
|
4
|
-
git: "bullet-train-co/bullet_train-
|
4
|
+
git: "bullet-train-co/bullet_train-core"
|
5
5
|
npm: "@bullet-train/bullet-train"
|
6
6
|
bullet_train-api:
|
7
|
-
git: "bullet-train-co/bullet_train-
|
7
|
+
git: "bullet-train-co/bullet_train-core"
|
8
8
|
bullet_train-fields:
|
9
9
|
git: "bullet-train-co/bullet_train-core"
|
10
10
|
npm: "@bullet-train/fields"
|
@@ -19,7 +19,7 @@ en:
|
|
19
19
|
bullet_train-obfuscates_id:
|
20
20
|
git: "bullet-train-co/bullet_train-core"
|
21
21
|
bullet_train-outgoing_webhooks:
|
22
|
-
git: "bullet-train-co/bullet_train-
|
22
|
+
git: "bullet-train-co/bullet_train-core"
|
23
23
|
bullet_train-scope_questions:
|
24
24
|
git: "bullet-train-co/bullet_train-core"
|
25
25
|
bullet_train-scope_validator:
|
@@ -28,7 +28,7 @@ en:
|
|
28
28
|
git: "bullet-train-co/bullet_train-core"
|
29
29
|
npm: "@bullet-train/bullet-train-sortable"
|
30
30
|
bullet_train-super_scaffolding:
|
31
|
-
git: "bullet-train-co/bullet_train-
|
31
|
+
git: "bullet-train-co/bullet_train-core"
|
32
32
|
bullet_train-super_load_and_authorize_resource:
|
33
33
|
git: "bullet-train-co/bullet_train-core"
|
34
34
|
bullet_train-themes:
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# Application Options
|
2
|
+
|
3
|
+
Bullet Train features a list of options available at your disposal to enable/disable functionalities that would otherwise take a significant amount of time to implement. Simply add any of the following environment variables to `config/application.yml` in your main Bullet Train application and restart your server for the options to apply.
|
4
|
+
|
5
|
+
The helper methods below can also be directly invoked in your application if you wish to have parts of your code depend on the functionality in question.
|
6
|
+
|
7
|
+
| Option | Type | Example | Helper Methods |
|
8
|
+
| --- | --- | --- | --- |
|
9
|
+
| HIDE_THINGS | Boolean | `"true"` | `scaffolding_things_disabled?` |
|
10
|
+
| HIDE_EXAMPLES | Boolean | `"true"` | `scaffolding_things_disabled?` |
|
11
|
+
| STRIPE_CLIENT_ID | String | `"your_stripe_client_id"` | `stripe_enabled?` |
|
12
|
+
| CLOUDINARY_URL | String | `"cloudinary://your_cloudinary_token_here"` | `cloudinary_enabled?` |
|
13
|
+
| TWO_FACTOR_ENCRYPTION_KEY | String | `"your_encryption_key"` | `two_factor_enabled_authentication?` |
|
14
|
+
| INVITATION_KEYS | String | `"ofr9h5h9ghzeodh, ofr9h5h9ghzeodi"` | `invitation_keys` `invitation_only?` |
|
15
|
+
| FONTAWESOME_NPM_AUTH_TOKEN | String | `"your_font_awesome_token"` | `font_awesome?` |
|
16
|
+
| SILENCE_LOGS | Boolean | `"true"` | `silence_logs?` |
|
17
|
+
| TESTING_PROVISION_KEY | String | `"asdf123"` | N/A |
|
18
|
+
|
19
|
+
| Option | Description |
|
20
|
+
| --- | --- |
|
21
|
+
| HIDE_THINGS | Hides Bullet Train demo models such as `CreativeConcept` and `TangibleThing`. |
|
22
|
+
| HIDE_EXAMPLES | Hides base models such as `CreativeConcept` and `TangibleThing`.
|
23
|
+
| STRIPE_CLIENT_ID | See [Bullet Train Billing for Stripe](/docs/billing/stripe.md) for more information and related environment variables. |
|
24
|
+
| CLOUDINARY_URL | Enables use of Cloudinary for handling images. |
|
25
|
+
| TWO_FACTOR_ENCRYPTION_KEY | Enables two-factor authentication through Devise. |
|
26
|
+
| INVITATION_KEYS | See more [Invitation Only](/docs/invitation_only.md) for more information. |
|
27
|
+
| FONTAWESOME_NPM_AUTH_TOKEN | Enables use of Font Awesome. |
|
28
|
+
| SILENCE_LOGS | Silences Super Scaffolding logs. |
|
29
|
+
| TESTING_PROVISION_KEY | Creates a test `Platform::Application` by accessing `/testing/provision?key=your_provision_key` |
|
data/docs/authentication.md
CHANGED
@@ -11,3 +11,12 @@ bin/resolve SessionsController --eject --open
|
|
11
11
|
|
12
12
|
## Customizing Views
|
13
13
|
You can customize Devise views using the same workflow you would use to customize any other Bullet Train views.
|
14
|
+
|
15
|
+
## Disabling Registration
|
16
|
+
|
17
|
+
Registration is enabled by default. You can disable registration, allowing signups via an invite code only, by using [Invitation Only Mode](/docs/invitation_only.md)
|
18
|
+
|
19
|
+
## Two factor authentication
|
20
|
+
|
21
|
+
This feature allows users to add two factor authentication.
|
22
|
+
It requires some setup - [Two Factor Authentication](/docs/two-factor-authentication.md)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# Examples and setup for the `file_field` Field Partial
|
2
|
+
|
3
|
+
## Active Storage
|
4
|
+
|
5
|
+
`file_field` is designed to be used with [Active Storage](https://edgeguides.rubyonrails.org/active_storage_overview.html). You will need to confgure Active Storage for your application before using this field partial. You can find instructions for doing so in the [Rails Guides](https://edgeguides.rubyonrails.org/active_storage_overview.html#setup).
|
6
|
+
|
7
|
+
In addition, Bullet Train has integrated the direct-uploads feature of Active Storage. For this to work, you need to have CORS configured for your storage endpoint. You can find instructions for doing so in the [Rails Guides](https://edgeguides.rubyonrails.org/active_storage_overview.html#cross-origin-resource-sharing-cors-configuration).
|
8
|
+
|
9
|
+
## Example
|
10
|
+
|
11
|
+
Add a 'document' file as an attachment to a `Post` model:
|
12
|
+
|
13
|
+
Add the following to `app/models/post.rb`:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
has_one_attached :document
|
17
|
+
```
|
18
|
+
|
19
|
+
Note, no database migration is required as ActiveStorage uses its own tables to store the attachments.
|
20
|
+
|
21
|
+
Run the following command to generate the scaffolding for the `document` field on the `Post` model:
|
22
|
+
|
23
|
+
```bash
|
24
|
+
./bin/super-scaffold crud-field Post document:file_field
|
25
|
+
```
|
data/docs/field-partials.md
CHANGED
@@ -118,7 +118,7 @@ Certain form field partials like `buttons` and `super_select` can also have thei
|
|
118
118
|
| `date_and_time_field` | `datetime` | | `assign_date_and_time` | [Date Range Picker](https://www.daterangepicker.com) | | |
|
119
119
|
| `date_field` | `date` | | `assign_date` | [Date Range Picker](https://www.daterangepicker.com) | | |
|
120
120
|
| `email_field` | `string` | | | | | |
|
121
|
-
| `file_field` | `attachment` | | [Active Storage](https://edgeguides.rubyonrails.org/active_storage_overview.html) | | |
|
121
|
+
| [`file_field`](/docs/field-partials/file-field.md) | `attachment` | | [Active Storage](https://edgeguides.rubyonrails.org/active_storage_overview.html) | | |
|
122
122
|
| `options` | `string` | Optionally | `assign_checkboxes` | | | |
|
123
123
|
| `password_field` | `string` | | | | | |
|
124
124
|
| `phone_field` | `string` | | | [International Telephone Input](https://intl-tel-input.com) | Ensures telephone numbers are in a format that can be used by providers like Twilio. | |
|
@@ -137,3 +137,5 @@ Set the data type to `jsonb` whenever passing the `multiple` option to a new att
|
|
137
137
|
## Additional Field Partials Documentation
|
138
138
|
- [`buttons`](/docs/field-partials/buttons.md)
|
139
139
|
- [`super_select`](/docs/field-partials/super-select.md)
|
140
|
+
- [`file_field`](/docs/field-partials/file-field.md)
|
141
|
+
|
data/docs/i18n.md
CHANGED
@@ -1,3 +1,31 @@
|
|
1
1
|
# Translations and Internationalization
|
2
2
|
|
3
3
|
Bullet Train and views generated by Super Scaffolding are localized by default, meaning all of the human-readable text in your application has been extracted into a YAML configuration file in `config/locales/en` and can be translated into any language you would like to target.
|
4
|
+
|
5
|
+
We override the native I18n translation method to automatically include the current team name or other objects depending on the string. For example, here's a description of a membership which you can find on a membership's show page:
|
6
|
+
|
7
|
+
```
|
8
|
+
The following are the details for Davidās Membership on Your Team.
|
9
|
+
```
|
10
|
+
|
11
|
+
The view can be found here in the `bullet_train-base` gem:<br/>[bullet_train-base/app/views/account/memberships/show.html.erb](https://github.com/bullet-train-co/bullet_train-base/blob/657e932cb4eb3e0c1f56c88c8365c2611de90e06/app/views/account/memberships/show.html.erb#L16)<br/>
|
12
|
+
<br/>
|
13
|
+
Looking at the view, you can see we are only passing a key to the translation method for I18n to process:
|
14
|
+
|
15
|
+
```erb
|
16
|
+
<%= t('.description') %>
|
17
|
+
```
|
18
|
+
|
19
|
+
However, looking at the [locale itself](https://github.com/bullet-train-co/bullet_train-base/blob/657e932cb4eb3e0c1f56c88c8365c2611de90e06/config/locales/en/memberships.en.yml#L82), you can see that the string takes two variables, `memberships_possessive` and `team_name`, to complete the string:
|
20
|
+
```yaml
|
21
|
+
description: The following are the details for %{memberships_possessive} Membership on %{team_name}.
|
22
|
+
```
|
23
|
+
|
24
|
+
Usually, you would pass the variable as a keyword argument:
|
25
|
+
```ruby
|
26
|
+
t('.description', memberships_possessive: memberships_possessive, team_name: current_team.name)
|
27
|
+
```
|
28
|
+
|
29
|
+
However, in Bullet Train, we override the original translation method to include variable names like this automatically in our locales. Check out the [locale helper](https://github.com/bullet-train-co/bullet_train-base/blob/main/app/helpers/account/locale_helper.rb) to get a closer look at how we handle strings for internationalization. For example, the two variables above are generated by the method `model_locales` in the locale helper.
|
30
|
+
|
31
|
+
You can find more information in the [indirection documentation](indirection) about using `bin/resolve` and logs to pinpoint where your locales are coming from.
|
data/docs/index.md
CHANGED
@@ -13,6 +13,7 @@
|
|
13
13
|
- [Overriding the Framework](/docs/overriding.md)
|
14
14
|
- [Setting up a Tunnel](/docs/tunneling.md)
|
15
15
|
- [JavaScript](/docs/javascript.md)
|
16
|
+
- [Internationalization](/docs/i18n.md)
|
16
17
|
|
17
18
|
## Developer Tools
|
18
19
|
- [Super Scaffolding](/docs/super-scaffolding.md)
|
@@ -20,6 +21,7 @@
|
|
20
21
|
- [Database Seeds](/docs/seeds.md)
|
21
22
|
- [Test Suite](/docs/testing.md)
|
22
23
|
- [Point-and-Click Test Writing](https://github.com/bullet-train-co/magic_test) <i class="ti ti-new-window ml-2"></i>
|
24
|
+
- [Application Options](/docs/application-options.md)
|
23
25
|
|
24
26
|
## Accounts & Teams
|
25
27
|
- [Authentication](/docs/authentication.md)
|
data/docs/invitation_only.md
CHANGED
data/docs/themes.md
CHANGED
@@ -1,27 +1,21 @@
|
|
1
1
|
# Themes
|
2
2
|
|
3
|
-
Bullet Train has a theme subsystem designed to allow you the flexibility to either extend or completely replace the stock āLightā
|
3
|
+
Bullet Train has a theme subsystem designed to allow you the flexibility to either extend or completely replace the stock āLightā UI template.
|
4
|
+
To reduce duplication of code across themes, Bullet Train implements the following three packages:
|
5
|
+
1. `bullet_train-themes`
|
6
|
+
2. `bullet_train-themes-tailwind_css`
|
7
|
+
3. `bullet_train-themes-light`
|
4
8
|
|
5
|
-
|
9
|
+
This is where all of Bullet Train's standard views are contained.
|
6
10
|
|
7
|
-
|
11
|
+
## Adding a New Theme (ejecting standard views)
|
8
12
|
|
9
|
-
-
|
10
|
-
|
11
|
-
- āCleanā (in the future)
|
12
|
-
- āTailwind CSSā
|
13
|
-
- āLightā
|
13
|
+
If you want to add a new theme, you can use the following command. This will copy all of the standard views from `bullet_train-themes-light` to `app/views/themes/` and configure your application to use the new theme. For example, let's make a new theme called "foo":
|
14
|
+
`> rake bullet_train:themes:light:eject[foo]`
|
14
15
|
|
15
|
-
|
16
|
+
After running this command, you will see that a few other files are edited to use this new theme. Whenever switching a theme, you will need to make the same changes to make sure your application is running with the theme of your choice.
|
16
17
|
|
17
|
-
|
18
|
-
- However, many concrete field types like `_text_field.html.erb` and `_phone_field.html.erb` leverage `_field.html.erb`, and they themselves are completely framework agnostic as a result. These partials can live in the shared āBaseā theme.
|
19
|
-
|
20
|
-
At run-time, this means:
|
21
|
-
|
22
|
-
- When rendering `_text_field.html.erb`, it renders from āBaseā.
|
23
|
-
- However, when `_text_field.html.erb` references `_field.html.erb`, that renders from āLightā.
|
24
|
-
- If you extend āLightā and override `_field.html.erb`, rendering `_text_field.html.erb` will now use your themeās `_field.html.erb`.
|
18
|
+
You can also pass an annotated path to a view after running `bin/resolve` to eject individual views to your application.
|
25
19
|
|
26
20
|
## Theme Component Usage
|
27
21
|
|
@@ -35,32 +29,11 @@ We say "within" because while a `shared` view partial directory does exist, the
|
|
35
29
|
|
36
30
|
### Dealing with Indirection
|
37
31
|
|
38
|
-
This small piece of indirection buys us an incredible amount of power in building and extending themes, but as with any indirection, it could potentially come at the cost of developer experience. That's why Bullet Train includes additional tools for smoothing over this experience. Be sure to read the section on [dealing with indirection].
|
39
|
-
|
32
|
+
This small piece of indirection buys us an incredible amount of power in building and extending themes, but as with any indirection, it could potentially come at the cost of developer experience. That's why Bullet Train includes additional tools for smoothing over this experience. Be sure to read the section on [dealing with indirection](./indirection.md).
|
40
33
|
|
41
34
|
## Theme Configuration
|
42
35
|
|
43
|
-
|
44
|
-
|
45
|
-
## Theme Structure
|
46
|
-
|
47
|
-
Themes are represented in a few places. Taking āLightā as an example, we have:
|
48
|
-
|
49
|
-
- A directory of theme-specific component partials in `app/views/themes/light`, including a layout ERB template.
|
50
|
-
- A theme-specific stylesheet in `app/javascript/stylesheets/light/application.scss`.
|
51
|
-
- A theme-specific pack in `app/javascript/packs/light.js`. Youāll see there that the actual JavaScript dependencies and code are shared across all themes. The whole purpose of this theme-specific pack is to serve up the theme-specific stylesheet.
|
52
|
-
- Theme-specific logos and images in `app/javascript/images/light`.
|
53
|
-
|
54
|
-
## Adding a New Theme
|
55
|
-
|
56
|
-
To extend the āLightā theme in a new theme called āTokyoā, we would:
|
57
|
-
|
58
|
-
1. Copy `app/javascript/packs/light.js` to `app/javascript/packs/tokyo.js` and update references to `light` therein to `tokyo`.
|
59
|
-
2. Copy `app/views/themes/light/layouts` to `app/views/themes/tokyo/layouts` and update references to `light` in the contained files to `tokyo`. It's possible this is too much duplication, but in practice most people want to customize these two layouts in their custom themes.
|
60
|
-
3. Create a new file at `app/javascript/stylesheets/tokyo/application.scss`. To start just add `@import "../light/application";` at the top, which represents the fact that āTokyoā extends āLightā. Any custom styles can be added below that.
|
61
|
-
4. Add `"tokyo"` as the first item in the `THEME_DIRECTORY_ORDER` array in `app/helpers/theme_helper.rb`.
|
62
|
-
|
63
|
-
You should be good to go! We'll try to add a generator for this in the future.
|
36
|
+
Your application will automatically be configured to use your new theme whenever you run the eject command. Run `> rake bullet_train:themes:light:install` to re-install the standard light theme.
|
64
37
|
|
65
38
|
## Additional Guidance and Principles
|
66
39
|
|
data/docs/trademark.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# Trademark Information
|
2
|
+
|
3
|
+
"Bullet Train" is a registered trademark of Bullet Train, Inc.
|
4
|
+
|
5
|
+
We love and encourage contributions to the Bullet Train ecosystem. That's our dream!
|
6
|
+
|
7
|
+
It's also important when you're building packages or presenting offerings in the Bullet Train ecosystem, they're not named in such a way that anyone could confuse your package or offering as being officially maintained or sanctioned by the Bullet Train team.
|
8
|
+
|
9
|
+
Here are some examples of names we would not typically object to:
|
10
|
+
|
11
|
+
- "Super Widget __for Bullet Train__"
|
12
|
+
- "MySQL Starter Kit __for Bullet Train__"
|
13
|
+
- "__BT__ Starter Kit with MySQL"
|
14
|
+
- "__BT__ Pro Tools"
|
15
|
+
|
16
|
+
Here are some examples that would require explicit permission:
|
17
|
+
|
18
|
+
- "Bullet Train Super Widget"
|
19
|
+
- "Bullet Train MySQL Starter Kit"
|
20
|
+
- "Bullet Train Application Template with MySQL"
|
21
|
+
- "Bullet Train Pro Tools"
|
22
|
+
- "Bullet Train Conference"
|
23
|
+
- "Bullet Train Podcast"
|
24
|
+
|
25
|
+
If you've got an idea and aren't sure about the name, message Andrew Culver privately [on Discord](https://discord.gg/bullettrain) or [via Twitter DM](https://twitter.com/andrewculver). If you know anything about trademarks, you know we're required to enforce our policies, so thank you for understanding! š
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# Two Factor Authentication
|
2
|
+
|
3
|
+
## Setup
|
4
|
+
|
5
|
+
run `bin/rails db:encryption:init` and use `bin/rails credentials:edit` to add the resulting keys to your `secrets.yml`
|
6
|
+
|
7
|
+
Add the following gems to your `Gemfile` and run `bundle install`
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem "devise-two-factor"
|
11
|
+
gem "rqrcode"
|
12
|
+
```
|
13
|
+
|
14
|
+
If you haven't already done so, set the environment variable `RAILS_MASTER_KEY` with the contents of `config/master.key`. Note, this file should not be committed to git, and you should keep it in a safe place.
|
15
|
+
|
16
|
+
Now in the user's Account Details page there will be an option to enable two factor, and when enabled the two factor code will be required at login.
|
@@ -87,6 +87,18 @@ module BulletTrain
|
|
87
87
|
}
|
88
88
|
|
89
89
|
result[:absolute_path] = file_path || class_path || partial_path || locale_path
|
90
|
+
|
91
|
+
# If we get the partial resolver template itself, that means we couldn't find the file.
|
92
|
+
if result[:absolute_path].match?("app/views/bullet_train/partial_resolver.html.erb")
|
93
|
+
puts "We could not find the partial you're looking for: #{@needle}".red
|
94
|
+
puts ""
|
95
|
+
puts "Please try passing the partial string using either of the following two ways:"
|
96
|
+
puts "1. Without underscore and extention: ".blue + "bin/resolve shared/attributes/code"
|
97
|
+
puts "2. Literal path with package name: ".blue + "bin/resolve bullet_train-themes/app/views/themes/base/attributes/_code.html.erb"
|
98
|
+
puts ""
|
99
|
+
exit
|
100
|
+
end
|
101
|
+
|
90
102
|
if result[:absolute_path]
|
91
103
|
if result[:absolute_path].include?("/bullet_train")
|
92
104
|
base_path = "bullet_train" + result[:absolute_path].partition("/bullet_train").last
|
@@ -123,6 +135,41 @@ module BulletTrain
|
|
123
135
|
end
|
124
136
|
|
125
137
|
def partial_path
|
138
|
+
# Parse literal partial strings.
|
139
|
+
if @needle.match?(/\.html\.erb$/)
|
140
|
+
partial_parts = @needle.split("/")
|
141
|
+
|
142
|
+
# TODO: We should probably just default to raising an error if the developer
|
143
|
+
# provides a literal partial string without the name of the package it's coming from.
|
144
|
+
if partial_parts.size <= 3
|
145
|
+
# If the string looks something like "shared/attributes/_code.html.erb",
|
146
|
+
# all we need to do is change it to "shared/attributes/code"
|
147
|
+
partial_parts.last.gsub!(/(_)|(\.html\.erb)/, "")
|
148
|
+
@needle = partial_parts.join("/")
|
149
|
+
elsif @needle.match?(/bullet_train-/)
|
150
|
+
# If it's a full path, we need to make sure we're getting it from the right package.
|
151
|
+
_, partial_view_package, partial_path_without_package = @needle.partition(/bullet_train-[a-z|\-_0-9.]*/)
|
152
|
+
|
153
|
+
# Pop off the version so we can call `bundle show` correctly.
|
154
|
+
# Also change `bullet_train-base` to `bullet_train`.
|
155
|
+
partial_view_package.gsub!(/[-|.0-9]*$/, "") if partial_view_package.match?(/[-|.0-9]*$/)
|
156
|
+
partial_view_package.gsub!("-base", "") if /base/.match?(@needle)
|
157
|
+
|
158
|
+
local_package_path = `bundle show #{partial_view_package}`.chomp
|
159
|
+
return local_package_path + partial_path_without_package
|
160
|
+
else
|
161
|
+
puts "You passed the absolute path for a partial literal, but we couldn't find the package name in the string:".red
|
162
|
+
puts "`#{@needle}`".red
|
163
|
+
puts ""
|
164
|
+
puts "Check the string one more time to see if the package name is there."
|
165
|
+
puts "i.e.: bullet_train-base/app/views/layouts/devise.html.erb".blue
|
166
|
+
puts ""
|
167
|
+
puts "If you're not sure what the package name is, run `bin/resolve --interactive`, follow the prompt, and pass the annotated path."
|
168
|
+
puts "i.e.: <!-- BEGIN /your/local/path/bullet_train-base/app/views/layouts/devise.html.erb -->".blue
|
169
|
+
exit
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
126
173
|
begin
|
127
174
|
annotated_path = ApplicationController.render(template: "bullet_train/partial_resolver", layout: nil, assigns: {needle: @needle}).lines[1].chomp
|
128
175
|
rescue ActionView::Template::Error => e
|
data/lib/bullet_train/version.rb
CHANGED
data/lib/bullet_train.rb
CHANGED
@@ -53,8 +53,13 @@ def default_url_options_from_base_url
|
|
53
53
|
|
54
54
|
# the name of this property doesn't match up.
|
55
55
|
default_url_options[:protocol] = parsed_base_url.scheme
|
56
|
+
default_url_options.compact!
|
56
57
|
|
57
|
-
default_url_options.
|
58
|
+
if default_url_options.empty?
|
59
|
+
raise "ENV['BASE_URL'] has not been configured correctly. Please check your environment variables and try one more time."
|
60
|
+
end
|
61
|
+
|
62
|
+
default_url_options
|
58
63
|
end
|
59
64
|
|
60
65
|
def inbound_email_enabled?
|
@@ -59,7 +59,16 @@ namespace :bullet_train do
|
|
59
59
|
if ARGV.first.present?
|
60
60
|
BulletTrain::Resolver.new(ARGV.first).run(eject: ARGV.include?("--eject"), open: ARGV.include?("--open"), force: ARGV.include?("--force"), interactive: ARGV.include?("--interactive"))
|
61
61
|
else
|
62
|
-
warn
|
62
|
+
warn <<~MSG
|
63
|
+
š
Usage: #{"`bin/resolve [path, partial, or URL] (--eject) (--open)`".blue}
|
64
|
+
|
65
|
+
OR
|
66
|
+
|
67
|
+
#{"`bin/resolve --interactive`".blue}
|
68
|
+
When you use the interactive flag, we will prompt you to pass an annotated partial like so and either eject or open the file.
|
69
|
+
These annotated paths can be found in your browser when inspecting elements:
|
70
|
+
<!-- BEGIN /your/path/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/bullet_train-themes-light-1.0.51/app/views/themes/light/_notices.html.erb -->
|
71
|
+
MSG
|
63
72
|
end
|
64
73
|
end
|
65
74
|
|
@@ -82,6 +91,55 @@ namespace :bullet_train do
|
|
82
91
|
puts ""
|
83
92
|
end
|
84
93
|
|
94
|
+
# Process any flags that were passed.
|
95
|
+
if arguments[:all_options].present?
|
96
|
+
flags_with_values = []
|
97
|
+
|
98
|
+
arguments[:all_options].split(/\s+/).each do |option|
|
99
|
+
if option.match?(/^--/)
|
100
|
+
flags_with_values << {flag: option.gsub(/^--/, "").to_sym, values: []}
|
101
|
+
else
|
102
|
+
flags_with_values.last[:values] << option
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
if flags_with_values.any?
|
107
|
+
flags_with_values.each do |process|
|
108
|
+
if process[:flag] == :link || process[:flag] == :reset
|
109
|
+
packages = process[:values]
|
110
|
+
|
111
|
+
gemfile_lines = File.readlines("./Gemfile")
|
112
|
+
new_lines = gemfile_lines.map do |line|
|
113
|
+
packages.each do |package|
|
114
|
+
if line.match?(package)
|
115
|
+
original_path = "gem \"bullet_train#{"-" + package if package}\""
|
116
|
+
local_path = "gem \"bullet_train#{"-" + package if package}\", path: \"local/bullet_train#{"-" + package if package}\""
|
117
|
+
|
118
|
+
case process[:flag]
|
119
|
+
when :link
|
120
|
+
line.gsub!(original_path, local_path)
|
121
|
+
puts "Setting local '#{package}' package to the Gemfile...".blue
|
122
|
+
break
|
123
|
+
when :reset
|
124
|
+
line.gsub!(local_path, original_path)
|
125
|
+
puts "Resetting '#{package}' package in the Gemfile...".blue
|
126
|
+
break
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
line
|
132
|
+
end
|
133
|
+
|
134
|
+
File.write("./Gemfile", new_lines.join)
|
135
|
+
system "bundle install"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
exit
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
85
143
|
framework_packages = I18n.t("framework_packages")
|
86
144
|
|
87
145
|
puts "Which framework package do you want to work on?".blue
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bullet_train
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1
|
4
|
+
version: 1.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Culver
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-12-
|
11
|
+
date: 2022-12-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: standard
|
@@ -522,6 +522,7 @@ files:
|
|
522
522
|
- app/views/account/memberships/_fields.html.erb
|
523
523
|
- app/views/account/memberships/_form.html.erb
|
524
524
|
- app/views/account/memberships/_index.html.erb
|
525
|
+
- app/views/account/memberships/_membership.html.erb
|
525
526
|
- app/views/account/memberships/_menu_item.html.erb
|
526
527
|
- app/views/account/memberships/_tombstones.html.erb
|
527
528
|
- app/views/account/memberships/edit.html.erb
|
@@ -534,6 +535,7 @@ files:
|
|
534
535
|
- app/views/account/teams/_form.html.erb
|
535
536
|
- app/views/account/teams/_index.html.erb
|
536
537
|
- app/views/account/teams/_menu_item.html.erb
|
538
|
+
- app/views/account/teams/_team.html.erb
|
537
539
|
- app/views/account/teams/_team.json.jbuilder
|
538
540
|
- app/views/account/teams/edit.html.erb
|
539
541
|
- app/views/account/teams/index.html.erb
|
@@ -624,12 +626,14 @@ files:
|
|
624
626
|
- db/migrate/20211020200855_add_doorkeeper_application_to_memberships.rb
|
625
627
|
- db/migrate/20211027002944_add_doorkeeper_application_to_users.rb
|
626
628
|
- docs/action-models.md
|
629
|
+
- docs/application-options.md
|
627
630
|
- docs/authentication.md
|
628
631
|
- docs/billing/stripe.md
|
629
632
|
- docs/billing/usage.md
|
630
633
|
- docs/desktop.md
|
631
634
|
- docs/field-partials.md
|
632
635
|
- docs/field-partials/buttons.md
|
636
|
+
- docs/field-partials/file-field.md
|
633
637
|
- docs/field-partials/super-select.md
|
634
638
|
- docs/font-awesome-pro.md
|
635
639
|
- docs/getting-started.md
|
@@ -652,7 +656,9 @@ files:
|
|
652
656
|
- docs/teams.md
|
653
657
|
- docs/testing.md
|
654
658
|
- docs/themes.md
|
659
|
+
- docs/trademark.md
|
655
660
|
- docs/tunneling.md
|
661
|
+
- docs/two-factor-authentication.md
|
656
662
|
- docs/upgrades.md
|
657
663
|
- docs/webhooks/incoming.md
|
658
664
|
- docs/webhooks/outgoing.md
|