Can I store a struct in a Coldfusion session variable? - session

I'm passing a struct to a CF Custom Tag. I'd like the CFM page that is this custom tag to assign this struct to a dynamically created session variable. Creating the session variable works when assigning it to a simple value such as a number, but fails when assigning it to the struct in the way I'm doing it below.
<cfset Evaluate("SESSION.myVar#ATTRIBUTES.count# = #ATTRIBUTES.myStruct#")>
I thought this was possible, but when I try to do so I get an error:
Complex object types cannot be converted to simple values.
This is how the struct is created:
<cfset testStruct = StructNew()>
<cfset testStruct.something = 2>
And passed through the custom tag:
<cf_myTag myStruct="#testStruct#" count="#i#">
I think the Evaluate portion is messing things up here.

As Leigh says, use array notation rather than Evaluate() to create your dynamically named session variable:
<cfset SESSION[ "myVar" & ATTRIBUTES.count ] = ATTRIBUTES.myStruct>

Yes you can,
Just use the duplicate method
<cfset SESSION.myVar = duplicate(ATTRIBUTES.myStruct) />

yes, you can just do the following:
note I'm using a cflock here to avoid any potential race conditions.
<cflock scope="session" throwontimeout="true" timeout="5" type="exclusive">
<cfset session["myVar" & ATTRIBUTES.count] = attributes.myStruct />
</cflock>

The issue above is the evaluate statement. It's trying to evaulate the structure as a simple value in the string, and then evaluate the string.
You can get around evaulate entirely. The reason is you want a dynamically named session variable?

Related

I have a need to see all session scope for all logged in users in ColdFusion2018

I start with this find;
<cfscript>
app = application.getApplicationSettings().name;
sessionCollection = application.sessionTracker.getSessionCollection(app);
</cfscript>
and then attempt to loop thru the collection;
<cfloop collection="#sessionCollection#" item="i">
#StructFind(i, 'CurrentAction')#
</cfloop>
which fails with;
You have attempted to dereference a scalar variable of type class java.lang.String as a structure with members.
I have tried
#CurrentAction[i]#
which fails with;
Variable CURRENTACTION is undefined.
when I know for a fact it exists
(https://ibb.co/ZJwsKFS)
Ive tried dot notation as well.
The purpose is to identify an ajax listener activity for one user, and affect an action on another specific user as a response. I am under the impression I can send an action by sessionID end user. Please feel free to let me know if this is NOT possible.
So this code:
<cfset x = {
a = 1
, b = 2
, c = 3
}>
<cfoutput>
<cfloop collection="#x#" item="i">
<li>#i#</li>
</cfloop>
</cfoutput>
Outputs the following:
A
B
C
Which means that i is the key of the struct defined in the collection attribute. Your code is trying to find a key in the collection (struct), but you're referencing the wrong variable.
To find the value of B, you would do StructFind(x, "b"). You don't need the cfloop either, you just need to check if the key exists, then get the value.
<cfset someVariable = "">
<cfif structKeyExists(x, "b")>
<cfset someVariable = structFind(x, "b")>
</cfif>

Using one variable for multiple items data in descriptive programming

I know that with Descriptive programming you can do something like this:
Browser("StackOverflow").Page("StackOverflow").Link("text:=Go To Next Page ", "html tag:=A").Click
But is it possible to create some kind of string so I can assign more than one data value and pass it as single variable? I've tried many combinations using escape characters and I always get error.
For example in the case above, let's say I have more properties in the Page object, so I'd normally have to do something like this:
Browser("StackOverflow").Page("name:=StackOverflow", "html id:=PageID")...etc...
But I'd like to pass "name:=StackOverflow", "html id:=PageID" as a single variable, so when writing many objects I'd only have to write:
Browser(BrowserString).Page(PageString).WebEdit("name:=asdfgh")
And the first part would remain static, so if the parents' data needs to be modified I'd only have to modify two variables and not all the objects created in all libraries.
Is it possible?
If I was not clear enough please let me know.
Thank you in advance!
I think what you're looking for is UFT's Description object
This allows you finer grained control on the description since in descriptive programming all values are regular expressions but with Description you can turn the regular expression functionality off for a specific property.
Set desc = Description.Create()
desc("html tag").Value = "A"
desc("innertext").Value = "More information..."
desc("innertext").RegularExpression = False
Browser("Example Domain").Navigate "www.example.com"
Browser("Example Domain").Page("Example Domain").WebElement(desc).Click
If you want to represent this with plain string then it's a bit more of a problem, you can write a helper function but I'm not sure I would recommend it.
Function Desc(descString)
Set ret = Description.Create()
values = Split(descString, "::")
For Each value In values
keyVal = Split(value, ":=")
ret(keyVal(0)).Value = keyVal(1)
Next
Set Desc = ret
End Function
' Usage
Browser("StackOverflow").Page("StackOverflow").WebElement(Desc("html tag:=H2::innertext:=some text")).Click
Further reading about descriptive programming.
As an alternative to Motti's excellent answer, you could also Set a variable to match your initial descriptive object and then extend it as required:
Set myPage = Browser("StackOverflow").Page("name:=StackOverflow", "html id:=PageID")
after which you can then use
myPage.WebEdit("name:=asdfgh")
throughout the rest of the code, so long as the myPage object stays in scope...

Assigning empty string if XML node doesn't exist in Freemarker

I have an XML document passed as root to a Freemarker template. I want some values from this XML to be assigned to variables as a string and later concatenate/print them out.
<#assign MyVar = root.child1.child2.child3.mynode>
The issue here is that even when a path doesn't exist MyVar gets assigned with a sequence+hash which cannot be printed out or converted to string. This variable although returns false for ?has_content, it needs an extra step for these checks and I have this same issue with many variables and across template files and modules.
The only solution I have been able to find was
<#assign MyVar = root.child1.child2.child3.mynode>
<#assign MyVar = MyVar ?has_content?then(MyVar , "")>
I am looking for something like the Default Value Operator which also checks for nulls like ?has_content.
Does Freemarker provide any simpler one line function to check if a variable has no content and assign it with a default?
In short:
<#assign myVar = root.child1.child2.child3.mynode[0]!''>
Or just <#assign myVar = root.child1.child2.child3.mynode[0]!> if the implicit multi-typed default value doesn't cause problems (like when you just print it with ${}).
Why: XML queries (just like XPath queries) always return a sequence of matching nodes. There are maybe 0 such nodes (or 1, or multiple). An empty sequence is not a "missing value" according the template language. It's an inconvenient mismatch with the XML data-model. But while the sequence always exists, its 1st element ([0]) doesn't, so you can use all the missing value handler operators with it as usual.

How can I set a FreeMarker variable with an interpolated value? error: "You can't use "${" here as you are already in FreeMarker-expression-mode."?

I am absolutly new in FreeMarker and I have the following problem working on a Spring MVC application that use this template engine.
So into a controller method I put an int representing the current year (2016) into the model, in this way:
model.addAttribute("annoCorrente", annoCorrente);
Then, into my FreeMarker page I have to assign this value to a variable, so I write the following expression:
<#assign a = ${annoCorrente}>
But in this way I obtain the following error message:
[col. 86] You can't use "${" here as you are already in FreeMarker-expression-mode. Thus, instead of ${myExpression}, just write myExpression. (${...} is only needed where otherwise static text is expected, i.e, outside FreeMarker tags and ${...}-s.)
Why? How can I correctly initizialize a FreeMarker variable with the value obtained from the model associated to this view?
Change <#assign a = ${annoCorrente}> to <#assign a = annoCorrente>
(or you can do <#assign a = "${annoCorrente}"> but this is not recommended)

Cast as integer in ColdFusion; sanitizing variables

I'm rusty at ColdFusion, I've been used to PHP for so long. What I want to do is something like this:
<?php
$id = (isset($_GET['id'])) ? (int)$_GET['id'] : 0;
?>
Basically, check for a url parameter called id and if it exists make sure it's an integer so I can safely use it in database queries. If it ends up zero, that's fine too.
I have:
<cfscript>
if (IsDefined("URL.id") AND IsNumeric(URL.id)) {
id = int(URL.id);
} else {
id = 0;
}
</cfscript>
This is working, but is awfully messy. Is there a better way to do this?
Recent versions of ColdFusion also have a ternary conditional operator:
<cfset id = (structKeyExists(URL, "id") and isNumeric(URL.id)) ? int(URL.id) : 0>
I would use cfparam. I'd also scope explicitly, but that's not necessary. I wouldn't use the IIF() function, because it makes use of evaluate(), which can be problematic, I'd also avoid DE() for the same reason. In this case, it won't be an issue, but I avoid them on general principle in any situation where it's not absolutely necessary. I've been using CF for a few years now, and it hasn't been necessary yet.
<cfparam name="url.id" default="0" />
<cfif isNumeric(url.id)>
<cfset local.id = int(url.id) />
<cfelse>
<cfset local.id = 0 />
</cfif>
To me, the simplest way to ensure your variable is an integer is to wrap the variable in val().
It attempts to parse the string and extract any integer found (at the beginning of the string). If none is found it returns 0.
If TestValue = "234A56?7'", Val(TestValue) returns 234.
If TestValue = "234'5678'9?'", Val(TestValue) returns 234.
If TestValue = "BG234", Val(TestValue) returns the value 0, (not an error).
If TestValue = "0", Val(TestValue) returns the value 0, (not an error).
See http://cfquickdocs.com/cf8/#Val
Apologies for raising an old thread but came up with this same question and found a simple solution that might help others with this issue
NumberFormat(URL.id)
There are also various masks that you can specify in different scenarios
ColdFusion Reference
A formatted number value:
If no mask is specified, returns the value as an integer with a thousands separator.
If the parameter value is "" (an empty string), returns 0.
http://help.adobe.com/livedocs/coldfusion/8/htmldocs/help.html?content=functions_m-r_08.html
You might also look into cfparam.
<cftry>
<cfparam name="url.id" default="0" type="integer">
<cfcatch>
<!--- log? etc --->
<cfset url.id = 0>
</cfcatch>
</cftry>
You can use IIF. It's cool.
<cfset id = IIf(IsDefined("URL.id") AND Int(URL.id), Int(URL.id), DE("0"))>

Resources