From 79aa743a9e77a4600429ab5f2025c8dfd12fce91 Mon Sep 17 00:00:00 2001 From: "prompt.ac/@jeffrey" Date: Wed, 9 Sep 2026 08:48:30 -0400 Subject: [PATCH] =?UTF-8?q?thomaslawson:=20TL=20=E2=80=94=20Valise=20works?= =?UTF-8?q?=20plugin=20(live=20Valise-backed=20artwork=20grids)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a [valise_works tag=...] shortcode that renders a live, cached grid of Thomas's Valise artworks, styled to match the In-the-Studio decade pages. Data is fetched server-side from api.valise.works and cached in a transient, so the API key never reaches the browser; the key is set under Settings → Valise. Installed and activated on thomaslawson.com, keyed, and verified live against the website-P-1980s tag (15 works, images + medium/dimensions). Draft page 2419 renders it. The existing hardcoded decade pages are untouched. Note: only ~29 works currently carry website-* tags, so broader decade coverage is gated on tagging more work in Valise. Claude-Session: https://claude.ai/code/session_01RTnreJFRGEq5h13CJv32Xd --- .../plugin-build/tl-valise-works.zip | Bin 0 -> 4320 bytes .../tl-valise-works/tl-valise-works.php | 259 ++++++++++++++++++ 2 files changed, 259 insertions(+) create mode 100644 gigs/thomaslawson.com/work-fia-edits/plugin-build/tl-valise-works.zip create mode 100644 gigs/thomaslawson.com/work-fia-edits/plugin-build/tl-valise-works/tl-valise-works.php diff --git a/gigs/thomaslawson.com/work-fia-edits/plugin-build/tl-valise-works.zip b/gigs/thomaslawson.com/work-fia-edits/plugin-build/tl-valise-works.zip new file mode 100644 index 0000000000000000000000000000000000000000..3b463884c786438f9543233ab9054584f3134b26 GIT binary patch literal 4320 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 OFfr_BU|`@D0Pz5@nBemO literal 0 HcmV?d00001 diff --git a/gigs/thomaslawson.com/work-fia-edits/plugin-build/tl-valise-works/tl-valise-works.php b/gigs/thomaslawson.com/work-fia-edits/plugin-build/tl-valise-works/tl-valise-works.php new file mode 100644 index 0000000000..487be9a763 --- /dev/null +++ b/gigs/thomaslawson.com/work-fia-edits/plugin-build/tl-valise-works/tl-valise-works.php @@ -0,0 +1,259 @@ + array( + 'Authorization' => 'Bearer ' . $key, + 'Accept' => 'application/json', + ), + 'timeout' => 20, + )); + if (is_wp_error($resp)) { return $resp; } + + $status = wp_remote_retrieve_response_code($resp); + if ($status !== 200) { + return new WP_Error('tl_valise_http_' . $status, 'Valise API returned HTTP ' . $status . '.'); + } + + $body = json_decode(wp_remote_retrieve_body($resp), true); + if (empty($body['data']) || !is_array($body['data'])) { break; } + + foreach ($body['data'] as $w) { + if (empty($w['tags']) || !is_array($w['tags'])) { continue; } + foreach ($w['tags'] as $t) { + $tt = isset($t['title']) ? $t['title'] : ''; + $ts = isset($t['slug']) ? $t['slug'] : ''; + if ($tt === $tag || $ts === $tag) { $out[] = $w; break; } + } + } + + $url = isset($body['page']['next']) ? $body['page']['next'] : null; + $guard++; + } + + usort($out, function ($a, $b) { + $ay = isset($a['year']) ? (string) $a['year'] : ''; + $by = isset($b['year']) ? (string) $b['year'] : ''; + if ($ay === $by) { + return strcmp(isset($a['title']) ? $a['title'] : '', isset($b['title']) ? $b['title'] : ''); + } + return strcmp($ay, $by); + }); + + return $out; +} + +function tl_valise_img_url($work, $width = 900) { + if (empty($work['images'][0]['url'])) { return ''; } + return preg_replace('#/rs:fit:\d+:\d+/#', '/rs:fit:' . intval($width) . ':0/', $work['images'][0]['url']); +} + +function tl_valise_img_tag($work, $alt) { + $base = tl_valise_img_url($work, 900); + if (!$base) { return ''; } + $src600 = tl_valise_img_url($work, 600); + $src1400 = tl_valise_img_url($work, 1400); + return sprintf( + '%s', + esc_url($base), esc_url($src600), esc_url($base), esc_url($src1400), esc_attr($alt) + ); +} + +/* ------------------------------------------------------------------ * + * Presentation + * ------------------------------------------------------------------ */ + +function tl_valise_print_css_once() { + static $done = false; + if ($done) { return ''; } + $done = true; + return << +.tl-valise{--vpanel:#fdf4e6;--vmuted:#6f6a61;--vline:#e7e0d1;margin:clamp(1.4rem,4vw,2.6rem) 0 0} +.tl-valise .vgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr)); + gap:clamp(1.6rem,3.2vw,2.8rem) clamp(1.3rem,2.6vw,2rem);margin:0;padding:0;list-style:none} +.tl-valise .vwork{margin:0;display:flex;flex-direction:column} +.tl-valise figure{margin:0} +.tl-valise .vframe{background:var(--vpanel);border:1px solid var(--vline);aspect-ratio:1/1; + display:flex;align-items:center;justify-content:center;overflow:hidden} +.tl-valise .vframe img{max-width:100%;max-height:100%;width:auto;height:auto;display:block} +.tl-valise .vframe.is-empty{color:var(--vmuted);font-size:.78rem;letter-spacing:.05em;text-transform:uppercase} +.tl-valise figcaption{display:flex;flex-direction:column;gap:.1rem;padding:.7rem .1rem 0} +.tl-valise .vt{font-weight:600;font-size:.82rem;letter-spacing:.05em;text-transform:uppercase;line-height:1.25} +.tl-valise .vy{color:var(--vmuted);font-size:.92rem} +.tl-valise .vm{color:var(--vmuted);font-style:italic;font-size:.88rem;margin-top:.15rem;line-height:1.4} +.tl-valise .vempty{color:var(--vmuted);font-style:italic} + +CSS; +} + +function tl_valise_works_shortcode($atts) { + $a = shortcode_atts(array( + 'tag' => '', + 'cache' => '3600', + ), $atts, 'valise_works'); + + $tag = sanitize_text_field($a['tag']); + if (!$tag) { return ''; } + $ttl = max(0, intval($a['cache'])); + + $ck = 'tl_valise_' . md5($tag . '|' . TL_VALISE_VER); + $bust = current_user_can('manage_options') && isset($_GET['valise_refresh']); + $cached = $bust ? false : get_transient($ck); + if ($cached !== false) { return $cached; } + + $works = tl_valise_fetch_by_tag($tag); + if (is_wp_error($works)) { + return current_user_can('manage_options') + ? '

Valise: ' . esc_html($works->get_error_message()) . '

' + : ''; + } + if (empty($works)) { + return current_user_can('manage_options') + ? '

Valise: no works tagged “' . esc_html($tag) . '”.

' + : ''; + } + + ob_start(); + echo tl_valise_print_css_once(); + echo '
    '; + foreach ($works as $w) { + $title = (isset($w['title']) && $w['title'] !== '') ? $w['title'] : 'Untitled'; + $year = isset($w['year']) ? $w['year'] : ''; + $med = isset($w['medium']) ? trim($w['medium']) : ''; + $dim = isset($w['dimensions']) ? trim($w['dimensions']) : ''; + $sub = trim(implode(' · ', array_filter(array($med, $dim)))); + $imgtag = tl_valise_img_tag($w, $title . ($year ? ', ' . $year : '')); + + echo '
  • '; + echo $imgtag ? '
    ' . $imgtag . '
    ' : '
    image to come
    '; + echo '
    ' . esc_html($title) . ''; + if ($year !== '') { echo '' . esc_html($year) . ''; } + if ($sub !== '') { echo '' . esc_html($sub) . ''; } + echo '
  • '; + } + echo '
'; + $html = ob_get_clean(); + + if ($ttl) { set_transient($ck, $html, $ttl); } + return $html; +} +add_shortcode('valise_works', 'tl_valise_works_shortcode'); + +/* ------------------------------------------------------------------ * + * Settings — Settings → Valise (one field: the API key) + * ------------------------------------------------------------------ */ + +add_action('admin_menu', function () { + add_options_page('Valise', 'Valise', 'manage_options', 'tl-valise', 'tl_valise_settings_page'); +}); + +add_action('admin_init', function () { + register_setting('tl_valise_group', 'tl_valise_api_key', array( + 'type' => 'string', + 'sanitize_callback' => function ($v) { return trim(sanitize_text_field($v)); }, + 'default' => '', + )); +}); + +add_filter('plugin_action_links_' . plugin_basename(__FILE__), function ($links) { + $links[] = 'Settings'; + return $links; +}); + +function tl_valise_settings_page() { + if (!current_user_can('manage_options')) { return; } + $source = tl_valise_key_source(); + $via_constant = (defined('VALISE_API_KEY') && VALISE_API_KEY); + ?> +
+

Valise

+

Connects the site to Thomas’s Valise vault. Drop + [valise_works tag="website-P-1980s"] onto any page to render a + live, cached grid of the works carrying that tag.

+

Status: + + key configured (). + + no key set — paste it below. + +

+
+ + + + + + + + +
+

Pages cache for one hour. To force a refresh, open a page with + ?valise_refresh=1 while logged in as an admin.

+
+