cfdump alternative to pass output as a string - coldfusion

I'd like to store the output of the cfdump as a string temporarily. I know there's a way to store it in a file, but is there a way that I can get the equivalent of cfdump format='text'?

Would using cfsavecontent work for you?
<cfsavecontent variable="theDump">
<cfdump var="#cgi#" expand="yes" format="text">
</cfsavecontent>
<cfoutput>
#theDump#
</cfoutput>

shaunw has the right idea. For bonus modularity, roll that into your own custom tag or component function so you can reuse it throughout your applications

Related

Problem with anchor links using resolveurl

I'm using <cfhttp> to pull in content from another site (coldfusion) and resolveurl="true" so all the links work. The problem I'm having is resolveurl is making the anchor links (href="#search") absolute links as well breaking them. My question is is there a way to make resolveurl="true" bypass anchor links somehow?
For starters, let's use the tutorial code from Adobe.com posted in the comments. You'll want to do something similar.
<cfhttp url="https://www.adobe.com"
method="get" result="httpResp" timeout="120">
<cfhttpparam type="header" name="Content-Type" value="application/json" />
</cfhttp>
<cfscript>
// Find all the URLs in a web page retrieved via cfhttp
// The search is case sensitive
result = REMatch("https?://([-\w\.]+)+(:\d+)?(/([\w/_\.]*(\?\S+)?)?)?", httpResp.Filecontent);
</cfscript>
<!-- Now, Loop through those URLs--->
<cfoutput>
<cfloop array="#result#" item="item" index="index">
<cfif LEFT(item, 1) is "##">
<!---Your logic if it's just an anchor--->
<cfelse>
<!---Your logic if it's a full link--->
</cfif>
<br/>
</cfloop>
</cfoutput>
If it tries to return a full URL before the anchor as you say, (I've been getting inconsistent results with resolveurl="true") hit it with this to only grab the bit you want.
<cfoutput>
<cfloop array="#result#" item="item" index="index">
#ListLast(item, "##")#
</cfloop>
</cfoutput>
What this code does is grab all the URLs, and parse them for anchors.
You'll have to decide what to do next inside your loop. Maybe preserve the values and add them to a new array, so you can save it somewhere with the links fixed?
It's impossible to assume in a situation like this.
There does not appear to be a way to prevent CF from resolving the hashes. In our usage of it the current result is actually beneficial since when we present content from another site we usually want the user to be sent there.
Here is a way to replace link href values with just anchor if one is present using regular expressions. I'm sure there are combinations of issues that could occur here if really malformed html.
<cfsavecontent variable="testcontent">
<strong>test</strong>
go to google
go to section
</cfsavecontent>
<cfset domain = replace("current.domain", ".", "\.", "all") />
<cfset match = "(href\s*=\s*(""|'))\s*(http://#domain#[^##'""]+)(##[^##'""]+)\s*(""|')" />
<cfset result = reReplaceNoCase(testcontent, match, "\1\4\6", "all") />
<cfoutput><pre>#encodeForHTML(result)#</pre></cfoutput>
Output
<strong>test</strong>
go to google
<a href="#section>go to section</a>
Another option if you are displaying the content in a normal page with js/jquery available is to run through each link on display and update it to just be the anchor. This will be less likely error with malformed html. Let me know if you have any interest in that approach.

How to add attributes ColdFusion tags in bulk

I was curious if there is a way to force a ColdFusion tag to hold an attribute as default, such as the datasource in cfquery.
For example instead of writing
<cfquery datasource="mydatasource">
I can write
<cfquery>
and the system automatically knows that the datasource is "mydatasource".
Would be really cool if this was possible.
It is actually possible for datasource, but not for everything.
You may set a this.datasource="mydatasource" as the default datasource in your Application.cfc
https://wikidocs.adobe.com/wiki/display/coldfusionen/Application+variables
The practical answer to your question are the custom tags. You can extend the features of ColdFusion tags to match your needs.
Taking into example the cfquery tag and wrapping a custom tag around it. Provide all the default values you want for the parameters of the cfquery into the tag's attribute default.
So essentially your custom tag page would be something like:
flexiquery.cfm
<cfif THISTAG.ExecutionMode EQ 'end'>
<cfparam name="Attributes.datasource" default="someDSN">
<cfparam name="Attributes.cacheWithin" default="#CreateTimeSpan(0,6,0,0)#">
<cfparam name="Attributes.maxRows" default="25">
<cfparam name="Attributes.timeOut" default="600">
<!--- some logic you want to perform --->
<cfquery datasource="#Attributes.datasource#"
cacheWithin="#Attributes.cacheWithin#"
maxRow="#Attributes.maxRows#"
timeOut="#Attributes.timeOut#"
<cfoutput>#THISTAG.GeneratedContent#</cfoutput>
</cfquery>
<!--- Caller assignment and other processing --->
</cfif>
And now you can use it and re-use it across your project, the way you wanted and even overriding the value you want to be different:
<cf_flexiquery>
<!--- you query here --->
</cf_flexiquery>
or
<cf_flexiquery maxRows="100" timeOut="1200">
<!--- you query here --->
</cf_flexiquery>
It gives you a fair idea of how to go with it. I have extended the custom tags features to leverage the features of cfhttp, cfpdf, cffile etc.
This is only way you can adopt the flexibility you want with ColdFusion tags and it works perfectly.

How to access the URL parameter id in Coldfusion if it's after a hashtag #?

After trying for about an hour without success... (coldfusion8) a dummy question, but I*m stuck:
My URL (Jquery Mobile, no pushstate, that's why it looks like it is):
http://www.page.de/test/mem/search.cfm#/test/mem/search.cfm?id=9900000003869
If I output:
<cfdump output="e:\website\dump.txt" label="catch" var="#url#">
I get this:
catch - struct
ID: 9900000003869
But how do i access it... I'm trying forever, nothing works:
<cfdump output="e:\website\dump.txt" label="catch" var="#id#">
<cfdump output="e:\website\dump.txt" label="catch" var="#ID#">
<cfdump output="e:\website\dump.txt" label="catch" var="#url.id#">
<cfdump output="e:\website\dump.txt" label="catch" var="#url.ID#">
<cfdump output="e:\website\dump.txt" label="catch" var="#StructGetValue(url,"id")d#">
...
Thanks for helping!
Ok... This works:
URL = http://www.page.de/test/mem/search.cfm#/test/mem/search.cfm?id=9900000003869
<cfset objRequest = GetPageContext().GetRequest() />
<cfset strUrl = right( objRequest.GetRequestUrl().Append( "?" & objRequest.GetQueryString() ).ToString(), 13)>
Credit
If someone finds an easier, please post. I will check as answer.
You're trying to read this from a txt file?
Can you not simply use:
<cfdump label="catch" var="#url.id#" />
Does that work?
EDIT:
Could you try capturing and formatting what you need first then after, writing it to the file?
For example, try using:
<cfsavecontent variable="myFileContents">
<cfoutput>#url.id#</cfoutput>
</cfsavecontent>
<cffile action="Write" file="e:\website\dump.txt" output="#myFileContents#" />
I have not tested this code, but give it a go and see!
Might want to put a check on that URL variable too using isDefined()
Good luck.
Doing some research on fragment identifiers (which is a new term to me :( )prompted by Peter and Duncan's comments, I've found from wiki: http://en.wikipedia.org/wiki/Fragment_identifier
The fragment identifier functions differently than the rest of the
URI: namely, its processing is exclusively client-side with no
participation from the server — of course the server typically helps
to determine the MIME type, and the MIME type determines the
processing of fragments. When an agent (such as a Web browser)
requests a resource from a Web server, the agent sends the URI to
the server, but does not send the fragment. Instead, the agent waits
for the server to send the resource, and then the agent processes the
resource according to the document type and fragment value.
now, being your client IS sending the fragment and the url variable is accessible to you for some reason, using it is done by my original post to follow.
<cfoutput>
is generally how you output a variable or other evaluations to the screen.
<cfset myName = "Travis">
<cfoutput>Hello, my name is #myName#</cfoutput>
You can also access the variable by using it in a statement that doesn't output anywhere.
<cfset myFullName = myName & " Mak">
You can also use the variables in a query
<cfquery name = "qSomeQuery" datasource = "#application.dsn#">
select * from table where id = #url.id#
</cfquery>
However, that's the bad way to use it in a query, you should always use cfquery param.
<cfquery name = "qSomeQuery" datasource = "#application.dsn#">
select * from table where id = <cfqueryparam cfsqltype="cf_sql_integer" value="#url.id#">
</cfquery>
The problem you're having in testing the variable is due to incorrect syntax.
<cfif isDefined("url.id")> verses <cfif isDefined(url.id)> a more accurate test is <cfif structKeyExists(url, "id")>
For some reason my CF server truncates everything in the url after the # but yours doesn't seem to have this problem. As your cfdump states, you can see your url variables so "accessing" the url variable is as easy as using it: #url.id# or testing it <cfif isDefined("url.id")>

ColdFusion: do i need to use structKeyExists for every element of a deep struct?

Let's say i've just parsed someone else's XML document which is a response to an API request. I want to know if a value nested deep inside exists. If my API request worked, it will be in the same place every time. If my API request fails, the root of the XML is very different.
If I try <cfif structKeyExists(myStruct.level1.level2.level3, 'myTarget')> on a failed api request, I get the fatal error: Element LEVEL1.LEVEL2 is undefined in MYSTRUCT.
Of course, I could try to depend on the root level of the XML telling me of success or failure, and not looking for the result if it failed, but... barring that solution, what should i do?
Do i need to check for the existence of each level of the struct? As in:
<cfif structKeyExists(myStruct, 'level1')
and structKeyExists(myStruct.level1, 'level2')
and structKeyExists(myStruct.level1.level2, 'level3')
and structKeyExists(myStruct.level1.level2.level3, 'myTarget')>
<!--- ... --->
</cfif>
This is not a real-world problem, this is just something i've faced too many times. Please don't tell me solutions that involve changing the API or solutions like those in the third paragraph.
Thanks!
edit: i should have mentioned why i can't use isDefined() - some of the keys do not have syntactically valid names, so isDefined() throws an error, eg myStruct.level1[42].level3
XMLSearch
I would use the parsed XML document (i.e. xmlDoc) and XMLSearch:
<cfset xmlDoc = xmlParse(responseData)>
<cfset nodes = XmlSearch(xmlDoc, '/level1/level2/level3/myTarget')>
<cfif arrayLen(nodes)>
<!--- do something, you have the "nodes" array to work with too --->
</cfif>
xpath for XMLSearch() assumes the structure keys are nodes. You would need to modify accordingly if, for instance, 'myTarget' is an attribute of a node.
StructFindKey
Another way of doing this would be StructFindKey.
<cfset result = structFindKey(myStruct, "myTarget")>
<cfif arrayLen(result) AND result.path EQ "level1.level2.level3">
<!--- do something --->
</cfif>
Conclusion
Haven't tested, but I believe either will be faster than using IsDefined() or a try-catch block. Has the advantage over XMLValidate() of not needing a DTD. And, even with a DTD, the node you want may be defined as optional, so it could still validate.
You could validate the XML against a DTD to make sure the document was in the right format. XmlParse() and XmlValidate() both take a DTD as a parameter.
<cfset validateResult = XmlValidate(myXmlDocument, myDTD)>
<cfif validateResult.status>
<!--- xml is valid continue processing --->
<cfelse>
<!--- xml did not validate handle the error --->
</cfif>
Personally I wouldn't go crazy checking for every level of a 'deep' structure like this. I would presume that if the top level exists the rest of the document will be as you expect, and I'd just address the document from there.
If you wanted you could perhaps try to address the value in your struct and wrap it in a try/catch. That way you can handle any errors at any 'level' in the same way.
<cftry>
<cfset myVar = myStruct.level1.level2.level3 />
<cfcatch type="any">
<!--- Handle error --->
</cfcatch>
</cftry>
Hope that helps some.
I know I'm going to get booed off the stage here, but this is where isDefined() can save you a lot of typing:
<cfif isDefined(structKeyExists(myStruct.level1.level2.level3)>
<!--- do something --->
</cfif>
I know this is a year old, but I'm going to put in an answer here. I struggled for a good long time with this one, till I found a simple solution. If I know the structure of the XML already, a simple IsDefined works to test if the node or node attribute exists. I don't think most people know you can do this, or have tried and failed because they didn't include single quotes in the IsDefined function.
So say I grab some user xml from a web service somewhere and want to display the user's ID.
<cfhttp url="https://mycompany.com/mywebservices/getusers" username="me" password="mysecret">
<cfset userXMLDoc = XMLParse(ToString(cfhttp.FileContent).trim())>
<cfif IsDefined('userXMLDoc.Data.Record.User.XmlAttributes.id')>
<cfdump var="#userXMLDoc.Data.Record.User.XmlAttributes.id#">
<cfelse>
<cfoutput>Failed: No User ID found</cfoutput>
</cfif>

How to do a cfdump inside a cfscript tag?

In order to debug I would like to dump certain variables on to my web page. How can I do that from inside a cfscript tag?
I tried the following but it isn't working:
<cfscript>
...
<cfif cgi.REMOTE_ADDR eq "IP">
<cfdump var="#var1#"><br/>
</cfif>
...
</cfscript>
Any clues on what can be done?
You can't do it directly like that in versions before CF 9. You can, however, use the dump() UDF found at CFLib. There's a whole library of UDFs there that mimic CF tags that don't have direct CFSCRIPT equivalents.
ColdFusion 9 (and up) offers the writeDump() function.
Adobe Documentation Linkfor WriteDump() function
use writeDump() like how you use writeOutput()
see writeDump on CF 9 reference
Isn't the following much easier and straightforward?
oAdmin = createObject("component", "cfide.adminapi.base");
oAdmin.dump(myVar);
It works on CF7 and forward, perhaps even earlier.
It would be fairly easy to write your own too. You just define a function in cfml rather than cfscript. You can use this to do cfaborts and cfloops as well.
Something like this (Off the top of my head...not executed).
<CFFUNCTION NAME="MyDump">
<CFARGUMENT NAME="OBJ" Required="TRUE">
<CFDUMP VAR="#Obj#">
</CFFUNCTION>
<CFSCRIPT>
if(cgi.REMOTE_ADDR eq "IP"){
MyDump(Var1);
}
</CFSCRIPT>
Now plain tag names allowed within cfscript starting ColdFusion 11
<cfscript>
cfdump (var=#myVar#);
</cfscript>
<cffunction name="setAbort" access="private" returntype="void" output="false">
<cfdump var="#arguments#"/><cfabort>
</cffunction>
For dump we use Writedump(myvar); instead of in cfscript and same we use abort; instead of for exit the execution of program at any instance.we use writeoutput(); instead of
<cfoutput>#myvar#</cfoutput>
below is the code for dump and abort in cfscript.
writedump(myvar); for dump
abort; for stop execution of programm
writeoutput(myvar); for output within cfscript