Redirect old WordPress ?page_id=/?p= links to their new paths
Both sites used WordPress's default plain permalinks -- no permalink_structure was ever set -- so every inbound link anyone has bookmarked, linked from another site, or has indexed in search is of the form ?page_id=N or ?p=N. Without a redirect those all land on the homepage with no indication of where the content went. scripts/export-wp.mjs now also writes redirects.map: a full ID -> new path table for every published page/post (not just the ones referenced from other content, since any of them could have outside inbound links), in the "id path" format Apache's txt RewriteMap wants. Regenerated on every export run alongside the content itself. The :443 vhost looks up page_id/p from the query string against that map and 301s to the resolved path, stripping the old query string. Unmapped IDs (deleted pages, a stray revision) fall through to the homepage rather than a bare 404. The redirect target is host-relative, so it works correctly under any ServerAlias this vhost answers for (including the still-dormant www.vicidial.com/vicidial.com aliases, with no changes needed once DNS is eventually pointed here). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,64 @@
|
|||||||
|
5 /features
|
||||||
|
7 /contact-us
|
||||||
|
11 /about-us
|
||||||
|
21 /blog/21
|
||||||
|
28 /blog/inbound-calls-outbound-calls-and-email-blended
|
||||||
|
36 /blog/agents-anywhere
|
||||||
|
67 /comparisons
|
||||||
|
69 /vicihost-vs-justgocloud
|
||||||
|
75 /vicihost-vs-ytel
|
||||||
|
82 /vicihost-vs-five9
|
||||||
|
94 /contact-form-error
|
||||||
|
96 /contact-form-success
|
||||||
|
107 /blog/agent-avatar-soundboards
|
||||||
|
131 /blog/high-level-data-encryption
|
||||||
|
157 /skills-based-routing-with-agent-ranking
|
||||||
|
164 /outbound-agent-controlled-broadcast-and-predictive-dialing
|
||||||
|
168 /integrated-call-recording
|
||||||
|
171 /three-way-calling-within-the-agent-screen
|
||||||
|
174 /scheduled-callbacks-agent-only-and-anyone
|
||||||
|
179 /web-configurable-ivrs-and-voicemail-boxes
|
||||||
|
182 /virtually-limitless-campaigns-lists-ivrs-inbound-queues
|
||||||
|
185 /immediate-or-scheduled-website-callbacks
|
||||||
|
188 /real-time-reports-with-click-to-listen-to-monitor-agent-phone-calls
|
||||||
|
191 /easy-importing-of-calling-lists-through-the-web-by-api-or-scheduled-by-ftp
|
||||||
|
203 /integrated-web-based-agent-phone-included-requires-no-agent-setup
|
||||||
|
206 /local-toll-free-and-international-inbound-phone-numbers-available
|
||||||
|
216 /full-usa-canada-and-uk-regulatory-compliance
|
||||||
|
219 /cellphone-filtering-for-tcpa-compliance
|
||||||
|
223 /ability-to-have-recordings-automatically-transferred-to-an-external-ftp-site
|
||||||
|
226 /quality-control-module-available
|
||||||
|
230 /pbx-features-allow-you-to-use-your-system-as-your-office-pbx
|
||||||
|
235 /included-support-time
|
||||||
|
238 /no-hidden-fees
|
||||||
|
241 /99-9-up-time-guarantee
|
||||||
|
244 /scalable-to-hundreds-of-logged-in-agents
|
||||||
|
247 /dedicated-dialing-server-hardware
|
||||||
|
250 /single-server-guaranteed-to-handle-at-least-25-agents-at-31-line-ratio
|
||||||
|
253 /moving-hosted-to-on-site-or-on-site-to-hosted
|
||||||
|
256 /month-to-month-terms-no-long-term-contracts
|
||||||
|
259 /agent-scripting-with-customer-data
|
||||||
|
262 /system-wide-per-campaign-and-inbound-dnc-lists
|
||||||
|
274 /internal-chat-and-broadcast-messaging-to-agents
|
||||||
|
286 /available-languages
|
||||||
|
294 /zoiper-webphone
|
||||||
|
301 /blog/crm-integration
|
||||||
|
324 /auto-generate-call-lists-based-on-dropped-inbound-queued-calls
|
||||||
|
327 /computer-ip-address-access-restrictions-for-web-resources
|
||||||
|
330 /dozens-of-standard-reports-which-can-be-emailed-out-on-a-set-schedule
|
||||||
|
350 /share-lead-data-across-vicidial-clusters-instantly-when-calls-are-transferred
|
||||||
|
354 /remote-api-control-of-agent-screens
|
||||||
|
372 /gdpr-compliance
|
||||||
|
376 /inbound-and-advanced-forecasting-reports
|
||||||
|
380 /inbound-post-call-customer-survey
|
||||||
|
383 /inbound-queue-preserve-place-in-line
|
||||||
|
386 /custom-data-field-forms-with-switchable-forms
|
||||||
|
390 /dnc-com-lead-filtering-and-inbound-call-filtering
|
||||||
|
395 /inbound-queue-closing-time-features
|
||||||
|
398 /outbound-callerid-groups-per-state-or-per-areacode
|
||||||
|
417 /call-quota-lead-ranking-for-multiple-contact-attempts
|
||||||
|
423 /multiple-configurable-options-for-leaving-messages-on-customer-voicemail-boxes
|
||||||
|
437 /two-factor-authentication-for-admin-web-users
|
||||||
|
441 /multi-country-international-dnc-list-filtering
|
||||||
|
452 /24-hour-called-count-limit-campaign-feature
|
||||||
|
475 /your-caller-id-numbers-and-spamscam-labels
|
||||||
@@ -30,6 +30,7 @@ const UPLOADS_DEST = path.join(ROOT, 'src/assets/uploads');
|
|||||||
const PAGES_DEST = path.join(ROOT, 'src/content/pages');
|
const PAGES_DEST = path.join(ROOT, 'src/content/pages');
|
||||||
const POSTS_DEST = path.join(ROOT, 'src/content/posts');
|
const POSTS_DEST = path.join(ROOT, 'src/content/posts');
|
||||||
const NAV_DEST = path.join(ROOT, 'src/data/nav.json');
|
const NAV_DEST = path.join(ROOT, 'src/data/nav.json');
|
||||||
|
const REDIRECTS_DEST = path.join(ROOT, 'redirects.map');
|
||||||
|
|
||||||
const turndown = new TurndownService({ headingStyle: 'atx', codeBlockStyle: 'fenced' });
|
const turndown = new TurndownService({ headingStyle: 'atx', codeBlockStyle: 'fenced' });
|
||||||
|
|
||||||
@@ -136,6 +137,17 @@ async function buildLinkMap(conn) {
|
|||||||
return new Map(rows.map((r) => [r.ID, r.post_type === 'post' ? `/blog/${r.post_name}` : `/${r.post_name}`]));
|
return new Map(rows.map((r) => [r.ID, r.post_type === 'post' ? `/blog/${r.post_name}` : `/${r.post_name}`]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apache RewriteMap (txt type) file: "<id><space><path>" per line, one for every published
|
||||||
|
// page/post, not just the ones referenced from other content -- anyone on the web could have
|
||||||
|
// bookmarked or linked to any of them via the old ?page_id=N / ?p=N plain-permalink URLs.
|
||||||
|
async function writeRedirectMap(linkMap) {
|
||||||
|
const lines = [...linkMap.entries()]
|
||||||
|
.sort(([a], [b]) => a - b)
|
||||||
|
.map(([id, target]) => `${id} ${target}`);
|
||||||
|
await fs.writeFile(REDIRECTS_DEST, `${lines.join('\n')}\n`);
|
||||||
|
console.log(`Wrote ${linkMap.size} redirect entries to ${REDIRECTS_DEST}`);
|
||||||
|
}
|
||||||
|
|
||||||
async function copyAttachments() {
|
async function copyAttachments() {
|
||||||
// Copy the whole uploads tree rather than just the 82 attachment rows: WordPress generates
|
// Copy the whole uploads tree rather than just the 82 attachment rows: WordPress generates
|
||||||
// multiple resized variants per image (e.g. "-1008x1024.png") that content <img> tags/srcsets
|
// multiple resized variants per image (e.g. "-1008x1024.png") that content <img> tags/srcsets
|
||||||
@@ -251,6 +263,7 @@ async function main() {
|
|||||||
await copyAttachments();
|
await copyAttachments();
|
||||||
const linkMap = await buildLinkMap(conn);
|
const linkMap = await buildLinkMap(conn);
|
||||||
const unresolved = new Set();
|
const unresolved = new Set();
|
||||||
|
await writeRedirectMap(linkMap);
|
||||||
|
|
||||||
await exportPostType(conn, 'page', PAGES_DEST, { attachmentPaths, thumbnails, linkMap, unresolved });
|
await exportPostType(conn, 'page', PAGES_DEST, { attachmentPaths, thumbnails, linkMap, unresolved });
|
||||||
await exportPostType(conn, 'post', POSTS_DEST, { attachmentPaths, thumbnails, linkMap, unresolved });
|
await exportPostType(conn, 'post', POSTS_DEST, { attachmentPaths, thumbnails, linkMap, unresolved });
|
||||||
|
|||||||
Reference in New Issue
Block a user