Module: Bible270::PlanHelper
- Defined in:
- app/helpers/bible270/plan_helper.rb
Instance Method Summary collapse
- #b270_avatar(reader, size: 72) ⇒ 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, track: 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: 72) ⇒ Object
141 142 143 144 145 146 147 148 149 |
# File 'app/helpers/bible270/plan_helper.rb', line 141 def b270_avatar(reader, size: 72) 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
135 136 137 138 139 |
# File 'app/helpers/bible270/plan_helper.rb', line 135 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
170 171 172 173 174 |
# File 'app/helpers/bible270/plan_helper.rb', line 170 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.
166 167 168 |
# File 'app/helpers/bible270/plan_helper.rb', line 166 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.
153 154 155 156 |
# File 'app/helpers/bible270/plan_helper.rb', line 153 def b270_date(time) local = Bible270.local_time(time) local&.strftime('%b %-d, %Y') end |
#b270_datetime(time) ⇒ Object
158 159 160 161 |
# File 'app/helpers/bible270/plan_helper.rb', line 158 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.
181 182 183 184 185 |
# File 'app/helpers/bible270/plan_helper.rb', line 181 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.
189 190 191 192 193 194 195 |
# File 'app/helpers/bible270/plan_helper.rb', line 189 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.
206 207 208 209 210 211 212 213 214 215 216 217 218 |
# File 'app/helpers/bible270/plan_helper.rb', line 206 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.
198 199 200 201 |
# File 'app/helpers/bible270/plan_helper.rb', line 198 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
234 235 236 237 238 239 240 241 242 |
# File 'app/helpers/bible270/plan_helper.rb', line 234 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.
223 224 225 226 227 228 229 230 231 232 |
# File 'app/helpers/bible270/plan_helper.rb', line 223 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 117 118 119 120 121 |
# File 'app/helpers/bible270/plan_helper.rb', line 112 def b270_passage_url(reference, version = nil) return '#' if reference.blank? selected_version = version || b270_bible_version if selected_version != Translations::ORIGINAL_LANGUAGES && current_reader&.blue_letter_bible? return Translations.passage_url(reference, selected_version, blue_letter: true) end Bible270.config.passage_url_builder.call(reference, selected_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, track: nil) ⇒ Object
Small grey "(NKJV)" after a reading, so it's clear which translation the link opens in. The original-language preference names the actual language used for each track rather than repeating the combined preference code.
126 127 128 129 130 131 132 133 |
# File 'app/helpers/bible270/plan_helper.rb', line 126 def b270_version_tag(version = nil, track: nil) label = version || b270_bible_version if label == Translations::ORIGINAL_LANGUAGES && track label = track.to_s == 'nt' ? 'Greek' : 'Hebrew' end content_tag :span, "(#{label})", 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 |