05 — BLOG SYSTEM CODE
VERIFIED CURRENT SYSTEM ✓WHAT THIS CONTROLS
This is not one piece of code. The Blog system is made from the BLOG parent, THE BLOGS page, ADD A BLOG POST page, BLOG ADMIN page and the active SHS Blog Admin PHP Code Snippet.
Captured: 22 September 2026
05A THE BLOGS: THE-BLOGS-RAW V12 — 978 lines / 42,626 bytes
05B ADD A BLOG POST: ADD-BLOG V01 — Text Block containing User Submitted Posts shortcode + live preview markup
05C BLOG ADMIN PAGE: BLOG-ADMIN-PAGE-RAW V07 — 733 lines / 35,232 bytes
05D BLOG ADMIN FUNCTIONS: BLOG-ADMIN-FUNCTIONS V12 — active PHP Code Snippet — 1,614 lines / 49,792 bytes
DEPENDENCIES / DO NOT CHANGE BLINDLY
Critical: BLOG ADMIN is a two-part system. The Blog Admin page RAW Code and the active PHP Code Snippet work together. Never replace one while assuming it represents the entire admin system.
THE BLOGS: uses the WordPress REST API to display published article content. Changes to post data, links or API behaviour can affect the public article browser.
ADD A BLOG POST: uses the
Navigation: BLOG is a parent menu item. Mobile parent behaviour is also connected to HEADER-NAV V01.
Rollback rule: preserve every verified component separately. A change to one component creates a new version of that component; do not silently replace the other masters.
BLOG PARENT — CURRENT EMPTY RAW CODE
Current RAW Code: EMPTY
CURRENT FUNCTION:
• BLOG is retained as a parent navigation item with submenus.
• Previous obsolete Unyson page-builder content has been removed.
• One empty RAW Code element has deliberately been retained for future rebuilding.
RULE:
Keep this page clean until new verified page-specific RAW Code is created.
Future changes must create a new BLOG-PARENT-RAW version.05A — THE BLOGS — V12 — VIEW / COPY MASTER
<!-- =========================================================
SHS — THE BLOGS
V12 — SMART LINKS INCLUDING PLAIN-TEXT URLS
Complete RAW Code replacement
Live WordPress REST API article browser
Editorial refinement: outlined numbers, article borders, black dates, stronger CTAs
========================================================= -->
<div id="shs-journal">
<section class="shs-journal-intro">
<div>
<div class="shs-kicker">SUSSEX HOMELESS SUPPORT</div>
<h1>THE BLOGS</h1>
</div>
<p class="shs-intro-copy">
Stories from the street, our volunteers, fundraising, events and the people
helping Sussex Homeless Support make a difference across Brighton & Hove.
</p>
</section>
<section class="shs-lead-grid">
<article class="shs-lead-card shs-reveal" data-shs-post="0">
<a href="#" class="shs-image-link shs-lead-photo" aria-label="Read featured story">
<img src="https://www.sussexhomelesssupport.co.uk/wp-content/uploads/2026/09/FrameV1.png"
alt="Sussex Homeless Support online auction">
<span class="shs-featured-pill">FEATURED STORY</span>
<span class="shs-story-number">01</span>
</a>
<div class="shs-lead-copy">
<div class="shs-meta"><b>18 SEPTEMBER 2026</b> <span></span> ONLINE AUCTION</div>
<h2><a href="#">OUR ONLINE AUCTION IS CREATING A BUZZ</a></h2>
<p>
Our online auction is turning donated treasures into vital funds for Sussex
Homeless Support — creating excitement, encouraging bidding and helping
us put every successful sale to work for people who need our support.
</p>
<a class="shs-read" href="#">READ STORY <b>→</b></a>
</div>
</article>
<div class="shs-side-stories shs-visual-prompts">
<article class="shs-side-card shs-reveal" data-shs-post="1">
<a href="#" class="shs-side-image">
<img src="https://www.sussexhomelesssupport.co.uk/wp-content/uploads/2026/09/Slide4.jpeg"
alt="Sussex Homeless Support volunteers">
<span class="shs-story-number shs-story-number-small">02</span>
</a>
<div class="shs-side-copy">
<div class="shs-meta"><b>16 SEPTEMBER 2026</b> <span></span> EVENTS</div>
<h3><a href="#">Community, music and one very busy weekend</a></h3>
<a class="shs-read" href="#">READ STORY <b>→</b></a>
</div>
</article>
<article class="shs-side-card shs-reveal" data-shs-post="2">
<a href="#shs-video-story" class="shs-side-image shs-video-thumb">
<img src="https://www.sussexhomelesssupport.co.uk/wp-content/uploads/2026/09/Slide5.jpeg"
alt="Sussex Homeless Support video story">
<span class="shs-video-shade"></span>
<span class="shs-play"><i></i></span>
<span class="shs-video-label">WATCH STORY</span>
<span class="shs-story-number shs-story-number-small">03</span>
</a>
<div class="shs-side-copy">
<div class="shs-meta"><b>VIDEO</b> <span></span> ON THE FRONT LINE</div>
<h3><a href="#shs-video-story">See the people and stories behind the work</a></h3>
<a class="shs-read" href="#shs-video-story">WATCH VIDEO <b>↓</b></a>
</div>
</article>
<article class="shs-side-card shs-reveal" data-shs-post="3">
<a href="#" class="shs-side-image">
<img src="https://www.sussexhomelesssupport.co.uk/wp-content/uploads/2026/09/Slide4.jpeg" alt="Sussex Homeless Support story">
<span class="shs-story-number shs-story-number-small">04</span>
</a>
<div class="shs-side-copy">
<div class="shs-meta"><b></b> <span></span> SHS NEWS</div>
<h3><a href="#">Published story</a></h3>
<a class="shs-read" href="#">READ STORY <b>→</b></a>
</div>
</article>
<article class="shs-side-card shs-reveal" data-shs-post="4">
<a href="#" class="shs-side-image">
<img src="https://www.sussexhomelesssupport.co.uk/wp-content/uploads/2026/09/Slide3.jpeg" alt="Sussex Homeless Support story">
<span class="shs-story-number shs-story-number-small">05</span>
</a>
<div class="shs-side-copy">
<div class="shs-meta"><b></b> <span></span> SHS NEWS</div>
<h3><a href="#">Published story</a></h3>
<a class="shs-read" href="#">READ STORY <b>→</b></a>
</div>
</article>
</div>
</section>
<section id="shs-reader" class="shs-reader" hidden>
<div class="shs-reader-main">
<button type="button" class="shs-back" id="shs-back-to-blogs">← BACK TO THE BLOGS</button>
<div class="shs-reader-meta" id="shs-reader-meta"></div>
<h2 id="shs-reader-title"></h2>
<img id="shs-reader-image" class="shs-reader-image" src="" alt="">
<div id="shs-reader-content" class="shs-reader-content"></div>
<div class="shs-article-end">
<span>SHS · STORIES FROM OUR COMMUNITY</span>
</div>
<section class="shs-keep-reading">
<div class="shs-keep-heading">
<div>
<div class="shs-kicker">DISCOVER MORE</div>
<h3>KEEP READING</h3>
</div>
<div class="shs-heading-line"></div>
</div>
<div id="shs-keep-reading-cards" class="shs-keep-cards"></div>
</section>
<div class="shs-article-nav">
<button type="button" id="shs-prev-article">← PREVIOUS ARTICLE</button>
<button type="button" id="shs-next-article">NEXT ARTICLE →</button>
</div>
</div>
<aside class="shs-published">
<div class="shs-kicker">ARCHIVE</div>
<h2>PUBLISHED<br>ARTICLES</h2>
<div id="shs-published-list"></div>
</aside>
</section>
<section class="shs-published-home shs-reveal">
<div class="shs-published-home-head">
<div class="shs-kicker">ARCHIVE</div>
<h2>PUBLISHED ARTICLES</h2>
</div>
<div id="shs-published-home-list" class="shs-published-home-list"></div>
</section>
<section id="shs-video-story" class="shs-video-feature shs-reveal">
<div class="shs-video-copy">
<div class="shs-kicker">SHS FILMS <span class="shs-live-dot"></span> VIDEO STORY</div>
<h2>NOT JUST<br>ANOTHER<br>BLOG POST.</h2>
<p>
Some stories are better seen and heard. This space can carry interviews,
volunteer films, event footage, appeals and short documentaries without
sending visitors away from the SHS website.
</p>
<div class="shs-video-note">VIDEO CONNECTION COMES WITH FUNCTIONALITY</div>
</div>
<button class="shs-video-stage" type="button" id="shs-video-demo" aria-label="Play video concept">
<img src="https://www.sussexhomelesssupport.co.uk/wp-content/uploads/2026/09/Slide2.jpeg"
alt="Sussex Homeless Support video preview">
<span class="shs-video-stage-shade"></span>
<span class="shs-big-play"><i></i></span>
<span class="shs-video-caption">
<b>WATCH</b>
<em>02:34</em>
</span>
</button>
</section>
<div class="shs-section-heading shs-reveal shs-legacy-grid">
<div>
<span>LATEST STORIES</span>
<h2>FROM OUR COMMUNITY</h2>
</div>
<div class="shs-heading-line"></div>
</div>
<section class="shs-story-grid shs-legacy-grid">
<article class="shs-story-card shs-reveal" data-shs-post="3">
<a href="#" class="shs-story-image">
<img src="https://www.sussexhomelesssupport.co.uk/wp-content/uploads/2026/09/Slide4.jpeg"
alt="Sussex Homeless Support fundraising">
<span class="shs-card-no">04</span>
</a>
<div class="shs-meta"><b>23 APRIL 2024</b> <span></span> FUNDRAISING</div>
<h3><a href="#">Mods Aid 24: a brilliant day of support</a></h3>
<p>Volunteers, organisers and supporters came together to raise funds and keep vital services moving.</p>
<a class="shs-read" href="#">READ STORY <b>→</b></a>
</article>
<article class="shs-story-card shs-reveal" data-shs-post="4">
<a href="#" class="shs-story-image">
<img src="https://www.sussexhomelesssupport.co.uk/wp-content/uploads/2026/09/Slide3.jpeg"
alt="Sussex Homeless Support outreach">
<span class="shs-card-no">05</span>
</a>
<div class="shs-meta"><b>24 OCTOBER 2022</b> <span></span> OUTREACH</div>
<h3><a href="#">Preparing for another Brighton winter</a></h3>
<p>As temperatures fall, practical support becomes even more important for people sleeping rough.</p>
<a class="shs-read" href="#">READ STORY <b>→</b></a>
</article>
<article class="shs-story-card shs-reveal" data-shs-post="5">
<a href="#" class="shs-story-image">
<img src="https://www.sussexhomelesssupport.co.uk/wp-content/uploads/2026/09/Slide5.jpeg"
alt="Sussex Homeless Support Street Kitchen">
<span class="shs-card-no">06</span>
</a>
<div class="shs-meta"><b>18 DECEMBER 2021</b> <span></span> COMMUNITY</div>
<h3><a href="#">Christmas Street Kitchen in Brighton</a></h3>
<p>A festive meal, essential supplies and a warm welcome from a remarkable team of volunteers.</p>
<a class="shs-read" href="#">READ STORY <b>→</b></a>
</article>
<article class="shs-story-card shs-reveal" data-shs-post="6">
<a href="#" class="shs-story-image shs-dark-card">
<div class="shs-quote-art">
<small>VOLUNTEERS</small>
<strong>PEOPLE<br>MAKE THIS<br>POSSIBLE.</strong>
</div>
<span class="shs-card-no">07</span>
</a>
<div class="shs-meta"><b>10 DECEMBER 2021</b> <span></span> PEOPLE</div>
<h3><a href="#">Meet some of the people behind SHS</a></h3>
<p>The time, energy and kindness donated by volunteers powers everything we do.</p>
<a class="shs-read" href="#">READ STORY <b>→</b></a>
</article>
<article class="shs-story-card shs-reveal" data-shs-post="7">
<a href="#" class="shs-story-image">
<img src="https://www.sussexhomelesssupport.co.uk/wp-content/uploads/2026/09/Slide2.jpeg"
alt="Sussex Homeless Support food support">
<span class="shs-card-no">08</span>
</a>
<div class="shs-meta"><b>12 SEPTEMBER 2021</b> <span></span> FOOD SUPPORT</div>
<h3><a href="#">Street Kitchen: every Saturday</a></h3>
<p>Food is only part of the story. A regular presence also creates trust, conversation and connection.</p>
<a class="shs-read" href="#">READ STORY <b>→</b></a>
</article>
<article class="shs-story-card shs-reveal" data-shs-post="8">
<a href="#" class="shs-story-image shs-orange-card">
<div class="shs-quote-art">
<small>GET INVOLVED</small>
<strong>YOUR HELP<br>CHANGES<br>LIVES.</strong>
</div>
<span class="shs-card-no">09</span>
</a>
<div class="shs-meta"><b>02 FEBRUARY 2021</b> <span></span> FUNDRAISING</div>
<h3><a href="#">Fundraising with a purpose</a></h3>
<p>From local events to corporate partnerships, there are many ways to help us deliver frontline support.</p>
<a class="shs-read" href="#">READ STORY <b>→</b></a>
</article>
</section>
<div class="shs-load-wrap shs-reveal shs-legacy-grid">
<button type="button" id="shs-load-demo">LOAD MORE STORIES <span>↓</span></button>
<p id="shs-feed-status">Loading published stories from Sussex Homeless Support…</p>
</div>
</div>
<style>
#shs-journal,#shs-journal *{box-sizing:border-box}
#shs-journal{
--orange:#f37021;--ink:#171717;--soft:#f3f1ed;--line:rgba(23,23,23,.16);
width:min(1220px,calc(100% - 48px));margin:0 auto;padding:28px 0 90px;
font-family:Arial,Helvetica,sans-serif;color:var(--ink)
}
#shs-journal a{color:inherit;text-decoration:none}
.shs-journal-intro{display:grid;grid-template-columns:1.15fr .85fr;gap:70px;align-items:end;padding:24px 0 38px;border-bottom:1px solid var(--ink)}
.shs-kicker,.shs-section-heading span{color:var(--orange);font-size:12px;line-height:1;letter-spacing:2.1px;font-weight:700}
.shs-journal-intro h1{margin:8px 0 0;font-size:clamp(58px,7.2vw,108px);line-height:.84;letter-spacing:-5px;font-weight:800}
.shs-intro-copy{max-width:520px;margin:0 0 4px auto;font-size:18px;line-height:1.5}
.shs-lead-grid{display:grid;grid-template-columns:1.45fr 1fr;gap:28px;padding:34px 0 34px}
.shs-visual-prompts{grid-template-columns:1fr 1fr!important;gap:18px!important}
.shs-visual-prompts .shs-side-image{height:145px}
.shs-visual-prompts .shs-side-copy{padding:13px 14px 15px}
.shs-visual-prompts .shs-side-copy h3{font-size:20px;line-height:1.05}
.shs-visual-prompts .shs-meta{font-size:9px}
.shs-visual-prompts .shs-read{font-size:10px;padding:9px 12px 8px}
.shs-legacy-grid{display:none!important}
.shs-lead-card,.shs-side-card{min-width:0;background:rgba(255,255,255,.92);border:1px solid rgba(23,23,23,.16);border-radius:6px;overflow:hidden}
.shs-image-link{display:block;height:455px;overflow:hidden;position:relative;background:var(--soft)}
.shs-image-link img,.shs-side-image img,.shs-story-image img,.shs-video-stage img{width:100%;height:100%;display:block;object-fit:cover;transition:transform .65s cubic-bezier(.2,.75,.25,1)}
.shs-lead-photo img{object-position:center 42%}
.shs-lead-card:hover img,.shs-side-card:hover img,.shs-story-card:hover img,.shs-video-stage:hover img{transform:scale(1.035)}
.shs-featured-pill{position:absolute;left:22px;top:22px;padding:9px 13px 8px;border-radius:99px;background:var(--orange);color:#fff;font-size:11px;letter-spacing:1.5px;font-weight:700}
.shs-story-number{position:absolute;right:18px;bottom:16px;display:grid;place-items:center;width:72px;height:72px;border:1px solid rgba(255,255,255,.95);border-radius:50%;background:rgba(0,0,0,.16);color:#fff;font-size:30px;line-height:1;font-weight:800;letter-spacing:-1px;text-shadow:0 1px 8px rgba(0,0,0,.3);backdrop-filter:blur(2px);-webkit-backdrop-filter:blur(2px)}
.shs-story-number-small{width:50px;height:50px;font-size:20px;right:12px;bottom:12px}
.shs-lead-copy{padding:25px 22px 22px}
.shs-meta{display:flex;align-items:center;gap:9px;margin-bottom:10px;color:var(--orange);font-size:11px;line-height:1.2;letter-spacing:1px;font-weight:700}
.shs-meta b{color:var(--ink);font:inherit;font-weight:700}
.shs-meta span{display:inline-block;width:18px;height:1px;background:var(--orange)}
.shs-lead-copy h2{max-width:800px;margin:0 0 14px;font-size:clamp(34px,3.4vw,55px);line-height:.98;letter-spacing:-2px;font-weight:800}
.shs-lead-copy p{max-width:760px;margin:0 0 18px;font-size:17px;line-height:1.55}
.shs-read{display:inline-flex;align-items:center;gap:10px;margin-top:3px;padding:11px 15px 10px;border:1px solid var(--ink);border-radius:99px;background:#fff;color:var(--ink)!important;font-size:12px;line-height:1;letter-spacing:1.15px;font-weight:800;transition:background .2s ease,color .2s ease,border-color .2s ease,transform .2s ease}
.shs-read b{color:var(--orange);font-size:18px;line-height:.7;transition:transform .2s ease,color .2s ease}
.shs-read:hover{background:var(--ink);color:#fff!important;transform:translateY(-1px)}
.shs-read:hover b{color:var(--orange);transform:translateX(3px)}
.shs-side-stories{display:grid;gap:28px;align-content:start}
.shs-side-image{display:block;height:225px;overflow:hidden;position:relative;background:#ddd}
.shs-side-copy{padding:17px 18px 19px}
.shs-side-copy h3,.shs-story-card h3{margin:0 0 13px;font-size:26px;line-height:1.04;letter-spacing:-.8px;font-weight:800}
/* VIDEO THUMB */
.shs-video-thumb{color:#fff}
.shs-video-shade,.shs-video-stage-shade{position:absolute;inset:0;background:linear-gradient(180deg,rgba(0,0,0,.08),rgba(0,0,0,.48))}
.shs-play,.shs-big-play{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);display:grid;place-items:center;border-radius:50%;background:var(--orange);box-shadow:0 8px 28px rgba(0,0,0,.2)}
.shs-play{width:64px;height:64px}
.shs-play i,.shs-big-play i{display:block;width:0;height:0;border-top:9px solid transparent;border-bottom:9px solid transparent;border-left:15px solid #fff;margin-left:4px}
.shs-video-label{position:absolute;left:16px;bottom:14px;font-size:11px;letter-spacing:1.5px;font-weight:700}
/* VIDEO FEATURE */
.shs-video-feature{display:grid;grid-template-columns:.72fr 1.28fr;min-height:480px;margin:0 0 54px;border-radius:6px;overflow:hidden;background:#171717;color:#fff}
.shs-video-copy{padding:46px 42px;display:flex;flex-direction:column;justify-content:center}
.shs-video-copy h2{margin:18px 0 20px;color:#fff;font-size:clamp(44px,4.7vw,72px);line-height:.82;letter-spacing:-3px;font-weight:800}
.shs-video-copy p{margin:0;max-width:420px;font-size:16px;line-height:1.55;color:rgba(255,255,255,.8)}
.shs-video-note{margin-top:28px;color:rgba(255,255,255,.48);font-size:10px;letter-spacing:1.35px;font-weight:700}
.shs-live-dot{display:inline-block;width:7px;height:7px;margin:0 6px 1px;border-radius:50%;background:var(--orange)}
.shs-video-stage{position:relative;display:block;width:100%;min-height:480px;padding:0;border:0;background:#222;overflow:hidden;cursor:pointer;text-align:left}
.shs-video-stage img{position:absolute;inset:0}
.shs-big-play{width:94px;height:94px;transition:transform .25s ease,background .25s ease}
.shs-big-play i{border-top-width:13px;border-bottom-width:13px;border-left-width:21px}
.shs-video-stage:hover .shs-big-play{transform:translate(-50%,-50%) scale(1.08);background:#fff}
.shs-video-stage:hover .shs-big-play i{border-left-color:var(--orange)}
.shs-video-caption{position:absolute;left:25px;right:25px;bottom:22px;display:flex;justify-content:space-between;align-items:center;color:#fff}
.shs-video-caption b{font-size:12px;letter-spacing:1.5px}
.shs-video-caption em{font-style:normal;font-size:12px;letter-spacing:1px}
/* STORIES */
.shs-section-heading{display:flex;gap:28px;align-items:flex-end;margin:0 0 27px}
.shs-section-heading h2{margin:7px 0 0;font-size:36px;line-height:1;letter-spacing:-1.5px}
.shs-heading-line{flex:1;height:1px;margin-bottom:4px;background:var(--ink)}
.shs-story-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:52px 28px}
.shs-story-card{min-width:0;padding:12px 12px 18px;border:1px solid rgba(23,23,23,.16);border-radius:6px;background:rgba(255,255,255,.88)}
.shs-story-image{display:block;height:245px;margin-bottom:18px;overflow:hidden;border-radius:6px;background:var(--soft);position:relative}
.shs-card-no{position:absolute;left:13px;top:11px;display:grid;place-items:center;width:38px;height:38px;border:1px solid rgba(23,23,23,.72);border-radius:50%;background:rgba(255,255,255,.88);color:var(--ink);font-size:11px;font-weight:800;letter-spacing:.5px;backdrop-filter:blur(2px);-webkit-backdrop-filter:blur(2px)}
.shs-story-card h3{font-size:28px}
.shs-story-card p{margin:0 0 17px;font-size:15px;line-height:1.55}
.shs-quote-art{height:100%;padding:25px;display:flex;flex-direction:column;justify-content:space-between}
.shs-orange-card{background:var(--orange);color:#fff}.shs-dark-card{background:#1d1d1d;color:#fff}
.shs-quote-art small{font-size:11px;letter-spacing:1.6px;font-weight:700}
.shs-quote-art strong{font-size:39px;line-height:.86;letter-spacing:-1.7px}
.shs-load-wrap{margin-top:68px;padding-top:28px;text-align:center;border-top:1px solid var(--line)}
#shs-load-demo{appearance:none;border:1px solid var(--ink);border-radius:99px;background:rgba(255,255,255,.82);color:var(--ink);padding:15px 24px 14px;font:700 12px/1 Arial,Helvetica,sans-serif;letter-spacing:1.25px;cursor:pointer;transition:.2s ease}
#shs-load-demo span{color:var(--orange);margin-left:7px}
#shs-load-demo:hover{background:var(--ink);color:#fff;transform:translateY(-2px)}
.shs-load-wrap p{margin:11px 0 0;font-size:12px;color:#777}
.shs-reveal{opacity:0;transform:translateY(18px);transition:opacity .65s ease,transform .65s ease}
.shs-reveal.shs-visible{opacity:1;transform:none}
/* ARTICLE READING EXPERIENCE V11 */
/* AUTOMATIC ARTICLE LINK BUTTONS */
.shs-reader-content a.shs-article-link-button{
display:inline-flex;
align-items:center;
gap:10px;
margin:6px 5px 10px 0;
padding:12px 17px 11px;
border:1px solid var(--ink);
border-radius:99px;
background:#fff;
color:var(--ink)!important;
text-decoration:none!important;
font-size:11px;
line-height:1;
font-weight:800;
letter-spacing:1px;
transition:.2s ease;
}
.shs-reader-content a.shs-article-link-button:after{
content:"→";
color:var(--orange);
font-size:17px;
line-height:.7;
}
.shs-reader-content a.shs-article-link-button:hover{
background:var(--ink);
color:#fff!important;
transform:translateY(-1px);
}
.shs-reader-content{
max-width:790px;
margin-right:auto;
color:#242424;
font-size:18px;
line-height:1.78;
}
.shs-reader-content>p:first-child{font-size:21px;line-height:1.65}
.shs-reader-content p{margin:0 0 1.45em}
.shs-reader-content h2,.shs-reader-content h3,.shs-reader-content h4{
margin:1.55em 0 .55em;
color:var(--ink);
line-height:1.04;
letter-spacing:-1px;
font-weight:800;
}
.shs-reader-content h2{font-size:36px}
.shs-reader-content h3{font-size:28px}
.shs-reader-content h4{font-size:22px}
.shs-reader-content blockquote{
margin:42px 0;
padding:7px 0 7px 27px;
border-left:5px solid var(--orange);
color:var(--ink);
font-size:27px;
line-height:1.28;
font-weight:700;
}
.shs-reader-content blockquote p{margin:0}
.shs-reader-content ul,.shs-reader-content ol{margin:0 0 1.5em;padding-left:25px}
.shs-reader-content li{margin-bottom:.55em}
.shs-reader-content figure{margin:36px 0}
.shs-reader-content img{
display:block;
width:auto;
max-width:100%;
height:auto;
margin:34px 0;
border-radius:6px;
}
.shs-reader-content figure img{margin:0}
.shs-reader-content figcaption{
margin-top:8px;
color:#666;
font-size:12px;
line-height:1.4;
}
.shs-reader-content hr{
margin:42px 0;
border:0;
border-top:1px solid var(--line);
}
.shs-article-end{
max-width:790px;
margin:48px 0 0;
padding:17px 0;
border-top:1px solid var(--ink);
border-bottom:1px solid var(--ink);
color:var(--orange);
font-size:10px;
font-weight:800;
letter-spacing:1.5px;
}
.shs-keep-reading{
margin-top:55px;
padding:28px;
border-radius:6px;
background:#171717;
color:#fff;
}
.shs-keep-heading{display:flex;align-items:flex-end;gap:25px;margin-bottom:24px}
.shs-keep-heading h3{margin:6px 0 0;font-size:38px;line-height:1;letter-spacing:-1.5px}
.shs-keep-heading .shs-heading-line{background:rgba(255,255,255,.45)}
.shs-keep-cards{display:grid;grid-template-columns:1fr 1fr;gap:18px}
.shs-keep-card{
appearance:none;
display:grid;
grid-template-columns:130px 1fr;
min-height:120px;
padding:0;
overflow:hidden;
border:1px solid rgba(255,255,255,.2);
border-radius:6px;
background:#222;
color:#fff;
text-align:left;
cursor:pointer;
}
.shs-keep-card img{width:130px;height:100%;min-height:120px;object-fit:cover}
.shs-keep-card-copy{padding:15px}
.shs-keep-card small{display:block;margin-bottom:7px;color:var(--orange);font-size:9px;font-weight:800;letter-spacing:1px}
.shs-keep-card strong{display:block;font-size:17px;line-height:1.12}
.shs-keep-card:hover strong{color:var(--orange)}
.shs-article-nav{
display:flex;
justify-content:space-between;
gap:12px;
margin-top:18px;
}
.shs-article-nav button{
appearance:none;
padding:12px 15px;
border:1px solid var(--ink);
border-radius:99px;
background:#fff;
color:var(--ink);
font:800 10px/1 Arial,Helvetica,sans-serif;
letter-spacing:1px;
cursor:pointer;
}
.shs-article-nav button:hover{background:var(--ink);color:#fff}
.shs-article-nav button:disabled{opacity:.28;cursor:default}
/* PUBLISHED ARTICLES + IN-PAGE ARTICLE READER */
.shs-published-home{display:grid;grid-template-columns:280px 1fr;gap:35px;margin:0 0 48px;padding:27px 0;border-top:1px solid var(--ink);border-bottom:1px solid var(--ink)}
.shs-published-home-head h2,.shs-published h2{margin:7px 0 0;font-size:32px;line-height:.92;letter-spacing:-1.4px}
.shs-published-home-list{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:0 28px}
.shs-pub-item{appearance:none;width:100%;display:grid;grid-template-columns:105px 1fr;gap:12px;padding:12px 0;border:0;border-bottom:1px solid var(--line);background:transparent;color:var(--ink);text-align:left;cursor:pointer}
.shs-pub-item time{color:#555;font-size:10px;line-height:1.3;font-weight:700;letter-spacing:.5px}
.shs-pub-item strong{font-size:14px;line-height:1.22;font-weight:800}
.shs-pub-item:hover strong,.shs-pub-item.shs-current strong{color:var(--orange)}
.shs-reader{display:grid;grid-template-columns:minmax(0,1fr) 315px;gap:48px;align-items:start;padding:34px 0 55px}
.shs-reader[hidden]{display:none!important}
.shs-reader-main{min-width:0}
.shs-back{appearance:none;margin:0 0 25px;padding:10px 14px;border:1px solid var(--ink);border-radius:99px;background:#fff;color:var(--ink);font:800 10px/1 Arial,Helvetica,sans-serif;letter-spacing:1px;cursor:pointer}
.shs-back:hover{background:var(--ink);color:#fff}
.shs-reader-meta{margin-bottom:12px;color:var(--orange);font-size:11px;font-weight:800;letter-spacing:1.2px}
.shs-reader-main>h2{margin:0 0 25px;max-width:850px;font-size:clamp(42px,5vw,72px);line-height:.94;letter-spacing:-2.7px}
.shs-reader-image{display:block;width:100%;max-height:600px;margin:0 0 28px;border-radius:6px;object-fit:cover}
.shs-reader-content{font-size:17px;line-height:1.7}
.shs-reader-content p{margin:0 0 1.25em}
.shs-reader-content img{max-width:100%;height:auto;border-radius:6px}
.shs-reader-content a{color:var(--orange)!important;text-decoration:underline!important}
.shs-published{position:sticky;top:120px;padding:22px;border:1px solid var(--line);border-radius:6px;background:rgba(255,255,255,.92)}
.shs-published #shs-published-list{margin-top:20px;max-height:68vh;overflow:auto;padding-right:5px}
.shs-published .shs-pub-item{grid-template-columns:1fr;gap:4px}
@media(max-width:900px){
#shs-journal{width:min(100% - 30px,760px);padding-top:18px}
.shs-journal-intro{grid-template-columns:1fr;gap:20px}.shs-journal-intro h1{font-size:70px;letter-spacing:-3px}.shs-intro-copy{margin:0}
.shs-lead-grid{grid-template-columns:1fr;padding-bottom:42px}.shs-image-link{height:390px}.shs-side-stories{grid-template-columns:1fr 1fr}
.shs-video-feature{grid-template-columns:1fr}.shs-video-stage{min-height:390px}.shs-story-grid{grid-template-columns:1fr 1fr}
.shs-published-home{grid-template-columns:1fr}.shs-published-home-list{grid-template-columns:1fr 1fr}
.shs-reader{grid-template-columns:1fr}.shs-published{position:static}.shs-published #shs-published-list{max-height:none}
}
@media(max-width:620px){
#shs-journal{width:calc(100% - 28px);padding-bottom:60px}.shs-journal-intro h1{font-size:54px;letter-spacing:-2.5px}.shs-intro-copy{font-size:16px}
.shs-image-link{height:300px}.shs-lead-copy h2{font-size:36px;letter-spacing:-1.3px}.shs-side-stories,.shs-story-grid{grid-template-columns:1fr}
.shs-side-image,.shs-story-image{height:250px}.shs-visual-prompts{grid-template-columns:1fr!important}.shs-visual-prompts .shs-side-image{height:230px}
.shs-published-home-list{grid-template-columns:1fr}.shs-pub-item{grid-template-columns:90px 1fr}
.shs-reader-main>h2{font-size:40px;letter-spacing:-1.6px}.shs-reader-content{font-size:16px}
.shs-reader-content>p:first-child{font-size:18px}.shs-reader-content blockquote{font-size:22px}
.shs-keep-reading{padding:20px}.shs-keep-cards{grid-template-columns:1fr}.shs-keep-card{grid-template-columns:105px 1fr}.shs-keep-card img{width:105px}
.shs-article-nav{flex-direction:column}.shs-article-nav button{width:100%}
.shs-video-copy{padding:36px 25px}.shs-video-copy h2{font-size:48px}.shs-video-stage{min-height:300px}
.shs-section-heading h2{font-size:29px}.shs-heading-line{display:none}
}
@media(prefers-reduced-motion:reduce){
.shs-reveal,.shs-image-link img,.shs-side-image img,.shs-story-image img,.shs-video-stage img{transition:none!important}
}
</style>
<script>
(function(){
var root=document.getElementById('shs-journal');
if(!root)return;
var API='/wp-json/wp/v2/posts?status=publish&per_page=30&_embed=1';
var posts=[];
var visibleCount=5;
function decodeHTML(value){
var t=document.createElement('textarea');
t.innerHTML=value||'';
return t.value;
}
function stripHTML(value){
var d=document.createElement('div');
d.innerHTML=value||'';
return (d.textContent||d.innerText||'').replace(/\s+/g,' ').trim();
}
function cleanExcerpt(post){
var text=stripHTML(post.excerpt && post.excerpt.rendered ? post.excerpt.rendered : '');
if(!text) text=stripHTML(post.content && post.content.rendered ? post.content.rendered : '');
if(text.length>185) text=text.slice(0,182).replace(/\s+\S*$/,'')+'…';
return text;
}
function featuredImage(post){
try{
var media=post._embedded['wp:featuredmedia'][0];
return media.media_details && media.media_details.sizes && media.media_details.sizes.large
? media.media_details.sizes.large.source_url
: media.source_url;
}catch(e){ return ''; }
}
function categoryName(post){
try{
var terms=post._embedded['wp:term'] || [];
for(var i=0;i<terms.length;i++){
for(var j=0;j<terms[i].length;j++){
if(terms[i][j].taxonomy==='category') return decodeHTML(terms[i][j].name).toUpperCase();
}
}
}catch(e){}
return 'SHS NEWS';
}
function postDate(post){
var d=new Date(post.date);
if(isNaN(d.getTime())) return '';
return d.toLocaleDateString('en-GB',{day:'2-digit',month:'long',year:'numeric'}).toUpperCase();
}
function setLink(el,url){
if(el) el.setAttribute('href',url||'#');
}
function fillMeta(card,post){
var meta=card.querySelector('.shs-meta');
if(meta) meta.innerHTML='<b>'+postDate(post)+'</b> <span></span> '+categoryName(post);
}
function fillImage(card,post){
var img=card.querySelector('img');
var url=featuredImage(post);
if(img && url){
img.src=url;
img.alt=decodeHTML(post.title.rendered||'Sussex Homeless Support story');
}
}
function fillCard(card,post,index){
if(!card || !post)return;
card.style.display='';
fillMeta(card,post);
fillImage(card,post);
/* Editorial override for the lead story:
use Christopher Crawford's Seascapes artwork instead of the post's stored featured image. */
if(index===0){
var leadImg=card.querySelector('img');
if(leadImg){
leadImg.src='https://www.sussexhomelesssupport.co.uk/wp-content/uploads/2026/09/Seascapes-Cards-20263.jpg';
leadImg.alt='Seascapes artwork by Christopher Crawford';
}
}
var title=card.querySelector('h2 a, h3 a');
if(title){
title.textContent=decodeHTML(post.title.rendered||'');
setLink(title,post.link);
}
var paragraph=card.querySelector('.shs-lead-copy p, .shs-story-card > p');
if(paragraph) paragraph.textContent=cleanExcerpt(post);
var read=card.querySelector('.shs-read');
if(read){
setLink(read,post.link);
read.innerHTML='READ STORY <b>→</b>';
}
var imageLink=card.querySelector('.shs-image-link, .shs-side-image, .shs-story-image');
setLink(imageLink,post.link);
var num=card.querySelector('.shs-story-number, .shs-card-no');
if(num) num.textContent=String(index+1).padStart(2,'0');
/* Remove prototype video treatment from story #3 when it becomes a normal live article. */
if(index===2){
if(imageLink){
imageLink.classList.remove('shs-video-thumb');
var shade=imageLink.querySelector('.shs-video-shade');
var play=imageLink.querySelector('.shs-play');
var label=imageLink.querySelector('.shs-video-label');
if(shade)shade.remove();
if(play)play.remove();
if(label)label.remove();
}
}
}
function articleImage(post,index){
if(index===0) return 'https://www.sussexhomelesssupport.co.uk/wp-content/uploads/2026/09/Seascapes-Cards-20263.jpg';
return featuredImage(post);
}
function buildPublishedList(container){
if(!container)return;
container.innerHTML='';
posts.forEach(function(post,index){
var button=document.createElement('button');
button.type='button';
button.className='shs-pub-item';
button.setAttribute('data-article-index',index);
button.innerHTML='<time>'+postDate(post)+'</time><strong>'+decodeHTML(post.title && post.title.rendered || '')+'</strong>';
button.addEventListener('click',function(){ openArticle(index); });
container.appendChild(button);
});
}
function improveArticleLinks(){
var content=document.getElementById('shs-reader-content');
if(!content)return;
function labelForURL(href){
var label='VISIT LINK';
try{
var host=(new URL(href)).hostname.replace(/^www\./,'').toLowerCase();
if(host.indexOf('facebook.com')!==-1) label='VIEW PICTURES ON FACEBOOK';
else if(host.indexOf('instagram.com')!==-1) label='VIEW ON INSTAGRAM';
else if(host.indexOf('youtube.com')!==-1 || host.indexOf('youtu.be')!==-1) label='WATCH ON YOUTUBE';
else if(host.indexOf('whatnot.com')!==-1) label='VISIT WHATNOT';
else if(host.indexOf('ebay.')!==-1) label='VISIT EBAY SHOP';
}catch(e){}
return label;
}
/* First: style URLs that WordPress has already made into links. */
content.querySelectorAll('a[href]').forEach(function(link){
var href=(link.getAttribute('href')||'').trim();
if(!/^https?:\/\//i.test(href))return;
var visible=(link.textContent||'').trim();
if(/^https?:\/\/|^www\./i.test(visible) || visible===href){
link.textContent=labelForURL(href);
link.classList.add('shs-article-link-button');
link.setAttribute('target','_blank');
link.setAttribute('rel','noopener');
}
});
/* Second: find plain-text URLs WordPress did NOT turn into <a> tags.
This is the case in older SHS posts such as:
https://facebook... HAS ALL THE PICTURES */
var walker=document.createTreeWalker(content,NodeFilter.SHOW_TEXT,null);
var textNodes=[];
while(walker.nextNode()){
var node=walker.currentNode;
if(node.parentElement && !node.parentElement.closest('a,script,style')) textNodes.push(node);
}
textNodes.forEach(function(node){
var text=node.nodeValue||'';
var regex=/(https?:\/\/[^\s<]+)/ig;
if(!regex.test(text))return;
regex.lastIndex=0;
var frag=document.createDocumentFragment();
var last=0;
var match;
while((match=regex.exec(text))!==null){
var raw=match[0];
var href=raw.replace(/[),.;!?]+$/,'');
var trailing=raw.slice(href.length);
frag.appendChild(document.createTextNode(text.slice(last,match.index)));
var a=document.createElement('a');
a.href=href;
a.target='_blank';
a.rel='noopener';
a.className='shs-article-link-button';
a.textContent=labelForURL(href);
frag.appendChild(a);
if(trailing)frag.appendChild(document.createTextNode(trailing));
last=match.index+raw.length;
}
frag.appendChild(document.createTextNode(text.slice(last)));
node.parentNode.replaceChild(frag,node);
});
}
function renderKeepReading(currentIndex){
var holder=document.getElementById('shs-keep-reading-cards');
if(!holder)return;
holder.innerHTML='';
var candidates=[];
for(var offset=1;offset<posts.length && candidates.length<2;offset++){
var idx=(currentIndex+offset)%posts.length;
if(idx!==currentIndex) candidates.push(idx);
}
candidates.forEach(function(idx){
var post=posts[idx];
var card=document.createElement('button');
card.type='button';
card.className='shs-keep-card';
var image=articleImage(post,idx);
card.innerHTML=
(image?'<img src="'+image+'" alt="">':'')+
'<span class="shs-keep-card-copy">'+
'<small>'+postDate(post)+' · '+categoryName(post)+'</small>'+
'<strong>'+decodeHTML(post.title && post.title.rendered || '')+'</strong>'+
'</span>';
card.addEventListener('click',function(){openArticle(idx);});
holder.appendChild(card);
});
}
function updateArticleNav(index){
var prev=document.getElementById('shs-prev-article');
var next=document.getElementById('shs-next-article');
if(prev){
prev.disabled=index<=0;
prev.onclick=function(){if(index>0)openArticle(index-1);};
}
if(next){
next.disabled=index>=posts.length-1;
next.onclick=function(){if(index<posts.length-1)openArticle(index+1);};
}
}
function openArticle(index){
var post=posts[index];
if(!post)return;
var reader=document.getElementById('shs-reader');
var title=document.getElementById('shs-reader-title');
var meta=document.getElementById('shs-reader-meta');
var image=document.getElementById('shs-reader-image');
var content=document.getElementById('shs-reader-content');
title.textContent=decodeHTML(post.title && post.title.rendered || '');
meta.textContent=postDate(post)+' · '+categoryName(post);
var imageURL=articleImage(post,index);
if(imageURL){
image.src=imageURL;
image.alt=decodeHTML(post.title && post.title.rendered || 'Sussex Homeless Support article');
image.style.display='';
}else{
image.removeAttribute('src');
image.style.display='none';
}
content.innerHTML=post.content && post.content.rendered ? post.content.rendered : '<p>'+cleanExcerpt(post)+'</p>';
improveArticleLinks();
renderKeepReading(index);
updateArticleNav(index);
root.querySelectorAll('.shs-pub-item').forEach(function(item){
item.classList.toggle('shs-current',parseInt(item.getAttribute('data-article-index'),10)===index);
});
Array.prototype.forEach.call(root.children,function(child){
if(child!==reader) child.style.display='none';
});
reader.hidden=false;
reader.style.display='grid';
/* Position the opened article comfortably below the fixed navigation. */
var fixedHeader=document.querySelector('.fw-header-main');
var headerBottom=fixedHeader ? fixedHeader.getBoundingClientRect().bottom : 0;
var readerTop=reader.getBoundingClientRect().top+window.pageYOffset;
var breathingRoom=34;
window.scrollTo({
top:Math.max(0,readerTop-headerBottom-breathingRoom),
behavior:'smooth'
});
}
function closeArticle(){
var reader=document.getElementById('shs-reader');
reader.hidden=true;
reader.style.display='none';
Array.prototype.forEach.call(root.children,function(child){
if(child!==reader) child.style.display='';
});
/* Return to the top of THE BLOGS with clearance below the fixed navigation. */
var fixedHeader=document.querySelector('.fw-header-main');
var headerBottom=fixedHeader ? fixedHeader.getBoundingClientRect().bottom : 0;
var rootTop=root.getBoundingClientRect().top+window.pageYOffset;
window.scrollTo({
top:Math.max(0,rootTop-headerBottom-34),
behavior:'smooth'
});
}
function bindVisualCards(){
root.querySelectorAll('[data-shs-post]').forEach(function(card){
var index=parseInt(card.getAttribute('data-shs-post'),10);
if(index>4)return;
card.querySelectorAll('a').forEach(function(link){
link.addEventListener('click',function(e){
e.preventDefault();
openArticle(index);
});
});
});
}
function render(){
var cards=root.querySelectorAll('[data-shs-post]');
cards.forEach(function(card){
var index=parseInt(card.getAttribute('data-shs-post'),10);
if(posts[index] && index<visibleCount){
fillCard(card,posts[index],index);
}else{
card.style.display='none';
}
});
var status=document.getElementById('shs-feed-status');
if(status){
status.textContent=posts.length
? posts.length+' published stor'+(posts.length===1?'y':'ies')+' available'
: 'No published stories found.';
}
var load=document.getElementById('shs-load-demo');
if(load){
/* V6 initially renders the first 9 into the approved V5 layout.
More-card generation comes next; button reports availability for now. */
if(posts.length<=9){
load.style.display='none';
}else{
load.style.display='';
load.innerHTML='MORE PUBLISHED STORIES <span>↓</span>';
}
}
buildPublishedList(document.getElementById('shs-published-home-list'));
buildPublishedList(document.getElementById('shs-published-list'));
bindVisualCards();
}
var backButton=document.getElementById('shs-back-to-blogs');
if(backButton) backButton.addEventListener('click',closeArticle);
fetch(API,{credentials:'same-origin'})
.then(function(response){
if(!response.ok) throw new Error('Feed request failed');
return response.json();
})
.then(function(data){
posts=Array.isArray(data)?data:[];
render();
})
.catch(function(){
var status=document.getElementById('shs-feed-status');
if(status) status.textContent='The live story feed could not be loaded.';
});
var items=root.querySelectorAll('.shs-reveal');
if('IntersectionObserver' in window){
var observer=new IntersectionObserver(function(entries){
entries.forEach(function(entry){
if(entry.isIntersecting){
entry.target.classList.add('shs-visible');
observer.unobserve(entry.target);
}
});
},{threshold:.08});
items.forEach(function(item){observer.observe(item);});
}else{
items.forEach(function(item){item.classList.add('shs-visible');});
}
var video=document.getElementById('shs-video-demo');
if(video){
video.addEventListener('click',function(){
var cap=video.querySelector('.shs-video-caption b');
if(cap)cap.textContent='VIDEO CONNECTION COMES NEXT';
});
}
var load=document.getElementById('shs-load-demo');
if(load){
load.addEventListener('click',function(){
var status=document.getElementById('shs-feed-status');
if(status) status.textContent='The next stage will expand this grid with the remaining published stories.';
});
}
})();
</script>
05B — ADD A BLOG POST — V01 — VIEW / COPY MASTER
<div id="shs-blog-form">
</div>
<div id="shs-blog-preview">
<div id="shs-blog-preview-heading">BLOG PREVIEW</div>
<div id="shs-blog-preview-card">
<div id="shs-blog-preview-label">SUSSEX HOMELESS SUPPORT</div>
<div id="shs-blog-preview-title">YOUR BLOG TITLE</div>
<div id="shs-blog-preview-image">
<div id="shs-blog-preview-placeholder">YOUR MAIN IMAGE</div>
</div>
<div id="shs-blog-preview-copy">Your blog post will appear here. This gives you an idea of how your finished article will look when it is published.</div>
</div>
</div>05C — BLOG ADMIN PAGE — V07 — VIEW / COPY MASTER
<!-- =========================================================
SUSSEX HOMELESS SUPPORT — BLOG ADMIN V7 — CONTROLLED WORDPRESS WRITE-BACK
VISUAL PROTOTYPE / RAW CODE
No WordPress writing actions yet.
========================================================= -->
<div id="shs-blog-admin">
<header class="sba-top">
<div>
<div class="sba-kicker">SUSSEX HOMELESS SUPPORT</div>
<h1>BLOG ADMIN</h1>
<p>A simple publishing desk for SHS stories, images and films.</p>
</div>
<div class="sba-user">
<span class="sba-live"></span>
<div><b>CONTENT DESK</b><small>WordPress connected later</small></div>
</div>
</header>
<nav class="sba-tabs" aria-label="Blog admin sections">
<button class="active" data-panel="published">PUBLISHED ARTICLES</button>
<button data-panel="editor">+ ADD NEW ARTICLE</button>
<button data-panel="films">SHS FILMS</button>
</nav>
<section class="sba-panel active" id="sba-published">
<div class="sba-panel-head">
<div>
<span>CONTENT</span>
<h2>PUBLISHED ARTICLES</h2>
</div>
<button class="sba-primary" data-open-editor>+ NEW ARTICLE</button>
</div>
<div class="sba-toolbar">
<input id="sba-search" type="search" placeholder="Search articles…" aria-label="Search articles">
<select id="sba-filter" aria-label="Filter articles"><option value="all">ALL ARTICLES</option><option value="article">ARTICLES</option><option value="video">VIDEO STORIES</option></select>
</div>
<div class="sba-table">
<div class="sba-row sba-row-head">
<span>STORY</span><span>TYPE</span><span>DATE</span><span>STATUS</span><span></span>
</div>
<div id="sba-live-articles">
<div class="sba-loading">LOADING PUBLISHED ARTICLES…</div>
</div>
</div>
</section>
<section class="sba-panel" id="sba-editor">
<div class="sba-panel-head">
<div><span>EDITOR</span><h2>CREATE A STORY</h2></div>
<button class="sba-secondary" data-panel-button="published">← ARTICLES</button>
</div>
<div id="sba-editing-banner" class="sba-editing-banner" hidden>
<div><span>EDITING EXISTING WORDPRESS ARTICLE</span><strong id="sba-editing-name"></strong></div>
<div id="sba-editing-id"></div>
</div>
<div class="sba-editor-grid">
<div class="sba-form">
<div class="sba-field">
<label>STORY TYPE</label>
<div class="sba-choice">
<button class="active" type="button">ARTICLE</button>
<button type="button">VIDEO STORY</button>
</div>
</div>
<div class="sba-field">
<label>HEADLINE</label>
<input id="sba-title" type="text" placeholder="Enter the story headline">
</div>
<div class="sba-two">
<div class="sba-field"><label>CATEGORY</label><select id="sba-category"><option>MAIN BLOG PAGE</option><option>COMMUNITY</option><option>FUNDRAISING</option><option>EVENTS</option><option>OUTREACH</option></select></div>
<div class="sba-field"><label>DATE</label><input id="sba-date" type="date"></div>
</div>
<div class="sba-field">
<label>FEATURED IMAGE</label>
<div class="sba-upload" id="sba-featured-upload">
<img id="sba-featured-existing" class="sba-featured-existing" src="" alt="" hidden>
<strong id="sba-featured-label">DROP IMAGE HERE</strong>
<span id="sba-featured-help">or choose an image from your computer</span>
<button type="button">CHOOSE IMAGE</button>
</div>
</div>
<div class="sba-field">
<label>ARTICLE</label>
<div class="sba-format">
<button type="button"><b>B</b></button><button type="button"><i>I</i></button>
<button type="button">H2</button><button type="button">QUOTE</button>
<button type="button">LINK</button>
</div>
<textarea id="sba-body" rows="12" placeholder="Write or paste the story here…"></textarea>
<small>Web addresses can automatically become clean SHS buttons on THE BLOGS.</small>
</div>
<div class="sba-field">
<label>ADDITIONAL IMAGES</label>
<div class="sba-upload sba-upload-small"><strong>ADD STORY IMAGES</strong><span>Multiple images supported in the finished version</span></div>
</div>
</div>
<aside class="sba-preview">
<div class="sba-preview-label">LIVE PREVIEW</div>
<div class="sba-preview-image"><span>FEATURED IMAGE</span></div>
<div class="sba-preview-meta">MAIN BLOG PAGE · PREVIEW</div>
<h3 id="sba-preview-title">YOUR STORY HEADLINE</h3>
<div id="sba-preview-body" class="sba-preview-body"><p>Your article introduction will appear here as you type.</p></div>
</aside>
</div>
<div class="sba-publish-bar">
<div><b>READY?</b><span>Preview before sending anything live.</span></div>
<div>
<button class="sba-secondary sba-disabled-action" type="button" disabled>SAVE DRAFT</button>
<button class="sba-secondary" id="sba-preview-button" type="button">PREVIEW</button>
<button class="sba-primary sba-disabled-action" id="sba-publish-button" type="button" disabled>PUBLISH ARTICLE →</button>
</div>
</div>
</section>
<section class="sba-panel" id="sba-films">
<div class="sba-panel-head"><div><span>VIDEO</span><h2>SHS FILMS</h2></div><button class="sba-primary" data-open-editor>+ ADD VIDEO STORY</button></div>
<div class="sba-film-box">
<strong>VIDEO STORIES WILL LIVE HERE</strong>
<p>Upload a video or paste a YouTube/Vimeo link, add a headline and featured image, then publish it directly into the SHS FILMS area of THE BLOGS.</p>
</div>
</section>
<div id="sba-update-modal" class="sba-update-modal" hidden>
<div class="sba-update-box">
<div class="sba-kicker">CONFIRM WORDPRESS UPDATE</div>
<h3>UPDATE THIS ARTICLE?</h3>
<p>Only the fields listed below will be changed. The featured image will remain untouched.</p>
<div id="sba-change-list" class="sba-change-list"></div>
<div class="sba-update-actions">
<button type="button" class="sba-secondary" id="sba-cancel-update">CANCEL</button>
<button type="button" class="sba-primary" id="sba-confirm-update">YES, UPDATE ARTICLE →</button>
</div>
<div id="sba-update-message" class="sba-update-message"></div>
</div>
</div>
<div id="sba-image-modal" class="sba-image-modal" hidden>
<div class="sba-image-modal-box">
<button type="button" id="sba-image-close" class="sba-image-close" aria-label="Close image">×</button>
<img id="sba-image-large" src="" alt="">
</div>
</div>
</div>
<style>
#shs-blog-admin,#shs-blog-admin *{box-sizing:border-box}
#shs-blog-admin{--o:#f37021;--ink:#171717;--line:#d9d9d9;--soft:#f4f3f0;width:min(1220px,calc(100% - 40px));margin:0 auto;padding:210px 0 80px;font-family:Arial,Helvetica,sans-serif;color:var(--ink)}
.sba-top{display:flex;justify-content:space-between;gap:30px;align-items:flex-end;padding:25px 0 30px;border-bottom:1px solid var(--ink)}
.sba-kicker,.sba-panel-head span,.sba-preview-label{color:var(--o);font-size:11px;font-weight:800;letter-spacing:1.8px}
.sba-top h1{margin:6px 0 8px;font-size:clamp(48px,6vw,86px);line-height:.85;letter-spacing:-4px}
.sba-top p{margin:0;font-size:16px}.sba-user{display:flex;align-items:center;gap:12px;padding:13px 16px;border:1px solid var(--line);border-radius:6px;background:#fff}.sba-user div{display:flex;flex-direction:column}.sba-user b{font-size:10px;letter-spacing:1px}.sba-user small{margin-top:3px;color:#777;font-size:9px}.sba-live{width:9px;height:9px;border-radius:50%;background:var(--o)}
.sba-tabs{display:flex;gap:7px;padding:20px 0}.sba-tabs button,.sba-choice button,.sba-format button{appearance:none;border:1px solid var(--ink);background:#fff;color:var(--ink);cursor:pointer;font:800 10px/1 Arial;letter-spacing:.8px}.sba-tabs button{padding:12px 15px;border-radius:99px}.sba-tabs button.active,.sba-choice button.active{background:var(--ink);color:#fff}
.sba-panel{display:none}.sba-panel.active{display:block}.sba-panel-head{display:flex;justify-content:space-between;align-items:flex-end;gap:20px;padding:26px 0 20px}.sba-panel-head h2{margin:5px 0 0;font-size:38px;line-height:1;letter-spacing:-1.5px}
.sba-primary,.sba-secondary,.sba-edit{appearance:none;cursor:pointer;border-radius:99px;font:800 10px/1 Arial;letter-spacing:1px}.sba-primary{padding:13px 18px;border:1px solid var(--o);background:var(--o);color:#fff}.sba-secondary{padding:12px 16px;border:1px solid var(--ink);background:#fff;color:var(--ink)}.sba-edit{padding:9px 12px;border:1px solid var(--ink);background:#fff}
.sba-toolbar{display:flex;gap:10px;padding:15px;border:1px solid var(--line);border-radius:6px 6px 0 0;background:var(--soft)}.sba-toolbar input{flex:1}.sba-toolbar input,.sba-toolbar select,.sba-field input,.sba-field select,.sba-field textarea{width:100%;padding:13px;border:1px solid #bbb;border-radius:4px;background:#fff;font:14px/1.4 Arial;color:var(--ink)}
.sba-table{border:1px solid var(--line);border-top:0;border-radius:0 0 6px 6px;overflow:hidden;background:#fff}.sba-row{display:grid;grid-template-columns:minmax(300px,1.6fr) .55fr .7fr .65fr 80px;gap:15px;align-items:center;padding:14px 16px;border-bottom:1px solid var(--line);font-size:11px}.sba-row:last-child{border-bottom:0}.sba-row-head{color:#777;font-size:9px;font-weight:800;letter-spacing:1px}.sba-story{display:flex;align-items:center;gap:13px;font-size:14px}.sba-thumb{display:grid;place-items:center;width:48px;height:48px;border-radius:4px;background:#222;color:#fff;font-weight:800}.sba-orange{background:var(--o)}.sba-status{color:var(--o);font-weight:800}
.sba-loading,.sba-empty{padding:28px 16px;color:#777;font-size:11px;font-weight:800;letter-spacing:1px}
.sba-article-count{margin-left:auto;color:#777;font-size:10px;font-weight:800;letter-spacing:1px}
.sba-story-title{min-width:0}
.sba-story-title b{display:block}
.sba-story-title small{display:block;margin-top:4px;color:#888;font-size:9px;font-weight:400}
.sba-editor-grid{display:grid;grid-template-columns:minmax(0,1.35fr) minmax(300px,.65fr);gap:30px;align-items:start}.sba-form{padding:25px;border:1px solid var(--line);border-radius:6px;background:#fff}.sba-field{margin-bottom:22px}.sba-field label{display:block;margin-bottom:8px;font-size:10px;font-weight:800;letter-spacing:1.2px}.sba-field small{display:block;margin-top:7px;color:#777;font-size:10px}.sba-two{display:grid;grid-template-columns:1fr 1fr;gap:15px}.sba-choice{display:flex;gap:7px}.sba-choice button{padding:11px 14px;border-radius:99px}.sba-upload{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:170px;padding:20px;border:1px dashed #aaa;border-radius:6px;background:var(--soft);text-align:center}.sba-upload strong{font-size:14px}.sba-upload span{margin:5px 0 13px;color:#777;font-size:11px}.sba-upload button{padding:9px 13px;border:1px solid var(--ink);border-radius:99px;background:#fff;font-size:10px;font-weight:800}.sba-upload-small{min-height:100px}.sba-format{display:flex;gap:5px;padding:7px;border:1px solid #bbb;border-bottom:0;border-radius:4px 4px 0 0;background:var(--soft)}.sba-format button{padding:8px 10px;border-color:#bbb;border-radius:3px}.sba-field textarea{border-radius:0 0 4px 4px;resize:vertical}
.sba-preview{position:sticky;top:120px;padding:18px;border:1px solid var(--line);border-radius:6px;background:#fff}.sba-preview-image{display:grid;place-items:center;height:250px;margin:14px 0 17px;border-radius:5px;background:#222;color:#777;font-size:11px;font-weight:800;letter-spacing:1px}.sba-preview-meta{color:var(--o);font-size:9px;font-weight:800;letter-spacing:1px}.sba-preview h3{margin:9px 0 12px;font-size:31px;line-height:1;letter-spacing:-1px}.sba-preview p{margin:0;font-size:14px;line-height:1.5}
.sba-publish-bar{display:flex;justify-content:space-between;align-items:center;gap:20px;margin-top:20px;padding:18px 20px;border-radius:6px;background:#171717;color:#fff}.sba-publish-bar>div{display:flex;align-items:center;gap:9px}.sba-publish-bar>div:first-child{flex-direction:column;align-items:flex-start}.sba-publish-bar span{color:#aaa;font-size:10px}.sba-publish-bar .sba-secondary{border-color:#777;background:transparent;color:#fff}
.sba-film-box{padding:50px;border:1px solid var(--line);border-radius:6px;background:#171717;color:#fff}.sba-film-box strong{font-size:30px}.sba-film-box p{max-width:680px;color:#ccc;font-size:16px;line-height:1.6}
/* V7 — CONTROLLED WORDPRESS ARTICLE UPDATES */
/* V7 — CONTROLLED WORDPRESS WRITE-BACK */
.sba-update-modal{
position:fixed;z-index:1000000;inset:0;display:flex;align-items:center;justify-content:center;
padding:30px;background:rgba(0,0,0,.78)
}
.sba-update-modal[hidden]{display:none!important}
.sba-update-box{width:min(620px,94vw);padding:28px;border-radius:6px;background:#fff;color:var(--ink);box-shadow:0 25px 80px rgba(0,0,0,.35)}
.sba-update-box h3{margin:8px 0 10px;font-size:34px;line-height:1;letter-spacing:-1.4px}
.sba-update-box>p{margin:0 0 18px;color:#555;font-size:14px;line-height:1.5}
.sba-change-list{border-top:1px solid var(--line);border-bottom:1px solid var(--line);padding:8px 0;margin-bottom:20px}
.sba-change{display:grid;grid-template-columns:120px 1fr;gap:15px;padding:10px 0;border-bottom:1px solid #eee}
.sba-change:last-child{border-bottom:0}.sba-change b{font-size:10px;letter-spacing:1px}.sba-change span{font-size:13px;line-height:1.4}
.sba-update-actions{display:flex;justify-content:flex-end;gap:8px}
.sba-update-message{margin-top:14px;font-size:12px;font-weight:700;line-height:1.45}
.sba-update-message.ok{color:#187a3b}.sba-update-message.error{color:#b3261e}
/* IMAGE PREVIEW — keep user inside Blog Admin */
.sba-preview-body img,.sba-featured-existing{cursor:zoom-in}
.sba-image-modal{
position:fixed;
z-index:999999;
inset:0;
display:flex;
align-items:center;
justify-content:center;
padding:35px;
background:rgba(0,0,0,.82);
}
.sba-image-modal[hidden]{display:none!important}
.sba-image-modal-box{
position:relative;
max-width:min(1100px,92vw);
max-height:88vh;
padding:12px;
border:1px solid var(--o);
border-radius:6px;
background:#fff;
}
.sba-image-modal-box img{
display:block;
max-width:100%;
max-height:82vh;
object-fit:contain;
}
.sba-image-close{
position:absolute;
z-index:2;
right:-15px;
top:-15px;
display:grid;
place-items:center;
width:36px;
height:36px;
padding:0;
border:0;
border-radius:50%;
background:var(--o);
color:#fff;
font:700 25px/1 Arial;
cursor:pointer;
}
.sba-toolbar input#sba-search{
display:block!important;
flex:1 1 auto!important;
width:auto!important;
min-width:260px!important;
height:44px!important;
padding:0 14px!important;
border:1px solid #bbb!important;
border-radius:4px!important;
background:#fff!important;
color:var(--ink)!important;
opacity:1!important;
}
.sba-toolbar select#sba-filter{flex:0 0 230px;height:44px}
.sba-editing-banner{
display:flex;
justify-content:space-between;
align-items:center;
gap:20px;
margin-bottom:18px;
padding:15px 18px;
border-left:5px solid var(--o);
border-radius:5px;
background:#171717;
color:#fff;
}
.sba-editing-banner[hidden]{display:none!important}
.sba-editing-banner>div:first-child{display:flex;flex-direction:column;gap:4px}
.sba-editing-banner span{color:var(--o);font-size:9px;font-weight:800;letter-spacing:1.2px}
.sba-editing-banner strong{font-size:17px}
#sba-editing-id{color:#aaa;font-size:10px;font-weight:800;letter-spacing:1px}
.sba-featured-existing{
display:block;
width:100%;
max-height:260px;
margin-bottom:13px;
object-fit:cover;
border-radius:5px;
}
.sba-preview-image{
overflow:hidden;
background-size:cover!important;
background-position:center!important;
}
.sba-preview-body{font-size:14px;line-height:1.55}
.sba-preview-body p{margin:0 0 1em}
.sba-preview-body img{max-width:100%;height:auto;border-radius:4px}
.sba-preview-body a{color:var(--o)}
.sba-disabled-action{opacity:.38!important;cursor:not-allowed!important}
@media(max-width:850px){#shs-blog-admin{padding-top:35px}.sba-top{align-items:flex-start}.sba-user{display:none}.sba-editor-grid{grid-template-columns:1fr}.sba-preview{position:static}.sba-row{grid-template-columns:1fr 80px}.sba-row-head{display:none}.sba-row>span{display:none}.sba-publish-bar{align-items:flex-start;flex-direction:column}.sba-tabs{overflow:auto}.sba-tabs button{white-space:nowrap}}
@media(max-width:560px){#shs-blog-admin{width:calc(100% - 28px)}.sba-top h1{font-size:54px;letter-spacing:-2px}.sba-two{grid-template-columns:1fr}.sba-form{padding:16px}.sba-publish-bar>div:last-child{display:grid;width:100%}.sba-publish-bar button{width:100%}}
</style>
<script>
(function(){
var root=document.getElementById('shs-blog-admin'); if(!root)return;
var API='/wp-json/wp/v2/posts?status=publish&per_page=100&_embed=1';
var livePosts=[];
var currentPost=null;
var tabs=root.querySelectorAll('.sba-tabs button');
var panels=root.querySelectorAll('.sba-panel');
function show(name){
tabs.forEach(function(b){b.classList.toggle('active',b.getAttribute('data-panel')===name)});
panels.forEach(function(p){p.classList.toggle('active',p.id==='sba-'+name)});
}
function decodeHTML(html){
var t=document.createElement('textarea');
t.innerHTML=html||'';
return t.value;
}
function stripHTML(html){
var div=document.createElement('div');
div.innerHTML=html||'';
return (div.textContent||div.innerText||'').replace(/\n{3,}/g,'\n\n').trim();
}
function formatDate(value){
if(!value)return '';
var d=new Date(value);
return d.toLocaleDateString('en-GB',{day:'2-digit',month:'short',year:'numeric'}).toUpperCase();
}
function inputDate(value){
return value ? String(value).slice(0,10) : '';
}
function excerptText(post){
var div=document.createElement('div');
div.innerHTML=(post.excerpt&&post.excerpt.rendered)||(post.content&&post.content.rendered)||'';
return (div.textContent||div.innerText||'').replace(/\s+/g,' ').trim();
}
function imageFor(post){
try{
var media=post._embedded && post._embedded['wp:featuredmedia'] && post._embedded['wp:featuredmedia'][0];
return media && media.source_url ? media.source_url : '';
}catch(e){return '';}
}
function categoryFor(post){
try{
var terms=post._embedded && post._embedded['wp:term'];
if(!terms)return 'MAIN BLOG PAGE';
for(var i=0;i<terms.length;i++){
for(var j=0;j<terms[i].length;j++){
if(terms[i][j].taxonomy==='category')return decodeHTML(terms[i][j].name).toUpperCase();
}
}
}catch(e){}
return 'MAIN BLOG PAGE';
}
function setSelectToText(select,text){
if(!select)return;
var wanted=(text||'').toUpperCase();
var found=false;
Array.prototype.forEach.call(select.options,function(opt){
if(opt.text.toUpperCase()===wanted){select.value=opt.value;found=true;}
});
if(!found){
var opt=document.createElement('option');
opt.text=text||'MAIN BLOG PAGE';
opt.value=text||'MAIN BLOG PAGE';
select.add(opt);
select.value=opt.value;
}
}
function refreshPreview(){
var title=root.querySelector('#sba-title');
var body=root.querySelector('#sba-body');
var pt=root.querySelector('#sba-preview-title');
var pb=root.querySelector('#sba-preview-body');
var meta=root.querySelector('.sba-preview-meta');
var category=root.querySelector('#sba-category');
var date=root.querySelector('#sba-date');
pt.textContent=(title&&title.value.trim())||'YOUR STORY HEADLINE';
if(currentPost && body && body.value===stripHTML(currentPost.content&¤tPost.content.rendered||'')){
pb.innerHTML=currentPost.content&¤tPost.content.rendered ? currentPost.content.rendered : '<p>Your article introduction will appear here as you type.</p>';
}else{
var text=body ? body.value.trim() : '';
pb.innerHTML='';
var p=document.createElement('p');
p.textContent=text || 'Your article introduction will appear here as you type.';
pb.appendChild(p);
}
meta.textContent=((category&&category.options[category.selectedIndex]&&category.options[category.selectedIndex].text)||'MAIN BLOG PAGE').toUpperCase()+
' · '+(date&&date.value ? formatDate(date.value+'T12:00:00') : 'PREVIEW');
}
function currentValues(){
var title=root.querySelector('#sba-title');
var body=root.querySelector('#sba-body');
var date=root.querySelector('#sba-date');
var category=root.querySelector('#sba-category');
return {
title:title ? title.value.trim() : '',
content:body ? body.value : '',
date:date ? date.value : '',
categoryText:category && category.options[category.selectedIndex] ? category.options[category.selectedIndex].text : ''
};
}
function getRestNonce(){
if(window.wpApiSettings && window.wpApiSettings.nonce)return window.wpApiSettings.nonce;
if(window.wp && window.wp.apiFetch && window.wp.apiFetch.nonceMiddleware && window.wpApiSettings)return window.wpApiSettings.nonce||'';
return '';
}
function enableUpdateIfEditing(){
var button=root.querySelector('#sba-publish-button');
if(!button)return;
if(currentPost){
button.disabled=false;
button.classList.remove('sba-disabled-action');
button.textContent='UPDATE ARTICLE →';
}else{
button.disabled=true;
button.classList.add('sba-disabled-action');
button.textContent='PUBLISH ARTICLE →';
}
}
function showUpdateConfirmation(){
if(!currentPost)return;
var modal=root.querySelector('#sba-update-modal');
var list=root.querySelector('#sba-change-list');
var msg=root.querySelector('#sba-update-message');
var v=currentValues();
list.innerHTML='';
[
['HEADLINE',v.title || '—'],
['CATEGORY',v.categoryText || '—'],
['DATE',v.date ? formatDate(v.date+'T12:00:00') : '—'],
['ARTICLE COPY',(v.content||'').replace(/\s+/g,' ').trim().slice(0,180)+((v.content||'').length>180?'…':'')]
].forEach(function(item){
var row=document.createElement('div');row.className='sba-change';
var b=document.createElement('b');b.textContent=item[0];
var span=document.createElement('span');span.textContent=item[1];
row.appendChild(b);row.appendChild(span);list.appendChild(row);
});
msg.className='sba-update-message';msg.textContent='';
modal.hidden=false;
document.documentElement.style.overflow='hidden';
}
function closeUpdateConfirmation(){
root.querySelector('#sba-update-modal').hidden=true;
document.documentElement.style.overflow='';
}
function categoryIdForSelected(){
var select=root.querySelector('#sba-category');
if(!select)return Promise.resolve(null);
var wanted=(select.options[select.selectedIndex].text||'').trim().toLowerCase();
return fetch('/wp-json/wp/v2/categories?per_page=100',{credentials:'same-origin'})
.then(function(r){if(!r.ok)throw new Error('Could not read WordPress categories');return r.json()})
.then(function(cats){
for(var i=0;i<cats.length;i++){
if(decodeHTML(cats[i].name).trim().toLowerCase()===wanted)return cats[i].id;
}
return null;
});
}
function updateWordPressArticle(){
if(!currentPost)return;
var confirmButton=root.querySelector('#sba-confirm-update');
var msg=root.querySelector('#sba-update-message');
var nonce=getRestNonce();
if(!nonce){
msg.className='sba-update-message error';
msg.textContent='WordPress has not supplied a secure REST nonce on this page, so no changes were sent. We need to enable authenticated REST access before write-back can be switched on safely.';
return;
}
confirmButton.disabled=true;
confirmButton.textContent='UPDATING…';
msg.className='sba-update-message';
msg.textContent='Checking category and preparing the update…';
categoryIdForSelected().then(function(categoryId){
var v=currentValues();
var payload={
title:v.title,
content:v.content,
date:v.date ? v.date+'T12:00:00' : currentPost.date,
status:'publish'
};
if(categoryId)payload.categories=[categoryId];
return fetch('/wp-json/wp/v2/posts/'+currentPost.id,{
method:'POST',
credentials:'same-origin',
headers:{
'Content-Type':'application/json',
'X-WP-Nonce':nonce
},
body:JSON.stringify(payload)
});
}).then(function(response){
if(!response.ok){
return response.json().catch(function(){return {message:'WordPress returned '+response.status}})
.then(function(err){throw new Error(err.message||('WordPress returned '+response.status))});
}
return response.json();
}).then(function(updated){
currentPost=updated;
msg.className='sba-update-message ok';
msg.textContent='ARTICLE UPDATED SUCCESSFULLY. The live WordPress post has been changed.';
confirmButton.textContent='UPDATED ✓';
/* Refresh the archive so title/date changes are immediately visible. */
return fetch(API,{credentials:'same-origin'}).then(function(r){return r.json()}).then(function(posts){
livePosts=posts.sort(function(a,b){return new Date(b.date)-new Date(a.date)});
renderArticles();
setTimeout(function(){
closeUpdateConfirmation();
confirmButton.disabled=false;
confirmButton.textContent='YES, UPDATE ARTICLE →';
loadEditor(updated);
},1100);
});
}).catch(function(error){
msg.className='sba-update-message error';
msg.textContent='NOT UPDATED: '+error.message;
confirmButton.disabled=false;
confirmButton.textContent='TRY UPDATE AGAIN →';
});
}
function loadEditor(post){
currentPost=post;
show('editor');
var title=root.querySelector('#sba-title');
var body=root.querySelector('#sba-body');
var date=root.querySelector('#sba-date');
var category=root.querySelector('#sba-category');
var banner=root.querySelector('#sba-editing-banner');
var bannerName=root.querySelector('#sba-editing-name');
var bannerId=root.querySelector('#sba-editing-id');
var img=root.querySelector('#sba-featured-existing');
var upload=root.querySelector('#sba-featured-upload');
var label=root.querySelector('#sba-featured-label');
var help=root.querySelector('#sba-featured-help');
var previewImage=root.querySelector('.sba-preview-image');
var publish=root.querySelector('#sba-publish-button');
title.value=decodeHTML(post.title&&post.title.rendered||'');
body.value=stripHTML(post.content&&post.content.rendered||'');
date.value=inputDate(post.date);
setSelectToText(category,categoryFor(post));
banner.hidden=false;
bannerName.textContent=decodeHTML(post.title&&post.title.rendered||'Untitled');
bannerId.textContent='WORDPRESS POST ID '+post.id;
var image=imageFor(post);
if(image){
img.src=image; img.alt=decodeHTML(post.title&&post.title.rendered||'Featured image'); img.hidden=false;
label.textContent='CURRENT FEATURED IMAGE';
help.textContent='Image replacement will be enabled in the next stage.';
upload.style.padding='12px';
previewImage.style.backgroundImage='linear-gradient(rgba(0,0,0,.12),rgba(0,0,0,.12)),url("'+image.replace(/"/g,'%22')+'")';
previewImage.innerHTML='';
}else{
img.hidden=true; img.removeAttribute('src');
label.textContent='NO FEATURED IMAGE';
help.textContent='Image upload will be enabled in the next stage.';
previewImage.style.backgroundImage='';
previewImage.innerHTML='<span>FEATURED IMAGE</span>';
}
publish.textContent='UPDATE ARTICLE →';
refreshPreview();
enableUpdateIfEditing();
var fixed=document.querySelector('.fw-header-main');
var headerBottom=fixed ? fixed.getBoundingClientRect().bottom : 0;
var editorTop=root.querySelector('#sba-editor').getBoundingClientRect().top+window.pageYOffset;
window.scrollTo({top:Math.max(0,editorTop-headerBottom-30),behavior:'smooth'});
}
function newEditor(){
currentPost=null;
show('editor');
root.querySelector('#sba-title').value='';
root.querySelector('#sba-body').value='';
root.querySelector('#sba-date').value='';
root.querySelector('#sba-editing-banner').hidden=true;
root.querySelector('#sba-featured-existing').hidden=true;
root.querySelector('#sba-featured-existing').removeAttribute('src');
root.querySelector('#sba-featured-label').textContent='DROP IMAGE HERE';
root.querySelector('#sba-featured-help').textContent='or choose an image from your computer';
root.querySelector('.sba-preview-image').style.backgroundImage='';
root.querySelector('.sba-preview-image').innerHTML='<span>FEATURED IMAGE</span>';
root.querySelector('#sba-publish-button').textContent='PUBLISH ARTICLE →';
root.querySelector('#sba-publish-button').disabled=true;
root.querySelector('#sba-publish-button').classList.add('sba-disabled-action');
refreshPreview();
}
function renderArticles(){
var holder=root.querySelector('#sba-live-articles');
var search=(root.querySelector('#sba-search').value||'').toLowerCase().trim();
var filter=root.querySelector('#sba-filter').value;
var shown=livePosts.filter(function(post){
var title=decodeHTML(post.title&&post.title.rendered||'').toLowerCase();
var excerpt=excerptText(post).toLowerCase();
return (!search || title.indexOf(search)!==-1 || excerpt.indexOf(search)!==-1) &&
(filter==='all' || filter==='article');
});
holder.innerHTML='';
if(!shown.length){
holder.innerHTML='<div class="sba-empty">NO PUBLISHED ARTICLES MATCH THIS SEARCH.</div>';
return;
}
shown.forEach(function(post,index){
var row=document.createElement('div'); row.className='sba-row';
var img=imageFor(post);
var story=document.createElement('div'); story.className='sba-story';
var thumb=document.createElement('div'); thumb.className='sba-thumb'+(index===0?' sba-orange':'');
if(img){thumb.style.backgroundImage='url("'+img.replace(/"/g,'"')+'")';thumb.style.backgroundSize='cover';thumb.style.backgroundPosition='center';}
else thumb.textContent=String(index+1).padStart(2,'0');
var titleWrap=document.createElement('div'); titleWrap.className='sba-story-title';
var title=document.createElement('b'); title.textContent=decodeHTML(post.title&&post.title.rendered||'Untitled');
var detail=document.createElement('small'); detail.textContent='WORDPRESS POST · ID '+post.id;
titleWrap.appendChild(title); titleWrap.appendChild(detail); story.appendChild(thumb); story.appendChild(titleWrap);
var type=document.createElement('span'); type.textContent='ARTICLE';
var date=document.createElement('span'); date.textContent=formatDate(post.date);
var status=document.createElement('span'); status.className='sba-status'; status.textContent='PUBLISHED';
var edit=document.createElement('button'); edit.className='sba-edit'; edit.type='button'; edit.textContent='EDIT →';
edit.addEventListener('click',function(){loadEditor(post)});
row.appendChild(story);row.appendChild(type);row.appendChild(date);row.appendChild(status);row.appendChild(edit);
holder.appendChild(row);
});
}
tabs.forEach(function(b){b.addEventListener('click',function(){
var panel=b.getAttribute('data-panel');
if(panel==='editor')newEditor(); else show(panel);
})});
root.querySelectorAll('[data-open-editor]').forEach(function(b){b.addEventListener('click',newEditor)});
root.querySelectorAll('[data-panel-button]').forEach(function(b){b.addEventListener('click',function(){show(b.getAttribute('data-panel-button'))})});
var choices=root.querySelectorAll('.sba-choice button');
choices.forEach(function(b){b.addEventListener('click',function(){choices.forEach(function(x){x.classList.remove('active')});b.classList.add('active')})});
['#sba-title','#sba-body','#sba-date'].forEach(function(sel){
var el=root.querySelector(sel); if(el)el.addEventListener('input',refreshPreview);
});
var cat=root.querySelector('#sba-category'); if(cat)cat.addEventListener('change',refreshPreview);
root.querySelector('#sba-search').addEventListener('input',renderArticles);
root.querySelector('#sba-filter').addEventListener('change',renderArticles);
root.querySelector('#sba-preview-button').addEventListener('click',refreshPreview);
var updateButton=root.querySelector('#sba-publish-button');
var updateModal=root.querySelector('#sba-update-modal');
var cancelUpdate=root.querySelector('#sba-cancel-update');
var confirmUpdate=root.querySelector('#sba-confirm-update');
if(updateButton)updateButton.addEventListener('click',showUpdateConfirmation);
if(cancelUpdate)cancelUpdate.addEventListener('click',closeUpdateConfirmation);
if(confirmUpdate)confirmUpdate.addEventListener('click',updateWordPressArticle);
if(updateModal)updateModal.addEventListener('click',function(e){if(e.target===updateModal)closeUpdateConfirmation()});
/* Images inside old WordPress posts can be wrapped in links to the media file.
Keep those clicks inside Blog Admin and show a modal preview instead. */
var imageModal=root.querySelector('#sba-image-modal');
var imageLarge=root.querySelector('#sba-image-large');
var imageClose=root.querySelector('#sba-image-close');
function openImagePreview(src,alt){
if(!src)return;
imageLarge.src=src;
imageLarge.alt=alt||'Article image';
imageModal.hidden=false;
document.documentElement.style.overflow='hidden';
}
function closeImagePreview(){
imageModal.hidden=true;
imageLarge.removeAttribute('src');
document.documentElement.style.overflow='';
}
root.addEventListener('click',function(e){
var img=e.target.closest('.sba-preview-body img, .sba-featured-existing');
if(!img)return;
e.preventDefault();
e.stopPropagation();
var link=img.closest('a[href]');
var src=link && /\.(jpe?g|png|gif|webp|svg)(\?.*)?$/i.test(link.href) ? link.href : img.currentSrc||img.src;
openImagePreview(src,img.alt);
});
if(imageClose)imageClose.addEventListener('click',closeImagePreview);
if(imageModal)imageModal.addEventListener('click',function(e){if(e.target===imageModal)closeImagePreview()});
document.addEventListener('keydown',function(e){
if(e.key!=='Escape')return;
if(imageModal && !imageModal.hidden)closeImagePreview();
if(updateModal && !updateModal.hidden)closeUpdateConfirmation();
});
fetch(API,{credentials:'same-origin'})
.then(function(response){if(!response.ok)throw new Error('WordPress returned '+response.status);return response.json()})
.then(function(posts){livePosts=posts.sort(function(a,b){return new Date(b.date)-new Date(a.date)});renderArticles()})
.catch(function(error){root.querySelector('#sba-live-articles').innerHTML='<div class="sba-empty">COULD NOT LOAD WORDPRESS ARTICLES. '+error.message+'</div>'});
})();
</script>
05D — BLOG ADMIN FUNCTIONS — V12 — VIEW / COPY ACTIVE PHP MASTER
/*
* SHS Blog Admin — V12
* Simple Sussex Homeless Support blog management screen.
*
* FEATURES
* - Pending submissions shown first.
* - Preview and Edit controls.
* - Publish pending/draft posts.
* - Unpublish published posts back to Draft.
* - Delete sends a post to WordPress Trash (not permanent deletion).
* - Confirmation before unpublishing or deleting.
* - WordPress capability checks + nonces on every action.
* - V4: securely connects the existing front-end /blog-admin/ editor
* to authenticated WordPress REST updates for logged-in editors.
* - V5: connects featured images to WordPress.
* - V6: CHOOSE IMAGE gives Upload from computer OR Media Library.
* - V7: connects SAVE ARTICLE to WordPress.
* - V8: protects/restores featured images during article updates.
* - V9: adds safe top clearance below the fixed navigation.
* - V10: MAIN BLOG PAGE is the only category.
* - V11: cleans the BLOG ADMIN header.
* - V12: connects the existing + ADD NEW ARTICLE control directly to the
* existing /addblog/ submission page. No duplicate article editor is created.
*
* Paste this as the COMPLETE replacement for the existing SHS Blog Admin snippet.
* Code Snippets already supplies <?php — do not add another opening PHP tag.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/* ---------------------------------------------------------
ADMIN MENU
--------------------------------------------------------- */
function shs_blog_admin_v3_add_menu() {
add_menu_page(
'SHS Blog Admin',
'SHS Blog Admin',
'edit_posts',
'shs-blog-admin',
'shs_blog_admin_v3_render_page',
'dashicons-welcome-write-blog',
25
);
}
add_action( 'admin_menu', 'shs_blog_admin_v3_add_menu' );
/* ---------------------------------------------------------
V4 — CONNECT THE EXISTING FRONT-END BLOG ADMIN EDITOR
--------------------------------------------------------- */
/*
* The existing BLOG ADMIN page already contains the visual editor and its
* EDIT buttons. Its JavaScript deliberately refuses to write to WordPress
* unless WordPress supplies a secure REST nonce.
*
* On /blog-admin/ only, and only for a logged-in user who can edit posts,
* we load WordPress's REST API support and explicitly provide that nonce.
* This does not expose editing access to public visitors.
*/
function shs_blog_admin_v4_frontend_rest_access() {
if ( ! is_user_logged_in() || ! current_user_can( 'edit_posts' ) ) {
return;
}
if ( ! is_page( 'blog-admin' ) ) {
return;
}
wp_enqueue_script( 'wp-api' );
wp_enqueue_media();
/*
* V9 LAYOUT FIX
* The site's fixed header overlaps the top of the BLOG ADMIN page.
* Add clearance to the page content only; editor internals are untouched.
*/
wp_register_style( 'shs-blog-admin-v9-layout', false );
wp_enqueue_style( 'shs-blog-admin-v9-layout' );
wp_add_inline_style(
'shs-blog-admin-v9-layout',
'body.page-blog-admin main, body.page-blog-admin .site-main, body.page-blog-admin #primary, body.page-blog-admin .entry-content { padding-top: 42px; }'
);
wp_add_inline_script(
'wp-api',
'window.wpApiSettings = window.wpApiSettings || {};'
. 'window.wpApiSettings.root = ' . wp_json_encode( esc_url_raw( rest_url() ) ) . ';'
. 'window.wpApiSettings.nonce = ' . wp_json_encode( wp_create_nonce( 'wp_rest' ) ) . ';',
'before'
);
/*
* V6 FEATURED IMAGE CONNECTION
* CHOOSE IMAGE -> Upload from computer OR Media Library.
* A computer upload is sent directly to WordPress Media via REST,
* then assigned as featured_media. No Media Library detour is shown.
* The chosen image is immediately reflected in visible editor/preview
* images where possible, then the page refreshes to read the saved state.
*/
$shs_media_js = <<<'JS'
(function () {
'use strict';
var activeObjectUrl = '';
function textOf(el) {
return (el && (el.textContent || el.innerText) || '').replace(/\s+/g, ' ').trim();
}
function findChooseImageButton() {
var nodes = document.querySelectorAll('button, a, input[type="button"], input[type="submit"]');
for (var i = 0; i < nodes.length; i++) {
var label = nodes[i].value || textOf(nodes[i]);
if (String(label).trim().toUpperCase() === 'CHOOSE IMAGE') return nodes[i];
}
return null;
}
function findPostId() {
var params = new URLSearchParams(window.location.search);
var keys = ['post_id', 'post', 'edit', 'id'];
for (var i = 0; i < keys.length; i++) {
var value = parseInt(params.get(keys[i]), 10);
if (value > 0) return value;
}
var match = textOf(document.body).match(/WORDPRESS\s+POST\s*ID\s*(\d+)/i);
return match ? parseInt(match[1], 10) : 0;
}
function apiRoot() {
return (window.wpApiSettings && window.wpApiSettings.root
? window.wpApiSettings.root
: '/wp-json/').replace(/\/$/, '');
}
function nonce() {
return window.wpApiSettings && window.wpApiSettings.nonce
? window.wpApiSettings.nonce : '';
}
function showMessage(message, isError) {
var box = document.getElementById('shs-v6-image-message');
var button = findChooseImageButton();
if (!box) {
box = document.createElement('div');
box.id = 'shs-v6-image-message';
box.style.marginTop = '12px';
box.style.padding = '10px 12px';
box.style.borderRadius = '4px';
box.style.fontSize = '13px';
box.style.fontWeight = '700';
if (button && button.parentNode) button.parentNode.insertBefore(box, button.nextSibling);
}
box.style.background = isError ? '#fff1f1' : '#eef8ef';
box.style.border = isError ? '1px solid #d63638' : '1px solid #46b450';
box.style.color = isError ? '#b32d2e' : '#1d5e2d';
box.textContent = message;
}
function immediatePreview(url) {
if (!url) return;
var imgs = Array.prototype.slice.call(document.querySelectorAll('img'));
var candidates = imgs.filter(function (img) {
var r = img.getBoundingClientRect();
return r.width > 180 && r.height > 100 && r.top > 100;
});
/* The editor normally has the current featured image and the live-preview image
as its first prominent content images. Update both immediately. */
candidates.slice(0, 2).forEach(function (img) {
img.src = url;
img.removeAttribute('srcset');
});
}
function saveFeaturedImage(postId, attachmentId, imageUrl) {
immediatePreview(imageUrl);
showMessage('Setting featured image…', false);
return fetch(apiRoot() + '/wp/v2/posts/' + postId, {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': nonce()
},
body: JSON.stringify({ featured_media: attachmentId })
}).then(function (response) {
if (!response.ok) {
return response.json().catch(function () { return {}; }).then(function (data) {
throw new Error(data.message || 'WordPress could not set the featured image.');
});
}
return response.json();
}).then(function () {
showMessage('Featured image saved.', false);
window.setTimeout(function () { window.location.reload(); }, 900);
});
}
function uploadComputerFile(file, postId) {
if (!file) return;
if (!/^image\//i.test(file.type || '')) {
showMessage('Please choose an image file.', true);
return;
}
if (activeObjectUrl) URL.revokeObjectURL(activeObjectUrl);
activeObjectUrl = URL.createObjectURL(file);
immediatePreview(activeObjectUrl);
showMessage('Uploading image…', false);
var form = new FormData();
form.append('file', file);
fetch(apiRoot() + '/wp/v2/media', {
method: 'POST',
credentials: 'same-origin',
headers: {
'X-WP-Nonce': nonce(),
'Content-Disposition': 'attachment; filename="' + encodeURIComponent(file.name) + '"'
},
body: form
}).then(function (response) {
if (!response.ok) {
return response.json().catch(function () { return {}; }).then(function (data) {
throw new Error(data.message || 'WordPress could not upload the image.');
});
}
return response.json();
}).then(function (media) {
if (!media || !media.id) throw new Error('WordPress did not return the uploaded image.');
var url = media.source_url || activeObjectUrl;
return saveFeaturedImage(postId, media.id, url);
}).catch(function (error) {
showMessage(error.message || 'The image could not be uploaded.', true);
});
}
function chooseFromComputer(postId) {
var input = document.createElement('input');
input.type = 'file';
input.accept = 'image/*';
input.style.display = 'none';
document.body.appendChild(input);
input.addEventListener('change', function () {
var file = input.files && input.files[0] ? input.files[0] : null;
uploadComputerFile(file, postId);
input.remove();
}, { once: true });
input.click();
}
function chooseFromLibrary(postId) {
if (!window.wp || !wp.media) {
showMessage('The WordPress Media Library is not available.', true);
return;
}
var frame = wp.media({
title: 'Choose featured image',
button: { text: 'Use this image' },
library: { type: 'image' },
multiple: false
});
frame.on('select', function () {
var attachment = frame.state().get('selection').first().toJSON();
if (!attachment || !attachment.id) return;
var url = attachment.url || (attachment.sizes && attachment.sizes.full ? attachment.sizes.full.url : '');
saveFeaturedImage(postId, attachment.id, url);
});
frame.open();
}
function closeChooser() {
var old = document.getElementById('shs-v6-image-chooser');
if (old) old.remove();
}
function openChooser(button, postId) {
closeChooser();
var panel = document.createElement('div');
panel.id = 'shs-v6-image-chooser';
panel.style.position = 'absolute';
panel.style.zIndex = '999999';
panel.style.background = '#fff';
panel.style.border = '1px solid #bbb';
panel.style.borderRadius = '6px';
panel.style.padding = '10px';
panel.style.boxShadow = '0 8px 25px rgba(0,0,0,.18)';
panel.style.display = 'flex';
panel.style.gap = '8px';
var rect = button.getBoundingClientRect();
panel.style.left = (window.scrollX + rect.left) + 'px';
panel.style.top = (window.scrollY + rect.bottom + 8) + 'px';
var upload = document.createElement('button');
upload.type = 'button';
upload.textContent = 'UPLOAD FROM COMPUTER';
upload.style.padding = '10px 12px';
upload.style.cursor = 'pointer';
var library = document.createElement('button');
library.type = 'button';
library.textContent = 'CHOOSE FROM LIBRARY';
library.style.padding = '10px 12px';
library.style.cursor = 'pointer';
upload.addEventListener('click', function () {
closeChooser();
chooseFromComputer(postId);
});
library.addEventListener('click', function () {
closeChooser();
chooseFromLibrary(postId);
});
panel.appendChild(upload);
panel.appendChild(library);
document.body.appendChild(panel);
}
function activate() {
var button = findChooseImageButton();
if (!button || button.dataset.shsV6Connected === 'yes') return;
button.dataset.shsV6Connected = 'yes';
button.addEventListener('click', function (event) {
event.preventDefault();
event.stopImmediatePropagation();
var postId = findPostId();
if (!postId) {
showMessage('I could not identify this WordPress post.', true);
return;
}
openChooser(button, postId);
}, true);
}
document.addEventListener('click', function (event) {
var panel = document.getElementById('shs-v6-image-chooser');
var button = findChooseImageButton();
if (panel && !panel.contains(event.target) && event.target !== button) closeChooser();
});
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', activate);
} else {
activate();
}
window.setTimeout(activate, 800);
})();
JS;
wp_add_inline_script( 'wp-api', $shs_media_js, 'after' );
/*
* V7 SAVE ARTICLE CONNECTION
* Works with the existing designed CREATE A STORY form rather than replacing it.
* The script identifies the current post and the visible headline/article fields,
* then updates only title/content through the authenticated WordPress REST API.
* It does NOT publish/unpublish the post, so its current status is preserved.
*/
$shs_save_js = <<<'JS'
(function () {
'use strict';
function cleanText(value) {
return String(value == null ? '' : value).replace(/\r\n/g, '\n');
}
function visible(el) {
if (!el) return false;
var s = window.getComputedStyle(el);
return s.display !== 'none' && s.visibility !== 'hidden' && el.getClientRects().length > 0;
}
function labelText(el) {
if (!el) return '';
var text = '';
if (el.id) {
var explicit = document.querySelector('label[for="' + CSS.escape(el.id) + '"]');
if (explicit) text += ' ' + (explicit.textContent || '');
}
var parentLabel = el.closest ? el.closest('label') : null;
if (parentLabel) text += ' ' + (parentLabel.textContent || '');
var parent = el.parentElement;
if (parent) {
var near = parent.querySelector('label, .label, [class*="label"]');
if (near) text += ' ' + (near.textContent || '');
}
text += ' ' + (el.getAttribute('placeholder') || '');
text += ' ' + (el.getAttribute('name') || '');
text += ' ' + (el.id || '');
return text.replace(/\s+/g, ' ').trim().toLowerCase();
}
function findPostId() {
var params = new URLSearchParams(window.location.search);
var keys = ['post_id', 'post', 'edit', 'id'];
for (var i = 0; i < keys.length; i++) {
var n = parseInt(params.get(keys[i]), 10);
if (n > 0) return n;
}
var body = (document.body.textContent || '').replace(/\s+/g, ' ');
var match = body.match(/WORDPRESS\s+POST\s*ID\s*(\d+)/i);
return match ? parseInt(match[1], 10) : 0;
}
function findSaveButton() {
var nodes = document.querySelectorAll('button, a, input[type="button"], input[type="submit"]');
for (var i = 0; i < nodes.length; i++) {
var text = (nodes[i].value || nodes[i].textContent || '').replace(/\s+/g, ' ').trim().toUpperCase();
if (text === 'SAVE ARTICLE' || text === 'SAVE' || text === 'UPDATE ARTICLE') {
return nodes[i];
}
}
return null;
}
function findHeadlineField() {
var fields = Array.prototype.slice.call(
document.querySelectorAll('input[type="text"], input:not([type]), textarea')
).filter(visible);
var preferred = fields.filter(function (el) {
return /headline|title/.test(labelText(el));
});
if (preferred.length) return preferred[0];
return fields.find(function (el) {
return el.tagName === 'INPUT';
}) || null;
}
function findArticleField() {
var textareas = Array.prototype.slice.call(document.querySelectorAll('textarea')).filter(visible);
var preferred = textareas.filter(function (el) {
return /article|content|story|body/.test(labelText(el)) && !/headline|title/.test(labelText(el));
});
if (preferred.length) return preferred[0];
if (textareas.length) {
textareas.sort(function (a, b) {
return (b.value || '').length - (a.value || '').length;
});
return textareas[0];
}
var editors = Array.prototype.slice.call(
document.querySelectorAll('[contenteditable="true"]')
).filter(visible);
return editors[0] || null;
}
function fieldValue(el) {
if (!el) return '';
if (el.matches && el.matches('[contenteditable="true"]')) {
return cleanText(el.innerHTML);
}
return cleanText(el.value);
}
function apiRoot() {
return (window.wpApiSettings && window.wpApiSettings.root
? window.wpApiSettings.root
: '/wp-json/').replace(/\/$/, '');
}
function nonce() {
return window.wpApiSettings && window.wpApiSettings.nonce
? window.wpApiSettings.nonce : '';
}
function messageBox(button) {
var box = document.getElementById('shs-v7-save-message');
if (!box) {
box = document.createElement('div');
box.id = 'shs-v7-save-message';
box.style.marginTop = '12px';
box.style.padding = '11px 13px';
box.style.borderRadius = '4px';
box.style.fontSize = '13px';
box.style.fontWeight = '700';
if (button && button.parentNode) {
button.parentNode.insertBefore(box, button.nextSibling);
}
}
return box;
}
function showMessage(button, text, error) {
var box = messageBox(button);
box.textContent = text;
box.style.background = error ? '#fff1f1' : '#eef8ef';
box.style.border = error ? '1px solid #d63638' : '1px solid #46b450';
box.style.color = error ? '#b32d2e' : '#1d5e2d';
}
function saveArticle(button) {
var postId = findPostId();
var headline = findHeadlineField();
var article = findArticleField();
if (!postId) {
showMessage(button, 'I could not identify this WordPress post.', true);
return;
}
if (!headline || !article) {
showMessage(button, 'I could not identify the Headline or Article Text field.', true);
return;
}
var title = fieldValue(headline).trim();
var content = fieldValue(article);
if (!title) {
showMessage(button, 'Please enter a headline before saving.', true);
if (headline.focus) headline.focus();
return;
}
var oldLabel = button.value || button.textContent || 'SAVE ARTICLE';
button.disabled = true;
if ('value' in button && button.tagName === 'INPUT') {
button.value = 'SAVING…';
} else {
button.textContent = 'SAVING…';
}
showMessage(button, 'Saving article…', false);
fetch(apiRoot() + '/wp/v2/posts/' + postId + '?context=edit&_fields=id,featured_media,status', {
method: 'GET',
credentials: 'same-origin',
headers: {
'X-WP-Nonce': nonce()
}
})
.then(function (response) {
if (!response.ok) {
throw new Error('WordPress could not read the current article before saving.');
}
return response.json();
})
.then(function (currentPost) {
var featuredMedia = parseInt(currentPost.featured_media, 10) || 0;
return fetch(apiRoot() + '/wp/v2/posts/' + postId, {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': nonce()
},
body: JSON.stringify({
title: title,
content: content,
featured_media: featuredMedia
})
});
})
.then(function (response) {
if (!response.ok) {
return response.json().catch(function () { return {}; }).then(function (data) {
throw new Error(data.message || 'WordPress could not save the article.');
});
}
return response.json();
})
.then(function () {
showMessage(button, 'Article saved successfully. Featured image protected.', false);
})
.catch(function (error) {
showMessage(button, error.message || 'The article could not be saved.', true);
})
.finally(function () {
button.disabled = false;
if ('value' in button && button.tagName === 'INPUT') {
button.value = oldLabel;
} else {
button.textContent = oldLabel;
}
});
}
function activate() {
var button = findSaveButton();
if (!button || button.dataset.shsV7Connected === 'yes') return;
button.dataset.shsV7Connected = 'yes';
button.addEventListener('click', function (event) {
event.preventDefault();
event.stopImmediatePropagation();
saveArticle(button);
}, true);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', activate);
} else {
activate();
}
window.setTimeout(activate, 800);
})();
JS;
wp_add_inline_script( 'wp-api', $shs_save_js, 'after' );
/*
* V8 FEATURED IMAGE RESTORE
* Read-only on page load: asks WordPress which featured image is actually assigned
* to the current post, then restores that image into the designed editor and preview.
*/
$shs_restore_js = <<<'JS'
(function () {
'use strict';
function postId() {
var p = new URLSearchParams(window.location.search);
var keys = ['post_id', 'post', 'edit', 'id'];
for (var i = 0; i < keys.length; i++) {
var n = parseInt(p.get(keys[i]), 10);
if (n > 0) return n;
}
var m = (document.body.textContent || '').match(/WORDPRESS\s+POST\s*ID\s*(\d+)/i);
return m ? parseInt(m[1], 10) : 0;
}
function root() {
return (window.wpApiSettings && window.wpApiSettings.root
? window.wpApiSettings.root : '/wp-json/').replace(/\/$/, '');
}
function nonce() {
return window.wpApiSettings && window.wpApiSettings.nonce
? window.wpApiSettings.nonce : '';
}
function restoreImage(url) {
if (!url) return;
var imgs = Array.prototype.slice.call(document.querySelectorAll('img'));
var prominent = imgs.filter(function (img) {
var r = img.getBoundingClientRect();
return r.width > 180 && r.height > 100 && r.top > 100;
});
prominent.slice(0, 2).forEach(function (img) {
img.src = url;
img.removeAttribute('srcset');
});
/* Replace obvious black/empty featured-image placeholders visually when
the existing page did not render an IMG element for them. */
var nodes = Array.prototype.slice.call(document.querySelectorAll('div, section, figure'));
nodes.forEach(function (el) {
var t = (el.textContent || '').replace(/\s+/g, ' ').trim().toUpperCase();
if ((t === 'FEATURED IMAGE' || t === 'NO FEATURED IMAGE') && el.children.length <= 2) {
el.style.backgroundImage = 'url("' + url.replace(/"/g, '\\"') + '")';
el.style.backgroundSize = 'cover';
el.style.backgroundPosition = 'center';
el.style.backgroundRepeat = 'no-repeat';
}
});
}
function load() {
var id = postId();
if (!id) return;
fetch(root() + '/wp/v2/posts/' + id + '?context=edit&_embed=wp:featuredmedia&_fields=id,featured_media,_links', {
credentials: 'same-origin',
headers: { 'X-WP-Nonce': nonce() }
})
.then(function (r) {
if (!r.ok) throw new Error('Could not read featured image.');
return r.json();
})
.then(function (post) {
var mediaId = parseInt(post.featured_media, 10) || 0;
if (!mediaId) return;
return fetch(root() + '/wp/v2/media/' + mediaId + '?_fields=id,source_url', {
credentials: 'same-origin',
headers: { 'X-WP-Nonce': nonce() }
});
})
.then(function (r) {
if (!r) return null;
if (!r.ok) throw new Error('Could not read featured image file.');
return r.json();
})
.then(function (media) {
if (media && media.source_url) restoreImage(media.source_url);
})
.catch(function () {
/* Read-only repair: fail silently rather than disrupting the editor. */
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function () {
window.setTimeout(load, 250);
});
} else {
window.setTimeout(load, 250);
}
})();
JS;
wp_add_inline_script( 'wp-api', $shs_restore_js, 'after' );
/*
* V10 CATEGORY SIMPLIFICATION
* There is currently only one valid destination: MAIN BLOG PAGE.
* Keep the existing field so the page's own save/confirmation logic remains
* compatible, but remove every other option and disable user choice.
*/
$shs_category_js = <<<'JS'
(function () {
'use strict';
function simplifyCategory() {
var selects = Array.prototype.slice.call(document.querySelectorAll('select'));
var category = null;
for (var i = 0; i < selects.length; i++) {
var el = selects[i];
var context = '';
if (el.id) {
var label = document.querySelector('label[for="' + CSS.escape(el.id) + '"]');
if (label) context += ' ' + (label.textContent || '');
}
if (el.parentElement) {
context += ' ' + (el.parentElement.textContent || '');
}
if (/category/i.test(context) || /main blog page/i.test(el.textContent || '')) {
category = el;
break;
}
}
if (!category) return;
var mainOption = Array.prototype.slice.call(category.options).find(function (opt) {
return (opt.textContent || '').trim().toUpperCase() === 'MAIN BLOG PAGE';
});
if (!mainOption) return;
var value = mainOption.value;
category.innerHTML = '';
var only = document.createElement('option');
only.value = value;
only.textContent = 'MAIN BLOG PAGE';
only.selected = true;
category.appendChild(only);
category.value = value;
category.style.pointerEvents = 'none';
category.style.appearance = 'none';
category.style.webkitAppearance = 'none';
category.style.backgroundImage = 'none';
category.style.cursor = 'default';
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', simplifyCategory);
} else {
simplifyCategory();
}
window.setTimeout(simplifyCategory, 500);
})();
JS;
wp_add_inline_script( 'wp-api', $shs_category_js, 'after' );
/*
* V11 INTERFACE CLEAN-UP
* Remove two obsolete front-end design elements only:
* 1. CONTENT DESK / WordPress connected later
* 2. SHS FILMS
*/
$shs_cleanup_js = <<<'JS'
(function () {
'use strict';
function normalizedText(el) {
return (el && (el.textContent || el.innerText) || '')
.replace(/\s+/g, ' ')
.trim()
.toUpperCase();
}
function removeFilmsControl() {
var controls = document.querySelectorAll('a, button, input[type="button"], input[type="submit"]');
Array.prototype.forEach.call(controls, function (el) {
var label = (el.value || normalizedText(el)).trim().toUpperCase();
if (label === 'SHS FILMS') {
el.remove();
}
});
}
function removeContentDesk() {
var nodes = Array.prototype.slice.call(
document.querySelectorAll('div, section, aside, article')
);
var candidates = nodes.filter(function (el) {
var text = normalizedText(el);
return text.indexOf('CONTENT DESK') !== -1 &&
text.indexOf('WORDPRESS CONNECTED LATER') !== -1;
});
if (!candidates.length) return;
/* Choose the smallest matching container so the surrounding BLOG ADMIN
header is never removed. */
candidates.sort(function (a, b) {
return a.querySelectorAll('*').length - b.querySelectorAll('*').length;
});
var target = candidates[0];
if (target && target.parentElement) {
target.remove();
}
}
function clean() {
removeFilmsControl();
removeContentDesk();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', clean);
} else {
clean();
}
window.setTimeout(clean, 300);
window.setTimeout(clean, 900);
})();
JS;
wp_add_inline_script( 'wp-api', $shs_cleanup_js, 'after' );
/*
* V12 ADD NEW ARTICLE ROUTING
* The site already has a working /addblog/ submission page.
* Reuse it rather than creating a second new-article form.
*/
$shs_add_article_js = <<<'JS'
(function () {
'use strict';
function connectAddArticle() {
var controls = document.querySelectorAll('a, button, input[type="button"], input[type="submit"]');
Array.prototype.forEach.call(controls, function (el) {
var label = (el.value || el.textContent || '')
.replace(/\s+/g, ' ')
.trim()
.toUpperCase();
if (label === '+ ADD NEW ARTICLE' || label === 'ADD NEW ARTICLE') {
if (el.dataset.shsV12Connected === 'yes') return;
el.dataset.shsV12Connected = 'yes';
if (el.tagName === 'A') {
el.setAttribute('href', '/addblog/');
el.removeAttribute('target');
} else {
el.addEventListener('click', function (event) {
event.preventDefault();
event.stopImmediatePropagation();
window.location.href = '/addblog/';
}, true);
}
}
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', connectAddArticle);
} else {
connectAddArticle();
}
window.setTimeout(connectAddArticle, 400);
})();
JS;
wp_add_inline_script( 'wp-api', $shs_add_article_js, 'after' );
}
add_action( 'wp_enqueue_scripts', 'shs_blog_admin_v4_frontend_rest_access', 20 );
/* ---------------------------------------------------------
HELPERS
--------------------------------------------------------- */
function shs_blog_admin_v3_page_url( $args = array() ) {
$url = admin_url( 'admin.php?page=shs-blog-admin' );
if ( ! empty( $args ) ) {
$url = add_query_arg( $args, $url );
}
return $url;
}
function shs_blog_admin_v3_action_url( $post_id, $action ) {
$url = add_query_arg(
array(
'action' => 'shs_blog_admin_v3_action',
'shs_do' => $action,
'post_id' => absint( $post_id ),
),
admin_url( 'admin-post.php' )
);
return wp_nonce_url(
$url,
'shs_blog_admin_v3_' . $action . '_' . absint( $post_id )
);
}
function shs_blog_admin_v3_status_label( $status ) {
$obj = get_post_status_object( $status );
if ( $obj && ! empty( $obj->label ) ) {
return $obj->label;
}
return ucfirst( (string) $status );
}
function shs_blog_admin_v3_notice_text( $notice ) {
$messages = array(
'published' => 'Post published.',
'unpublished' => 'Post moved back to Draft.',
'trashed' => 'Post moved to Trash.',
'nochange' => 'No change was required.',
);
return isset( $messages[ $notice ] ) ? $messages[ $notice ] : '';
}
/* ---------------------------------------------------------
SECURE ACTION HANDLER
--------------------------------------------------------- */
function shs_blog_admin_v3_handle_action() {
if ( ! is_user_logged_in() || ! current_user_can( 'edit_posts' ) ) {
wp_die( esc_html__( 'You do not have permission to perform this action.' ) );
}
$post_id = isset( $_GET['post_id'] ) ? absint( $_GET['post_id'] ) : 0;
$action = isset( $_GET['shs_do'] ) ? sanitize_key( wp_unslash( $_GET['shs_do'] ) ) : '';
if ( ! $post_id || ! in_array( $action, array( 'publish', 'unpublish', 'trash' ), true ) ) {
wp_safe_redirect( shs_blog_admin_v3_page_url() );
exit;
}
check_admin_referer(
'shs_blog_admin_v3_' . $action . '_' . $post_id
);
$post = get_post( $post_id );
if ( ! $post || 'post' !== $post->post_type ) {
wp_safe_redirect(
shs_blog_admin_v3_page_url(
array( 'shs_notice' => 'nochange' )
)
);
exit;
}
if ( ! current_user_can( 'edit_post', $post_id ) ) {
wp_die( esc_html__( 'You do not have permission to change this post.' ) );
}
$notice = 'nochange';
if ( 'publish' === $action ) {
if ( ! current_user_can( 'publish_posts' ) ) {
wp_die( esc_html__( 'You do not have permission to publish posts.' ) );
}
if ( 'publish' !== $post->post_status ) {
wp_publish_post( $post_id );
$notice = 'published';
}
} elseif ( 'unpublish' === $action ) {
if ( 'publish' === $post->post_status ) {
wp_update_post(
array(
'ID' => $post_id,
'post_status' => 'draft',
)
);
$notice = 'unpublished';
}
} elseif ( 'trash' === $action ) {
if ( ! current_user_can( 'delete_post', $post_id ) ) {
wp_die( esc_html__( 'You do not have permission to delete this post.' ) );
}
$trashed = wp_trash_post( $post_id );
if ( $trashed ) {
$notice = 'trashed';
}
}
wp_safe_redirect(
shs_blog_admin_v3_page_url(
array( 'shs_notice' => $notice )
)
);
exit;
}
add_action( 'admin_post_shs_blog_admin_v3_action', 'shs_blog_admin_v3_handle_action' );
/* ---------------------------------------------------------
ONE POST ROW
--------------------------------------------------------- */
function shs_blog_admin_v3_post_row( $post ) {
$post_id = $post->ID;
$title = get_the_title( $post_id );
$title = $title ? $title : '(No title)';
$author_name = get_the_author_meta( 'display_name', $post->post_author );
$status = get_post_status( $post_id );
$status_name = shs_blog_admin_v3_status_label( $status );
$edit_link = current_user_can( 'edit_post', $post_id ) ? get_edit_post_link( $post_id ) : '';
$preview = get_preview_post_link( $post );
$thumb = get_the_post_thumbnail_url( $post_id, 'thumbnail' );
if ( 'publish' === $status ) {
$preview = get_permalink( $post_id );
}
$excerpt = '';
if ( ! empty( $post->post_content ) ) {
$excerpt = wp_trim_words(
wp_strip_all_tags( strip_shortcodes( $post->post_content ) ),
24,
'…'
);
}
?>
<tr>
<td class="shs-title-cell">
<strong class="shs-post-title">
<?php echo esc_html( $title ); ?>
</strong>
<?php if ( $excerpt ) : ?>
<div class="shs-excerpt">
<?php echo esc_html( $excerpt ); ?>
</div>
<?php endif; ?>
</td>
<td>
<?php echo esc_html( $author_name ? $author_name : '—' ); ?>
</td>
<td>
<span class="shs-status shs-status-<?php echo esc_attr( $status ); ?>">
<?php echo esc_html( $status_name ); ?>
</span>
</td>
<td>
<?php echo esc_html( get_the_date( 'j M Y, H:i', $post_id ) ); ?>
</td>
<td class="shs-image-cell">
<?php if ( $thumb ) : ?>
<img
class="shs-thumb"
src="<?php echo esc_url( $thumb ); ?>"
alt=""
>
<?php else : ?>
<span class="shs-muted">No image</span>
<?php endif; ?>
</td>
<td class="shs-actions-cell">
<div class="shs-actions">
<?php if ( $preview ) : ?>
<a
class="button button-secondary"
href="<?php echo esc_url( $preview ); ?>"
target="_blank"
rel="noopener"
>
Preview
</a>
<?php endif; ?>
<?php if ( $edit_link ) : ?>
<a
class="button button-secondary"
href="<?php echo esc_url( $edit_link ); ?>"
>
Edit
</a>
<?php endif; ?>
<?php if ( 'publish' !== $status && current_user_can( 'publish_posts' ) ) : ?>
<a
class="button button-primary shs-publish-button"
href="<?php echo esc_url( shs_blog_admin_v3_action_url( $post_id, 'publish' ) ); ?>"
>
Publish
</a>
<?php elseif ( 'publish' === $status ) : ?>
<a
class="button button-secondary"
href="<?php echo esc_url( shs_blog_admin_v3_action_url( $post_id, 'unpublish' ) ); ?>"
onclick="return confirm('Move this published post back to Draft? It will no longer be live on the website.');"
>
Unpublish
</a>
<?php endif; ?>
<?php if ( current_user_can( 'delete_post', $post_id ) ) : ?>
<a
class="button shs-delete-button"
href="<?php echo esc_url( shs_blog_admin_v3_action_url( $post_id, 'trash' ) ); ?>"
onclick="return confirm('Move this post to the WordPress Trash?');"
>
Delete
</a>
<?php endif; ?>
</div>
</td>
</tr>
<?php
}
/* ---------------------------------------------------------
POSTS TABLE
--------------------------------------------------------- */
function shs_blog_admin_v3_table( $posts ) {
?>
<div class="shs-table-wrap">
<table class="widefat fixed striped shs-blog-table">
<thead>
<tr>
<th class="shs-title-heading">Post</th>
<th>Author</th>
<th>Status</th>
<th>Date</th>
<th>Image</th>
<th class="shs-actions-heading">Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ( $posts as $post ) : ?>
<?php shs_blog_admin_v3_post_row( $post ); ?>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php
}
/* ---------------------------------------------------------
MAIN ADMIN PAGE
--------------------------------------------------------- */
function shs_blog_admin_v3_render_page() {
if ( ! current_user_can( 'edit_posts' ) ) {
wp_die( esc_html__( 'You do not have permission to access this page.' ) );
}
$pending_posts = get_posts(
array(
'post_type' => 'post',
'post_status' => 'pending',
'posts_per_page' => 100,
'orderby' => 'date',
'order' => 'DESC',
)
);
$other_posts = get_posts(
array(
'post_type' => 'post',
'post_status' => array( 'draft', 'publish', 'private', 'future' ),
'posts_per_page' => 100,
'orderby' => 'date',
'order' => 'DESC',
)
);
$total_posts = count( $pending_posts ) + count( $other_posts );
$notice = isset( $_GET['shs_notice'] )
? sanitize_key( wp_unslash( $_GET['shs_notice'] ) )
: '';
$notice_text = shs_blog_admin_v3_notice_text( $notice );
?>
<div class="wrap shs-blog-admin">
<div class="shs-page-heading">
<div>
<h1>SHS Blog Admin</h1>
<p class="shs-intro">
Review submissions and manage Sussex Homeless Support blog posts from one simple screen.
</p>
</div>
<div class="shs-total-card">
<strong><?php echo esc_html( $total_posts ); ?></strong>
<span>Total posts</span>
</div>
</div>
<?php if ( $notice_text ) : ?>
<div class="notice notice-success is-dismissible">
<p><?php echo esc_html( $notice_text ); ?></p>
</div>
<?php endif; ?>
<section class="shs-section shs-pending-section">
<div class="shs-section-heading">
<div>
<h2>Pending submissions</h2>
<p>Review these before they appear on the website.</p>
</div>
<span class="shs-count shs-count-pending">
<?php echo esc_html( count( $pending_posts ) ); ?>
</span>
</div>
<?php if ( $pending_posts ) : ?>
<?php shs_blog_admin_v3_table( $pending_posts ); ?>
<?php else : ?>
<div class="shs-empty">
No pending submissions at the moment.
</div>
<?php endif; ?>
</section>
<section class="shs-section">
<div class="shs-section-heading">
<div>
<h2>All other blog posts</h2>
<p>Published posts, drafts and scheduled/private posts.</p>
</div>
<span class="shs-count">
<?php echo esc_html( count( $other_posts ) ); ?>
</span>
</div>
<?php if ( $other_posts ) : ?>
<?php shs_blog_admin_v3_table( $other_posts ); ?>
<?php else : ?>
<div class="shs-empty">
No other blog posts were found.
</div>
<?php endif; ?>
</section>
</div>
<style>
.shs-blog-admin{
max-width:1600px;
padding-bottom:50px;
}
.shs-page-heading{
display:flex;
justify-content:space-between;
align-items:flex-end;
gap:30px;
margin:28px 0 26px;
}
.shs-blog-admin h1{
margin:0 0 7px;
font-size:34px;
line-height:1.1;
font-weight:700;
}
.shs-intro{
margin:0;
max-width:820px;
color:#50575e;
font-size:15px;
line-height:1.55;
}
.shs-total-card{
min-width:120px;
padding:13px 18px;
background:#fff;
border:1px solid #dcdcde;
border-radius:7px;
text-align:center;
}
.shs-total-card strong{
display:block;
font-size:27px;
line-height:1;
}
.shs-total-card span{
display:block;
margin-top:5px;
color:#646970;
font-size:12px;
font-weight:600;
}
.shs-section{
margin:0 0 34px;
}
.shs-pending-section{
padding:20px;
background:#fff8e5;
border:1px solid #e7c96b;
border-radius:8px;
}
.shs-section-heading{
display:flex;
align-items:center;
justify-content:space-between;
gap:20px;
margin:0 0 14px;
}
.shs-section-heading h2{
margin:0 0 3px;
font-size:22px;
line-height:1.2;
}
.shs-section-heading p{
margin:0;
color:#646970;
}
.shs-count{
display:inline-flex;
align-items:center;
justify-content:center;
min-width:38px;
height:38px;
padding:0 11px;
border-radius:999px;
background:#e2e4e7;
font-size:15px;
font-weight:700;
}
.shs-count-pending{
background:#dba617;
color:#111;
}
.shs-empty{
padding:20px;
background:#fff;
border:1px solid #dcdcde;
border-radius:5px;
color:#646970;
}
.shs-table-wrap{
overflow-x:auto;
background:#fff;
border:1px solid #dcdcde;
border-radius:6px;
}
.shs-blog-table{
margin:0;
border:0;
box-shadow:none;
}
.shs-blog-table th,
.shs-blog-table td{
padding:14px 12px;
vertical-align:middle;
}
.shs-blog-table .shs-title-heading,
.shs-blog-table .shs-title-cell{
width:32%;
}
.shs-actions-heading,
.shs-actions-cell{
width:300px;
}
.shs-post-title{
display:block;
font-size:14px;
line-height:1.35;
}
.shs-excerpt{
margin-top:7px;
color:#646970;
font-weight:400;
line-height:1.45;
}
.shs-status{
display:inline-block;
padding:5px 9px;
border-radius:999px;
background:#f0f0f1;
font-size:12px;
font-weight:700;
white-space:nowrap;
}
.shs-status-publish{
background:#e4f5e9;
color:#126b31;
}
.shs-status-pending{
background:#fff0b8;
color:#755700;
}
.shs-status-draft{
background:#f0f0f1;
color:#50575e;
}
.shs-status-private{
background:#eee4ff;
color:#5b358a;
}
.shs-status-future{
background:#e3efff;
color:#245b98;
}
.shs-thumb{
display:block;
width:64px;
height:64px;
object-fit:cover;
border-radius:4px;
}
.shs-muted{
color:#8c8f94;
}
.shs-actions{
display:flex;
flex-wrap:wrap;
gap:6px;
align-items:center;
}
.shs-actions .button{
margin:0;
}
.shs-publish-button{
font-weight:600;
}
.shs-delete-button{
color:#b32d2e !important;
border-color:#d63638 !important;
background:#fff !important;
}
.shs-delete-button:hover{
color:#fff !important;
border-color:#b32d2e !important;
background:#b32d2e !important;
}
@media (max-width:1100px){
.shs-page-heading{
align-items:flex-start;
flex-direction:column;
}
.shs-blog-table .shs-title-heading,
.shs-blog-table .shs-title-cell{
width:300px;
}
.shs-actions-heading,
.shs-actions-cell{
width:260px;
}
}
</style>
<?php
}