Diff -- /var/www/vhosts/3dshawn.com/abforge.3dshawn.com/email_preferences.cgi
Diff

/var/www/vhosts/3dshawn.com/abforge.3dshawn.com/email_preferences.cgi

added on local at 2026-07-01 16:01:19

Added
+115
lines
Removed
-0
lines
Context
0
unchanged
Blobs
from
to 905eb47ae44a
Restore this content →
Unified diff
Naive LCS-based line diff. Additions in emerald, deletions in rose. Both sides decompressed live from BLOB_STORE.
1#!/usr/bin/perl
2#======================================================================
3# ABForge -- /email_preferences.cgi
4#
5# Logged-in user-facing email preference center.
6# - Shows per-category toggle (transactional is always on, can't toggle)
7# - Shows recent emails sent to this user (last 10)
8# - POST act=save updates user_email_preferences rows
9#======================================================================
10use strict;
11use warnings;
12
13use lib '/var/www/vhosts/3dshawn.com/abforge.3dshawn.com';
14use CGI;
15use MODS::DBConnect;
16use MODS::ABForge::Wrapper;
17use MODS::ABForge::Config;
18use MODS::Login;
19
20my $q = CGI->new;
21my $form = $q->Vars;
22my $wrap = MODS::ABForge::Wrapper->new;
23my $db = MODS::DBConnect->new;
24my $cfg = MODS::ABForge::Config->new;
25my $DB = $cfg->settings('database_name');
26
27my $u = MODS::ABForge::PM::require_login();
28my $uid = $u->{user_id} + 0;
29
30my $dbh = $db->db_connect;
31my $flash = '';
32
33my @CATS = (
34 { key => 'lifecycle', label => 'Lifecycle emails', description => 'Trial reminders, plan changes, downgrade notices, winback. We recommend keeping these on.', default_on => 1 },
35 { key => 'product', label => 'Product updates', description => 'New features, release notes, what shipped this month. Once or twice a month.', default_on => 0 },
36 { key => 'marketing', label => 'Tips & marketing', description => 'Onboarding tips, customer stories, occasional promotions.', default_on => 0 },
37);
38
39if (($ENV{REQUEST_METHOD} || '') eq 'POST' && ($form->{act} || '') eq 'save') {
40 foreach my $c (@CATS) {
41 my $sub = $form->{"pref_$c->{key}"} ? 1 : 0;
42 $db->db_readwrite($dbh, qq~
43 INSERT INTO ${DB}.user_email_preferences (user_id, category, is_subscribed)
44 VALUES ('$uid', '$c->{key}', '$sub')
45 ON DUPLICATE KEY UPDATE is_subscribed='$sub'
46 ~, $0, __LINE__);
47 }
48 $flash = 'Preferences saved.';
49}
50
51my %prefs;
52{
53 my @rows = $db->db_readwrite_multiple($dbh, qq~
54 SELECT category, is_subscribed FROM ${DB}.user_email_preferences WHERE user_id='$uid'
55 ~, $0, __LINE__);
56 foreach my $r (@rows) { $prefs{$r->{category}} = $r->{is_subscribed} ? 1 : 0; }
57}
58
59my @recent = $db->db_readwrite_multiple($dbh, qq~
60 SELECT template_slug, subject, status, created_at
61 FROM ${DB}.email_sends_log
62 WHERE user_id='$uid'
63 ORDER BY id DESC LIMIT 10
64~, $0, __LINE__);
65
66$db->db_disconnect($dbh);
67
68print "Content-Type: text/html; charset=utf-8\r\nCache-Control: no-store\r\n\r\n";
69
70my $flash_html = $flash ? qq~<div style="padding:10px 14px;background:rgba(34,197,94,.12);border:1px solid rgba(34,197,94,.3);border-radius:8px;color:#86efac;font-size:13px;margin-bottom:18px">$flash</div>~ : '';
71
72my $rows = '';
73foreach my $c (@CATS) {
74 my $sub = defined($prefs{$c->{key}}) ? $prefs{$c->{key}} : $c->{default_on};
75 my $checked = $sub ? 'checked' : '';
76 $rows .= qq~
77 <label style="display:flex;align-items:flex-start;gap:14px;padding:16px;border:1px solid rgba(255,255,255,.08);border-radius:10px;margin-bottom:10px;cursor:pointer;background:rgba(20,20,30,.4)">
78 <input type="checkbox" name="pref_$c->{key}" value="1" $checked style="margin-top:3px;width:18px;height:18px">
79 <div>
80 <div style="font-weight:600;color:#e2e8f0;font-size:14px">$c->{label}</div>
81 <div style="color:#94a3b8;font-size:13px;line-height:1.5;margin-top:4px">$c->{description}</div>
82 </div>
83 </label>~;
84}
85
86my $recent_html = '';
87foreach my $r (@recent) {
88 my $color = $r->{status} eq 'sent' ? '#86efac' : ($r->{status} =~ /^suppressed/ ? '#fbbf24' : '#fca5a5');
89 my $sj = _h($r->{subject});
90 $recent_html .= qq~
91 <div style="display:flex;justify-content:space-between;align-items:center;padding:8px 0;border-bottom:1px solid rgba(255,255,255,.05);font-size:12px">
92 <div><span style="color:#cbd5e1">$sj</span><div style="color:#64748b;font-size:11px;font-family:ui-monospace,Consolas,Menlo,monospace">$r->{template_slug}</div></div>
93 <div><span style="color:$color;font-size:11px;text-transform:uppercase;letter-spacing:.05em">$r->{status}</span><div style="color:#94a3b8;font-size:11px;text-align:right">$r->{created_at}</div></div>
94 </div>~;
95}
96
97my $body = qq~
98<div class="page-pad" style="max-width:720px">
99 <h1 style="margin:0 0 6px;font-size:28px;color:#fff">Email preferences</h1>
100 <p style="color:#94a3b8;margin:0 0 24px;font-size:14px">Choose which emails you'd like to receive from ABForge. Transactional emails (password resets, invite acceptance, billing receipts) are always sent regardless.</p>
101 $flash_html
102
103 <form method="POST" action="/email_preferences.cgi">
104 <input type="hidden" name="act" value="save">
105 $rows
106 <div style="margin-top:18px"><button type="submit" class="btn btn-primary btn-lg">Save preferences</button></div>
107 </form>
108
109 <h2 style="font-size:16px;color:#fff;margin:32px 0 12px">Recent emails sent to you</h2>
110 <div style="border:1px solid rgba(255,255,255,.08);border-radius:10px;padding:8px 16px;background:rgba(20,20,30,.4)">$recent_html</div>
111</div>~;
112
113$wrap->render({ userinfo => $u, page_key => 'email_preferences', title => 'Email preferences', body => $body });
114
115sub _h { my $s = shift // ''; $s =~ s/&/&amp;/g; $s =~ s/</&lt;/g; $s =~ s/>/&gt;/g; $s =~ s/"/&quot;/g; $s =~ s/'/&#39;/g; return $s; }
Keyboard: j next diff k previous diff g top G bottom r restore c compile-check ? help