I'm using the excellent validate CFC by Ryan J. Heldt http://validation.riaforge.org/
but have a problem with the email validation RE. RFC 5322 allows the following characters
! # $ % & ' * + - / = ? ^ _ ` { | } ~
however the RE in validate.cfc rejects JohnO'Connell#somewhere.com because of the apostrophe.
The RE in question is in the following code block
<cffunction name="validateEmail" returntype="void" access="private" output="false">
<cfargument name="parameters" type="string" required="true" />
<cfset var rr = 0 />
<cfloop index="rr" list="#arguments.parameters#" delimiters=";">
<cfif isDefined("#listGetAt(rr,1,"|")#") and len(_fields[listGetAt(rr,1,"|")]) and not reFind("^[a-zA-Z][\w\.-]*[a-zA-Z0-9]#[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]$",_fields[listGetAt(rr,1,"|")])>
<cfset registerError(listGetAt(rr,1,"|"),listGetAt(rr,2,"|")) />
</cfif>
</cfloop>
<cfreturn />
</cffunction>
my knowledge of RE's is not up to suggesting a solution, and although I have notified Ryan about this (and another bug a year ago) he doesn't seem to be in bug fixing mode.
Can anyone suggest an alternative regular expression please?
I'll take a stab at updating the RegEx to allow those special characters in the name, but as a general rule of thumb I have very loose validation on email addresses; because seemingly nobody implements them according to spec. My validation usually consists of:
contains '#'
contains 1+ characters before '#'
contains 3+ characters after '#'
1+ characters after '#' must be '.'
While this allows for a lot of false positives to slip through, it also won't create any false negatives.
I'm not going to try to update that regex to spec as it's nowhere near complex enough to match the spec exactly. If you just want to allow special characters in the name, then use this:
and not reFind("^[a-zA-Z][\w\.\##\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]*[a-zA-Z0-9]#[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]$"
This is my typical regex for emails:
^['_a-zA-Z0-9-\+~]+(\.['_a-zA-Z0-9-\+~]+)*#([a-zA-Z_0-9-]+\.)+(([a-zA-Z]{2})|(aero|asia|biz|cat|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|net|org|pro|tel|travel))$
what version of CF are you using? Since CF8, you can use IsValid() to check against emails:
<cfset myemail = "me#exampl.ecom">
<cfoutput>#IsValid("email", myemail)#</cfoutput>
Related
Given this XML fragment (I've removed superfluous fluff):
<Event name="DataComplete">
<Task id="d20a0053-7678-43ba-bc8a-ece24dcff15b"/>
<DataItems>
<DataItem name="Survey" type="task">
<Value status="NotStarted" taskId="00000000-0000-0000-0000-000000000000" />
</DataItem>
<GroupDataItem name="CT_Visit"> --- this may repeat
<ItemGroup id="1" >
<DataItem name="Special Contractor" type="string">Yes</DataItem>
What xPath expression will determine if any DataItem with name="Special Contractor" has the value "Yes".
I'm trying something like this:
Yes = /Event/Task/DataItems/GroupDataItem/ItemGroup/DataItem/#[normalize-space() = 'Special Contractor']
and many variations usually resulting in "invalid xPath expression".
Any clues most welcome. Thanks!
[EDIT]
Thanks for the answers Jiri and Will. Will was close, but as my question states, I'm trying to determine if any* element has the value Yes. I should have been more explicit in saying that I need a boolean, true or false. Adapting Will's answer led me to this:
"Yes" = //Event/Task/DataItems/GroupDataItem/ItemGroup/DataItem[#name='Special Contractor']
This returns a simple Boolean='true' or Boolean='false'.
Thanks guys!
/Event/DataItems/GroupDataItem/ItemGroup/DataItem[#name = "Special Contractor"][. = "Yes"]
Returns the DataItem in question. Note that this will be a sequence of matching DataItem elements if there are more than one. If you just want a boolean:
exists(/Event/DataItems/GroupDataItem/ItemGroup/DataItem[#name = "Special Contractor"][. = "Yes"])
(as an aside; I removed Task from the xpath, since it's not actually an ancestor of the DataItem in the XML fragment you posted, even though the indentation makes it look like it is.)
Use this xpath
/Event/Task/DataItems/GroupDataItem/ItemGroup/DataItem[#name='Special Contractor']
for following xml:
<Event name="DataComplete">
<Task id="d20a0053-7678-43ba-bc8a-ece24dcff15b">
<DataItems>
<DataItem name="Survey" type="task">
<Value status="NotStarted" taskId="00000000-0000-0000-0000-000000000000" />
</DataItem>
<GroupDataItem name="CT_Visit"> --- this may repeat
<ItemGroup id="1" >
<DataItem name="Special Contractor" type="string">Yes</DataItem>
</ItemGroup>
</GroupDataItem>
</DataItems>
</Task>
...
</Event>
If the task is really non-pair element, then omit it from the xpath expression.
I am new to ColdFusion and just started learning about Ajax. The question I have is that I found on the web some cool coding to implement two pull down menus, where is the second one is dependent on what is selected from the first. The goal though is to pull all the values in the pull down list from a select query.
I am using the binding feature, which I just learned about. Everything in the first pull down is pulling correctly. The problem though is the second one. The error I am getting from AJAX logger is "Error invoking: Error Executing Database Query" Any advice would be greatly appreciated. Also thank you in advance for your help.
select.cfc
<cfcomponent output="false">
<!--- Get array of Catagory Description types --->
<cffunction name="cat_description" access="remote" returnType="array">
<!--- Define variables --->
<cfset var data="">
<cfset var result=ArrayNew(2)>
<cfset var i=0>
<!--- Get data --->
<cfquery name="getServiceCat" datasource="SOME_DATABASE">
SELECT DISTINCT CAT_DESC
FROM service_table
ORDER BY CAT_DESC
</cfquery>
<!--- Convert results to array--->
<cfloop index="i" from="1" to="#getServiceCat.recordcount#">
<cfset result[i][1]=getServiceCat.CAT_DESC[i]>
<cfset result[i][2]=getServiceCat.CAT_DESC[i]>
</cfloop>
<!--- And return it --->
<cfreturn result>
</cffunction>
<!--- Get Service Type by Cat description type --->
<cffunction name="getServiceType2" access="remote" returnType="array">
<cfargument name="CAT_DESC" type="string" required="true">
<!--- Define variables --->
<cfset var data="">
<cfset var result=ArrayNew(2)>
<cfset var i=0>
<!--- Get data --->
<cfquery name="getServiceType2" datasource="SOME_DATABASE" dbtype="query">
SELECT DISTINCT com_service_code, report_desc
FROM service_table
WHERE CAT_DESC = #ARGUMENTS.CAT_DESC#
ORDER BY report_desc
</cfquery>
</cfif>
<!--- Convert results to array --->
<cfloop index="i" from="1" to="#getServiceType2.recordcount#">
<cfset result[i][1]=getServiceType2.com_service_code[i]>
<cfset result[i][2]=getServiceType2.report_desc[i]>
</cfloop>
<!--- And return it --->
<cfreturn result>
</cffunction>
</cfcomponent>
The above is the cfc which handles all the queries. One of the main reason for this solution is the simplicity of the code that is required within the form.
...
<td>Select Category: <cfselect name="catdesc"
bind="cfc:select.cat_description()"
bindonload="true"/><br />
</td>
...
<td>Select Service: <cfselect name="service"
bind="cfc:select.getServiceType2({catdesc})"
bindonload="false"/>
</td>
I have searched for almost 2 days trying to find a solution to the query error. I know that the query works in SQL so I believe the issue has to do with AJAX and not correctly implementing the WHERE clause for the second pull down.
Again any advice or suggestions would be great. Additionally if you would do this solution another way I am more than happy to try that as well. As I stated I am very new to this language. Only 2 weeks in.
Testing CFC's
Always test cfc's in CF before plugging them into ajax. There is no point messing with bindings until after you have verified the cfc works without error, because if it does not work in CF, it is not going work with ajax either. The only difference is the errors will be harder to find.
As others suggested, start small. Test the query by itself. Then move onto to testing the CFC either using <cfinvoke> or simply invoke it from your browser with test values like:
http://yourserver/path/to/select.cfc?method=getServiceType2&cat_desc=
http://yourserver/path/to/select.cfc?method=getServiceType2&cat_desc=someValue
Error
In regards to the error, we need to see the full error message to provide more specific advice. However, looking at the query/code some likely causes are:
CAT_DESC is a varchar column, in which case your argument must be enclosed in single quotes. Otherwise the database will think the argument value is an object name (table or column).
WHERE CAT_DESC = '#ARGUMENTS.CAT_DESC#'
.. OR CAT_DESC is a numeric column, but your argument is empty. That would result in an invalid sql statement. You need to ensure a valid number is passed into the query (Or you could skip the WHERE clause when the argument is empty, depending on the desired results). One common approach is using the val() function to convert empty strings and other non-numeric values to zero, ie:
WHERE CAT_DESC = #val(ARGUMENTS.CAT_DESC)#
It also looks like you have a stray </cfif> after the second query. (Assuming it is not a copy/paste error)
One other thing, your second query specifies both datasource and dbtype. Those two attributes are mutually exclusive. Having both may cause a syntax error (I have not tried it). Either way you should only use one of them (most likely datasource).
SQL Injection
That said - the query above is vulnerable to sql injection. You should always use <cfqueryparam> on all variable query parameters to guard against sql injection. It has other benefits as well (performance, data type checking, etcetera). But sql injection protection is the most critical in a web application.
<!--- substitute the correct type for your db column --->
WHERE CAT_DESC = <cfqueryparam value="#ARGUMENTS.CAT_DESC#"
cfsqltype="cf_sql_varchar">
Improvements
As of CF8.0.1+, cfselect can bind to a query object (not just an array). So instead of building an array, simply return the raw query from the function.
<cffunction name="getServiceType2" access="remote" returnType="query">
<cfargument name="CAT_DESC" ....>
<cfset var data="">
<cfquery name="data" ..>
SELECT TheQueryColumnToDisplay, TheQueryColumnUsedForListValue
FROM ...
WHERE ...
</cfquery>
<cfreturn data>
</cffunction>
Then specify which column(s) to use for the display and value attributes:
<cfselect name="service"
bind="cfc:select.getServiceType2({catdesc})"
display="TheQueryColumnToDisplay"
value="TheQueryColumnUsedForListValue" ...>
My suggestion is to do one thing at a time. For your specific situation,
First, get your query to work with a cfquery tag.
Second, get it to work inside a function where you pass an argument to the function.
Next, put the function inside a cfc and call it from a cfc page using either or by creating an object and calling the function.
Finally do the bind.
This approach will make the errors more visible so that you can do something about them.
Other observations
use query parameters.
try to pass an id field to your query instead of a text description
I have a web page. The HTML source contains this text:
<meta property="og:title" content="John"/>
John is an example, the name may vary.
I am sure that og:title will appear only once in the text.
This is my code:
$browser.goto( url )
x = $browser.html.gsub( /^.*<meta property="og:title" content="(.+?)".>/m, '\1' )
I expected to find the name John in my variable x
The '\1' should give me the first part I put in the parenthesis, i.e. (.+?), i.e. John, right?
Also, I used a dot . to match a slash / , is there a better way?
Using Watir API:
x = browser.meta.attribute_value "content"
I was not able to access the meta element using either css and xpath.
If you only want the value of content:
html = '<meta property="og:title" content="John"/>'
=> "<meta property=\"og:title\" content=\"John\"/>"
html[/property="og:title" content="([^"]+)"/, 1]
=> "John"
If you're not familiar with regex, "([^"]+)" might throw you. It means "from the first ", grab everything until the next ". In effect it means "grab everything inside the double-quotes.
That code will return all of the HTML, with the matching code (which is everything between the start of the string up to and including the />) replaced by 'John'. So that comes down to "John", followed by the HTML that was after the /> of that meta property.
If you only want to extract the name, and that tag occurs only once, you can use something like:
#browser.html =~ /<meta property="og:title" content="(.+?)"/
x = $1
I am trying to match the value of the following HTML snippet:
<input name="example" type="hidden" value="matchTextHere" />
with the following:
x = response.match(/<input name="example" type="hidden" value="^.+$" \/>/)[0]
why is this not working? it doesn't match 'matchTextHere'
edit:
when i use:
x = response.match(/<input name="example" type="hidden" value="(.+)" \/>/)[0]
it matches the whole html element, and not just the value 'matchTextHere'
^ matches start of a line and $ matches end of the line. Change ^.+$ to \w+ and it will work for values that doesn't contain any symbols. Make it a parenthetical group to capture the value - (\w+)
Update: to match anything between the quotes (assuming that there aren't any quotes in the value), use [^"]+. If there are escaped quotes in the value, it is a different ballgame. .+ will work in this case, but it will be slower due to backtracking. .+ first matches upto the end of the string (because . matches even a "), then looks for a " and fails. Then it comes back one position and looks for a " and fails again - and so on until it finds the " - if there was one more attribute after value, then you will get matchTextHere" nextAttr="something as the match.
x = response.match(/<input name="example" type="hidden" value="([^"]+)" \/>/)[1]
That being said, the regex will fail if there is an extra space between any of the attribute values. Parsing html with regex is not a good idea - and if you must use regex, you can allow extra spaces using \s+
/<input\s+name="example"\s+type="hidden"\s+value="([^"]+)"\s*\/>/
Because you have a start-of-line token (^) and an end-of-line token ($) in your regular expression. I think you meant to capture the value, this might solve your problem: value="(.+?)".
Beware, though, that processing html with regular expressions is not a good idea, it can even drive you crazy. Better use an html parser instead.
You don't need the ^ and $:
x = response.match(/<input name="example" type="hidden" value=".+" \/>/)[0]
you just need to change [0] to [1]
response='<input name="example" type="hidden" value="matchTextHere" />'
puts response.match(/<input name="example" type="hidden" value="(.*?)" \/>/)[1]
matchTextHere
The following code errors:
<cfdbinfo datasource="#Application.DSN#" name="getCols" type="columns" table="#this.tableName#">
<cftry>
<cfquery name="getColumnDetails" dbtype="query">
SELECT COLUMN_NAME,TYPE_NAME
FROM getCols
WHERE IS_PRIMARYKEY = 'NO'
</cfquery>
<cfcatch>
<cfset this.ErrorState = true>
<cfthrow message="General DB Error">
</cfcatch>
</cftry>
<cfloop query="getColumnDetails">
<cfargument name="#getColumnDetails.COLUMN_NAME#" displayName="values" type="Any" required="false" />
</cfloop>
but I would really like to know if it is possible to dynamically set the arguments for a CFC — or is it better to simply pass in a struct and deal with that?
Thanks
Rob
Unlikely.
Two ways, as you said, don't define the cfargument tags and instead look for them being passed in with StructKeyExists(ARGUMENTS, aDynamicName) or, create a code generator and write these methods to a file.
One way I've tried to do similar things to what you're doing is something along these lines:
<cffunction name="doSomethingWithDatabase">
<cfargument name="potentialColumns" type="string">
<cfargument name="columnValues" type="struct">
and then loop over the list of potential columns, using each element in the list as the index to search for in the columnValues struct. if that value exists in the struct, then yo'ure good; otherwise, you ignore that column in the update.
you'd then call the function something like this:
to get the columns you're looking for
alternately, you could ignore the potentialColumns argument and just get that information in your cfc:
<cffunction name="doSomethingWithDatabase">
<cfargument name="columnValues" type="struct">
<cfset potentialColumns = getMyColumns()>
.... loop....