Skip to content

Commit 466570f

Browse files
authored
Merge pull request #1927 from wheels-dev/fw-fixes
Fw fixes
2 parents 1755f9a + 386b695 commit 466570f

File tree

7 files changed

+288
-278
lines changed

7 files changed

+288
-278
lines changed
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
component {
2+
3+
public array function parseSummary(required string summaryPath) {
4+
local.nav = [];
5+
6+
if (fileExists(expandPath(arguments.summaryPath))) {
7+
local.summaryContent = fileRead(expandPath(arguments.summaryPath));
8+
local.lines = listToArray(local.summaryContent, chr(10));
9+
10+
local.currentSection = "";
11+
local.currentSubsection = "";
12+
local.currentItems = [];
13+
local.currentSubItems = [];
14+
15+
for (local.line in local.lines) {
16+
local.trimmedLine = trim(local.line);
17+
18+
if (!len(local.trimmedLine)) continue;
19+
20+
if (reFind("^##{1,2}\s", local.trimmedLine)) {
21+
if (len(local.currentSection)) {
22+
arrayAppend(local.nav, {
23+
"title": local.currentSection,
24+
"items": local.currentItems
25+
});
26+
}
27+
28+
local.currentSection = trim(reReplace(local.trimmedLine, "^##+\s*", ""));
29+
local.currentItems = [];
30+
local.currentSubsection = "";
31+
local.currentSubItems = [];
32+
}
33+
34+
else if (reFind("^\*\s+[^\[]+$", local.trimmedLine)) {
35+
if (len(local.currentSubsection) && arrayLen(local.currentSubItems)) {
36+
arrayAppend(local.currentItems, {
37+
"title": local.currentSubsection,
38+
"items": local.currentSubItems
39+
});
40+
}
41+
42+
local.currentSubsection = trim(reReplace(local.trimmedLine, "^\*\s+", ""));
43+
local.currentSubItems = [];
44+
}
45+
46+
else if (reFind("^\*\s+\[", local.trimmedLine)) {
47+
local.linkMatch = reMatch("\*\s+\[([^\]]+)\]\(([^\)]+)\)", local.trimmedLine);
48+
49+
if (arrayLen(local.linkMatch)) {
50+
local.title = reReplace(local.linkMatch[1], "\*\s+\[([^\]]+)\]\(([^\)]+)\)", "\1");
51+
local.link = reReplace(local.linkMatch[1], "\*\s+\[([^\]]+)\]\(([^\)]+)\)", "\2");
52+
53+
local.link = reReplace(local.link, "\.md$", "");
54+
local.link = reReplace(local.link, "^/", "");
55+
56+
local.linkItem = {
57+
"title": local.title,
58+
"link": local.link
59+
};
60+
61+
if (len(local.currentSubsection)) {
62+
arrayAppend(local.currentSubItems, local.linkItem);
63+
} else {
64+
arrayAppend(local.currentItems, local.linkItem);
65+
}
66+
}
67+
}
68+
}
69+
70+
if (len(local.currentSubsection) && arrayLen(local.currentSubItems)) {
71+
arrayAppend(local.currentItems, {
72+
"title": local.currentSubsection,
73+
"items": local.currentSubItems
74+
});
75+
}
76+
77+
if (len(local.currentSection)) {
78+
arrayAppend(local.nav, {
79+
"title": local.currentSection,
80+
"items": local.currentItems
81+
});
82+
}
83+
}
84+
85+
return local.nav;
86+
}
87+
88+
public string function renderGuideItems(required array items, required string currentPath) output=true {
89+
for (var item in arguments.items) {
90+
if (structKeyExists(item, "link")) {
91+
// Determine if the link is external
92+
var isExternal = reFindNoCase("^(http|https)://", item.link) > 0;
93+
94+
// Clean internal .md links
95+
if (!isExternal) {
96+
var cleanLink = lcase(reReplace(item.link, "\.md$", ""));
97+
var isActive = (arguments.currentPath == cleanLink) ? " active" : "";
98+
}
99+
100+
if (isExternal) {
101+
// External Link
102+
writeOutput(
103+
'<a href="#item.link#" target="_blank" rel="noopener noreferrer" class="item">#item.title#</a>'
104+
);
105+
} else {
106+
// Internal Guide Link (routed through /wheels/guides/)
107+
writeOutput(
108+
'<a href="/wheels/guides/#cleanLink#" class="item#isActive#">#item.title#</a>'
109+
);
110+
}
111+
112+
} else if (structKeyExists(item, "items")) {
113+
// It's a subsection/group
114+
writeOutput('<div class="header">#item.title#</div>');
115+
writeOutput('<div class="list">');
116+
writeOutput(renderGuideItems(item.items, arguments.currentPath));
117+
writeOutput('</div>');
118+
}
119+
}
120+
121+
return "";
122+
}
123+
124+
}

core/src/wheels/public/docs/guides.cfm

Lines changed: 11 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -7,123 +7,34 @@ param name="request.wheels.params.format" default="html";
77
local.docsPath = "/wheels/docs/src/";
88
local.summaryPath = local.docsPath & "SUMMARY.md";
99
10-
// Function to parse navigation from SUMMARY.md
11-
function parseSummary(summaryPath) {
12-
local.nav = [];
13-
14-
if (fileExists(arguments.summaryPath)) {
15-
local.summaryContent = fileRead(arguments.summaryPath);
16-
local.lines = listToArray(local.summaryContent, chr(10));
17-
18-
local.currentSection = "";
19-
local.currentSubsection = "";
20-
local.currentItems = [];
21-
local.currentSubItems = [];
22-
23-
for (local.line in local.lines) {
24-
local.trimmedLine = trim(local.line);
25-
26-
// Skip empty lines
27-
if (!len(local.trimmedLine)) continue;
28-
29-
// Main section headers: "# " or "## "
30-
if (reFind("^##{1,2}\s", local.trimmedLine)) {
31-
// Save previous section
32-
if (len(local.currentSection)) {
33-
arrayAppend(local.nav, {
34-
"title": local.currentSection,
35-
"items": local.currentItems
36-
});
37-
}
38-
39-
// Reset everything for new section
40-
local.currentSection = trim(reReplace(local.trimmedLine, "^##+\s*", ""));
41-
local.currentItems = [];
42-
local.currentSubsection = "";
43-
local.currentSubItems = [];
44-
}
45-
46-
// Subsection title (not a link)
47-
else if (reFind("^\*\s+[^\[]+$", local.trimmedLine)) {
48-
// If previous subsection exists, push it
49-
if (len(local.currentSubsection) && arrayLen(local.currentSubItems)) {
50-
arrayAppend(local.currentItems, {
51-
"title": local.currentSubsection,
52-
"items": local.currentSubItems
53-
});
54-
}
55-
56-
local.currentSubsection = trim(reReplace(local.trimmedLine, "^\*\s+", ""));
57-
local.currentSubItems = [];
58-
}
59-
60-
// Navigation items (Markdown links)
61-
else if (reFind("^\*\s+\[", local.trimmedLine)) {
62-
local.linkMatch = reMatch("\*\s+\[([^\]]+)\]\(([^\)]+)\)", local.trimmedLine);
63-
64-
if (arrayLen(local.linkMatch)) {
65-
local.title = reReplace(local.linkMatch[1], "\*\s+\[([^\]]+)\]\(([^\)]+)\)", "\1");
66-
local.link = reReplace(local.linkMatch[1], "\*\s+\[([^\]]+)\]\(([^\)]+)\)", "\2");
67-
68-
local.link = reReplace(local.link, "\.md$", "");
69-
local.link = reReplace(local.link, "^/", "");
70-
71-
local.linkItem = {
72-
"title": local.title,
73-
"link": local.link
74-
};
75-
76-
if (len(local.currentSubsection)) {
77-
arrayAppend(local.currentSubItems, local.linkItem);
78-
} else {
79-
arrayAppend(local.currentItems, local.linkItem);
80-
}
81-
}
82-
}
83-
}
84-
85-
// Final section/subsection flush
86-
if (len(local.currentSubsection) && arrayLen(local.currentSubItems)) {
87-
arrayAppend(local.currentItems, {
88-
"title": local.currentSubsection,
89-
"items": local.currentSubItems
90-
});
91-
}
92-
93-
if (len(local.currentSection)) {
94-
arrayAppend(local.nav, {
95-
"title": local.currentSection,
96-
"items": local.currentItems
97-
});
98-
}
99-
}
100-
101-
return local.nav;
102-
}
103-
10410
// Get navigation structure
105-
local.navigation = parseSummary(local.summaryPath);
11+
local.docsHelper = new wheels.public.docs.DocsHelper();
12+
local.navigation = local.docsHelper.parseSummary(local.summaryPath);
10613
10714
// Determine which guide to display
10815
local.guidePath = "";
10916
local.guideContent = "";
11017
local.guideTitle = "Wheels Documentation";
11118
11219
if (len(request.wheels.params.path)) {
113-
local.guidePath = local.docsPath & request.wheels.params.path & ".md";
20+
if(right(request.wheels.params.path, 3) eq '.md'){
21+
local.guidePath = local.docsPath & request.wheels.params.path;
22+
} else {
23+
local.guidePath = local.docsPath & request.wheels.params.path & ".md";
24+
}
11425
11526
// Check if file exists
116-
if (fileExists(local.guidePath)) {
117-
local.guideContent = fileRead(local.guidePath);
27+
if (fileExists(expandPath(local.guidePath))) {
28+
local.guideContent = fileRead(expandPath(local.guidePath));
11829
local.guideTitle = request.wheels.params.path;
11930
} else {
12031
local.guideContent = "Guide not found: " & request.wheels.params.path;
12132
}
12233
} else {
12334
// Default content - show introduction
12435
local.readmePath = local.docsPath & "README.md";
125-
if (fileExists(local.readmePath)) {
126-
local.guideContent = fileRead(local.readmePath);
36+
if (fileExists(expandPath(local.readmePath))) {
37+
local.guideContent = fileRead(expandPath(local.readmePath));
12738
local.guideTitle = "Introduction";
12839
} else {
12940
local.guideContent = "## Wheels Documentation\n\nSelect a guide from the navigation menu.";

core/src/wheels/public/docs/layouts/guides.cfm

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
<div class="item">
3030
<div class="header">#sectionData.title#</div>
3131
<div class="list">
32-
#renderGuideItems(sectionData.items, docs.path)#
32+
#local.docsHelper.renderGuideItems(sectionData.items, docs.path)#
3333
</div>
3434
</div>
3535
</cfloop>
@@ -63,43 +63,6 @@
6363
</div>
6464
</div>
6565

66-
<cffunction name="renderGuideItems" access="public" returntype="string" output="true">
67-
<cfargument name="items" required="true" type="array">
68-
<cfargument name="currentPath" required="true" type="string">
69-
70-
<cfloop array="#arguments.items#" index="item">
71-
<cfif structKeyExists(item, "link")>
72-
<!--- Determine if the link is external --->
73-
<cfset isExternal = reFindNoCase("^(http|https)://", item.link) GT 0>
74-
75-
<!--- Clean internal .md links --->
76-
<cfif !isExternal>
77-
<cfset cleanLink = lcase(reReplace(item.link, "\.md$", ""))>
78-
<cfset isActive = (arguments.currentPath EQ cleanLink) ? " active" : "">
79-
</cfif>
80-
81-
<cfif isExternal>
82-
<!--- External Link --->
83-
<a href="#item.link#" target="_blank" rel="noopener noreferrer" class="item">#item.title#</a>
84-
<cfelse>
85-
<!--- Internal Guide Link (routed through /wheels/guides/) --->
86-
<a href="/wheels/guides/#cleanLink#" class="item#isActive#">#item.title#</a>
87-
</cfif>
88-
89-
<cfelseif structKeyExists(item, "items")>
90-
<!--- It's a subsection/group --->
91-
<div class="header">#item.title#</div>
92-
<div class="list">
93-
<cfoutput>
94-
#renderGuideItems(item.items, arguments.currentPath)#
95-
</cfoutput>
96-
</div>
97-
</cfif>
98-
</cfloop>
99-
100-
<cfreturn "">
101-
</cffunction>
102-
10366

10467
<!--- JavaScript for enhanced functionality --->
10568
<script>
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
component {
2+
// Setup the test environment
3+
function setTestboxEnvironment() {
4+
// creating backup for original environment
5+
if (structKeyExists(server, "boxlang")) {
6+
application.$$$wheels = {}
7+
for (local.key in application.wheels) {
8+
if (IsSimpleValue(application.wheels[local.key]) || IsArray(application.wheels[local.key]) || IsStruct(application.wheels[local.key])) {
9+
application.$$$wheels[local.key] = application.wheels[local.key]
10+
}
11+
}
12+
} else {
13+
application.$$$wheels = Duplicate(application.wheels)
14+
}
15+
16+
// load testbox routes
17+
application.wo.$include(template = "/tests/routes.cfm")
18+
application.wo.$setNamedRoutePositions()
19+
20+
local.AssetPath = "/tests/_assets/"
21+
22+
application.wo.set(rewriteFile = "index.cfm")
23+
application.wo.set(controllerPath = local.AssetPath & "controllers")
24+
application.wo.set(viewPath = local.AssetPath & "views")
25+
application.wo.set(modelPath = local.AssetPath & "models")
26+
application.wo.set(wheelsComponentPath = "/wheels")
27+
28+
/* turn off default validations for testing */
29+
application.wheels.automaticValidations = false
30+
application.wheels.assetQueryString = false
31+
application.wheels.assetPaths = false
32+
33+
/* redirections should always delay when testing */
34+
application.wheels.functions.redirectTo.delay = true
35+
36+
/* enable transactions for proper test isolation */
37+
application.wheels.transactionMode = "commit"
38+
39+
/* turn off request query caching */
40+
application.wheels.cacheQueriesDuringRequest = false
41+
42+
// CSRF
43+
application.wheels.csrfCookieName = "_wheels_test_authenticity"
44+
application.wheels.csrfCookieEncryptionAlgorithm = "AES"
45+
application.wheels.csrfCookieEncryptionSecretKey = GenerateSecretKey("AES")
46+
application.wheels.csrfCookieEncryptionEncoding = "Base64"
47+
48+
// Setup CSRF token and cookie. The cookie can always be in place, even when the session-based CSRF storage is being
49+
// tested.
50+
dummyController = application.wo.controller("dummy")
51+
csrfToken = dummyController.$generateCookieAuthenticityToken()
52+
53+
cookie[application.wheels.csrfCookieName] = Encrypt(
54+
SerializeJSON({authenticityToken = csrfToken}),
55+
application.wheels.csrfCookieEncryptionSecretKey,
56+
application.wheels.csrfCookieEncryptionAlgorithm,
57+
application.wheels.csrfCookieEncryptionEncoding
58+
)
59+
if(structKeyExists(url, "db") && listFind("mysql,sqlserver,postgres,h2", url.db)){
60+
application.wheels.dataSourceName = "wheelstestdb_" & url.db;
61+
} else if (application.wheels.coreTestDataSourceName eq "|datasourceName|") {
62+
application.wheels.dataSourceName = "wheelstestdb";
63+
} else {
64+
application.wheels.dataSourceName = application.wheels.coreTestDataSourceName;
65+
}
66+
application.testenv.db = application.wo.$dbinfo(datasource = application.wheels.dataSourceName, type = "version")
67+
68+
local.populate = StructKeyExists(url, "populate") ? url.populate : true
69+
if (local.populate) {
70+
include "/tests/populate.cfm"
71+
}
72+
}
73+
}

0 commit comments

Comments
 (0)