Skip to content
This repository was archived by the owner on Apr 21, 2026. It is now read-only.

Commit b3d3f2d

Browse files
authored
Merge pull request #304 from wheels-dev/feature/seo-updates
Feature/seo updates
2 parents b4b1c9a + d6efae0 commit b3d3f2d

7 files changed

Lines changed: 41 additions & 40 deletions

File tree

app/controllers/web/HomeController.cfc

Lines changed: 28 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -315,33 +315,6 @@ component extends="app.Controllers.Controller" {
315315
}
316316
}
317317

318-
// Add API functions from all versions dynamically
319-
var apiVersions = getAvailableVersions();
320-
for (var version in apiVersions) {
321-
var versionClean = replace(version, "v", "", "one");
322-
var docData = getDocJSON(version);
323-
if (!structIsEmpty(docData) && structKeyExists(docData, "functions")) {
324-
for (var func in docData.functions) {
325-
if (structKeyExists(func, "slug")) {
326-
// Get the function details using getFunctionFromDocs to verify it exists
327-
var verifiedFunc = getFunctionFromDocs(docData, func.slug);
328-
if (!structIsEmpty(verifiedFunc)) {
329-
// Add all format routes for this function
330-
var formats = ["html", "json", "xml", "pdf"];
331-
for (var fmt in formats) {
332-
arrayAppend(urls, {
333-
loc: getBaseUrl() & "/api/v" & versionClean & "/" & func.slug & "." & fmt,
334-
lastmod: dateFormat(now(), "yyyy-mm-dd"),
335-
changefreq: "weekly",
336-
priority: "0.6"
337-
});
338-
}
339-
}
340-
}
341-
}
342-
}
343-
}
344-
345318
// Add other public pages
346319
var publicPages = [
347320
"/blog",
@@ -376,21 +349,24 @@ component extends="app.Controllers.Controller" {
376349
}
377350

378351
// Generate XML
379-
var xml = '<?xml version="1.0" encoding="UTF-8"?>
380-
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
381-
352+
xml = '<?xml version="1.0" encoding="UTF-8"?>' & chr(10);
353+
xml &= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
354+
xml = replace(xml, "\\n", chr(10), "all");
382355
for (var link in urls) {
383-
xml &= '
384-
<url>
385-
<loc>#link.loc#</loc>
386-
<lastmod>#link.lastmod#</lastmod>
387-
<changefreq>#link.changefreq#</changefreq>
388-
<priority>#link.priority#</priority>
389-
</url>';
356+
// Skip if loc is missing or empty
357+
if (!structKeyExists(link, "loc") || !len(trim(link.loc ?: ""))) continue;
358+
var loc = xmlEscape(link.loc ?: "");
359+
var lastmod = xmlEscape(link.lastmod ?: "");
360+
var changefreq = xmlEscape(link.changefreq ?: "");
361+
var priority = xmlEscape(link.priority ?: "");
362+
xml &= '<url>' &
363+
' <loc>' & loc & '</loc>' &
364+
' <lastmod>' & lastmod & '</lastmod>' &
365+
' <changefreq>' & changefreq & '</changefreq>' &
366+
' <priority>' & priority & '</priority>' &
367+
'</url>';
390368
}
391-
392-
xml &= '
393-
</urlset>';
369+
xml &= chr(10) & '</urlset>';
394370

395371
var sitemapPath = expandPath("/sitemap/sitemap.xml");
396372
model("Log").log(
@@ -437,6 +413,18 @@ component extends="app.Controllers.Controller" {
437413
renderText(text="Failed to generate sitemap. Please try again later.");
438414
}
439415
}
416+
417+
// Escape XML special characters for safe XML output
418+
private string function xmlEscape(required string value) {
419+
if (isNull(arguments.value) || !len(arguments.value & "")) return "";
420+
var s = arguments.value & ""; // force string
421+
s = replace(s, "&", "&amp;", "all");
422+
s = replace(s, "<", "&lt;", "all");
423+
s = replace(s, ">", "&gt;", "all");
424+
s = replace(s, '"', "&quot;", "all");
425+
s = replace(s, "'", "&apos;", "all");
426+
return s;
427+
}
440428

441429
function downloads() {
442430
// serves download page

app/views/layout.cfm

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,19 @@
1515
<cfset isNews = find("/news", pathInfo) or isBlog and !find("/blog/create", pathInfo)>
1616
<cfset isAuthPage = (isLogin OR isRegister OR isForgotPassword OR isResetPassword)>
1717

18+
<!--- Set og:image for key public pages --->
19+
<cfif isHome>
20+
<cfset ogImage = getBaseUrl() & "/images/og/home-og.png">
21+
<cfelseif isCommunity>
22+
<cfset ogImage = getBaseUrl() & "/images/og/community-og.png">
23+
<cfelseif isGuideDocs>
24+
<cfset ogImage = getBaseUrl() & "/images/og/guides-og.png">
25+
<cfelseif isDocs>
26+
<cfset ogImage = getBaseUrl() & "/images/og/docs-og.png">
27+
<cfelseif isApi>
28+
<cfset ogImage = getBaseUrl() & "/images/og/api-og.png">
29+
</cfif>
30+
1831
<cfset pageTitle = "Wheels - An open source CFML framework inspired by Ruby on Rails">
1932
<cfset ogTitle = "Wheels - An open source CFML framework inspired by Ruby on Rails">
2033
<cfset metaDescription = "Modern CFML web framework inspired by Rails. Build powerful, fast, and clean web apps with Wheels.dev's intuitive MVC architecture.">

public/images/og/api-og.png

879 KB
Loading

public/images/og/community-og.png

54.4 KB
Loading

public/images/og/docs-og.png

675 KB
Loading

public/images/og/guides-og.png

508 KB
Loading

public/images/og/home-og.png

74.6 KB
Loading

0 commit comments

Comments
 (0)