ColdFusion looping over an array with an empty/undefined field - coldfusion

I am downloading data from an API from one of our vendors. The data is an array but some of the fields are empty and come over as undefined. I am able to get most of the information out with a loop but when I add the field "notes" it fails with the error of:
"Element notes is undefined in a CFML structure referenced as part of an expression. The specific sequence of files included or processed is:
C:\websites\Fire\Reports\xml_parse\Crewsense_payroll_loop.cfm, line:
21 "
When I look at the dump I see that the field shows as "undefined". I've run out of ideas. Any help would be greatly appreciated. I've included the entire code and a link to the dump showing the array.
<cfhttp url="https://api.crewsense.com/v1/payroll? access_token=as;lkdfj;alskdfj;laksdfj&token_type=bearer&start=2019-01-05%2019:00:00&end=2019-01-06%2007:59:00" method="GET" resolveurl="YES" result="result">
</cfhttp>
<cfoutput>
<cfset ApiData = deserializeJSON(result.filecontent)>
<cfset API_ArrayLength = arraylen(ApiData)>
<cfloop index="i" from="1" to=#API_ArrayLength#>
#i# #ApiData[i]["name"]#
#ApiData[i]["employee_id"]#
#ApiData[i]["start"]#
#ApiData[i]["end"]#
#ApiData[i]["total_hours"]#
#ApiData[i]["work_type"]#
#ApiData[i]["work_code"]#
#ApiData[i]["user_id"]#
#ApiData[i]["notes"]# <---Fails here when added--->
<cfset i = i+1>
<br>
</cfloop>
<cfdump var="#ApiData#">
</cfoutput>
Dump

When dealing with data structures that have optional elements you will need to check for their existence before trying to access them. Otherwise you will get that error. I have added a snippet with an if condition utilizing the structKeyExists() function to your code as an example.
<cfhttp url="https://api.crewsense.com/v1/payroll? access_token=as;lkdfj;alskdfj;laksdfj&token_type=bearer&start=2019-01-05%2019:00:00&end=2019-01-06%2007:59:00" method="GET" resolveurl="YES" result="result">
</cfhttp>
<cfoutput>
<cfset ApiData = deserializeJSON(result.filecontent)>
<cfset API_ArrayLength = arraylen(ApiData)>
<cfloop index="i" from="1" to=#API_ArrayLength#>
#i# #ApiData[i]["name"]#
#ApiData[i]["employee_id"]#
#ApiData[i]["start"]#
#ApiData[i]["end"]#
#ApiData[i]["total_hours"]#
#ApiData[i]["work_type"]#
#ApiData[i]["work_code"]#
#ApiData[i]["user_id"]#
<cfif structKeyExists(ApiData[i],"notes")>
#ApiData[i]["notes"]# <!--- Show 'notes' if it exists --->
<cfelse>
'notes' is not available <!--- Do something here (or not) --->
</cfif>
<cfset i = i+1>
<br>
</cfloop>
<cfdump var="#ApiData#">
</cfoutput>

Related

Can't edit/use variable in expression but I can output it

I've got the following code in a method:
<cffunction name="serviceTicketValidate" access="public" output="yes" returntype="void" hint="Validate the service ticket">
<cfargument name="service_ticket" type="string" required="yes" hint="The ST to validate" />
<!--- Contact the CAS server to validate the ticket --->
<cfhttp url="#Variables.cas_server#serviceValidate" method="get">
<cfhttpparam name="ticket" value="#Arguments.service_ticket#" type="url" />
<cfhttpparam name="service" value="#Variables.service#" type="url" />
</cfhttp>
<!--- Received a valid XML response --->
<cfif IsXML(cfhttp.FileContent)>
<cfset XMLobj = XmlParse(cfhttp.fileContent)>
<!--- Check for the cas:user tag --->
<cfset CASuser = XmlSearch(XMLobj, "cas:serviceResponse/cas:authenticationSuccess/cas:user")>
<!--- Set the username to the value --->
<cftry>
<cfif variables.username NEQ ''>
<cfdump var="#Variables.username#" /><cfreturn/>
</cfif>
<cfif ArrayLen(CASuser)>
<cfset Variables['username'] = CASuser[1].XmlText />
</cfif>
<cfcatch>
<cfdump var="#cfcatch#" /><cfabort/>
</cfcatch>
</cftry>
<!--- Search for cas:attributes --->
<cfset CASattributes = XmlSearch(XMLobj, "cas:serviceResponse/cas:authenticationSuccess/cas:attributes")>
<!--- Go through all the attributes and add them to the attributes struct --->
<cfif ArrayLen(CASattributes)>
<cfloop array=#CASattributes[1].XmlChildren# index="attribute">
<cfset StructInsert(Variables.attributes,RemoveChars(attribute.XmlName,1,Find(":",attribute.XmlName)),attribute.XmlText)/>
</cfloop>
</cfif>
</cfif>
Note I added the cftry and cfcatch to see what is going on exactly. I've also added the if username != blank to debug as well. This method is called in another method like so:
<cfinvoke method="serviceTicketValidate">
<cfinvokeargument name="service_ticket" value="#service_ticket#" />
</cfinvoke>
<cfdump var="test2" /><cfabort/>
Again I've added the dump and abort for testing. The variable.username is defied and set to an empty string when the component is initiated and the component is initiated into a session variable.
So get this... when the whole process runs the first time I get output on my screen test2 as expected. Then, the next time the same thing is run, the session exists, thus the variable.username is set to something. In the first code block I can dump variables.username and see the username. However if I try to use variables.username in a conditional expression (like in that if statement) or if I remove the if statement and let the script try to change the value of variable.username, there are no errors, it just breaks out of the script completely. It ends that method, and the method that called it and I don't see test2 like I would think. It all just ends for some reason.
If you need further details I can provide more code but I tried to trim out as much as I thought was relevant. All methods are in the same component, all methods are public. Why can't I change the value of variables.username and why is there no error?
EDIT:
I think it may have something to do with the cflock but I'm debugging some stuff right now. I had a redirect inside the code block that is inside the lock. So I guess it never unlocks. But I even waited after the timeout and it still remained locked. I thought the lock was supposed to expire after the timeout.
I'm a little confused but it seems like you're trying to use a cfc's variables scope to set caller variables. The variables scope is not available to the caller the way it seems you are trying to use it.
index.cfm
<cfoutput>
<cfset objTest = createObject("component", "testscope").init()><br><Br>
<cfset objTest.checkValue()><br><br>
Calling page is checking the existence of testvar: #isDefined("variables.testvar")#
</cfoutput>
testscope.cfm
<cfcomponent displayname="testscope">
<cffunction name="init" access="public">
init() just set variables.testvar.
<cfset variables.testvar = "Okay, set">
<cfreturn This>
</cffunction>
<!--- Set some more variables --->
<cffunction name="checkValue" access="public">=
<cfoutput>Checkvalue is checking the value of testvar: #variables.testvar#</cfoutput>
</cffunction>
</cfcomponent>
The output is
init() just set variables.testvar.
Checkvalue is checking the value of testvar: Okay, set
Calling page is checking the existence of testvar: false

Getting data from the function using an Object in CF

I have a CFC object and a function which gets me the data which I want. Now I want to use that data and provide it to an already defined custom tag attribute. When I dump the #iEngine.listScore()# I get some parameters. But my problem is how should I provide those to an attribute?
<cfdump var="#iEngine.listScores()#" label="Swapnil Test - Function ListScore">
<cfset filename="ACE_DataExtract_#DateFormat(now(),'dd.mmm.yyyy')#.xls" />
<!--- Calling Custom tags to create/output xls files --->
<cfmodule template="#request.library.customtags.virtualpath#excel.cfm" file="#filename#" sheetname="ACE Report">
<cfmodule template="#request.library.customtags.virtualpath#exceldata.cfm"
query="#iEngine.listScores()#"
action="AddWorksheet"
sheetname="ACE Report"
colorscheme="blue"
useheaders="true"
contentformat="#{bold=true}#"
customheaders="#ListScore#">
<cfoutput>Excel Extract - ACE Report - #DateFormat(Now(),"d-mmm-yyyy")#</cfoutput>
</cfmodule>
</cfmodule>
Here I want to provide the data of iEngine.listScore() to the "Query" attribute in "exceldata" custom tag.
Below is the dump of iEngine.listScore()
I would write a transform Data function to change your array-struct to a query object, then pass that on....
<cffunction name="transformData" result="query">
<cfargument name="inArray" type="array">
<cfset local.qryReturn = queryNew("actiondate,actionId,closedate")>
<!--- You may look up queryNew and also set your dataTypes --->
<cfloop array="#arguments.inArray#" index="i">
<cfset QueryAddRow(local.qryReturn)>
<cfset querySetCell(local.qryReturn,"actionDate",i["actiondate"])>
<cfset querySetCell(local.qryReturn,"actionid",i["actionid"])>
<cfset querySetCell(local.qryReturn,"closedate",i["closedate"])>
</cfloop>
<cfreturn local.qryReturn>
</cffunction>
<cfset test = [
{actiondate='1/1/2015',actionid=134,closedate=''},
{actiondate='1/2/2015',actionid=135,closedate=''},
{actiondate='1/3/2015',actionid=136,closedate=''}
]>
<cfdump var="#test#">
<cfset resultQry = transformData(test)>
<cfif NOT isquery(resultQry)>
Exit invalid Data.
<cfelse>
<cfdump var="#resultQry#">
</cfif>

cfindex custom fields that are used for display no indexible

Currently working with Coldfusion 10 and cfsearch.
After many hours of trial and error, I have managed to get cfindex to collect all my custom field data so that it is available to me as fields for the output on my search results page.
My only issue now, is that the search will be able to search for things like true / false strings and it will return in the search results.
I could run a QoQ to strip those out, im just wondering if anyone has any suggestions for adding fields to an index but not having them searchable.
Any ideas greatly appreciated
my current code is below
<cftry>
<!-- create new searchable collection -->
<!--- cfcollection action= "create" collection="testsearch" path= "#expandPath('/assets/scripts/server/solr/')#" ---->
<cfcollection action= "list" name="collectionlist">
<cfdump var="#collectionlist#">
<cfquery name="MyQuery" datasource="#request.DSN#" maxrows="10">
SELECT *
FROM Store_products
</cfquery>
<cfdump var="#myQuery#">
<cfset myAttr = structNew()>
<cfset variables.columns = "COST,CREATEDDATE,CUSTOM_MESSAGE,CUSTOM_MESSAGE_LENGTH,DISPLAYNAME,DONTINCLUDEINSTOCKUPDATE,FEATUREDPRODUCT,FINALDAYS,FREEDELIVERY,HEIGHT,HTMLMETADESCRIPTION,HTMLMETAKEYWORDS,IMAGE1,IMAGE10,IMAGE2,IMAGE3,IMAGE4,IMAGE5,IMAGE6,IMAGE7,IMAGE8,IMAGE9,ISBUNDLE,ISNEWPRODUCT,ISONLINE,ISONSALE,ISSILENT,LONGDESCRIPTION,MANUFACTURER,NOVOUCHERS,ONLINEEXCLUSIVE,PARTNUMBER,PRICE,PRICELEVEL1,PRICELEVEL10,PRICELEVEL11,PRICELEVEL12,PRICELEVEL13,PRICELEVEL14,PRICELEVEL15,PRICELEVEL2,PRICELEVEL3,PRICELEVEL4,PRICELEVEL5,PRICELEVEL6,PRICELEVEL7,PRICELEVEL8,PRICELEVEL9,PRODUCTCATEGORY,PRODUCTCOLOR,PRODUCTMATERIAL,PRODUCTNAME,PRODUCTSIZE,PRODUCTSTYLE,PRODUCTTITLE,PRODUCTVIEWS,RRP,SALEEND,SALEPRICE,SALESTART,SHORTDESCRIPTION,SOLDOUT,STOCKQUANTITY,STORESORTORDER,TAXEXEMPT,VOLUME,WEIGHT,WIDTH">
<cfloop from="1" to="#ListLen(variables.columns,',')#" index="idx">
<cfset myAttr[listGetAt(variables.columns,idx,',') & "_s"] = listGetAt(variables.columns,idx,',') >
</cfloop>
<cfindex attributecollection="#myAttr#" action="update" collection="testsearch" type="custom" body="DISPLAYNAME,HTMLMETADESCRIPTION,HTMLMETAKEYWORDS,LONGDESCRIPTION,MANUFACTURER,PARTNUMBER,PRODUCTNAME,PRODUCTTITLE,RRP,SHORTDESCRIPTION" query="MyQuery" key= "productname">
<cfsearch name="searchResults" collection="testsearch" criteria="false" >
<cfdump var="#searchResults#">
<cfcatch type="any">
<cfdump var="#cfcatch#">
</cfcatch>
</cftry>

coldfusion 9 variable undefined in session

I have a page that prints an array with some information to the screen from a session variable (session.stufailedarray). At the top of the page, there a link to export the information to excel. When I try this (in Firefox, IE and Chrome) it works fine. But users keep telling me that they're receiving an error message: "Element stufailarray is undefined is session". I know the variable is there because it just printed it to the screen and I can see it in the debugging. Why would this be happening and only sometimes?
Code that generates error:
<cfset ind=0>
<cfset anArray=arrayNew(2)>
<cfloop array="#session.stufailarray#" index="k">
<cfset ind+=1>
<cfset session.failed=find("UPDATE FAILED: ", "#k#")>
<cfset session.rrr=REFind("\d{9,9}", "#k#")>
<cfset idno=mid("#k#", REFind("\d{9,9}", "#k#"), 9)>
<cfset failed=mid("#k#", Refind("UPDATE FAILED: ", "#k#"), Len(#k#)-(Refind("UPDATE FAILED: ", "#k#")))>
<cfset anArray[ind][1]=#idno#>
<cfset anArray[ind][2]=#failed#>
</cfloop>
<!--- Set content type. --->
<cfcontent type="Application/vnd.ms-excel">
<cfheader name="Content-Disposition" value="filename=load_status.xls">
<cfoutput>
<table cols=2 border=1>
<cfloop from="1" to ="#ArrayLen(anArray)#" index="row">
<tr>
<td>#anArray[row][1]#</td>
<td>#anArray[row][2]#</td>
</tr>
</cfloop>
</table>
</cfoutput>
Try this instead:
<!--- Set content type. --->
<cfset anArray=[]/>
<cfif isDefined(session.stufailedarray)>
<cfset anArray=session.stufailedarray/>
</cfif>
<cfcontent type="Application/vnd.ms-excel">
<cfheader name="Content-Disposition" value="filename=load_status.xls">
<cfoutput>
<table cols=2 border=1>
<cfloop from="1" to ="#ArrayLen(anArray)#" index="row">
<tr>
<td>#anArray[row][1]#</td>
<td>#anArray[row][2]#</td>
</tr>
</cfloop>
</table>
</cfoutput>
Make sure that you configured and enabled application session properly.
To use session variables, enable them in two places:
ColdFusion Administrator The Application.cfc initialization code
This.sessionManagement variable or the active cfapplication tag.
ColdFusion Administrator, Application.cfc, and the cfapplication tag
also provide facilities for configuring session variable behavior,
including the variable time-out.
Configuring and using session variables
According to your question, you have a variable called session.stufailedarray. However in the code that you posted (which generates the error), you have session.stufailarray. This is also the error message that you are getting.
"Element stufailarray is undefined is session"
Note that the set (available) variable, the failed is passed tense, which the error variable is in present tense.

Searching a folder (recursively) for duplicate photos using Coldfusion?

After moving and backing up my photo collection a few times I have several duplicate photos, with different filenames in various folders scattered across my PC. So I thought I would write a quick CF (9) page to find the duplicates (and can then add code later to allow me to delete them).
I have a couple of queries:-
At the moment I am just using file size to match the image file, but I presume matching EXIF data or matching hash of image file binary would be more reliable?
The code I lashed together sort of works, but how could this be done to search outside web root?
Is there a better way?
p
<cfdirectory
name="myfiles"
directory="C:\ColdFusion9\wwwroot\images\photos"
filter="*.jpg"
recurse="true"
sort="size DESC"
type="file" >
<cfset matchingCount=0>
<cfset duplicatesFound=0>
<table border=1>
<cfloop query="myFiles" endrow="#myfiles.recordcount#-1">
<cfif myfiles.size is myfiles.size[currentrow + 1]>
<!---this file is the same size as the next row--->
<cfset matchingCount = matchingCount + 1>
<cfset duplicatesFound=1>
<cfelse>
<!--- the next file is a different size --->
<!--- if there have been matches, display them now --->
<cfif matchingCount gt 0>
<cfset sRow=#currentrow#-#matchingCount#>
<cfoutput><tr>
<cfloop index="i" from="#sRow#" to="#currentrow#">
<cfset imgURL=#replace(directory[i], "C:\ColdFusion9\wwwroot\", "http://localhost:8500/")#>
<td><img height=200 width=200 src="#imgURL#\#name[i]#"></td>
</cfloop></tr><tr>
<cfloop index="i" from="#sRow#" to="#currentrow#">
<td width=200>#name[i]#<br>#directory[i]#</td>
</cfloop>
</tr>
</cfoutput>
<cfset matchingCount = 0>
</cfif>
</cfif>
</cfloop>
</table>
<cfif duplicatesFound is 0><cfoutput>No duplicate jpgs found</cfoutput></cfif>
This is pretty fun task, so I've decided to give it a try.
First, some testing results on my laptop with 4GB RAM, 2x2.26Ghz CPU and SSD: 1,143 images, total 263.8MB.
ACF9: 8 duplicates, took ~2.3 s
Railo 3.3: 8 duplicates, took ~2.0 s (yay!)
I've used great tip from this SO answer to pick the best hashing option.
So, here is what I did:
<cfsetting enablecfoutputonly="true" />
<cfset ticks = getTickCount() />
<!--- this is great set of utils from Apache --->
<cfset digestUtils = CreateObject("java","org.apache.commons.codec.digest.DigestUtils") />
<!--- cache containers --->
<cfset checksums = {} />
<cfset duplicates = {} />
<cfdirectory
action="list"
name="images"
directory="/home/trovich/images/"
filter="*.png|*.jpg|*.jpeg|*.gif"
recurse="true" />
<cfloop query="images">
<!--- change delimiter to \ if you're on windoze --->
<cfset ipath = images.directory & "/" & images.name />
<cffile action="readbinary" file="#ipath#" variable="binimage" />
<!---
This is slow as hell with any encoding!
<cfset checksum = BinaryEncode(binimage, "Base64") />
--->
<cfset checksum = digestUtils.md5hex(binimage) />
<cfif StructKeyExists(checksums, checksum)>
<!--- init cache using original on 1st position when duplicate found --->
<cfif NOT StructKeyExists(duplicates, checksum)>
<cfset duplicates[checksum] = [] />
<cfset ArrayAppend(duplicates[checksum], checksums[checksum]) />
</cfif>
<!--- append current duplicate --->
<cfset ArrayAppend(duplicates[checksum], ipath) />
<cfelse>
<!--- save originals only into the cache --->
<cfset checksums[checksum] = ipath />
</cfif>
</cfloop>
<cfset time = NumberFormat((getTickcount()-ticks)/1000, "._") />
<!--- render duplicates without resizing (see options of cfimage for this) --->
<cfoutput>
<h1>Found #StructCount(duplicates)# duplicates, took ~#time# s</h1>
<cfloop collection="#duplicates#" item="checksum">
<p>
<!--- display all found paths of duplicate --->
<cfloop array="#duplicates[checksum]#" index="path">
#HTMLEditFormat(path)#<br/>
</cfloop>
<!--- render only last duplicate, they are the same image any way --->
<cfimage action="writeToBrowser" source="#path#" />
</p>
</cfloop>
</cfoutput>
Obviously, you can easily use duplicates array to review the results and/or run some cleanup job.
Have fun!
I would recommend split up the checking code into a function which only accepts a filename.
Then use a global struct for checking for duplicates, the key would be "size" or "size_hash" and the value could be an array which will contain all filenames that matches this key.
Run the function on all jpeg files in all different directories, after that scan the struct and report all entries that have more than one file in it's array.
If you want to show an image outside your webroot you can serve it via < cfcontent file="#filename#" type="image/jpeg">