Diff -- /var/www/vhosts/3dshawn.com/shop.3dshawn.com/update_listing.cgi
Diff

/var/www/vhosts/3dshawn.com/shop.3dshawn.com/update_listing.cgi

added on local at 2026-07-01 22:10:07

Added
+194
lines
Removed
-0
lines
Context
0
unchanged
Blobs
from
to 34b27659cabe
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# ShopCart -- update a storefront_listings row.
4#
5# POST params:
6# action = feature | hide | show | toggle_visible | move_up | move_down | set_sort
7# model_id = which model's listing to update
8# value = (only used by set_sort) the new sort_order integer
9#
10# Authorization: the model must belong to the logged-in user AND the
11# user's storefront. Both are verified before any write.
12#
13# Returns JSON { success: 1, ... } or { success: 0, error: "..." }.
14#======================================================================
15use strict;
16use warnings;
17
18use lib '/var/www/vhosts/3dshawn.com/shop.3dshawn.com';
19use CGI;
20use MODS::DBConnect;
21use MODS::Login;
22use MODS::ShopCart::Config;
23
24$| = 1;
25
26my $q = CGI->new;
27my $db = MODS::DBConnect->new;
28my $auth = MODS::Login->new;
29my $config = MODS::ShopCart::Config->new;
30my $DB = $config->settings('database_name');
31
32print "Content-Type: application/json\nCache-Control: no-store, no-cache, must-revalidate, max-age=0\nPragma: no-cache\nExpires: 0\n\n";
33
34our $SAVE_DONE = 0;
35END {
36 if (!$SAVE_DONE) {
37 print qq~{"success":0,"error":"server aborted mid-update -- check MODS/DB_ERRORS.log for the failing SQL and DBI message"}~;
38 }
39}
40
41sub fail {
42 my ($msg) = @_;
43 $msg =~ s/"/\\"/g;
44 print qq~{"success":0,"error":"$msg"}~;
45 $SAVE_DONE = 1;
46 exit;
47}
48
49my $userinfo = $auth->login_verify();
50fail('not authenticated') unless $userinfo && $userinfo->{user_id};
51require MODS::ShopCart::Permissions;
52fail('not authorized') unless MODS::ShopCart::Permissions->new->has_feature($userinfo, 'edit_models');
53
54my $uid = $userinfo->{user_id};
55$uid =~ s/[^0-9]//g;
56fail('bad user id') unless $uid;
57
58my $action = lc($q->param('action') || '');
59$action =~ s/[^a-z_]//g;
60fail('missing action') unless $action;
61
62my %ALLOWED_ACTIONS = map { $_ => 1 } qw(feature hide show toggle_visible move_up move_down set_sort);
63fail("unknown action '$action'") unless $ALLOWED_ACTIONS{$action};
64
65my $model_id = $q->param('model_id') || '';
66$model_id =~ s/[^0-9]//g;
67fail('missing or bad model_id') unless $model_id;
68
69my $dbh = $db->db_connect();
70
71# Resolve the user's storefront. Single-storefront-per-user for now.
72my $store = $db->db_readwrite($dbh,
73 qq~SELECT id FROM `${DB}`.storefronts WHERE user_id='$uid' ORDER BY id LIMIT 1~,
74 $ENV{SCRIPT_NAME}, __LINE__);
75unless ($store && $store->{id}) {
76 $db->db_disconnect($dbh);
77 fail('no storefront for this user');
78}
79my $sid = $store->{id};
80
81# Verify the model belongs to this user and isn't purged.
82my $owner = $db->db_readwrite($dbh,
83 qq~SELECT user_id, purged_at FROM `${DB}`.models WHERE id='$model_id' LIMIT 1~,
84 $ENV{SCRIPT_NAME}, __LINE__);
85unless ($owner && $owner->{user_id} && $owner->{user_id} eq $uid) {
86 $db->db_disconnect($dbh);
87 fail('model not found or not yours');
88}
89if ($owner->{purged_at}) {
90 $db->db_disconnect($dbh);
91 fail('model has been permanently deleted');
92}
93
94# Make sure a listing row exists. Some actions (feature, show) should
95# auto-create the listing if it isn't there yet; "hide" on a missing
96# row is a no-op success.
97my $listing = $db->db_readwrite($dbh,
98 qq~SELECT visible, sort_order FROM `${DB}`.storefront_listings
99 WHERE storefront_id='$sid' AND model_id='$model_id' LIMIT 1~,
100 $ENV{SCRIPT_NAME}, __LINE__);
101
102if (!$listing || !defined $listing->{visible}) {
103 if ($action eq 'hide') {
104 $db->db_disconnect($dbh);
105 print qq~{"success":1,"action":"$action","noop":1}~;
106 $SAVE_DONE = 1;
107 exit;
108 }
109 # Create the row with a sensible default; feature/show will fix it up.
110 $db->db_readwrite($dbh, qq~
111 INSERT IGNORE INTO `${DB}`.storefront_listings SET
112 storefront_id = '$sid',
113 model_id = '$model_id',
114 visible = 1,
115 override_price_cents = NULL,
116 sort_order = 0,
117 added_at = NOW()
118 ~, $ENV{SCRIPT_NAME}, __LINE__);
119}
120
121#----------------------------------------------------------------------
122# Action dispatch
123#----------------------------------------------------------------------
124if ($action eq 'feature') {
125 # Promote to the top: new sort_order = MIN(existing) - 1.
126 my $r = $db->db_readwrite($dbh,
127 qq~SELECT COALESCE(MIN(sort_order), 0) - 1 AS new_top
128 FROM `${DB}`.storefront_listings WHERE storefront_id='$sid'~,
129 $ENV{SCRIPT_NAME}, __LINE__);
130 my $new_top = (defined $r->{new_top}) ? $r->{new_top} : -1;
131 $db->db_readwrite($dbh, qq~
132 UPDATE `${DB}`.storefront_listings
133 SET sort_order = '$new_top', visible = 1
134 WHERE storefront_id='$sid' AND model_id='$model_id' LIMIT 1
135 ~, $ENV{SCRIPT_NAME}, __LINE__);
136}
137elsif ($action eq 'hide') {
138 $db->db_readwrite($dbh, qq~
139 UPDATE `${DB}`.storefront_listings SET visible = 0
140 WHERE storefront_id='$sid' AND model_id='$model_id' LIMIT 1
141 ~, $ENV{SCRIPT_NAME}, __LINE__);
142}
143elsif ($action eq 'show') {
144 $db->db_readwrite($dbh, qq~
145 UPDATE `${DB}`.storefront_listings SET visible = 1
146 WHERE storefront_id='$sid' AND model_id='$model_id' LIMIT 1
147 ~, $ENV{SCRIPT_NAME}, __LINE__);
148}
149elsif ($action eq 'toggle_visible') {
150 my $newv = $listing && $listing->{visible} ? 0 : 1;
151 $db->db_readwrite($dbh, qq~
152 UPDATE `${DB}`.storefront_listings SET visible = '$newv'
153 WHERE storefront_id='$sid' AND model_id='$model_id' LIMIT 1
154 ~, $ENV{SCRIPT_NAME}, __LINE__);
155}
156elsif ($action eq 'move_up' || $action eq 'move_down') {
157 # Find the neighbor in the chosen direction and swap sort_orders.
158 my $cur_sort = $listing->{sort_order} || 0;
159 my $cmp = ($action eq 'move_up') ? '<' : '>';
160 my $ord = ($action eq 'move_up') ? 'DESC' : 'ASC';
161 my $neighbor = $db->db_readwrite($dbh, qq~
162 SELECT model_id, sort_order FROM `${DB}`.storefront_listings
163 WHERE storefront_id='$sid' AND sort_order $cmp '$cur_sort'
164 ORDER BY sort_order $ord LIMIT 1
165 ~, $ENV{SCRIPT_NAME}, __LINE__);
166 if ($neighbor && defined $neighbor->{model_id}) {
167 my $n_mid = $neighbor->{model_id};
168 my $n_sort = $neighbor->{sort_order};
169 $db->db_readwrite($dbh, qq~
170 UPDATE `${DB}`.storefront_listings SET sort_order='$n_sort'
171 WHERE storefront_id='$sid' AND model_id='$model_id' LIMIT 1
172 ~, $ENV{SCRIPT_NAME}, __LINE__);
173 $db->db_readwrite($dbh, qq~
174 UPDATE `${DB}`.storefront_listings SET sort_order='$cur_sort'
175 WHERE storefront_id='$sid' AND model_id='$n_mid' LIMIT 1
176 ~, $ENV{SCRIPT_NAME}, __LINE__);
177 }
178}
179elsif ($action eq 'set_sort') {
180 my $value = $q->param('value');
181 $value = 0 unless defined $value && length $value;
182 $value =~ s/[^0-9-]//g;
183 $value = int($value || 0);
184 $db->db_readwrite($dbh, qq~
185 UPDATE `${DB}`.storefront_listings SET sort_order='$value'
186 WHERE storefront_id='$sid' AND model_id='$model_id' LIMIT 1
187 ~, $ENV{SCRIPT_NAME}, __LINE__);
188}
189
190$db->db_disconnect($dbh);
191
192print qq~{"success":1,"action":"$action","model_id":"$model_id"}~;
193$SAVE_DONE = 1;
194exit;
Keyboard: j next diff k previous diff g top G bottom r restore c compile-check ? help