too much pathinfo using buildURL - coldfusion

I try to get an impression how to build a REST-API using FW/1 version 4.
I set this in my Application.cfc:
<cfset variables.framework.routes = [
{ "$GET/persons/:id/$" = "/persons/show/id/:id" }
]>
My controller method
<cffunction name="show">
<cfargument name="rc" type="struct" required="true">
<cfset local.strURL = variables.fw.buildURL(
action = 'persons.show',
queryString = 'id=123'
)>
<cfset variables.fw.renderData().data( { strURL = local.strURL } ).type( "json" )>
</cffunction>
I call /persons/123 and get this output:
{"URL":"/persons/show/id/123"}
I don't like the /show/ pathinfo in this generated URL. It's not part of the URL I requested in the browser (or via AJAX etc.). Is there a way to get rid of this in the generated URL?

This is not a functionality in FW/1. This was considered in 2012, but was rejected.
See: https://github.com/framework-one/fw1/issues/145
The core issue was (and is) according to Sean Corfield:
Interesting idea but I think you'll find that, in general, routes =>
URLs is actually a many-to-one mapping so you can't reliably go
backwards from URLs (actions / params) to a unique route. I've talked
that over a few times with people and so far no one has managed to
come up with a mapping that works in all cases.

Related

Coldfusion global user object

I am trying to set a coldfusion user object for each person as they log in. Similar to how rails has devise and I can call current_user.id or current_user.username from wherever I am in the site. I have a users table that stores roles and other user information. I want to query that and assign all of the fields to a sort of global user object. I can then use that for components, page display etc.
I am trying to figure out where and how to do this. I have tried initializing a component like this in onsessionstart, onrequeststart etc, but when I try to reference globalUser.id in a component for instance it hits a 500 error because globalUser is not defined.
<cfset globalUser = CreateObject( "component",
"controllers.user" ).globalUser(empidname= '#getauthuser()#') />
Any recommended ways to do this? Any plugins that provide functionality like this?
You want to use session variables.
<cffunction name="onSessionStart">
<cfset session.globalUser = CreateObject( "component",
"controllers.user" ).globalUser(empidname= '#getauthuser()#') />
...
</cffunction>
In order for sessions to be enabled, you have to alter application.cfc
component {
this.name = "AppName";
this.sessionManagement = true;

Exempting Coldfusion page from authentication

I need to post from a non-secure CF page to a secure CF page. I don't want to have to go through and implement the user authentication on the page sending the values because its a rather cumbersome process due to the way this legacy site was setup and secondly because the page sending the values is acting as a service between two unrelated order management systems as opposed to a user.
Right now, when I try to post to, the response result is a redirect to the login of the homepage. Is there a way to make an exception for a posting or receiving page from forcing user authentication?
I'm using <cfhttp> to post the values to post page which has a series of <cfparam>'s that I'm passing the values to. Once I pass those values into the post page is when the post page triggers a redirect to the home page because the post page is an internal page in the order management system and is displayed as a client logs in and a session is created for them.
Since you did not provide any code, here is a guess what it might look like and how you could add an exception for specific requests:
<cffunction name="onRequestStart" access="public" output="false" returnType="boolean">
<cfargument name="targetPage" type="string" required="true">
<!--- treat initialized SESSION or matching request token (rtoken) as successful authentication --->
<cfset LOCAL.isAuthenticated = (
isDefined("SESSION.userID")
or
( structKeyExists(FORM, "rtoken") and (FORM["rtoken"] eq "some-secret-only-you-know") )
)>
<cfif LOCAL.isAuthenticated>
<!--- do something... --->
<!--- not authenticated --->
<cfelse>
<!--- redirect to login --->
<cflocation url="login.cfm" statusCode="303" addToken="false">
</cfif>
</cffunction>
Now you could simple add the key-value-pair rtoken=some-secret-only-you-know (i.e. <input type="hidden" name="rtoken" value="some-secret-only-you-know" />) to your POST to bypass the session based authentification.
Disclaimer: Only use this method if the POST parameters (form fields) are not public/editable by the user.
Feel free to provide actual context so I can assist in a more concrete way.
I have written a couple of apps with similar, but not identical requirements. Here is how I handled those requirements in the last one I wrote. All this code is the Application.cfc file in the methods specified.
In onApplicationStart:
application.securityNotNeededPages =
"somePage.cfm,someOtherPage.cfm,someMorePages.cfm";
In onRequestStart
var ThisPage = listlast(cgi.PATH_INFO, "/");
...
if (ListFindNoCase(application.securityNotNeededPages, ThisPage) is false) {
security related code
}
else {
code for when the page does not to be secured
}

ColdFusion Component Not Returning Query

I have tried, off and on, for about 6 months or so to figure out CFC's but never could get it. So now I have SQL and LDAP queries scattered around in different pages that I would like to consolidate into a component. The query below works in it's own CF page, (I've omitted some of the query details for the post) but I'm getting a blank page instead of any results. This is my queries.cfc:
<cfcomponent>
<cffunction name="EmployeeQuery" access="public" returntype="query">
<cfargument name="EmployeeID" required="yes" type="string">
<cfldap action = "query"
name = "EmployeeAdd"
attributes = "distinguishedName, displayName"
filter = "sAMAccountName=#Arguments.EmployeeID#"
start = ""
scope="SUBTREE"
maxrows="1"
server = ""
username=""
password=""
separator=";" />
<cfreturn EmployeeAdd>
</cffunction>
</cfcomponent>
I've got a simple entry form where I enter text, click submit, and on the action page I have:
<cfif IsDefined("form.btnEmployeeAdd")>
<cfinvoke component="queries"
method="EmployeeQuery"
cfinvokeargument
name="EmployeeID"
value="#form.txtEmployeeID#">
<h3>Confirm Employee Entered</h3>
<cfoutput>#EmployeeAdd.displayName#</cfoutput>
</cfif>
My result is a blank page, I don't even get the h3 text. As mentioned, all this works fine in .cfm pages, but it craps the bed when I try to put it in a .cfc. As with all documentation on this, there's so many different ways to do this, but nothing I've tried works so I was hoping I could get a push in the right direction.
Your cfinvoke is missing a returnVariable
<cfinvoke component="queries" method="EmployeeQuery" returnVariable="EmployeeAdd">
<cfinvokeargument name="EmployeeID" value="#form.txtEmployeeID#">
</cfinvoke>
<h3>Confirm Employee Entered</h3>
<cfoutput>#EmployeeAdd.displayName#</cfoutput>
Alternatively if you're on CF10 or higher you could do this instead of your cfinvoke
<cfset employeeAdd = new queries().EmployeeQuery(form.txtEmployeeID)>
Looking at your code to invoke the cfc method, it seems like you are doing it in a wrong way. You have added cfinvokeargument inside cfinvoke as an attribute. cfinvokeargument should be added to cfinvoke body, instead. And as #matt suggested you need to add returnvariable attribute to cfinvoke in order to get results returned from the method. Like this.
<cfinvoke component="queries" method="EmployeeQuery" returnVariable="EmployeeAdd">
<cfinvokeargument name="EmployeeID" value="#form.txtEmployeeID#">
</cfinvoke>
Also as #matt suggested, if you are using cf10 or higher you can use new to instantiate the cfc and then call the method using . notation. Like this.
<cfset employeeAdd = new queries().EmployeeQuery(form.txtEmployeeID)>

Setting session variables with JavaScript in ColdFusion

I have a website with multiple tabs. Each tab runs a separate report based on a set of filters that take their values from session variables.
How things work now:
While the user is inside a report tab they can open a filter menu to select the options that they need to run their report (doctor names, locations, date, etc) and then they can hit the run button to get their report. When the user clicks "run" the form is saving the variables inside the session where they are available to run other reports without having to click "run" or define them again and again.
What I am trying to do:
Instead of having only a "run" button inside the form I need an "Apply" button that will set the session variables from the form without running the current report. This way the user can pre-define their variables without being forced to run a report they don't need.
I tried using ajax that calls a function outside my application which is setting up variables based on the user's selection.
My challenge is to get those variables back from the function in some format where I could use them in updating the current session variables.
This is a sample of my code:
The Apply button:
Apply
My Ajax Function:
function setSession(){
var formData = $('form').serialize();
$.ajax({
url:'/mod_example/components/exampleCFCs/xUtility.cfc?method=setSessionVariables',
data: formData
});
};
And part of my function:
<cfcomponent output="no">
<cffunction name="setSessionVariables" access="remote" returntype="any">
<cfargument name="docid" type="string" required="no">
<cfif isDefined('docid')>
<cfset session.doctorids = docid>
</cfif>
<cfif isDefined('docid')>
<cfreturn session.doctorids>
<cfelse>
<cfreturn 0>
</cfif>
</cffunction>
</cfcomponent>
What I need is to get the value of session.doctorids to be able to update my session variables with the new value.
It sounds like you have this utility cfc in a shared directory and you are calling it directly. As you've noticed, the problem with that is that you end up with multiple sessions. You can get around this issue be setting up a Facade cfc within your application and make your ajax calls to that cfc.
If you only want to expose the setSessionVariables then you could use this cfc:
<cfcomponent output="no">
<cffunction name="setSessionVariables" access="remote" returntype="any">
<cfset var xUtility = createObject('component','mod_example.components.exampleCFCs.xUtility')>
<cfreturn xUtility.setSessionVariables(argumentCollection=ARGUMENTS)>
</cffunction>
</cfcomponent>
If you want to expose all methods of the utility cfc, then you can extend it:
<cfcomponent output="no" extends="mod_example.components.exampleCFCs.xUtility">
</cfcomponent>
This would allow you to call methods on the utility cfc while maintaining a single session scope (per user of course).
EDIT:
Been a while since i've worked in wheels...but i remember not liking AJAX in the wheels framework. If you create a new subfolder and call it 'remoting' and put the facade in there, and drop an application.cfc in there that looks like this:
<cfcomponent >
<cfset this.name = 'whatever_your_wheels_app_name_is'>
<cfset this.SessionManagement=true>
</cfcomponent>
You should be able to use that facade and this application.cfc will piggyback on the existing application with the same name. The problem with this approach would be if the application times out, and a remote call is the first request to the application, then the wheels application scope might not get set up properly.
It would be best if you could extend the root application.cfc and just override the onRequestStart method so that the framework will ignore the request. To do that you would need to make a mapping in the cfadmin to the root of your project and use this for your remoting/application.cfc
<cfcomponent extends="mappingName.Application">
<cffunction name="onRequestStart">
<cfargument name="requestname" required="true" />
<cfset structDelete(this,'onRequest')>
<cfset structDelete(this,'onRequestEnd')>
<cfset structDelete(VARIABLES,'onRequest')>
<cfset structDelete(VARIABLES,'onRequestEnd')>
<cfreturn true>
</cffunction>
</cfcomponent>
The way that wheels uses `cfinclude' all over the place, you may need to look at this post about extending the appliciation: http://techblog.troyweb.com/index.php/2011/09/cfwheels-workarounds-numero-uno-application-proxy/
There are some wheels plugins (http://cfwheels.org/docs/1-1/chapter/wheels-ajax-and-you) that allow you to use the controller actions / views / routes via ajax so you could look into those also.

How do I access a UDF library in the APPLICATION scope using a shortened name?

I am using ColdFusion 8.0.1.
I created a UDF library and put it in a CFC. I load the library in the APPLICTION scope like this:
// CREATE STRUCTURE OBJECTS
if (not isDefined("APPLICATION.AppOBJ") or not isStruct(APPLICATION.AppOBJ)) {
APPLICATION.AppOBJ = structNew();
APPLICATION.AppOBJ.udf_library = createObject("component", "udf.udf_library");
}
The library works great! But I want to reduce the code needed to access the functions, to shorten the reference. Currently, I have to access the functions like this:
APPLICATION.AppOBJ.udf_library.myFunction();
I want to be able to reference this library object as "UDF", like this:
UDF.myFunction();
In another ColdFusion 9 project (Again, this is a CF8 project!), I am able to do this right after I create the ojbect
<cfset udf = APPLICATION.AppOBJ.udf_library>
In the current project, this doesn't work in the application.cfm file. It DOES however, work when I put it on the page that it is being used.
My question is how far upstream can I put this last line of code to have the variable available on any page in the application? Is there a difference between CF8 and CF9 for this type of thing? Is the difference because I am working in application.CFM versus application.CFC?
Thanks!!!
-- EDIT -- MORE INFORMATION ---
The files that I am trying to access the APPLICATION.AppOBJ.udf_library object are within a custom tag. Might that matter?
-- ANSWER -- THANKS TO MICAH AND BEN NADEL ---
I haven't tried this yet but I think it should work as the idea comes from Ben Nadel's blog entry entitled Creating Globally Accessible User Defined Functions In ColdFusion (Safer Version)
<cfcomponent output="false" hint="I define the application settings and event handlers.">
<!--- Define the application. --->
<cfset this.name = "TestApp" >
<cfset this.applicationTimeout = createTimeSpan( 0, 0, 5, 0 ) >
<!---
Add all of our "global" methods to the URL scope. Since
ColdFusion will automatically seach the URL scope for
non-scoped variables, it will find our non-scoped method
names.
--->
<cfset structAppend( url, createObject( "component", "udf.udf_library" ) ) >
</cfcomponent>
You should now be able to access MyFunction() globally.
If you want to access the function as UDF.MyFunction() then I think you should be able modify Ben's example to the following:
<cfset UDF = StructNew() >
<cfset structAppend( UDF, createObject( "component", "udf.udf_library" ) ) >
<cfset structAppend( url, UDF ) >