Class: Studio::EmailsController
- Inherits:
-
ApplicationController
- Object
- ApplicationController
- Studio::EmailsController
- Defined in:
- app/controllers/studio/emails_controller.rb
Overview
/admin/emails — the standard transactional-email page every Studio app gets, modelled on the living style guide (/admin/style): a plain host-inherited controller whose view is a bare content wrapper, so it renders inside each host's application layout and picks up that app's navbar and theme.
It lists Studio::EmailCatalog's registry — one row per registered email, each showing its live banner and whether that banner is the INHERITED engine default or an APP-OWNED override — and writes an override through the shared crop modal. Replaces /admin/email_images, which now redirects here.
An app whose host never set Studio.s3_bucket_prefix cannot store an override. That is a read-only page, not an error: uploads_available? gates the write actions and the view explains why, so the page still shows what each email is currently sending.
Constant Summary collapse
- MAX_BYTES =
8.megabytes
Instance Method Summary collapse
-
#copy ⇒ Object
PATCH /admin/emails/:key/copy — the banner's words and logo.
-
#destroy ⇒ Object
DELETE /admin/emails/:key — drop this app's override and fall back to the inherited default.
- #index ⇒ Object
-
#logo ⇒ Object
PATCH /admin/emails/:key/logo — upload a logo for THIS email, or drop it.
-
#raw ⇒ Object
GET /admin/emails/:key/raw — the rendered email itself, as the iframe source on #show.
-
#settings ⇒ Object
PATCH /admin/emails/:key/settings — operator-tunable knobs.
-
#show ⇒ Object
GET /admin/emails/:key — one email: its banner, its type, and a live preview built from the host's sample data.
-
#update ⇒ Object
PATCH /admin/emails/:key — upload/replace this app's own banner.
Instance Method Details
#copy ⇒ Object
PATCH /admin/emails/:key/copy — the banner's words and logo.
Separate from #settings rather than one big form: the tint is a slider you nudge while looking at the picture, the copy is a sentence you write. They save independently so tuning one never risks clobbering an unsaved edit to the other. Saves EVERYTHING the page edits, in one write. The page has a single Save button, so it must: three buttons that each saved a third of the form meant an operator who changed the subject and the tint had to notice there were two places to press.
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'app/controllers/studio/emails_controller.rb', line 126 def copy rescue_and_log do attrs = copy_params Studio::EmailSetting.set_copy(@key, attrs) # The button's on/off is a boolean, so it is not a COPY_FIELD and needs # its own write — the same shape that once made hide_logo a dead control. Studio::EmailSetting.set_cta_enabled(@key, attrs[:cta_enabled]) if attrs.key?(:cta_enabled) # The footer is SHARED across every email, so it is stored under its own # key rather than this one, and the page posts its two inputs on every # save. update_footer (not set_footer) is what makes that safe: it writes # only on a real change, so blank posts from a page nobody edited cannot # wipe the footer every email sends. # # The form spells the footer's logo `footer_logo_url` because `logo_url` # is already this email's OWN banner logo; the model takes it as # `logo_url`, so the rename is undone here. Studio::EmailSetting.(discord_url: attrs[:discord_url], logo_url: attrs[:footer_logo_url]) if params.key?(:scrim_percent) percent = params[:scrim_percent] percent = nil unless percent.present? && Studio::EmailSetting::SCRIM_RANGE.cover?(percent.to_i) Studio::EmailSetting.set_scrim(@key, percent) end redirect_to admin_email_path(@key), status: :see_other, notice: "Saved." end rescue StandardError redirect_to admin_email_path(@key), status: :see_other, alert: "Couldn't save the banner text. Please try again." end |
#destroy ⇒ Object
DELETE /admin/emails/:key — drop this app's override and fall back to the inherited default.
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'app/controllers/studio/emails_controller.rb', line 184 def destroy # rescue_and_log, like #update: destroy is a WRITE path (it drops an # ImageCache row and deletes the S3 object behind it), and the bare rescue # below turns any failure into a friendly alert. Without the log that # failure is invisible — the admin sees "try again" and nothing reaches # ErrorLog to say why. rescue_and_log do reverted = Studio::EmailCatalog.revert(@key) # label() falls back to the humanised key, so an ORPHAN (no registry # entry at all) still gets a readable message rather than a blank one. label = Studio::EmailCatalog.label(@key).presence || @key.humanize notice = if reverted "#{label} reverted to the inherited default." else "#{label} was already using the inherited default." end redirect_to admin_emails_path, notice: notice, status: :see_other end rescue StandardError redirect_to admin_emails_path, alert: "Couldn't revert the image. Please try again.", status: :see_other end |
#index ⇒ Object
23 24 25 26 27 28 29 30 31 32 |
# File 'app/controllers/studio/emails_controller.rb', line 23 def index @entries = Studio::EmailCatalog.entries @uploads_available = Studio::EmailCatalog.uploads_available? # The list renders every banner and subject AS THEY WOULD ARRIVE, which has # no answer until someone is receiving them. @targets = Studio::EmailPreviewTarget.all @target = Studio::EmailPreviewTarget.resolve(params[:target]) @preview_name = @target&.name end |
#logo ⇒ Object
PATCH /admin/emails/:key/logo — upload a logo for THIS email, or drop it.
Its own action and its own ImageCache purpose: a logo is a small transparent mark, a banner is 3:1 artwork that may be animated, and reverting one must not touch the other.
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'app/controllers/studio/emails_controller.rb', line 161 def logo if params[:remove].present? rescue_and_log { Studio::EmailCatalog.revert_logo(@key) } return redirect_to admin_email_path(@key), status: :see_other, notice: "Back to the standard logo." end file = params[:image] unless valid_image?(file) = file.blank? ? "Choose an image to upload." : "Use a PNG, JPG, WebP or GIF under 8 MB." return redirect_to admin_email_path(@key), alert: , status: :see_other end rescue_and_log do Studio::EmailCatalog.store_logo(@key, io: file, content_type: file.content_type) redirect_to admin_email_path(@key), notice: "Logo updated.", status: :see_other end rescue StandardError redirect_to admin_email_path(@key), alert: "Couldn't save the logo. Please try again.", status: :see_other end |
#raw ⇒ Object
GET /admin/emails/:key/raw — the rendered email itself, as the iframe source on #show. Layout-less on purpose: this response IS the email.
A preview builder is host code run against whatever sample data this environment happens to hold, so it is expected to fail sometimes. It renders the failure as a readable page inside the iframe rather than 500ing, so one broken builder costs one preview, not the manager.
69 70 71 72 73 74 |
# File 'app/controllers/studio/emails_controller.rb', line 69 def raw html = Studio::EmailCatalog.preview_html(@key) return render(html: preview_unavailable_html.html_safe, layout: false) if html.nil? render html: html.html_safe, layout: false end |
#settings ⇒ Object
PATCH /admin/emails/:key/settings — operator-tunable knobs.
The scrim is the dial between a readable header and a visible picture, and the right value depends on artwork that changes without a deploy. Blank clears the override so the email falls back to the registry default rather than being pinned to whatever that default was on the day.
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'app/controllers/studio/emails_controller.rb', line 98 def settings percent = params[:scrim_percent] if percent.present? && !Studio::EmailSetting::SCRIM_RANGE.cover?(percent.to_i) return redirect_to admin_email_path(@key), status: :see_other, alert: "Tint must be between 0 and 100." end rescue_and_log do Studio::EmailSetting.set_scrim(@key, percent) redirect_to admin_email_path(@key), status: :see_other, notice: percent.present? ? "Tint set to #{percent.to_i}%." : "Tint reset to the default." end rescue StandardError redirect_to admin_email_path(@key), status: :see_other, alert: "Couldn't save the tint. Please try again." end |
#show ⇒ Object
GET /admin/emails/:key — one email: its banner, its type, and a live preview built from the host's sample data.
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'app/controllers/studio/emails_controller.rb', line 36 def show # nil for an ORPHAN — an email that left the registry while this app still # holds an upload for it. The view renders a minimal page whose only real # affordance is Revert, which is the whole reason the route stays open. @entry = Studio::EmailCatalog.entry(@key) @orphan = @entry.nil? return render(:orphan) if @orphan @subject = Studio::EmailCatalog.preview_subject(@key) @preview_error = Studio::EmailCatalog.preview_error(@key) @uploads_available = Studio::EmailCatalog.uploads_available? # WHO this is previewed as. The banner greets by name, so "what does this # email look like" has no answer until someone is receiving it — and the # two people who break email differently (an admin with a full name on # file, a member with none) are both offered. @targets = Studio::EmailPreviewTarget.all @target = Studio::EmailPreviewTarget.resolve(params[:target]) @preview_name = @target&.name # The banner AS IT ARRIVES, built the same way a mailer builds it so the # page cannot show something the inbox never gets. @banner = Studio::Banner.for(@key, name: @preview_name) @subject = Studio::EmailCatalog.subject_for(@key, name: @preview_name) || @subject end |
#update ⇒ Object
PATCH /admin/emails/:key — upload/replace this app's own banner.
77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'app/controllers/studio/emails_controller.rb', line 77 def update file = params[:image] unless valid_image?(file) = file.blank? ? "Choose an image to upload." : "Use a PNG, JPG, WebP or GIF under 8 MB." return redirect_to admin_emails_path, alert: , status: :see_other end rescue_and_log do Studio::EmailCatalog.store(@key, io: file, content_type: file.content_type) redirect_to admin_emails_path, notice: "#{Studio::EmailCatalog.label(@key)} banner updated.", status: :see_other end rescue StandardError redirect_to admin_emails_path, alert: "Couldn't save the image. Please try again.", status: :see_other end |