I am working on a plugin that handles some more advanced searching of site members and in looking at the Advanced Search for the Site Members (cpublicusers.dsp_advancedsearch.cfm) I found that the way it does the pagination is that it sets the search form to the session. I tried to do the following using this code:
<cfif NOT structKeyExists(SESSION,'reportForm')>
<cfset SESSION.reportForm = FORM />
<cfset sessionisset = true />
<cfelse>
<cfset sessionisset = false />
The sessionisset variable is just for my testing.
When I try to navigate to the pagination I am finding that the session.reportForm variable is being cleared. Can anyone explain why this would be.
FORM is a system structure that's re-initialized on each page. Structures are passed "by reference" meaning you're just storing a pointer to FORM. So when FORM is reinitialized - so is your session variable.
The fix is to make a deep copy of the structure so it's not affected by changes to FORM.
view sourceprint?
<cfset SESSION.reportForm = duplicate(FORM) />
Related
I am very new to ColdFusion. I am building a very basic system in which I now need to set a session variable, in case of successful login. But I don't know how to set session in ColdFusion or how to check it on application pages.
I searched for solution but could not find satisfactory solution. I need some example in which a session is set in case of login, and destroyed on logout.
I've read about Application.cfm, but where this file is located?
This could be a simple question and may be repeating in some ways.
Thanks.
First of all you should probably use:
Application.cfc
You can either use:
OnSessionStart
In which case you don't need to lock your session variables, as Coldfusion takes care of this.
If you set your session variable outside of this method, you may need to lock the variable like:
Before login:
<cfif NOT StructKeyExists(session,"authenticated")>
<cflock scope="session" timeout="30" type="exclusive">
<cfset session.authenticated = false />
</cflock>
</cfif>
New account creation:
When a user logs in, remember to use something like BCrypt() to hash the password & store in DB. Don't encrypt passwords as these can be unencrypted and this can create a potential security loophole.
https://github.com/tonyjunkes/BCryptCFC
<cfset salt = BCrypt.genSalt()>
<cfset hash = BCrypt.hashString("password", salt)>
Login validation:
After a user has logged in, use BCrypt() to check whether the clear text password matches the password hash in your DB:
<cfset BCrypt.checkString("password", hash)>
Also check that the 'username' [e-mail] matches...
If BCrypt() validation is successful, then set your 'session' variable:
<cflock scope="session" timeout="30" type="exclusive">
<cfset session.authenticated = true />
</cflock>
This is a very basic implementation of how the login should work, but it gives you an idea of how to get started.
Another tip, is that, if you are locking your 'session' variables, then instead of having to continually use:
<cflock>
When reading from the session, it would be advisable to convert your 'sesssion' variable into the 'variables' scope, like:
<cflock scope="session" timeout="10" type="readOnly">
<cfset variables.lckauthenticated = session.authenticated />
</cflock>
You can then use:
variables.lckauthenticated
Outside of the <cflock> tag.
Now, there is some debate as to whether you need to lock 'session' variables, but my rule, and one that is recommended in Adobe's Official Documentation, is to lock 'session' variables, if you are reading & writing to them outside of onSessionStart.
Then, when your user logs out, just set:
<cflock scope="session" timeout="30" type="exclusive">
<cfset session.authenticated = false />
</cflock>
You can then use this flag, to determine what is displayed to your user.
I usually set several 'session' variables during a successful login, like:
session.userid
session.roleid
There are other things like 'session' rotation, which help to safeguard your session, but these are more advanced topics for another post...
If there is a better way to go about this (which is quite likely), please let me know how to go about it.
I'm working on some code that is supposed to dynamically set the form variables as regular variables so that we can be lazy and not have to refer to the variable with form.somevariable name.
That part works perfectly. Until I start testing for URL conflicts in which a URL variable has the same name. For instance. . .
I have a form that passes two variables; FirstName and LastName. If I hit the page, the form shows up, I input a first and last name and click submit. The code works perfectly.
However, if I have URL variables with the same names, the code reports the url variable values instead of the form values.
Some sample values;
url.FirstName = Joe
url.LastName = Black
form.FirstName = Steve
form.LastName = White
My code that exposes the form variable will correctly find the form field names, but then when I 'evaluate' the value of the given form field, it will return the value of the URL variable of the same name rather than the form variable.
What I am really wanting (as I described briefly up above) is to have code that automatically converts client, URL and Form variables into 'regular variables' so that you don't have to write lots of extra code grabbing them later on. Frameworks like CFWHEELS and ColdBox do this by default, but at the company I work out, we aren't using any of them. I need it to expose the URL variables, but give presidence to form variables if they have the same name, because they are likely to be intended to do an update or such.
The code follows Feel free to ignore the code for the URL and client variables if you wish as they don't directly affect how the form code works, I have tested with them commented out and I get the same result. I provided all of it to give a more complete idea of what I have been toying with so far. Please note that I don't normally use 'evaluate'. There is probably a better way to go, but I don't know what it is.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++
First Name
Last Name
URL variables:
<cfloop index="i" list="#paramsurl#">
<cfset myDynVar = Evaluate(i)>
<!--- Let's output the dynamically created variable as a test --->
#i# = #myDynVar#<br />
</cfloop>
<cfoutput>
<b>Field Names:</b> #Form.FieldNames#
<p>
<b>Field Values:</b><br>
<cfloop INDEX="TheField" list="#Form.FieldNames#">
#TheField# = #Evaluate(TheField)#<br>
<cfset TheField = Evaluate(TheField)>
</cfloop>
</p>
Lets try and output the two form fields without using the "form." notation<br>
FirstName : #FirstName# <br />
LastName : #LastName#
</cfoutput>
The client variables currently available are:
<cfset nVarCounter = 1>
<cfloop list="#GetClientVariablesList()#" index="whichClientVar">
#whichClientVar# : #client[whichClientVar]#<br />
<cfset whichClientVar = Evaluate(whichClientVar)>
</cfloop>
You should always scope your variables. When you use evaluate it runs through the scope order and it pulls the values out of the url scope before it gets to the form scope
You can use associative array notation to pull the data (as seen below).
<cfoutput>
<b>Field Names:</b> #Form.FieldNames#
<p>
<b>Field Values:</b><br>
<cfloop INDEX="TheField" list="#Form.FieldNames#">
#TheField# = #form[TheField]#<br><!--- specify form scope --->
<cfset myField = structKeyExists(url,TheField) ? url.TheField : form.TheField>
</cfloop>
</p>
</cfoutput>
You can 'copy' the values from form scope and url scope into the variables scope by using structAppend().
structAppend( variables, form, true );
structAppend( variables, url, false );
In the first line, any element of the form scope is copied to the variables scope and if a variable already exists with the same name in variables scope, it will overwrite that value with the value from the form scope.
In the second line, elements form URL scope are copied to variables scope but if a variable already exists in the variables scope, it is NOT overwritten.
You can do this for ANY scope and any other ColdFusion structure. You can also reorder them so that one scope has precedence over the others.
In CF 10 or Railo 4, you could use the defaults() function of the Underscore.cfc library to succinctly accomplish what you're trying to do. Example:
// instantiate Underscore library
_ = new Underscore();
// copy values from form and url scopes into the variables scope
_.defaults(variables, form, url);
This function "fills in" any undefined values in the first struct to the values in the subsequent structs. It works from left to right, so in this example it gives precedence to values in form over the values in url.
Disclaimer: I wrote the Underscore.cfc library.
Please refer to the following Adobe documentation for order of precedence:
http://help.adobe.com/en_US/ColdFusion/9.0/Developing/WSc3ff6d0ea77859461172e0811cbec09af4-7fdf.html
If you want to reverse this precedence for some reason you should be able to just set all of your FORM fields into the variables scope...
Maybe something like ...
<cfloop collection=#form# item="varName">
<cfset SetVariable("variables.#varName#", evaluate("FORM." & varName))>
</cfloop>
Thanks for all of the great ideas.
Following is what I ended up going with.
<cfset scopes = "url,client,form">
<cfloop list="#scopes#" index="i">
<cfloop list="#structKeyList( evaluate( i ) )#" index="j">
<cfset structInsert( VARIABLES, j, evaluate( i & '["' & j & '"]' ), true ) />
</cfloop>
</cfloop>
<cfoutput>
Lets try and output the two form fields without using the "form." notation and make sure that the URL variables are NOT over writing the Form ones<br>
FirstName : #FirstName# <br />
LastName : #LastName#<br />
</cfoutput>
<cfdump var="#VARIABLES#" abort="1" label="Combined Variables Scope stuff" />
I am obfuscating URL's in my app (which is great), but I'd like to disable this for pagination URL's because I'd like the user to be able to enter whatever number they like.
Settings.cfm:
<cfset set(obfuscateURLs = true) />
Home.cfc (controller):
<cffunction name="home">
<cfparam name="params.page" default="1" />
<cfset linkList = model("link").findAll(
select="linkTitle,linkPoints,linkID,linkAuthority,linkCreated,linkUpVoteCount,linkDownVoteCount,linkCommentCount,userName,userID",
include="user",
order="linkPoints DESC",
handle="linkListPaging",
page=params.page,
perPage=5
) />
</cffunction>
Home.cfm (view)
<ul class="pagination">
<cfoutput>
#paginationLinks(
route="paginateLatest",
handle="linkListPaging",
page=1,
name="page",
windowSize=5,
prependToPage="<li>",
appendToPage="</li>",
classForCurrent="current"
)#
</cfoutput>
</ul>
Can I do DeObfuscate on an as needed basis?
Thanks,
Michael
The setting to obfuscate params is an all-or-nothing deal. Just as you can't override this behavior for linkTo(), you cannot override it for paginationLinks() either.
I would suggest building a plugin as I bet there will be other developers out there who would want this in the future. There may be a way to tell the controller to not obfuscate/deobfuscate a parameter named page. You would need to update both how urlFor() works as well as how the controller deobfuscates as it handles incoming requests. You may also consider providing a configuration option to use set() to "blacklist" a set of params keys to never be obfuscated (with page being a default).
I am using ColdFusion 9.0.1
I am taking over a site and the guy before me created about 100 variables and put them into the APPLICATION scope. I believe that his 100 variables were continuously being overwritten with each page load.
Basically, he had this in Application.cfc:
APPLICTION.VariableOne = "SomeStringOne";
APPLICTION.VariableTwo = "SomeStringTwo";
APPLICTION.VariableThree = "SomeStringThree";
My plan is to keep it simple and yet very readable is to test for a specific structure in the application scope. If it's not there, create the structure and variables:
if (not isDefined("APPLICTION.AppInfo") or not isStruct(APPLICTION.AppInfo)) {
APPLICTION.AppInfo = structNew();
APPLICTION.AppInfo.VariableOne = "SomeStringOne";
APPLICTION.AppInfo.VariableTwo = "SomeStringTwo";
APPLICTION.AppInfo.VariableThree = "SomeStringThree";
}
Of course, once the site is live and we are done creating all of the application variables, I'd move this into the into the onApplicationStart() method.
The solution that I want has to be more about "readability" and less about "efficiency". Several non-CFers, but very experience coders will be using this and will need to "get it" quickly.
Does my plan have any gaping holes or is it too inefficient?
Is there a more readable way of creating and managing application variables?
Why not move the definition into onApplicationStart() right now? If you need to reset them during development, you could always pass in a URL variable to flag it for reset, like so:
<!--- in Application.cfc --->
<cffunction name="onRequestStart">
<cfif IsDefined("url.resetApp")>
<cfset ApplicationStop()>
<cfabort><!--- or, if you like, <cflocation url="index.cfm"> --->
</cfif>
</cffunction>
Actually, after re-reading the OP, and reading the suggested solutions, I'm going to have to agree with the OP on his setup, for this very important reason:
This, in onApplicationStart()
APPLICTION.AppInfo = structNew();
APPLICTION.AppInfo.VariableOne = "SomeStringOne";
APPLICTION.AppInfo.VariableTwo = "SomeStringTwo";
Can then later be turned into this, within onRequestStart()
<cflock name="tmp" type="readonly" timeout="15">
<cfset REQUEST.AppInfo = APPLICATION.AppInfo />
</cflock>
Your app can then go on to access the REQUEST vars conveniently, esp, if you decide you want to cache CFCs in the same scope--they would simply go into a separate key:
APPLICATION.Model.MyObject = CreateObject('component','myobject');
Which, of course, also gets poured into REQUEST (if you choose)
Want to go Jake Feasel's route above? No problem:
<cfif isDefined('URL.reload')>
<cfset APPLICATION.Model = StructNew() />
</cfif>
Now you're able to flexibly kill your object cache but maintain your vars (or vice versa as you choose).
This is a great setup for another reason: If you want to build in your own Development/Production "mode", in which the development mode always recompiles the CFCs, but the production mode keeps them cached. The only change you have to make on top of this, is the REQUEST set noted above:
<cfif (isProduction)>
<cflock name="tmp" type="readonly" timeout="15">
<cfset REQUEST.AppInfo = APPLICATION.AppInfo />
</cflock>
<cfelse>
<cfset REQUEST.AppInfo = StructNew() />
<cfset REQUEST.AppInfo.VariableOne = "SomeStringOne" />
...etc...
</cfif>
You can also make the setting of vars and the creation of objects into a private method within Application.cfc, for even further convenience.
I would go ahead and just use OnApplicationStart but back in the pre Application.cfc days we used to do something like Application.Build and if the Build value was different then we did all of our sets on Application variables. So quick and dirty would be something like:
<cfparam name="Application.Build" default="" />
<cfset Build = "28-Nov-2011" />
<cfif Application.Build IS NOT Variables.Build OR StructKeyExists(URL, "Rebuild")>
<cfset Application.Build = Variables.Build />
<!--- A bunch of other CFSETs --->
</cfif>
This method though was something we used back when all we had was the Application.cfm
Im editing my first ColdFusion script .... I have a form which has <input type="hidden" name="name" value="1">.
On the processing page i want to take that value and set it as a POST variable so i can send it onto another page.
I know how to do it in PHP, like so
$_POST['somename'] = $_POST['name']
How would i do that in CF?
Following the idiom in your php code, you can do something like this:
<cfset form['somename'] = form['name']>
...or, if in cfscript:
form['somename'] = form['name'];
If you're concerned about the existence of the variable, you can precede the assignment with <cfparam>:
<cfparam name="form.name" default=""><!--- assuming blank ok as default --->
<cfset form['somename'] = form['name']>
...or in script:
param name='form.name' default='';
form['somename'] = form['name'];
Of course you can also wrap the assignment in a conditional:
if( structkeyexists(form,'name') ){
form.somename = form.name; // dot notation alternative to bracket syntax
}
This all begs the question of what exactly you're trying to achieve with this approach.
The ColdFusion syntax is similar. "Post" variables are available in the system structure FORM, and "Get" variables in the system structure URL. Like in PHP, values can be accessed using associative array notation. You can also use dot notation (for valid field names)
<cfset otherVariable = FORM["variableName"] >
<cfset otherVariable = FORM.variableName >
i want to take that value and set it
as a POST variable so i can send it
onto another page.
I am not quite sure what you mean there. Typically, you do not need to reassign FORM or URL values. You simply reference the variable in your code.
<cfoutput>
Go To Other Page
</cfoutput>
You can try this by checking if the post variable is set and then storing it with scope of FORM.
<cfif isdefined ("FORM.name")>
<cfset FORM.somename="#FORM.name#">
</cfif>