Class: Bible270::AdminController

Inherits:
ApplicationController show all
Defined in:
app/controllers/bible270/admin_controller.rb

Overview

Small administrative panel: remove readers, adjust their completions, and move them to a given day of the plan.

Access is controlled by Bible270.config.admin_emails / admin_resolver. With neither set the panel is unreachable, and every action 404s rather than 403s so its existence isn't advertised.

Constant Summary collapse

LAST_BROADCAST_AT =
'last_broadcast_at'
LAST_BROADCAST_SUBJECT =
'last_broadcast_subject'

Instance Method Summary collapse

Instance Method Details

#broadcastObject

Put the reader on a given day as of today, or set an explicit start date.



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'app/controllers/bible270/admin_controller.rb', line 84

def broadcast
  subject = params[:subject].to_s.strip
  body = params[:body].to_s.strip

  if subject.empty? || body.empty?
    return redirect_to(admin_path, alert: 'A message needs both a subject and something to say.')
  end

  recipients = Reader.where.not(email: [nil, '']).order(:id)
  return redirect_to(admin_path, alert: 'Nobody has an email address.') if recipients.empty?

  sent = deliver_broadcast(recipients, subject, body)
  Setting.write(LAST_BROADCAST_AT, Time.current.iso8601)
  Setting.write(LAST_BROADCAST_SUBJECT, subject)

  redirect_to admin_path,
              notice: "Sent to #{sent} #{'reader'.pluralize(sent)}."
end

#commentsObject

Everything is visible by default; this lists it all so anything unsuitable can be taken down.



52
53
54
55
56
57
58
59
# File 'app/controllers/bible270/admin_controller.rb', line 52

def comments
  scope = Comment.includes(:reader, parent: :reader).order(created_at: :desc)
  @filter = params[:filter].to_s
  scope = scope.hidden if @filter == 'hidden'
  scope = scope.approved if @filter == 'visible'
  @comments = scope.limit(200)
  @hidden_count = Comment.hidden.count
end

#complete_throughObject



175
176
177
178
179
180
181
182
183
# File 'app/controllers/bible270/admin_controller.rb', line 175

def complete_through
  day = params[:day].to_i
  if @reader.mark_through!(day)
    redirect_to admin_reader_path(@reader),
                notice: day.zero? ? 'All check-offs cleared.' : "Marked days 1-#{day} complete."
  else
    redirect_to admin_reader_path(@reader), alert: 'Give a day between 0 and 270.'
  end
end

#deliver_broadcast(recipients, subject, body) ⇒ Object



206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'app/controllers/bible270/admin_controller.rb', line 206

def deliver_broadcast(recipients, subject, body)
  later = Bible270.config.registration_notice_deliver_later
  sent = 0

  recipients.find_each do |reader|
    mail = NoticeMailer.broadcast(reader_id: reader.id, subject: subject, body: body)
    later ? mail.deliver_later : mail.deliver_now
    sent += 1
  rescue StandardError => e
    Rails.logger.error("[bible270] broadcast to reader #{reader.id} failed: #{e.class}: #{e.message}")
  end

  sent
end

#destroyObject



77
78
79
80
81
# File 'app/controllers/bible270/admin_controller.rb', line 77

def destroy
  name = @reader.display_name
  @reader.destroy
  redirect_to admin_path, notice: "Removed #{name} and all their check-offs and reflections."
end

#destroy_commentObject



71
72
73
74
75
# File 'app/controllers/bible270/admin_controller.rb', line 71

def destroy_comment
  day = @comment.day
  @comment.destroy
  redirect_back_to_comments "Deleted the reflection on day #{day}."
end

#hide_commentObject



61
62
63
64
# File 'app/controllers/bible270/admin_controller.rb', line 61

def hide_comment
  @comment.hide!
  redirect_back_to_comments "Hidden #{@comment.reader.display_name}'s reflection on day #{@comment.day}."
end

#indexObject



31
32
33
34
35
36
37
38
39
40
41
# File 'app/controllers/bible270/admin_controller.rb', line 31

def index
  @enrollment_closed = Setting.enrollment_closed?
  @enrollment_closed_at = Setting.enrollment_closed_at
  @readers = Reader.all.to_a.sort_by(&:sort_name)
  counts = Checkoff.group(:reader_id, :day).count
  @days_completed = Hash.new(0)
  counts.each { |(rid, day), n| @days_completed[rid] += 1 if n >= Plan.total_parts(day) }
  @reachable = Reader.where.not(email: [nil, '']).count
  @last_broadcast_at = parsed_time(Setting.read(LAST_BROADCAST_AT))
  @last_broadcast_subject = Setting.read(LAST_BROADCAST_SUBJECT)
end

#parsed_time(value) ⇒ Object

Failures are counted rather than raised: one bad address must not stop the rest of the message going out, and the admin is told how many were sent. A stored timestamp that cannot be read is treated as absent rather than blowing up the admin page.



198
199
200
201
202
203
204
# File 'app/controllers/bible270/admin_controller.rb', line 198

def parsed_time(value)
  return nil if value.blank?

  Time.parse(value.to_s)
rescue ArgumentError, TypeError
  nil
end

#remove_avatarObject



170
171
172
173
# File 'app/controllers/bible270/admin_controller.rb', line 170

def remove_avatar
  @reader.remove_avatar!
  redirect_to admin_reader_path(@reader), notice: "Removed #{@reader.display_name}'s picture."
end

#showObject



43
44
45
46
# File 'app/controllers/bible270/admin_controller.rb', line 43

def show
  @days_completed = @reader.days_completed
  @comments = @reader.comments.order(created_at: :desc).limit(50)
end

#toggle_dayObject



185
186
187
188
189
190
191
192
# File 'app/controllers/bible270/admin_controller.rb', line 185

def toggle_day
  day = params[:day].to_i
  if @reader.toggle_day!(day)
    redirect_to admin_reader_path(@reader, anchor: "day-#{day}")
  else
    redirect_to admin_reader_path(@reader), alert: 'That day is outside the plan.'
  end
end

#unhide_commentObject



66
67
68
69
# File 'app/controllers/bible270/admin_controller.rb', line 66

def unhide_comment
  @comment.unhide!
  redirect_back_to_comments "Restored #{@comment.reader.display_name}'s reflection on day #{@comment.day}."
end

#update_bible_versionObject



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'app/controllers/bible270/admin_controller.rb', line 103

def update_bible_version
  reader = Reader.find_by(id: params[:id])
  return redirect_to(admin_path, alert: 'No such reader.') if reader.nil?

  code = params[:bible_version].to_s
  source = params[:passage_source].presence || Reader::DEFAULT_PASSAGE_SOURCE
  unless Reader::PASSAGE_SOURCES.include?(source)
    return redirect_to admin_reader_path(reader), alert: 'That is not a reading-link source on the list.'
  end

  # Blank means "no preference": the reader follows the site default, which is
  # not the same as choosing whichever translation the site happens to use now.
  if code.empty?
    reader.update(bible_version: nil, passage_source: source)
    return redirect_to admin_reader_path(reader),
                       notice: "#{reader.display_name} now follows the site default " \
                               "(#{Bible270.config.bible_version})."
  end

  if reader.update_bible_version(code)
    reader.update_column(:passage_source, source)
    redirect_to admin_reader_path(reader),
                notice: "#{reader.display_name} now reads the #{reader.bible_version_label}."
  else
    redirect_to admin_reader_path(reader), alert: 'That is not a translation on the list.'
  end
end

#update_enrollmentObject

---- enrolment --------------------------------------------------------



21
22
23
24
25
26
27
28
29
# File 'app/controllers/bible270/admin_controller.rb', line 21

def update_enrollment
  if params[:state] == 'closed'
    Setting.close_enrollment!
    redirect_to admin_path, notice: 'Closed to new readers. Existing readers can still sign in.'
  else
    Setting.open_enrollment!
    redirect_to admin_path, notice: 'Open to new readers.'
  end
end

#update_profileObject

Name and picture together, mirroring what a reader can change about themselves on their own profile.



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'app/controllers/bible270/admin_controller.rb', line 153

def update_profile
  problems = []
  if (params[:first_name].present? || params[:last_name].present?) && !@reader.update_names(params[:first_name],
                                                                                            params[:last_name])
    problems << 'both a first and last name'
  end
  if params[:avatar].present? && !@reader.attach_avatar(params[:avatar])
    problems << (@reader.errors[:avatar].first || 'a valid image')
  end

  if problems.empty?
    redirect_to admin_reader_path(@reader), notice: 'Updated.'
  else
    redirect_to admin_reader_path(@reader), alert: "Needs #{problems.to_sentence}."
  end
end

#update_startObject



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'app/controllers/bible270/admin_controller.rb', line 131

def update_start
  if params[:start_date].present?
    # set_start_date!, not update_start_date!: an admin is not subject to
    # config.allow_reader_start_date, which governs what readers may do to
    # their own dates.
    if @reader.set_start_date!(params[:start_date])
      redirect_to admin_reader_path(@reader),
                  notice: "Start date set to #{@reader.started_on.strftime('%B %-d, %Y')}."
    else
      redirect_to admin_reader_path(@reader),
                  alert: "Couldn't read #{params[:start_date].inspect} as a date."
    end
  elsif params[:day].present? && @reader.restart_on!(day: params[:day])
    redirect_to admin_reader_path(@reader),
                notice: "#{@reader.display_name} is now on day #{params[:day]}."
  else
    redirect_to admin_reader_path(@reader), alert: 'Give a start date or a day between 1 and 270.'
  end
end