Module: Bible270::PlanHelper
- Defined in:
- app/helpers/bible270/plan_helper.rb
Instance Method Summary collapse
- #b270_avatar(reader, size: 34) ⇒ Object
- #b270_avatar_src(reader) ⇒ Object
- #b270_bible_version ⇒ Object
- #b270_can_delete?(comment) ⇒ Boolean
-
#b270_can_edit?(comment) ⇒ Boolean
Only the writer may change their own words.
- #b270_custom_footer ⇒ Object
-
#b270_date(time) ⇒ Object
Timestamps in the plan's zone rather than UTC.
- #b270_datetime(time) ⇒ Object
-
#b270_day_cell(day, reader: nil, today: nil) ⇒ Object
One cell of the 270-day grid.
-
#b270_day_cell_classes(day, reader: nil, today: nil) ⇒ Object
Shared with the admin grid, which is a button_to rather than a link because it toggles the day rather than navigating to it.
-
#b270_day_reading_links(day) ⇒ Object
The same references as links to the passage, in the reader's own translation when they have chosen one — b270_passage_url falls back to the site default for a visitor.
-
#b270_day_readings(day) ⇒ Object
"Genesis 1-3 · Matthew 1 · Psalm 1" as plain text, for a title attribute.
- #b270_day_status_class(reader, day) ⇒ Object
-
#b270_days_with_reflections ⇒ Object
The days that have at least one visible reflection, fetched once per request.
- #b270_favicon_tag ⇒ Object
-
#b270_footer ⇒ Object
The signed-in reader's translation, falling back to the site default for visitors who aren't signed in.
- #b270_mark(size: 38) ⇒ Object
-
#b270_omniauth_path(provider) ⇒ Object
Path that begins the OmniAuth request phase.
- #b270_passage_url(reference, version = nil) ⇒ Object
- #b270_provider_label(provider) ⇒ Object
- #b270_track(track) ⇒ Object
-
#b270_version_tag(version = nil) ⇒ Object
Small grey "(NKJV)" after a reading, so it's clear which translation the link opens in.
-
#b270_with_mentions(body) ⇒ Object
The mark beside the title in the header.
Instance Method Details
#b270_avatar(reader, size: 34) ⇒ Object
130 131 132 133 134 135 136 137 138 |
# File 'app/helpers/bible270/plan_helper.rb', line 130 def b270_avatar(reader, size: 34) src = b270_avatar_src(reader) if src image_tag src, class: 'b270-avatar', width: size, height: size, alt: reader.display_name else content_tag :span, reader.initials, class: 'b270-avatar b270-avatar-fallback', style: "width:#{size}px;height:#{size}px;line-height:#{size}px" end end |
#b270_avatar_src(reader) ⇒ Object
124 125 126 127 128 |
# File 'app/helpers/bible270/plan_helper.rb', line 124 def b270_avatar_src(reader) return main_app.rails_blob_path(reader.avatar, only_path: true) if reader.avatar_uploaded? reader.avatar_url.presence end |
#b270_bible_version ⇒ Object
108 109 110 |
# File 'app/helpers/bible270/plan_helper.rb', line 108 def b270_bible_version current_reader&.effective_bible_version || Translations.resolve(nil) end |
#b270_can_delete?(comment) ⇒ Boolean
159 160 161 162 163 |
# File 'app/helpers/bible270/plan_helper.rb', line 159 def b270_can_delete?(comment) return false unless signed_in? comment.reader_id == current_reader.id || Bible270.config.admin?(current_reader) end |
#b270_can_edit?(comment) ⇒ Boolean
Only the writer may change their own words. An admin can remove a reflection but not rewrite it: putting words in someone's mouth is worse than taking them away, and taking them away is already the moderator's job.
155 156 157 |
# File 'app/helpers/bible270/plan_helper.rb', line 155 def b270_can_edit?(comment) signed_in? && comment.reader_id == current_reader.id end |
#b270_custom_footer ⇒ Object
47 48 49 50 51 52 |
# File 'app/helpers/bible270/plan_helper.rb', line 47 def case Bible270.config. when :partial then render(Bible270.config.) when :html then content_tag(:footer, Bible270.config..html_safe, class: 'b270-footer') end end |
#b270_date(time) ⇒ Object
Timestamps in the plan's zone rather than UTC. Dates — started_on and the like — need no conversion and are formatted inline.
142 143 144 145 |
# File 'app/helpers/bible270/plan_helper.rb', line 142 def b270_date(time) local = Bible270.local_time(time) local&.strftime('%b %-d, %Y') end |
#b270_datetime(time) ⇒ Object
147 148 149 150 |
# File 'app/helpers/bible270/plan_helper.rb', line 147 def b270_datetime(time) local = Bible270.local_time(time) local&.strftime('%b %-d, %Y at %-I:%M %p') end |
#b270_day_cell(day, reader: nil, today: nil) ⇒ Object
One cell of the 270-day grid. Four views drew this by hand in four slightly different ways; now they all call this.
The title attribute carries the readings, so hovering a square says what is on that day — no JavaScript, and it works for keyboard focus too.
170 171 172 173 174 |
# File 'app/helpers/bible270/plan_helper.rb', line 170 def b270_day_cell(day, reader: nil, today: nil) link_to day, day_path(day), class: b270_day_cell_classes(day, reader: reader, today: today), title: b270_day_readings(day) end |
#b270_day_cell_classes(day, reader: nil, today: nil) ⇒ Object
Shared with the admin grid, which is a button_to rather than a link because it toggles the day rather than navigating to it.
178 179 180 181 182 183 184 |
# File 'app/helpers/bible270/plan_helper.rb', line 178 def b270_day_cell_classes(day, reader: nil, today: nil) classes = ['b270-cell', b270_day_status_class(reader, day)] classes << 'talk' if b270_days_with_reflections.include?(day) classes << 'now' if today && day == today classes.reject(&:blank?).join(' ') end |
#b270_day_reading_links(day) ⇒ Object
The same references as links to the passage, in the reader's own translation when they have chosen one — b270_passage_url falls back to the site default for a visitor.
195 196 197 198 199 200 201 202 203 204 205 206 207 |
# File 'app/helpers/bible270/plan_helper.rb', line 195 def b270_day_reading_links(day) readings = Bible270::Plan.readings_for(day) links = Bible270::Plan::TRACKS.keys.filter_map do |track| reference = readings[track] next if reference.blank? link_to reference, b270_passage_url(reference), class: 'b270-reflink', target: '_blank', rel: 'noopener' end safe_join(links, ' · ') end |
#b270_day_readings(day) ⇒ Object
"Genesis 1-3 · Matthew 1 · Psalm 1" as plain text, for a title attribute.
187 188 189 190 |
# File 'app/helpers/bible270/plan_helper.rb', line 187 def b270_day_readings(day) readings = Bible270::Plan.readings_for(day) Bible270::Plan::TRACKS.keys.filter_map { |track| readings[track] }.join(' · ') end |
#b270_day_status_class(reader, day) ⇒ Object
223 224 225 226 227 228 229 230 231 |
# File 'app/helpers/bible270/plan_helper.rb', line 223 def b270_day_status_class(reader, day) return '' unless reader case reader.day_status(day) when :complete then 'complete' when :partial then 'partial' else '' end end |
#b270_days_with_reflections ⇒ Object
The days that have at least one visible reflection, fetched once per request. Called from the layout's grid, which no controller sets up, so it cannot be an instance variable assigned in an action.
212 213 214 215 216 217 218 219 220 221 |
# File 'app/helpers/bible270/plan_helper.rb', line 212 def b270_days_with_reflections @b270_days_with_reflections ||= if defined?(Bible270::Comment) Bible270::Comment.approved.distinct.pluck(:day).to_set else Set.new end rescue StandardError Set.new end |
#b270_favicon_tag ⇒ Object
99 100 101 102 103 104 105 106 |
# File 'app/helpers/bible270/plan_helper.rb', line 99 def b270_favicon_tag favicon = Bible270.config.favicon return if favicon == false href = favicon.presence || Favicon.data_uri type = href.start_with?('data:image/svg+xml', 'http') || href.end_with?('.svg') ? 'image/svg+xml' : nil tag.link(rel: 'icon', type: type, href: href) end |
#b270_footer ⇒ Object
The signed-in reader's translation, falling back to the site default for visitors who aren't signed in. Applies to every page the engine's layout renders, i.e. everything under the mount point. Pages rendered inside a host layout need this in that layout's
instead. The footer, from whichever source the host configured. A partial is looked up in the host's view paths as well as the engine's, so 'shared/footer' finds app/views/shared/_footer.html.erb. The footer, from whichever source the host configured. A partial is looked up in the host's view paths as well as the engine's, so 'shared/footer' finds app/views/shared/_footer.html.erb. config.footer_placement decides whether a custom footer replaces the engine's or sits alongside it.
33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'app/helpers/bible270/plan_helper.rb', line 33 def config = Bible270.config return if config. == :none return render('bible270/shared/footer') if config. == :default parts = [] if config. default = render('bible270/shared/footer') config. == :after ? parts.unshift(default) : parts.push(default) end safe_join(parts) end |
#b270_mark(size: 38) ⇒ Object
88 89 90 91 92 93 94 95 96 97 |
# File 'app/helpers/bible270/plan_helper.rb', line 88 def b270_mark(size: 38) configured = Bible270.config.header_mark return nil if configured == false if configured.is_a?(String) && configured.present? return image_tag(configured, width: size, height: size, alt: '', class: 'b270-mark') end content_tag :span, Favicon.inline_svg(size: size).html_safe, class: 'b270-mark' end |
#b270_omniauth_path(provider) ⇒ Object
Path that begins the OmniAuth request phase. Must be POSTed to: OmniAuth 2.0+ refuses GET on its own routes (CVE-2015-9284), so every sign-in control in these views is a button_to, not a link_to.
12 13 14 15 |
# File 'app/helpers/bible270/plan_helper.rb', line 12 def b270_omniauth_path(provider) prefix = Bible270.config.omniauth_path_prefix || "#{request.script_name}/auth" "#{prefix}/#{provider}" end |
#b270_passage_url(reference, version = nil) ⇒ Object
112 113 114 115 116 |
# File 'app/helpers/bible270/plan_helper.rb', line 112 def b270_passage_url(reference, version = nil) return '#' if reference.blank? Bible270.config.passage_url_builder.call(reference, version || b270_bible_version) end |
#b270_provider_label(provider) ⇒ Object
17 18 19 |
# File 'app/helpers/bible270/plan_helper.rb', line 17 def b270_provider_label(provider) Bible270.config.label_for_provider(provider) end |
#b270_track(track) ⇒ Object
5 6 7 |
# File 'app/helpers/bible270/plan_helper.rb', line 5 def b270_track(track) Plan::TRACKS.fetch(track.to_s) end |
#b270_version_tag(version = nil) ⇒ Object
Small grey "(NKJV)" after a reading, so it's clear which translation the link opens in.
120 121 122 |
# File 'app/helpers/bible270/plan_helper.rb', line 120 def b270_version_tag(version = nil) content_tag :span, "(#{version || b270_bible_version})", class: 'b270-version' end |
#b270_with_mentions(body) ⇒ Object
The mark beside the title in the header. Larger than the favicon and inlined as SVG, so it stays crisp at any size and needs no asset pipeline. Renders "@handle" as a link to that reader. An unresolved or ambiguous handle is left as plain text, which is how a writer discovers it found nobody.
Built by walking the matches and escaping everything between them, rather than escaping afterwards: the body is reader-supplied, so the only safe construction is one where every non-link fragment is escaped by hand.
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'app/helpers/bible270/plan_helper.rb', line 62 def b270_with_mentions(body) text = body.to_s readers = Bible270::Reader.mentioned_in(text) pieces = [] cursor = 0 text.to_enum(:scan, Bible270::Mentions::PATTERN).each do match = Regexp.last_match pieces << ERB::Util.html_escape(text[cursor...match.begin(0)]) handle = Bible270::Mentions.normalize(match[1]) reader = readers.find { |candidate| candidate.answers_to?(handle) } pieces << if reader link_to(match[0], reader_path(reader), class: 'b270-mention') else ERB::Util.html_escape(match[0]) end cursor = match.end(0) end pieces << ERB::Util.html_escape(text[cursor..].to_s) safe_join(pieces) end |