Module: Bible270::PlanHelper

Defined in:
app/helpers/bible270/plan_helper.rb

Instance Method Summary collapse

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
     :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_versionObject



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

Returns:

  • (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.

Returns:

  • (Boolean)


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


47
48
49
50
51
52
# File 'app/helpers/bible270/plan_helper.rb', line 47

def b270_custom_footer
  case Bible270.config.footer_style
  when :partial then render(Bible270.config.footer_partial)
  when :html then (:footer, Bible270.config.footer_html.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

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_reflectionsObject

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_tagObject



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

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 b270_footer
  config = Bible270.config
  return if config.footer_style == :none
  return render('bible270/shared/footer') if config.footer_style == :default

  parts = [b270_custom_footer]
  if config.keep_default_footer?
    default = render('bible270/shared/footer')
    config.resolved_footer_placement == :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

   :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

   :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