bible270 0.13.1 → 0.14.0
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 +4 -4
- data/CHANGELOG.md +45 -2
- data/README.md +5 -3
- data/app/mailers/bible270/notice_mailer.rb +32 -0
- data/app/models/bible270/reader.rb +25 -0
- data/app/views/bible270/days/index.html.erb +2 -1
- data/app/views/bible270/notice_mailer/new_reader.html.erb +28 -0
- data/app/views/bible270/notice_mailer/new_reader.text.erb +11 -0
- data/lib/bible270/configuration.rb +34 -0
- data/lib/bible270/names.rb +7 -1
- data/lib/bible270/version.rb +1 -1
- data/lib/generators/bible270/install/install_generator.rb +68 -4
- data/lib/generators/bible270/install/templates/bible270.rb.tt +33 -1
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 54e58fbb0af2af30cb7cdfd8f47c2d57526d464ece9c987365cec2dcb2331d43
|
|
4
|
+
data.tar.gz: b508bbb3ea2cd450cade1173173c680a4fcb34ad41fd3a498bef32cb2b2d9fe5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b7880113db1ceb742f5857e004279ce00674b2adb385f2b3ebfaa16a5eaf910c31f2d452e29db91211f77d622d207a3bff3df64091c1d3b7908a4de93f72674c
|
|
7
|
+
data.tar.gz: a7b080c48d3072eab3554ac4fe4696bf2d8bd0ad69ef202fb7248c134a2b8685597d950222e72b80ba15f7c9449f8007c45bf1f9ec99be38d0228007ddfa23bb
|
data/CHANGELOG.md
CHANGED
|
@@ -6,11 +6,52 @@ All notable changes to bible270. Format follows [Keep a Changelog](https://keepa
|
|
|
6
6
|
|
|
7
7
|
See the changes since the last release:
|
|
8
8
|
|
|
9
|
-
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.
|
|
9
|
+
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.14.0...main)**
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## [0.14.0] - 2026-07-30
|
|
13
|
+
|
|
14
|
+
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.13.1...v0.14.0)**
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **Registration notices.** `config.registration_notice_emails` takes an explicit list or `:admins`
|
|
19
|
+
(following `admin_emails`); off unless set. The notice names the reader, their email, how they
|
|
20
|
+
signed in, when they joined and the running total, and links to their admin page when
|
|
21
|
+
`config.mailer_host` is set. Hooked to reader creation rather than to a controller, so email
|
|
22
|
+
sign-in, OmniAuth and bridged host users are all covered. Failures are logged, never raised —
|
|
23
|
+
nobody is blocked from joining by an SMTP problem. `registration_notice_deliver_later` moves
|
|
24
|
+
delivery off the request.
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- The installer's `bible270.rb` template was missing its closing `end`, so `generate bible270:install`
|
|
29
|
+
wrote an initializer that crashed the host app on boot. A test now renders every generator template
|
|
30
|
+
and compiles the result, across three mount points.
|
|
31
|
+
|
|
32
|
+
## [0.13.1] — 2026-07-29
|
|
33
|
+
|
|
34
|
+
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.13.0...v0.13.1)**
|
|
35
|
+
|
|
36
|
+
Nothing consequential.
|
|
37
|
+
|
|
38
|
+
## [0.13.0] — 2026-07-29
|
|
39
|
+
|
|
40
|
+
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.12.0...v0.13.0)**
|
|
41
|
+
|
|
42
|
+
### Added
|
|
43
|
+
|
|
44
|
+
- favicon support
|
|
45
|
+
- admin ability to update any user's profile, including avatar
|
|
46
|
+
- custom footer options, to replace standard footer, or append to it
|
|
47
|
+
|
|
48
|
+
### Fixed
|
|
49
|
+
|
|
50
|
+
- Correct verb for reading prompt: 'Start' and 'Continue
|
|
10
51
|
|
|
11
52
|
## [0.12.0] — 2026-07-29
|
|
12
53
|
|
|
13
|
-
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.11.0...v0.12.0)**
|
|
54
|
+
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v0.11.0...v0.12.0)**
|
|
14
55
|
|
|
15
56
|
### Added
|
|
16
57
|
|
|
@@ -249,6 +290,8 @@ This is the initial public release.
|
|
|
249
290
|
|
|
250
291
|
* Email sign-in requires working Action Mailer delivery in the host application. Set `config.mailer_from`; delivery is inline by default, or set `email_sign_in_deliver_later` when a queue backend is available.
|
|
251
292
|
|
|
293
|
+
[0.13.1]: https://github.com/avonderluft/bible270/releases/tag/v0.13.1
|
|
294
|
+
[0.13.0]: https://github.com/avonderluft/bible270/releases/tag/v0.13.0
|
|
252
295
|
[0.12.0]: https://github.com/avonderluft/bible270/releases/tag/v0.12.0
|
|
253
296
|
[0.11.0]: https://github.com/avonderluft/bible270/releases/tag/v0.11.0
|
|
254
297
|
[0.10.0]: https://github.com/avonderluft/bible270/releases/tag/v0.10.0
|
data/README.md
CHANGED
|
@@ -49,9 +49,10 @@ That's the whole install. The generator is interactive and walks you through it:
|
|
|
49
49
|
|
|
50
50
|
1. asks where to mount the plan;
|
|
51
51
|
2. asks which sign-in methods you want — email link, OmniAuth, or both — and adds any OmniAuth strategy gems to your `Gemfile`;
|
|
52
|
-
3.
|
|
53
|
-
4.
|
|
54
|
-
5.
|
|
52
|
+
3. Prompts to install ActiveStorage for user avatars
|
|
53
|
+
4. copies migrations into your `db/migrate` and offers to run them;
|
|
54
|
+
5. writes `config/initializers/bible270.rb` (and `omniauth.rb` if you chose social sign-in); and
|
|
55
|
+
6. adds `mount Bible270::Engine, at: Bible270.config.mount_at` to `config/routes.rb`.
|
|
55
56
|
|
|
56
57
|
(Migrations run before the config is written, so a problem in a generated initializer can't block the database work.)
|
|
57
58
|
|
|
@@ -76,6 +77,7 @@ bin/rails generate bible270:install --defaults \
|
|
|
76
77
|
| `--mailer-from=ADDR` | From: address for sign-in emails |
|
|
77
78
|
| `--bundle` / `--no-bundle` | Run `bundle install` after adding strategy gems |
|
|
78
79
|
| `--migrate` / `--no-migrate` | Run `db:migrate` at the end |
|
|
80
|
+
| `--active-storage` / `--no-active-storage` | Install Active Storage if missing (for picture uploads) |
|
|
79
81
|
|
|
80
82
|
The installer is safe to re-run: it leaves an existing `mount` line alone, skips strategy gems already in your Gemfile, and Rails prompts before overwriting an initializer.
|
|
81
83
|
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Bible270
|
|
4
|
+
# Notices to whoever runs the plan, as opposed to mail sent to readers.
|
|
5
|
+
class NoticeMailer < ApplicationMailer
|
|
6
|
+
def new_reader(reader_id:, recipients:)
|
|
7
|
+
@reader = Reader.find_by(id: reader_id)
|
|
8
|
+
return message.perform_deliveries = false if @reader.nil? || recipients.blank?
|
|
9
|
+
|
|
10
|
+
@app_name = Bible270.config.app_name
|
|
11
|
+
@total = Reader.count
|
|
12
|
+
@admin_url = admin_url_for(@reader)
|
|
13
|
+
|
|
14
|
+
mail to: recipients, subject: "#{@app_name}: #{@reader.display_name} has joined"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
# A mailer has no request, so the host is config.mailer_host or nothing. No
|
|
20
|
+
# link is better than a broken one.
|
|
21
|
+
def admin_url_for(reader)
|
|
22
|
+
host = Bible270.config.mailer_host
|
|
23
|
+
return nil if host.blank?
|
|
24
|
+
|
|
25
|
+
Bible270::Engine.routes.url_helpers.admin_reader_url(
|
|
26
|
+
reader, host: host, protocol: host.start_with?('localhost') ? 'http' : 'https'
|
|
27
|
+
)
|
|
28
|
+
rescue StandardError
|
|
29
|
+
nil
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -18,6 +18,12 @@ module Bible270
|
|
|
18
18
|
|
|
19
19
|
def self.avatar_uploads? = AVATAR_UPLOADS
|
|
20
20
|
|
|
21
|
+
# Fires for every way a reader comes into existence — email sign-in, OmniAuth,
|
|
22
|
+
# or a bridged host user — rather than in each controller, so no path can
|
|
23
|
+
# quietly skip it. after_create_commit, so nothing is sent for a reader whose
|
|
24
|
+
# transaction rolls back.
|
|
25
|
+
after_create_commit :notify_of_registration
|
|
26
|
+
|
|
21
27
|
validates :display_name, presence: true
|
|
22
28
|
validates :uid, uniqueness: { scope: :provider }, allow_nil: true
|
|
23
29
|
|
|
@@ -276,6 +282,25 @@ module Bible270
|
|
|
276
282
|
update!(started_on: Plan.to_date(on) - (day - 1))
|
|
277
283
|
end
|
|
278
284
|
|
|
285
|
+
# Delivery problems must never stop someone joining, so this logs rather than
|
|
286
|
+
# raises. Registration happens mid-request, hence the deliver_later option.
|
|
287
|
+
def notify_of_registration
|
|
288
|
+
recipients = Bible270.config.registration_notice_recipients
|
|
289
|
+
return if recipients.empty?
|
|
290
|
+
|
|
291
|
+
notice = NoticeMailer.new_reader(reader_id: id, recipients: recipients)
|
|
292
|
+
if Bible270.config.registration_notice_deliver_later
|
|
293
|
+
notice.deliver_later
|
|
294
|
+
else
|
|
295
|
+
notice.deliver_now
|
|
296
|
+
end
|
|
297
|
+
rescue StandardError => e
|
|
298
|
+
Rails.logger.error("[bible270] could not send the registration notice for #{id}: #{e.class}: #{e.message}")
|
|
299
|
+
end
|
|
300
|
+
# Private, but declared this way rather than with a `private` section: methods
|
|
301
|
+
# defined below here are called from controllers and views.
|
|
302
|
+
private :notify_of_registration
|
|
303
|
+
|
|
279
304
|
def reload_progress
|
|
280
305
|
@checked_counts = nil
|
|
281
306
|
self
|
|
@@ -51,7 +51,8 @@
|
|
|
51
51
|
<%= b270_avatar(r) %>
|
|
52
52
|
<div>
|
|
53
53
|
<div class="name"><%= link_to r.display_name, reader_path(r), style: "text-decoration:none" %></div>
|
|
54
|
-
|
|
54
|
+
<% @day_number = @days_completed[r.id] == 1 ? "day" : "days" %>
|
|
55
|
+
<div class="sub"><%= @days_completed[r.id] %> <%= @day_number %> read</div>
|
|
55
56
|
</div>
|
|
56
57
|
<div class="stat"><span class="big"><%= (@days_completed[r.id].to_f / Bible270::Plan::DAYS * 100).round %>%</span></div>
|
|
57
58
|
</div>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<body style="margin:0;padding:24px;background:#ece6da;font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;color:#231f18">
|
|
4
|
+
<div style="max-width:520px;margin:0 auto;background:#f4efe6;border:1px solid #d7cfbf;border-radius:14px;padding:28px">
|
|
5
|
+
<h1 style="margin:0 0 6px;font-family:Georgia,serif;font-size:20px;font-weight:600">
|
|
6
|
+
<%= @reader.display_name %> has joined
|
|
7
|
+
</h1>
|
|
8
|
+
<p style="margin:0 0 18px;font-size:14px;color:#6d6558"><%= @app_name %></p>
|
|
9
|
+
|
|
10
|
+
<table style="font-size:14px;border-collapse:collapse">
|
|
11
|
+
<tr><td style="padding:3px 12px 3px 0;color:#6d6558">Name</td><td><%= @reader.display_name %></td></tr>
|
|
12
|
+
<tr><td style="padding:3px 12px 3px 0;color:#6d6558">Email</td><td><%= @reader.email.presence || '(none given)' %></td></tr>
|
|
13
|
+
<tr><td style="padding:3px 12px 3px 0;color:#6d6558">Signed in with</td><td><%= @reader.provider.presence || 'the host application' %></td></tr>
|
|
14
|
+
<tr><td style="padding:3px 12px 3px 0;color:#6d6558">Joined</td><td><%= @reader.created_at.strftime('%B %-d, %Y at %-I:%M %p') %></td></tr>
|
|
15
|
+
</table>
|
|
16
|
+
|
|
17
|
+
<p style="margin:18px 0 0;font-size:14px;color:#6d6558">
|
|
18
|
+
That makes <%= @total %> reader<%= 's' unless @total == 1 %> taking part.
|
|
19
|
+
</p>
|
|
20
|
+
|
|
21
|
+
<% if @admin_url %>
|
|
22
|
+
<p style="margin:18px 0 0">
|
|
23
|
+
<a href="<%= @admin_url %>" style="display:inline-block;background:#231f18;color:#ece6da;text-decoration:none;padding:10px 18px;border-radius:10px;font-weight:600;font-size:14px">Manage this reader</a>
|
|
24
|
+
</p>
|
|
25
|
+
<% end %>
|
|
26
|
+
</div>
|
|
27
|
+
</body>
|
|
28
|
+
</html>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<%= @reader.display_name %> has joined <%= @app_name %>.
|
|
2
|
+
|
|
3
|
+
Name: <%= @reader.display_name %>
|
|
4
|
+
Email: <%= @reader.email.presence || '(none given)' %>
|
|
5
|
+
Signed in with: <%= @reader.provider.presence || 'the host application' %>
|
|
6
|
+
Joined: <%= @reader.created_at.strftime('%B %-d, %Y at %-I:%M %p') %>
|
|
7
|
+
|
|
8
|
+
That makes <%= @total %> reader<%= 's' unless @total == 1 %> taking part.
|
|
9
|
+
<% if @admin_url %>
|
|
10
|
+
Manage them: <%= @admin_url %>
|
|
11
|
+
<% end %>
|
|
@@ -211,6 +211,20 @@ module Bible270
|
|
|
211
211
|
Array(admin_emails).map { |e| e.to_s.downcase }.include?(reader.email.to_s.downcase)
|
|
212
212
|
end
|
|
213
213
|
|
|
214
|
+
# The addresses to notify, normalised and de-duplicated. :admins follows
|
|
215
|
+
# admin_emails, the common case — but an explicit list wins, so a secretary
|
|
216
|
+
# who isn't an admin can still be told.
|
|
217
|
+
def registration_notice_recipients
|
|
218
|
+
requested = registration_notice_emails
|
|
219
|
+
requested = admin_emails if requested.to_s == 'admins'
|
|
220
|
+
|
|
221
|
+
Array(requested).map { |address| EmailSignIn.normalize_email(address) }.compact.uniq
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def notify_on_registration?
|
|
225
|
+
registration_notice_recipients.any?
|
|
226
|
+
end
|
|
227
|
+
|
|
214
228
|
def admin_configured?
|
|
215
229
|
admin_resolver.respond_to?(:call) || Array(admin_emails).any?
|
|
216
230
|
end
|
|
@@ -261,6 +275,23 @@ module Bible270
|
|
|
261
275
|
# Bible270::Translations::VERSIONS; narrow it to offer fewer.
|
|
262
276
|
attr_accessor :bible_versions
|
|
263
277
|
|
|
278
|
+
# Who to email when someone new joins. Three forms:
|
|
279
|
+
#
|
|
280
|
+
# config.registration_notice_emails = %w[andrew@example.org] # an explicit list
|
|
281
|
+
# config.registration_notice_emails = :admins # everyone in admin_emails
|
|
282
|
+
# config.registration_notice_emails = [] # off (default)
|
|
283
|
+
#
|
|
284
|
+
# An explicit list is used as given, so you can notify someone who isn't an admin.
|
|
285
|
+
attr_accessor :registration_notice_emails
|
|
286
|
+
|
|
287
|
+
# Send the notice through Active Job rather than inline: registration happens
|
|
288
|
+
# during a request, so inline delivery makes the new reader wait for SMTP.
|
|
289
|
+
attr_accessor :registration_notice_deliver_later
|
|
290
|
+
|
|
291
|
+
# Host for links in notification mail. A mailer has no request to derive it
|
|
292
|
+
# from, so without this the notice names the reader but cannot link to them.
|
|
293
|
+
attr_accessor :mailer_host
|
|
294
|
+
|
|
264
295
|
attr_accessor :after_sign_out_path, :email_sign_in_max_per_window, :passage_url_builder, :tagline, :footer_html, :footer, :favicon, :avatar_max_bytes, :chapter_breaks, :admin_emails, :admin_resolver, :bible_version
|
|
265
296
|
|
|
266
297
|
# Public labels.
|
|
@@ -321,6 +352,9 @@ module Bible270
|
|
|
321
352
|
@avatar_max_bytes = Avatars::DEFAULT_MAX_BYTES
|
|
322
353
|
@chapter_breaks = {}
|
|
323
354
|
@chapter_breaks_path = nil
|
|
355
|
+
@registration_notice_emails = []
|
|
356
|
+
@registration_notice_deliver_later = false
|
|
357
|
+
@mailer_host = nil
|
|
324
358
|
@admin_emails = []
|
|
325
359
|
@admin_resolver = nil
|
|
326
360
|
@email_sign_in_deliver_later = false
|
data/lib/bible270/names.rb
CHANGED
|
@@ -56,7 +56,13 @@ module Bible270
|
|
|
56
56
|
# Best effort at splitting a single name, for readers who arrived through
|
|
57
57
|
# OmniAuth with only a display name. Everything after the first word is the
|
|
58
58
|
# surname, so "Andrew von der Luft" keeps its particles together.
|
|
59
|
-
|
|
59
|
+
#
|
|
60
|
+
# Deliberately not called `split`: a module method with the same name as
|
|
61
|
+
# String#split invites a bare `split(...)` inside this module to resolve here
|
|
62
|
+
# instead, and RuboCop's Style/StringChars autocorrect has been known to
|
|
63
|
+
# rewrite `split` calls into a receiverless `chars`, which then resolves
|
|
64
|
+
# against the module and fails confusingly.
|
|
65
|
+
def split_display_name(display_name)
|
|
60
66
|
parts = squish(display_name).split
|
|
61
67
|
return nil if parts.size < 2
|
|
62
68
|
|
data/lib/bible270/version.rb
CHANGED
|
@@ -39,6 +39,8 @@ module Bible270
|
|
|
39
39
|
class_option :email, type: :boolean, desc: 'Enable passwordless email sign-in'
|
|
40
40
|
class_option :mailer_from, type: :string, desc: 'From: address for sign-in emails'
|
|
41
41
|
class_option :migrate, type: :boolean, desc: 'Run db:migrate at the end'
|
|
42
|
+
class_option :active_storage, type: :boolean,
|
|
43
|
+
desc: 'Install Active Storage if missing (needed for picture uploads)'
|
|
42
44
|
class_option :bundle, type: :boolean, desc: 'Run bundle install after adding strategy gems'
|
|
43
45
|
class_option :defaults, type: :boolean, default: false, desc: 'Accept all defaults, ask nothing'
|
|
44
46
|
|
|
@@ -112,7 +114,41 @@ module Bible270
|
|
|
112
114
|
run 'bundle install' if @bundled
|
|
113
115
|
end
|
|
114
116
|
|
|
115
|
-
# ---- 5.
|
|
117
|
+
# ---- 5. Active Storage ------------------------------------------------
|
|
118
|
+
#
|
|
119
|
+
# Picture uploads need it. `rails new` enables the framework but the tables
|
|
120
|
+
# come from a migration that must be installed, so check for that migration
|
|
121
|
+
# rather than assuming a fresh app has it.
|
|
122
|
+
|
|
123
|
+
def ensure_active_storage
|
|
124
|
+
return if skip_rails_commands?
|
|
125
|
+
|
|
126
|
+
unless active_storage_enabled?
|
|
127
|
+
@active_storage = :unavailable
|
|
128
|
+
say ''
|
|
129
|
+
say ' Active Storage is not enabled here, so picture uploads will be off.', :yellow
|
|
130
|
+
say ' (Was this app generated with --skip-active-storage?)'
|
|
131
|
+
return
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
if active_storage_installed?
|
|
135
|
+
@active_storage = :present
|
|
136
|
+
say ' Active Storage already installed.', :green
|
|
137
|
+
return
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
unless decide(:active_storage, 'Install Active Storage, so readers can upload a picture?', true)
|
|
141
|
+
@active_storage = :declined
|
|
142
|
+
return
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
say ''
|
|
146
|
+
say 'Installing Active Storage', :yellow
|
|
147
|
+
rails_command 'active_storage:install'
|
|
148
|
+
@active_storage = :installed
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# ---- 6. migrations ----------------------------------------------------
|
|
116
152
|
|
|
117
153
|
# Migrations run before the initializers are written, so a problem in the
|
|
118
154
|
# generated config can never block the database work.
|
|
@@ -139,7 +175,7 @@ module Bible270
|
|
|
139
175
|
end
|
|
140
176
|
end
|
|
141
177
|
|
|
142
|
-
# ----
|
|
178
|
+
# ---- 7. initializers ---------------------------------------------------
|
|
143
179
|
|
|
144
180
|
def create_engine_initializer
|
|
145
181
|
create_file 'config/initializers/bible270.rb', engine_initializer
|
|
@@ -151,7 +187,7 @@ module Bible270
|
|
|
151
187
|
create_file 'config/initializers/omniauth.rb', omniauth_initializer
|
|
152
188
|
end
|
|
153
189
|
|
|
154
|
-
# ----
|
|
190
|
+
# ---- 8. routing -------------------------------------------------------
|
|
155
191
|
|
|
156
192
|
def add_route
|
|
157
193
|
if routes_mounted?
|
|
@@ -175,7 +211,7 @@ module Bible270
|
|
|
175
211
|
end
|
|
176
212
|
end
|
|
177
213
|
|
|
178
|
-
# ----
|
|
214
|
+
# ---- 9. what is left to do -------------------------------------------
|
|
179
215
|
|
|
180
216
|
def summary
|
|
181
217
|
say ''
|
|
@@ -185,6 +221,7 @@ module Bible270
|
|
|
185
221
|
say " Sign-in #{auth_summary}"
|
|
186
222
|
say " Initializers #{initializer_summary}"
|
|
187
223
|
say " Migrations #{@migrated ? 'copied and run' : 'copied (not yet run)'}"
|
|
224
|
+
say " Picture uploads #{active_storage_summary}"
|
|
188
225
|
say ''
|
|
189
226
|
|
|
190
227
|
remaining = outstanding_steps
|
|
@@ -217,6 +254,20 @@ module Bible270
|
|
|
217
254
|
true
|
|
218
255
|
end
|
|
219
256
|
|
|
257
|
+
# config/storage.yml is written by `rails new` unless --skip-active-storage.
|
|
258
|
+
def active_storage_enabled?
|
|
259
|
+
return true if File.exist?(File.join(destination_root, 'config', 'storage.yml'))
|
|
260
|
+
|
|
261
|
+
application = File.join(destination_root, 'config', 'application.rb')
|
|
262
|
+
File.exist?(application) && File.read(application).include?('active_storage')
|
|
263
|
+
rescue StandardError
|
|
264
|
+
false
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def active_storage_installed?
|
|
268
|
+
Dir.glob(File.join(destination_root, 'db', 'migrate', '*create_active_storage_tables*')).any?
|
|
269
|
+
end
|
|
270
|
+
|
|
220
271
|
# ---- prompting --------------------------------------------------------
|
|
221
272
|
|
|
222
273
|
def quiet? = options[:defaults]
|
|
@@ -383,6 +434,16 @@ module Bible270
|
|
|
383
434
|
body.include?('path_prefix') && !body.include?('Bible270.config.auth_path_prefix')
|
|
384
435
|
end
|
|
385
436
|
|
|
437
|
+
def active_storage_summary
|
|
438
|
+
case @active_storage
|
|
439
|
+
when :installed then 'Active Storage installed'
|
|
440
|
+
when :present then 'Active Storage already present'
|
|
441
|
+
when :declined then 'off — Active Storage not installed'
|
|
442
|
+
when :unavailable then 'off — Active Storage not enabled in this app'
|
|
443
|
+
else 'unchanged'
|
|
444
|
+
end
|
|
445
|
+
end
|
|
446
|
+
|
|
386
447
|
def outstanding_steps
|
|
387
448
|
steps = []
|
|
388
449
|
steps << "Add to config/routes.rb: #{MOUNT_LINE}" if @route_failed
|
|
@@ -395,6 +456,9 @@ module Bible270
|
|
|
395
456
|
steps << 'bundle install' if @providers.any? && !@bundled
|
|
396
457
|
steps << 'bin/rails bible270:install:migrations' if skip_rails_commands?
|
|
397
458
|
steps << 'bin/rails db:migrate' unless @migrated
|
|
459
|
+
if @active_storage == :declined
|
|
460
|
+
steps << 'bin/rails active_storage:install && bin/rails db:migrate — for picture uploads'
|
|
461
|
+
end
|
|
398
462
|
|
|
399
463
|
if @providers.any?
|
|
400
464
|
steps << 'Set each provider\'s client id/secret in credentials or ENV'
|
|
@@ -37,6 +37,19 @@ Bible270.configure do |config|
|
|
|
37
37
|
# config.after_sign_in_path = "<%= mount_at %>"
|
|
38
38
|
# config.after_sign_out_path = "/"
|
|
39
39
|
|
|
40
|
+
# --- Admin ----------------------------------------------------------------
|
|
41
|
+
# config.admin_emails = %w[andrew@example.org]
|
|
42
|
+
# or
|
|
43
|
+
# config.admin_resolver = ->(reader) { reader.email.to_s.end_with?('@example.org') }
|
|
44
|
+
#
|
|
45
|
+
# --- Notify admin of new user sign-ins -------------------------------------
|
|
46
|
+
# Who to email when someone new joins. :admins follows admin_emails, or give an
|
|
47
|
+
# explicit list. Leave empty for no notices.
|
|
48
|
+
# config.registration_notice_emails = :admins # everyone in admin_emails
|
|
49
|
+
# config.registration_notice_emails = %w[admin@example.com] # or an explicit list
|
|
50
|
+
# config.mailer_host = 'example.com' # lets the notice link to the reader
|
|
51
|
+
# config.registration_notice_deliver_later = true # needs Active Job
|
|
52
|
+
|
|
40
53
|
# --- Start date ------------------------------------------------------------
|
|
41
54
|
# Community-wide start date (Date, Time, or "YYYY-MM-DD"), or nil for undated.
|
|
42
55
|
# config.start_date = Date.new(2026, 9, 6)
|
|
@@ -44,4 +57,23 @@ Bible270.configure do |config|
|
|
|
44
57
|
|
|
45
58
|
# --- Reading links ---------------------------------------------------------
|
|
46
59
|
config.bible_version = "NKJV"
|
|
47
|
-
|
|
60
|
+
|
|
61
|
+
# --- Custom chapter breaks -------------------------------------------------
|
|
62
|
+
# config.chapter_breaks = { 'Psalm 78' => [20, 39, 55] }
|
|
63
|
+
#
|
|
64
|
+
# or YAML file:
|
|
65
|
+
# config.chapter_breaks_path = Rails.root.join('config/bible270_breaks.yml')
|
|
66
|
+
# config/bible270_breaks.yml — re-read whenever it changes
|
|
67
|
+
# Psalm 35: []
|
|
68
|
+
# Psalm 78: [20, 39, 55]
|
|
69
|
+
|
|
70
|
+
# --- Custom footer options -------------------------------------------------
|
|
71
|
+
# The engine's pages end with a short note about how the plan works. Replace it with your own
|
|
72
|
+
# - a partial in your app at /app/views/layouts/_bible270_footer.html.erb
|
|
73
|
+
# config.footer_partial = 'layouts/bible270_footer'
|
|
74
|
+
# config.footer_html = '<p>© 2026 …</p>'
|
|
75
|
+
# config.footer = false # no footer at all
|
|
76
|
+
#_
|
|
77
|
+
# config.footer_placement = :after # engine's note, then yours
|
|
78
|
+
# config.footer_placement = :before # yours, then the engine's note
|
|
79
|
+
# config.footer_placement = :replace # default
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bible270
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.14.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrew vonderLuft
|
|
@@ -76,6 +76,7 @@ files:
|
|
|
76
76
|
- app/controllers/bible270/sessions_controller.rb
|
|
77
77
|
- app/helpers/bible270/plan_helper.rb
|
|
78
78
|
- app/mailers/bible270/application_mailer.rb
|
|
79
|
+
- app/mailers/bible270/notice_mailer.rb
|
|
79
80
|
- app/mailers/bible270/sign_in_mailer.rb
|
|
80
81
|
- app/models/bible270/application_record.rb
|
|
81
82
|
- app/models/bible270/checkoff.rb
|
|
@@ -95,6 +96,8 @@ files:
|
|
|
95
96
|
- app/views/bible270/days/_start_date.html.erb
|
|
96
97
|
- app/views/bible270/days/index.html.erb
|
|
97
98
|
- app/views/bible270/days/show.html.erb
|
|
99
|
+
- app/views/bible270/notice_mailer/new_reader.html.erb
|
|
100
|
+
- app/views/bible270/notice_mailer/new_reader.text.erb
|
|
98
101
|
- app/views/bible270/profiles/edit.html.erb
|
|
99
102
|
- app/views/bible270/readers/index.html.erb
|
|
100
103
|
- app/views/bible270/readers/show.html.erb
|