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.
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'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?
I am trying to use Mandrill with ColdFusion 6.1. ColdFusion is not one of Mandrell's supported languages. I am pretty sure I need to use cfhttp and Post. Are there any resources or example to help me along.
I'm surprised that no one has released a CFC yet (and had previously tweeted about it), but the API is pretty simple. For example, you can communicate to Mandrill directly using javascript.
https://bitbucket.org/mailchimp/mandrill-api-js/
Mandril uses a restful API. Here's a link to the full documentation:
https://mandrillapp.com/api/docs/
In order to send a message, you need to POST a JSON packet using your own API key. (In the demo link, you can modify the JSON and click the "try it" button to send a test message.)
https://mandrillapp.com/api/docs/messages.JSON.html#method-send
You may need to manually generate your own JSON as ColdFusion tends to convert numbers and boolean values into strings.
You can simply use a cfmail tag and set the smtp username and password to the values provided by Mandrill. They have a full set of documentation on using SMTP with Mandrill.
Here's some sample code:
<cfmail
from="you#you.com"
subject="Your subject line"
to="him#him.com"
type="HTML"
server="smtp.mandrillapp.com"
port="587"
username="yourusername"
password="yourkey"
>
<cfmailparam name="X-MC-Tags" value="Some tag to track in Mandrill" />
<cfmailparam name="X-MC-Track" value="opens,clicks" />
<cfmailparam name="X-MC-Autotext" value="false" />
<html>
<head>
<style type="text/css">
</style>
</head>
<body>
A whole lot of body content goes here.
</body>
</html>
</cfmail>
i have create a new page in alfresco share but the page cannot be displayed without login! how can i make this page enabled without login.
my file in "/alfresco/templates/blog/demo/custom-viewer.ftl".
and this file contains "custom-viewer.ftl":
<#include "include/alfresco-template.ftl" />
<#templateHeader>
<#script type="text/javascript" src="${url.context}/res/modules/documentlibrary/doclib-actions.js" group="document-details"/>
<#link rel="stylesheet" type="text/css" href="${url.context}/res/components/document- details/document-details-panel.css" group="document-details"/>
<#templateHtmlEditorAssets />
</#>
<#templateBody>
<#region id="web-preview" scope="template"/>
</#>
<#templateFooter>
</#>
and the file in "/alfresco/site-data/pages/custom-viewer.xml".
and this file contains "custom-viewer.xml":
<?xml version='1.0' encoding='UTF-8'?>
<page>
<title>Custom Viewer</title>
<template-instance>custom-viewer</template-instance>
<authentication>none</authentication>
</page>
the page is work correctly but i need it to work without login? any help please?!!
The thing is probably not your page which needs login but the components it's including.
I'm seeing component regioun web-preview, if this defaults to the default web-preview: site-webscripts\org\alfresco\components\preview\web-preview.get.desc.xml
Then this components needs authentication, there is no <authentication> tag, so it defaults to user.
If you delete that <#region....> tag, you'll see the page.
In our web application we have lots of JS and CSS files.
We need to append a version number to these references every time we deploy the code. This is to make sure that users always get the latest deployed version of the file, and the file will not be served from browser's cache. We plan to do this using our build script.
We need to append a parameter to the file reference like <script type="text/javascript src=./abc/test.js?v=1231/>.
We need to do this for all CSS and JS files.
Can someone please suggest the best appraoch to do this?
We are planning to do this using regular expression. We will append a random number to the file name.
Can you please help me how I can find out all the CSS and JS reference in my HTML page?
Do you need to find all the CSS and javascript references in your HTML files? It would be simpler to use a replaceable token in these files and then run a task in ant to do the replacements. Ant only needs to find the tokens.
You could do this with the ReplaceRegExp task (to make the substitution in place), or you could do a Copy or Move with a FilterSet (to make the substitution in copies of the target files).
Here is an example using ReplaceRegExp:
<project default="test">
<property name="build.version" value="1231"/>
<target name="test">
<replaceregexp match="\#BUILD_VERSION\#" replace="${build.version}">
<fileset dir="test">
<include name="*.html"/>
</fileset>
</replaceregexp>
</target>
</project>
Here is a sample file before running the build:
<html>
<head>
<script type="text/javascript" src="./abc/test.js?v=#BUILD_VERSION#"/>
</head>
</html>
And here it is after:
<html>
<head>
<script type="text/javascript" src="./abc/test.js?v=1231"/>
</head>
</html>