Struct being populated with duplicate data over and over - recursion

Consider the following code:
<cfset result.enrollments = {} />
<cfset result.enrollments = getCorrectionList(SESSION.id,SESSION.term) />
<cfdump var="#result#" /><Cfabort/>
<cffunction name="getCorrectionList">
<cfargument name="id" required="true" type="string" />
<cfargument name="term" required="true" type="numeric" default="#getCurrentSemester().code#" />
<cfset result = {} />
<cfset result.status = 500 />
<cfset result.message = 'Unknown Error' />
<cfhttp url="THERE IS A REAL URL HERE" />
<cfif cfhttp.statusCode EQ '200 OK'>
<cfset courses = deserializeJson(cfhttp.fileContent,false) />
<cfif courses.recordCount EQ 0>
<cfset result.message = 'You are not currently enrolled in any courses for #ARGUMENTS.term#' />
<cfdump var="#result#" />
<cfreturn result />
</cfif>
<!--- MORE STUFF --->
Now when this runs I get an single struct output with two keys message and status. This is from the dump inside of the getCorrectionList function.
I then get a second struct output with the keys enrollments, message, and status. Inside the enrollments key is another struct with enrollments, message, and status. Inside that enrollments key is another struct with the same keys and so on 50 times with the last struct being empty.
Seems like some recursive actions is going on but where/how?
I have no idea what is going on. As you can see from my code there are no loops. I know the URL resolves correctly and it returns a query and has a recordcount. I can see the data dump at the right spots. But how can the #result# in the function show a single struct but the #result# outside the function show a 50 deep struct repeating itself. It doesn't make any sense.

I dunno why neither Leigh or Scott actually made their comments answers, but they're both right.
You have this reference to result outside your function:
<cfset result.enrollments = getCorrectionList(SESSION.id,SESSION.term) />
And at the end of your function you do this:
<cfreturn result />
Which in effect means you're doing this:
<cfset result.enrollments = result />
Meaning result.enrollments is a reference back to its own parent result.
And <cfdump> is duly displaying the circular reference.
As they both said, you need to localise your variables inside a function, either by varing them:
<cfset var result = {} />
Or explicitly putting them in the local scope:
<cfset local.result = {} />
That will make the function's result be a discrete variable, not simply another reference to the calling code' result variable.
You should always localise your function variables, unless you specifically mean to be referencing a calling-code variable, in which event it makes you code clearer if you explicitly scope it to make it obvious what you mean, eg:
<cfset variables.result = {} />
But this is not what you are meaning to do here. Localise your function's variables.

Related

MDX How to use CASE to check a level value

I'm new to BI and MDX and have a need to do this. I have a dimension that looks like this:
<Dimension name="JobDim">
<Hierarchy name="Job" hasAll="true" primaryKey="jobID">
<Table name="JobDim" schema="dw"/>
<Level name="Job Code" column="jobCode" type="String" uniqueMembers="false"/>
</Hierarchy>
<Hierarchy name="Job Status" hasAll="true" primaryKey="jobID">
<Table name="JobDim" schema="dw"/>
<Level name="Job Status" column="status" type="String" hideMemberIf="IfBlankName"/>
</Hierarchy>
</Dimension>
I have a calculated measure and I need to set the value of this measure depending on the 'job status', so if the job is 'complete', set the measure to 100, otherwise set its value to 200 (the value to be used is more complex but for this example purpose, this is good enough). I'm using something like this:
<CalculatedMember name="Earned Revenue" dimension="Measures" formatString="$#,###.00;($#,###.00)" aggregator="sum">
<Formula>case [JobDim.Job Status].CurrentMember
when [JobDim.Job Status].[Complete] then 100
else 200
end
</Formula>
</CalculatedMember>
I've tried different variations of the code above, but none seem to work (some even cause a crash when I try to run the report). Anyone has done something like this in the past that can provide me an answer?
You need to evaluate that tuple against some measure. Remember that in MDX evaluations are done against the level/aggregate results, not on a row by row basis.
From your description it looks like Job Status should be a property of the level Job Code, and not a level.
Try the following on your schema:
<Dimension name="JobDim">
<Hierarchy name="Job" hasAll="true" primaryKey="jobID">
<Table name="JobDim" schema="dw"/>
<Level name="Job Code" column="jobCode" type="String" uniqueMembers="false">
<Property name="Job Status" column="status" type="String"/>
</Level>
</Hierarchy>
</Dimension>
and on your calculated measure
<CalculatedMember name="Earned Revenue" dimension="Measures" formatString="$#,###.00;($#,###.00)" aggregator="sum">
<Formula>case [JobDim.Job Status].CurrentMember.Properties("Job Status")
when "Complete" then 100
else 200
end
</Formula>
</CalculatedMember>
Better to use IIF than CASE if possible as it is generally quicker. Assuming [JobDim.Job Status].[Complete] is a member in your cube then the IS operator should be ok to use:
IIF(
[JobDim.Job Status].CurrentMember
IS [JobDim.Job Status].[Complete]
,100
,200
)

CF 8 Decrypt function returning value enclosed with <pre> </pre>

I am using the following code to encrypt user password.
<cfset "EncryptedNewPass" = Encrypt(#HTMLCodeFormat(NewPass)#, Request.PasswordKey)>
Then compare it to the value stored in the database and it works fine. However, if i get the value from the database and use decrypt as follows
<cfset DecryptedPass = Decrypt(#getOrigPassFP.pass#, Request.PasswordKey)>
and do cfout, the value is wrapped with <pre> </pre> tag. The issue that I am having is since we also use the password to open a PDF document, it is throwing an error. I tried using replace to strip the tag but when it is displayed or passed to a variable, it still contains the <pre> tag.
You are putting the <pre> tags in there yourself!! What do you think HTMLCodeFormat() does??!
From the comments: I understand what you are saying but my issue is how do I remove the pre tags once it is decrypted before encrypting it back?
The following code is one example of how to remove the <PRE> and </PRE> tags from your decrypted values. (I am outputting all of the values so you can follow along when you execute this code.)
<cfset NewPass = "this_is_the_password">
<cfset PasswordKey = "this_is_the_really_weak_key">
<cfset EncryptedNewPass = Encrypt(HTMLCodeFormat(NewPass), PasswordKey)>
<cfset DecryptedPass = Decrypt(EncryptedNewPass, PasswordKey)>
<cfoutput>
<p>NewPass = #NewPass#</p>
<p>PasswordKey = #PasswordKey#</p>
<p>EncryptedNewPass = #EncryptedNewPass#</p>
<p>DecryptedPass = #HTMLEditFormat(DecryptedPass)#</p>
<cfif Left(DecryptedPass,5) EQ "<PRE>">
<cfset DecryptedPass = Right(DecryptedPass,(Len(DecryptedPass)-5))>
<p>Found and removed <PRE> tag = #HTMLEditFormat(DecryptedPass)#</p>
</cfif>
<cfif Right(DecryptedPass,6) EQ "</PRE>">
<cfset DecryptedPass = Left(DecryptedPass,(Len(DecryptedPass)-6))>
<p>Found and removed </PRE> tag = #HTMLEditFormat(DecryptedPass)#</p>
</cfif>
</cfoutput>
Looking at that code, this block will remove the <PRE> from the beginning of the string:
<cfif Left(DecryptedPass,5) EQ "<PRE>">
<cfset DecryptedPass = Right(DecryptedPass,(Len(DecryptedPass)-5))>
<p>Found and removed <PRE> tag = #HTMLEditFormat(DecryptedPass)#</p>
</cfif>
And this block will remove the </PRE> from the end of the string:
<cfif Right(DecryptedPass,6) EQ "</PRE>">
<cfset DecryptedPass = Left(DecryptedPass,(Len(DecryptedPass)-6))>
<p>Found and removed </PRE> tag = #HTMLEditFormat(DecryptedPass)#</p>
</cfif>
I only used HTMLEditFormat() to output the values for you to see in the browser. Do NOT use these functions when encrypting or decrypting your values.
You also need to be aware that the HTMLCodeFormat() function does more than just wrap the given string in <PRE> tags. That function also changes the characters: <, >, &, and " to their HTML character entity equivalent. If any of your users used those characters in their password then your decryption of them will fail (will not be equal to their actual password).
This function converts the following characters to HTML character entities:
Text character Encoding
< <
> >
& &
" "

Use cfdirectory to find unmatched files in two directories and perform function

In my application the user uploads a photos and the cfc resizes the photo, creates a new image and resizes that new image to a thumbnail. Trouble is, this function wasn't available earlier on in the game. I want to now look at the images directory and figure out which images don't have thumbnails.
I'm thinking that I could use cfdirectory to output a struct of both directories then loop over the files that only exist in the images and not in the thumbnails directory and run a function to resize the images and send them to the thumbnails directory.
Is this flawed thinking? Is there an easier way?
That's a perfectly reasonable approach, and you don't even have to use recursive code. Just use the recursive option in CFDirectory to get a list of all files and use both the file name and path combined as the key, which guarantees a unique file you're checking. You may have to modify the result a bit so you know exactly where to put the new thumbnail, but this should get you pretty close.
<cfset originals_path = expandPath('originals') />
<cfset thumbs_path = expandPath('thumbs') />
<cfset no_thumbs = find_missing_thumbs(originals_path, thumbs_path) />
<cfdump var="#no_thumbs#" />
<cffunction name="find_missing_thumbs">
<cfargument name="o" />
<cfargument name="t" />
<cfset var originals = 0 />
<cfset var thumbs = 0 />
<cfset var missing_thumbs = [] />
<cfset var massaged_originals = 0 />
<cfset var massaged_thumbs = 0 />
<cfset var qSearch = 0 />
<cfdirectory action="list" directory="#arguments.o#" name="originals" recurse="true" />
<cfdirectory action="list" directory="#arguments.t#" name="thumbs" recurse="true" />
<cfquery name="massaged_originals" dbtype="query">
select name, directory + name as fullpath from originals
</cfquery>
<cfquery name="massaged_thumbs" dbtype="query">
select name, directory + name as fullpath from thumbs
</cfquery>
<cfloop query="massaged_originals">
<cfquery name="qSearch" dbtype="query">
select massaged_thumbs.name from massaged_thumbs where massaged_thumbs.fullpath = '#massaged_originals.fullpath#'
</cfquery>
<cfif qSearch.recordCount eq 0>
<cfset arrayAppend(missing_thumbs, massaged_originals.name) />
</cfif>
</cfloop>
<cfreturn missing_thumbs />
</cffunction>

Error with Decrypt for "Could not perform unpadding: invalid pad byte.."

Using CF8 and MySQL 5.1, I am trying to encrypt() a password upon creation and then decrypt() at login. I can get the decrypt() to work fine on a test page but when I put it in a cfincluded page with cflogin I get the error "An error occurred while trying to encrypt or decrypt your input string: com.rsa.jsafe.crypto.dr: Could not perform unpadding: invalid pad byte.. ". It is the same code and DB from my test page to my app.
application.cfc:
<cfif NOT IsDefined("Request.PasswordKey")>
<cfset request.PasswordKey = generateSecretKey("AES")>
<cfset request.algorithm = "AES">
<cfset request.encoding = "hex">
</cfif>
test page which works fine:
FORM DATA: <br/>
form password:<cfoutput>#form.passwd#</cfoutput><br/>
<cfset encrypted = Encrypt(form.passwd,Request.PasswordKey,Request.algorithm,Request.encoding)>
Encrypted: <cfoutput>#encrypted#</cfoutput><br/>
Decrypted: <cfoutput>#Decrypt(variables.encrypted,Request.PasswordKey,Request.algorithm,Request.encoding)#</cfoutput><br/>
<br/>
QUERY DATA<br/>
<cfinvoke component="components.userQ" method="login" returnvariable="qLogin">
<cfinvokeargument name="formData" value="#form#">
</cfinvoke>
<cfoutput>qLogin password: #qlogin.encPasswd#</cfoutput><br/>
<cfoutput>Decrypted encPasswd from qLogin: #Decrypt(qlogin.encPasswd,Request.PasswordKey,Request.algorithm,Request.encoding)#</cfoutput>
Decrypt() in app page that is erroring:
<cfset unEnPasswd = #Decrypt(qlogin.encPasswd,Request.PasswordKey,Request.algorithm,Request.encoding)#>
I can get the default CFMX_COMPAT encrypt() and decrypt() to work fine in my app with the same code, just changing the key, algorithm, and encoding variables.
BTW, I am also storing the encrypted strings as varchar() in the DB so it doesn't mess up the padding (so I read). I tried BLOB but get a bytearray error.
Any help or thoughts are greatly appreciated.
You're creating a new secret key on every request,
Really your code should be more like:
<cffunction name="onApplicationStart" returnType="boolean" output="false">
<cfset application.PasswordKey = generateSecretKey("AES")>
</cffunction>
<cffunction name="onRequestStart" returnType="boolean" output="false">
<cfset request.PasswordKey = application.PasswordKey />
<cfset request.algorithm = "AES" />
<cfset request.encoding = "hex" />
</cffunction>
Though really you want to have the password key hardcoded in a config file otherwise if you restart your server you won't be able to access any of your passwords ever again...
Disable jsafe. Add -Dcoldfusion.disablejsafe=true to your jvm config.

How to obtain the FINAL redirected URL using Coldfusion

Given a URL like:
before: http://feeds.wsjonline.com/~r/wsj/health/feed/~3/felKuQPa41U/
which redirects eventually to:
after: http://blogs.wsj.com/health/2009/08/14/insurance-salesman-to-obama-why-are-you-vilifying-insurers/
Using Coldfusion, how can I obtain that final (after) URL? I believe CFHTTP will redirect automatically up to 4 times, but I can't find a way to obtain that final redirected URL.
ideas? thxs
Searching Google may help, sometimes. http://www.bennadel.com/blog/934-Ask-Ben-Handling-Redirects-With-ColdFusion-CFHttp.htm
if you get a redirect with cfhttp you have two options. 1) you can follow (as you say, up to 4 of them in a row). You could also handle them manually by not following them and checking the location variable of the result. THe code would be something like this (note that this is psudo-coldfusion, my syntax might be off:
<cfset lastgoodURL = "http://bar.com" />
<cfset foo = false />
<cfloop while="foo eq false">
<cfhttp url="#lastgoodURL#" redirect="false" name="baz" />
<cfif length(baz.responseHeader.Location) eq 0>
<cfbreak />
</cfif>
<cfset lastgoodURL = baz.responseHeader.Location />
</cfloop>

Resources