I'm stumped trying to get static content, partials, and layouts to behave and display. I am trying to create:
1 main layout to hold header/footer data
1 partial view for the homepage since it's body layout is different
1 partial view for all other pages
All partials should feed into the main layout which I will build under views/layout.cfm
Here are the files.
Controller/Home.cfc - contains functions for index and privacy
<cfcomponent extends="Controller">
<cffunction name="index">
<cfset qRecipes = model("tblRecipes").findAll(
select="id, name, image, homepage_order",
where="homepage_order > 0",
order="homepage_order",
maxrows=4
) />
</cffunction>
<cffunction name="privacy">
</cffunction>
</cfcomponent>
views/home/index.cfm - Should display the homepage layout
views/home/privacy.cfm - Contain static text wrapped in cfsavecontent like so.
<cfsavecontent variable="foo">
xxxxxxxx
</cfsavecontent>
<cfset contentFor("foo") />
The documentation doesn't provide enough in-depth examples for me grasp at what I'm missing. The main layout will look like:
<cfoutput>#includePartial("/shared/header")#
#styleSheetLinkTag(source="homepage", head=true)#
</cfoutput>asdfsafd
<body>
<div id="page-wrap">
<header>
<cfoutput>#includePartial(partial="/shared/socialmedia", cache=1440)#</cfoutput>
<nav id="top-navigation">
<cfoutput>#includePartial("/shared/topnav")#</cfoutput>
</nav>
</header>
<cfoutput>#includeContent()#</cfoutput> <!--- All partial data should output here --->
</body>
</html>
Since I'm placing all the text for privacy into a variable would I need another page to output #includeContent("foo")# before the main layout would load? Or can I have a page full of text not have to be wrapped in cfsavecontent?
There is no need to wrap your Privacy page in <cfsavecontent>. Try it without the <cfsavecontent> tags and without the contentFor("foo"). Then its contents should appear where you have #includeContent()# in the main layout.
What are you trying to do with the home page that's different? Anything?
Related
Is there a way to capture the complete HTML code from CFDocument before it generates the actual Document/PDF (for debugging)?
The tag itself doesn't seem to support it.
What works partially is storing the separate parts of the HTML via cfsavecontent but I can't capture the whole thing at once:
<cfsavecontent variable="test123">
<style type="text/css" media="screen">
<!-- Style based on paperSize-->
<cfoutput>
#request.paperSize.css#
</cfoutput>
</style>
</cfsavecontent>
<cfdump var="#test123#" output="#expandPath('./test123.html')#" format="html">
So I thought of two ways:
Raising the log level of the underlying PDF component or some other way to revealing the code from there? (Is it iText?)
"Escaping" the HTML code so I can read the HTML in the PDF (no idea how to do this in CF)
Any ideas how I could go about this?
I have a small coldfusion section of our site that all uses similar js and css files and page structure. The code is currently repeated for each file, and I'd like to factor it out and set something up using a master page and templates.
Master.cfm page:
<!--- Master template, includes all necessary js and css files.
Expects following variables to be defined:
- pageName - name of the file to be loaded as the body of the page
- title - text to be used as the title of the page and as the header text in the header bar --->
<cfinclude template="_lockedPage.cfm" />
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>#title#</title>
... all script and css links here ...
<script type="text/javascript" src="js/jquery-1.9.1.min.js"></script>
<script type="text/javascript" src="js/jquery.mobile-1.3.2.js"></script>
... etc ...
</head>
<body>
<div data-role="page">
<div class="headerDiv" data-role="header" data-theme="b" data-position="fixed">
<a id="backButton" data-role="button" data-direction="reverse" data-rel="back" data-icon="arrow-l" data-iconpos="left" data-theme="a">Back</a>
<h1><cfoutput>#title#</cfoutput></h1>
Home
</div>
<div data-role="content" class="container">
<cfinclude template="#pageName#.cfm" />
</div>
</div>
</body>
</html>
Then a page example would be something like this. CustomerSearch.cfm:
<cfscript>
title = "Customer Search";
pageName = "_customer-search";
include "Master.cfm";
</cfscript>
And then I would need a _customer-search.cfm page that would include all the body content for the page.
This means that I would need 2 files for every page that we currently have - the outer page that defines the variable and includes the master page, and the template page that has the individual page content.
Is this a good logical structure? Is there anyway to improve it?
You have the right idea, but I think you'll end up with a lot of unnecessary files. You could instead create a header.cfm and a footer.cfm that contain your global HTML. Each page would include those files and the content would be written between.
<cfset title = "Customer Search">
<cfinclude template="global_header.cfm">
<!--- This will be the content of your page. --->
<cfinclude template="global_footer.cfm">
This file would be named customer_search.cfm. Anytime you update the header or footer, it's a global change.
If you have a lot of business logic and query code that needs to exist on multiple pages, you might look into using an MVC framework to help you organize and reuse code. I prefer ColdBox (try ColdBox Lite), but many people use Framework/1.
I found that using custom tags was another simple solution and in my opinion a better solution to creating a separate header.cfm and footer.cfm.
In master.cfm:
<cfif ThisTag.ExecutionMode EQ 'start'>
[HEADER]
<cfelse>
[FOOTER]
<cfif>
In each content page:
<cf_master>
[CONTENT GOES HERE]
</cf_master>
If you'd like to pass in variables to the master page simply add it as an attribute to the opening tag:
<cf_master Title="Content Title">
And make sure the attribute is specified in the master file:
<cfparam name="Attributes.Title" default=""/>
<head>
<title><cfoutput>#Attributes.Title#</cfoutput></title>
</head>
The key for me was understanding the ThisTag.ExectuionMode. If you use custom tags you can either use just one tag or use an opening and closing tag. If you use an opening and closing tag then you can choose to include some content in the opening tag <cf_master>, and other content in the closing tag </cf_master>. That is why you need the if/else condition in master.cfm. In this case it is useful because then you can include a HEADER in the opening tag and a FOOTER in the closing tag.
Also, in case this isn't obvious, when you call your custom tag, it should match the name of the file where the code is stored. In my case <cf_master> matches master.cfm.
I used this page as a tutorial for custom tags: https://www.petefreitag.com/item/64.cfm
The Application.cfc can be a great use for common page design. Basically have one template and inject the pages generated content. Dan Bracuk commented in the other solution about using the Application.cfc onRequestStart() and onRequestEnd() methods but I use it slightly differently. Here is my general setup:
Application.cfc
// This is <cfscript> but it could be regular CFML too
component {
public function onRequest( required string targetPage ) {
// Capture/buffer the requested pages output
savecontent variable='LOCAL.output' {
include ARGUMENTS.targetPage;
}
// Use the output as the page content
// if the page did not specify content
param string REQUEST.content = LOCAL.output;
// Inject the design template
// which should output the page content somewhere
include '/path/to/template.cfm';
}
}
template.cfm
<!DOCTYPE html>
<cfparam name="REQUEST.title" type="string" /><!--- required --->
<cfparam name="REQUEST.head" type="string" default="" />
<cfparam name="REQUEST.content" type="string" /><!--- required --->
<html>
<head>
<title><cfoutput>#REQUEST.title#</cfoutput></title>
<link rel="stylesheet" href="path/to/common.css" />
<script src="path/to/common.js"></script>
<cfoutput>#REQUEST.head#</cfoutput>
</head>
<body>
<header>...</header>
<cfoutput>#REQUEST.content#</cfoutput>
<footer>...</footer>
</body>
</html>
each-page.cfm
<cfset REQUEST.title = "My Page Title" />
<cfsavecontent variable="REQUEST.head">
<!-- page specific head elements here -->
</cfsavecontent>
<!-- Regular page code/HTML output here -->
<!--- or you could use another <cfsavecontent> block --->
<!--- to save specific output sections --->
<p>Hello World</p>
This way allows you to keep the template all within one file which is much easier when designing it in a WYSIWYG manor. It also allows each page to set variables used in the design template, since the requested page is executed before the design template is included.
And, there is no need to <cfinclude> templates on each page since Application.cfc onRequest() will get called for ALL pages by default. If there are .cfm pages which should NOT include the design template, such as PDF output, then you'll need to add some logic to just dump the output and not include the design template.
I was wondering if its possible to exclude data from an wraparound page footer based on which content is loaded into the index page in ColdFusion
Roughly it would be something like.
IF (not = pageToBeExcluded)
THEN {
Show content
}
There are any number of ways to do that.
The simplest is probably to key on the page address.
<cfif CGI.SCRIPT_NAME DOES NOT CONTAIN "someArbitraryPage.cfm">
<!--- show this content --->
...
</cfif>
As mentioned, you might want to use getCurrentTemplatePath() or getBaseTemplatePath().
Personally, I would probably set a variable in the template where I want this to happen. The main benefit of this is that I don't need to keep changing my footer code every time I add (or remove) a file from my list of files where I want this.
<!--- in the template itself --->
<cfset request.suppressFooterContent = true>
Then, in the footer:
<cfparam name="request.suppressFooterContent" default="false">
<cfif NOT request.suppressFooterContent>
<!--- display content here --->
...
</cfif>
I am obfuscating URL's in my app (which is great), but I'd like to disable this for pagination URL's because I'd like the user to be able to enter whatever number they like.
Settings.cfm:
<cfset set(obfuscateURLs = true) />
Home.cfc (controller):
<cffunction name="home">
<cfparam name="params.page" default="1" />
<cfset linkList = model("link").findAll(
select="linkTitle,linkPoints,linkID,linkAuthority,linkCreated,linkUpVoteCount,linkDownVoteCount,linkCommentCount,userName,userID",
include="user",
order="linkPoints DESC",
handle="linkListPaging",
page=params.page,
perPage=5
) />
</cffunction>
Home.cfm (view)
<ul class="pagination">
<cfoutput>
#paginationLinks(
route="paginateLatest",
handle="linkListPaging",
page=1,
name="page",
windowSize=5,
prependToPage="<li>",
appendToPage="</li>",
classForCurrent="current"
)#
</cfoutput>
</ul>
Can I do DeObfuscate on an as needed basis?
Thanks,
Michael
The setting to obfuscate params is an all-or-nothing deal. Just as you can't override this behavior for linkTo(), you cannot override it for paginationLinks() either.
I would suggest building a plugin as I bet there will be other developers out there who would want this in the future. There may be a way to tell the controller to not obfuscate/deobfuscate a parameter named page. You would need to update both how urlFor() works as well as how the controller deobfuscates as it handles incoming requests. You may also consider providing a configuration option to use set() to "blacklist" a set of params keys to never be obfuscated (with page being a default).
I have a ColdFusion page calling a cfm page as a popup through window.open(..). The target page is a cfm that loads a PDF file. The called page code is the following:
<cfcontent type="application/pdf" file="/deploy/cfusion.ear/cfusion.war/myPDF.pdf"/>
<cfflush>
<script language="javascript">
window.location.reload();
</script>
Unfortunately, I am getting only a blank page unless I manually refresh the page (going to the popup URL bar and hitting Enter) to have its contents displayed by the browser.
What is strange is that if I replace the caller page code from window.open() to document.url = the PDF is displayed without the need of refreshing the page.
Do you have any suggestions here how to call the target page as a popup and having it load without the need of a manual refresh?
Thanks.
The problem is that you are mixing javascript and PDF content together. It should really just be this:
<cfcontent type="application/pdf" file="/deploy/cfusion.ear/cfusion.war/myPDF.pdf"/>
This will return the full contents of that PDF to the browser.
What were you trying to do with the javascript code?
edit It sounds like it could be something to do with caching. To prevent that, try adding some cache control headers to your file:
<cfheader name="expires" value="#getHttpTimeString(now())#">
<cfheader name="pragma" value="no-cache">
<cfheader name="cache-control" value="no-cache, no-store, must-revalidate">
<cfcontent type="application/pdf" file="/deploy/cfusion.ear/cfusion.war/myPDF.pdf"/>
If that doesn't work, try adding this one too:
<cfheader name="Content-Disposition" value="attachment; filename=myPDF.pdf">
The solution I adopted was that of calling a proxy page and then that proxy page generates the PDF file. So:
window.open('2') // open in a popup the PDF
document.location = '3' // proxy
cfcontent type='application/pdf' file='...' // generate PDF
Why I cannot have 1 and 3 only is for now a mystery but in my case it works perfectly.