Diff -- /var/www/vhosts/webstls.com/httpdocs/marketplace_export.cgi
Diff

/var/www/vhosts/webstls.com/httpdocs/marketplace_export.cgi

added on WebSTLs (webstls.com) at 2026-07-01 22:26:35

Added
+176
lines
Removed
-0
lines
Context
0
unchanged
Blobs
from
to b18f2e6fa29b
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# WebSTLs -- per-model marketplace export.
4#
5# /marketplace_export.cgi?model_id=N
6#
7# Renders a printable page with every field a marketplace listing
8# needs, plus copy buttons and direct download links for the files.
9# Works TODAY for every platform -- the creator pastes into Etsy,
10# MyMiniFactory, whatever they need to upload to manually.
11#
12# When a real API adapter for a platform ships, the "Push to <name>"
13# buttons here become live actions instead of just copy hints.
14#======================================================================
15use strict;
16use warnings;
17
18use lib '/var/www/vhosts/webstls.com/httpdocs';
19use CGI;
20use MODS::Template;
21use MODS::DBConnect;
22use MODS::Login;
23use MODS::WebSTLs::Config;
24use MODS::WebSTLs::Wrapper;
25use MODS::WebSTLs::Marketplaces;
26
27my $q = CGI->new;
28my $form = $q->Vars;
29my $tfile = MODS::Template->new;
30my $db = MODS::DBConnect->new;
31my $auth = MODS::Login->new;
32my $config = MODS::WebSTLs::Config->new;
33my $wrap = MODS::WebSTLs::Wrapper->new;
34my $mp = MODS::WebSTLs::Marketplaces->new;
35my $DB = $config->settings('database_name');
36
37my $userinfo = $auth->login_verify();
38if (!$userinfo) {
39 print "Status: 302 Found\nLocation: /login.cgi\n\n";
40 exit;
41}
42require MODS::WebSTLs::Permissions;
43MODS::WebSTLs::Permissions->new->require_feature($userinfo, 'manage_marketplaces');
44
45my $uid = $userinfo->{user_id};
46$uid =~ s/[^0-9]//g;
47
48my $model_id = $form->{model_id} || 0;
49$model_id =~ s/[^0-9]//g;
50
51unless ($model_id) {
52 print "Status: 302 Found\nLocation: /models.cgi\n\n";
53 exit;
54}
55
56my $dbh = $db->db_connect();
57
58# Look up the model. We join storefronts so we can verify ownership in
59# one round-trip.
60my $model = $db->db_readwrite($dbh, qq~
61 SELECT m.*, s.user_id AS owner_id, s.id AS storefront_id_check,
62 s.name AS store_name, s.subdomain AS store_sub
63 FROM `${DB}`.models m
64 JOIN `${DB}`.storefronts s ON s.id = m.storefront_id
65 WHERE m.id = '$model_id'
66 LIMIT 1
67~, $ENV{SCRIPT_NAME}, __LINE__);
68
69unless ($model && $model->{id}) {
70 $db->db_disconnect($dbh);
71 print "Status: 302 Found\nLocation: /models.cgi\n\n";
72 exit;
73}
74unless ($model->{owner_id} && $model->{owner_id} eq $uid) {
75 $db->db_disconnect($dbh);
76 print "Status: 302 Found\nLocation: /models.cgi\n\n";
77 exit;
78}
79
80# Pricing display
81my $cents = $model->{base_price_cents} || 0;
82my $price = sprintf('%.2f', $cents / 100);
83my $currency = $model->{currency} || 'USD';
84
85# Tags -- per-platform character limits and formats. The model row's
86# tags column would be where these come from once tagging is built;
87# for now show the raw value or a placeholder.
88my $tags_raw = $model->{tags} || $model->{category} || '';
89
90# Image references -- once model_images table exists, look up real
91# files. For now we use the same placeholder logic as the storefront.
92my $hero_idx = ($model->{id} % 39) + 1;
93my @images = ( "/assets/images/$hero_idx.webp" );
94
95# License blurb -- model row would hold the chosen license code. For
96# now, a sensible default that creators can edit at the marketplace.
97my $license_default = "Personal use only. Not for commercial reproduction or resale. Buyers may print this design for their own use; no redistribution of the digital file is permitted.";
98
99# Per-platform export hints: how many chars Etsy allows in title, etc.
100# Drives the visible warning chips on each platform's tile.
101my %LIMITS = (
102 etsy => { title => 140, tags_max => 13, desc => 102400 },
103 gumroad => { title => 100, tags_max => 0, desc => 100000 },
104 mmf => { title => 100, tags_max => 10, desc => 50000 },
105 thangs => { title => 100, tags_max => 10, desc => 50000 },
106 cults3d => { title => 100, tags_max => 8, desc => 50000 },
107 printables => { title => 100, tags_max => 10, desc => 50000 },
108 thingiverse => { title => 100, tags_max => 10, desc => 50000 },
109 cgtrader => { title => 100, tags_max => 8, desc => 50000 },
110 patreon => { title => 80, tags_max => 0, desc => 200000 },
111 amazon => { title => 200, tags_max => 5, desc => 2000 },
112 walmart => { title => 200, tags_max => 5, desc => 4000 },
113);
114
115my @platform_tiles;
116foreach my $p (@{ $mp->all }) {
117 my $lim = $LIMITS{ $p->{slug} } || { title => 100, tags_max => 5, desc => 5000 };
118 my $title_chars = length($model->{title} || '');
119 my $desc_chars = length($model->{description} || '');
120 my $title_over = ($title_chars > $lim->{title}) ? 1 : 0;
121 my $desc_over = ($desc_chars > $lim->{desc}) ? 1 : 0;
122 push @platform_tiles, {
123 slug => $p->{slug},
124 name => $p->{name},
125 signup_url => $p->{signup_url},
126 title_limit => $lim->{title},
127 tags_max => $lim->{tags_max},
128 desc_limit => $lim->{desc},
129 title_chars => $title_chars,
130 desc_chars => $desc_chars,
131 title_over => $title_over,
132 desc_over => $desc_over,
133 title_status => $title_over ? '<span class="pill warning">Title too long</span>' : '<span class="pill success">Title OK</span>',
134 is_planned => $p->{status} eq 'planned' ? 1 : 0,
135 };
136}
137
138$db->db_disconnect($dbh);
139
140# HTML-escape helper for safe rendering. We do this on Perl side so the
141# template doesn't have to think about it.
142sub _h {
143 my ($s) = @_;
144 $s //= '';
145 $s =~ s/&/&amp;/g; $s =~ s/</&lt;/g; $s =~ s/>/&gt;/g;
146 $s =~ s/"/&quot;/g; $s =~ s/'/&#39;/g;
147 return $s;
148}
149
150my $tvars = {
151 model_id => $model->{id},
152 model_title => _h($model->{title} || ''),
153 model_slug => $model->{slug} || '',
154 model_desc => _h($model->{description} || ''),
155 model_desc_raw => $model->{description} || '', # for copy-to-clipboard
156 model_price => $price,
157 model_currency => $currency,
158 model_tags => _h($tags_raw),
159 model_license => _h($license_default),
160 store_name => _h($model->{store_name} || ''),
161 hero_image => $images[0],
162 platforms => \@platform_tiles,
163 platform_count => scalar(@platform_tiles),
164};
165
166print "Content-Type: text/html; charset=utf-8\nCache-Control: no-store, no-cache, must-revalidate, max-age=0\nPragma: no-cache\nExpires: 0\n\n";
167
168my $body = join('', $tfile->template('webstls_marketplace_export.html', $tvars, $userinfo));
169
170$wrap->render({
171 userinfo => $userinfo,
172 page_key => 'marketplaces',
173 title => 'Export listing',
174 body => $body,
175});
176exit;
Keyboard: j next diff k previous diff g top G bottom r restore c compile-check ? help