ColdFusion10 CFDocument debug output - coldfusion

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?

Related

Using layouts, partials, and includeContent() in CFWheels

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?

Parsing og: tags with ColdFusion regex

If one wants to extract/match Open Graph (og:) tags from html, using regex (and ColdFusion 9+), how would one go about doing it?
And the tricky bit is that is has to cover both possible variations of tag formation as in the following examples:
<meta property="og:type" content="website" />
<meta content="website" property="og:type"/>
So far all I got is this:
<cfset tags = ReMatch('(og:)(.*?)>',html_content)>
It does match both of the links, however only the first type has the content bit returned with it. And content is something that I require.
Just to make it absolutely clear, the desired output should be an array with all of the OG tags (they could be 'type,image,author,description etc.). That means it should be flexible and not based on the og:type example alone.
Of course if it's possible, the ideal output would be a struct with the first column being the name of tag, and the second containing the value (content). But that can be achieved with the post processing and is not as important as extracting the tags themselves.
Cheers,
Simon
So you want an array like ['og:author','og:type', 'og:image'...]?
Try using a regex like og:([\w]+)
That should give you a start. You will have duplicates if you have two of the same og:foo meta tags.
You can look at JSoup also to help parse the HTML for you. It makes it a lot easier.
There are a few good blog posts on using it in CFML
jQuery-like parsing in Java
Parsing, Traversing, And Mutating HTML With ColdFusion And jSoup
Ok, so after the suggestion from #abbottmw (thank you very much!), here's the solution:
Download Jsoup jar file from here: http://jsoup.org/download
Then initiate it like this:
<cfhttp url="...." result="oghtml" > /*to get your html content*/
<cfscript>
paths = expandPath("/lib/jsoup.jar"); //or wherever you decide to place the file
loaderObj =createObject("component","javaloader.JavaLoader").init([expandPath('/lib/jsoup.jar')]);
jsoup = loaderObj.create("org.jsoup.Jsoup");
doc = jsoup.parse(oghtml);
tags = doc.select("meta[property*=og:]");
</cfscript>
<cfloop index="e" array="#tags#">
<cfoutput>
#e.attr("property")# | #e.attr("content")#<br />
</cfoutput>
</cfloop>
And that is it. The complete list of og tags is in the [tags] array.
Of course it's not the regex solutions, which was originally requested, but hey, it works!

Mandrill in ColdFusion

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>

How to structure a master page with coldfusion?

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.

How to generate PDF from .cfm page in Mac

I am using <cfdocument> tag of coldfusion 7. Using CFEclipse and working on MacOS.
I have written the following code:
<cfdocument format="pdf">
SitePoint.com - Introduction to ColdFusion 7
PDF Generation
This is an example of PDF generation using ColdFusion 7.
</cfdocument>
But instead of asking me to save this file in .pdf format, its trying to open it in .cfm format.
How can I save it in .pdf format? Thanks!
Unless you tell it otherwise, the webserver returns the results of a CFM call as text. You need to use CFContent with CFHeader to alert the browser that the results it will be recieving are of a different type. Something like:
<cfheader name="Content-Disposition" value="inline; filename=document.pdf">
<cfcontent type="application/x-pdf">
<cfdocument>...</cfdocument>
I may have the MIME type wrong there. I'm doing this from memory. Check the docs for more help.
If you are on CF8 or higher then use the saveAsName attribute:
<cfdocument saveAsName=""
Either that or the method suggested by Ben above should work
You might also need to import the style sheet as well. So you can get the desired formatting. It needs to be imported after cfdocument.
<cfdocument
format="pdf"
filename = "canwriteurfile.pdf"
overwrite = "yes"
marginBottom = ".2"
marginLeft = ".4"
marginRight = ".4"
marginTop = ".2">
<style type="text/css">#import "pdf.css";</style>
BLAHHH BLAHHH PDF FORMATTING STUFF
</cfdocument>
Try:
<cfdocument format="PDF" name="myPDF">
<html>
<body>
SitePoint.com - Introduction to ColdFusion 7
PDF Generation
This is an example of PDF generation using ColdFusion 7.
</body>
</html>
</cfdocument>
<cfpdf action = "write" destination = "pdf_path" source = "myPDF" overwrite = "yes"/>
<cflocation url="pdf_path"/>
Whis this you save the PDF on disk