Retrieving codes within parenthesis in ColdFusion version 4.5 - regex

I have a string retrieved from the database that can contain a series of codes in either {} or [] brackets as well as plain, user entered text. For example, each of the following would be possible values:
[code]
[code1][code2]
{code}
{code1}{code2}
{code1} Some user entered text. {code2}{code3} Some more user entered text.
Etc. etc.
What I need to do using ColdFusion is extract the codes within the {} and [] brackets so I can retrieve their descriptions from a database. For example:
{code1} Some user entered text. {code2}{code3} Some more user entered text.
Would become a list similar to:
{code1}|{code2}|{code3}
Normally I could just do something like REMatch but unfortunately I'm stuck doing this on a server running ColdFusion version 4.5 (groan) so my options are limited.
I'm thinking maybe I could do some Replaces on the string to convert it into a pipe delimited list that I can then easily process but I'm not sure if there might be a more straight forward approach? I'm not even really sure what a sensible way to process this using a Replace would be.

<cfset myString = "{code1} Some user entered text {code2}{code3} More user entered text" />
<cfset myArray = listToArray(myString, "{[") />
<cfloop index="i" from="1" to="#arrayLen(myArray)#">
<cfset myArray[i] = "{" & listFirst(myArray[i], "}]") & "}" />
</cfloop>
<cfdump var="#myArray#" />
<hr>
<cfset myList = arrayToList(myArray, "|") />
<cfdump var="#myList#" />
TryCF.com Gist:
https://trycf.com/gist/6035ddc5cd3daa81bc0943f1af33323a/lucee5?theme=monokai

Related

Problem with anchor links using resolveurl

I'm using <cfhttp> to pull in content from another site (coldfusion) and resolveurl="true" so all the links work. The problem I'm having is resolveurl is making the anchor links (href="#search") absolute links as well breaking them. My question is is there a way to make resolveurl="true" bypass anchor links somehow?
For starters, let's use the tutorial code from Adobe.com posted in the comments. You'll want to do something similar.
<cfhttp url="https://www.adobe.com"
method="get" result="httpResp" timeout="120">
<cfhttpparam type="header" name="Content-Type" value="application/json" />
</cfhttp>
<cfscript>
// Find all the URLs in a web page retrieved via cfhttp
// The search is case sensitive
result = REMatch("https?://([-\w\.]+)+(:\d+)?(/([\w/_\.]*(\?\S+)?)?)?", httpResp.Filecontent);
</cfscript>
<!-- Now, Loop through those URLs--->
<cfoutput>
<cfloop array="#result#" item="item" index="index">
<cfif LEFT(item, 1) is "##">
<!---Your logic if it's just an anchor--->
<cfelse>
<!---Your logic if it's a full link--->
</cfif>
<br/>
</cfloop>
</cfoutput>
If it tries to return a full URL before the anchor as you say, (I've been getting inconsistent results with resolveurl="true") hit it with this to only grab the bit you want.
<cfoutput>
<cfloop array="#result#" item="item" index="index">
#ListLast(item, "##")#
</cfloop>
</cfoutput>
What this code does is grab all the URLs, and parse them for anchors.
You'll have to decide what to do next inside your loop. Maybe preserve the values and add them to a new array, so you can save it somewhere with the links fixed?
It's impossible to assume in a situation like this.
There does not appear to be a way to prevent CF from resolving the hashes. In our usage of it the current result is actually beneficial since when we present content from another site we usually want the user to be sent there.
Here is a way to replace link href values with just anchor if one is present using regular expressions. I'm sure there are combinations of issues that could occur here if really malformed html.
<cfsavecontent variable="testcontent">
<strong>test</strong>
go to google
go to section
</cfsavecontent>
<cfset domain = replace("current.domain", ".", "\.", "all") />
<cfset match = "(href\s*=\s*(""|'))\s*(http://#domain#[^##'""]+)(##[^##'""]+)\s*(""|')" />
<cfset result = reReplaceNoCase(testcontent, match, "\1\4\6", "all") />
<cfoutput><pre>#encodeForHTML(result)#</pre></cfoutput>
Output
<strong>test</strong>
go to google
<a href="#section>go to section</a>
Another option if you are displaying the content in a normal page with js/jquery available is to run through each link on display and update it to just be the anchor. This will be less likely error with malformed html. Let me know if you have any interest in that approach.

is there a way to add some spacing to cfmail type=text emails in Coldfusion when using variables only?

I'm a little in awe on how my first Cfmails are looking.
Problem is, I'm using variables for both text and content and I would still like to have some sort of spacing.
For example, if I have:
<cfprocessingdirective suppresswhitespace="No">
<cfmail
TO="#Local.User.email#"
FROM="..."
SERVER="..."
USERNAME="..."
PASSWORD="..."
WRAPTEXT="80"
SUBJECT="#tx_automailer_register_subject# - #Local.User.Comp#">
#tx_automailer_default_hello#
#tx_automailer_register_info#
#tx_automailer_register_iln#: #Local.User.iln#
#tx_firma#: #Local.User.firma#
#tx_ansprechpartner#: #Local.User.ansprechpartner#
#tx_adresse#: #Local.User.adresse#
#tx_plz#: #Local.User.plz#
#tx_ort#: #Local.User.ort#
...
The only place this looks nice is my cfc :-) In the mail itself everything is going bazooka.
Question:
Is there a way to space this? I have also tried to space according to length of variables, but this also does not really do any good and I'm not really keen on doing math for this...
Thanks for help!
The only option may be to post process the content. Build up the pretty content in a cfsavecontent, then run through cleanup function.
<cfprocessingdirective suppresswhitespace="No">
<cfsavecontent variable="message">
#tx_automailer_default_hello#
#tx_automailer_register_info#
#tx_automailer_register_iln#: #Local.User.iln#
#tx_firma#: #Local.User.firma#
#tx_ansprechpartner#: #Local.User.ansprechpartner#
#tx_adresse#: #Local.User.adresse#
#tx_plz#: #Local.User.plz#
#tx_ort#: #Local.User.ort#
</cfsavecontent>
<cfmail
TO="#Local.User.email#"
FROM="..."
SERVER="..."
USERNAME="..."
PASSWORD="..."
WRAPTEXT="80"
SUBJECT="#tx_automailer_register_subject# - #Local.User.Comp#"
>#cleanupTextMessage(message)#</cfmail>
<cffunction name="cleanupTextMessage" output="false">
<cfargument name="content" />
<!--- remove whitespace at beginning of each line --->
<cfset arguments.content = reReplace(arguments.content, "^\s+", "", "all") />
<!--- replace any multiple whitespace characters with one space --->
<cfset arguments.content = reReplace(arguments.content, "\s+", " ", "all") />
<cfreturn arguments.content />
</cffunction>
You might actually be able to nest the cfsavecontent inside cfmail, or create a custom tag that does savecontent and function actions.
Note: I was answering under the assumption the question was "how to make code look good without affecting the resulting text message". If you were trying to do something different with the resulting text output let me know.
You can use HTML To do it by adding the TYPE="html" to your cfmail attributes. Then put in a "pre" tag if you want that sysprint type look. as in
<pre>
#tx_automailer_default_hello#
#tx_automailer_register_info#
....
</pre>
Or you could add a table as in:
<table
<tr>
<td>#tx_automailer_default_hello#</td>
</tr>
<tr><td>
#tx_automailer_register_info#
</td>
If you want to stick with plain text you need to make sure you have tabs/spaces counted correctly and that none of your lines is longer than 80 chars (or they will wrap..without a beat too).
If you're set on plaintext email and are confident that the recipient will be using a fixed-width font, you can use lJustify() to align your text and pad with spaces.
Left justifies characters in a string of a specified length.
#lJustify(tx_automailer_register_iln & ":",32)# #lJustify(Local.User.iln,25)#
#lJustify(tx_firma & ":",32)# #lJustify(Local.User.firma,25)#
#lJustify(tx_ansprechpartner & ":",32)# #lJustify(Local.User.ansprechpartner,25)#
#lJustify(tx_adresse & ":",32)# #lJustify(Local.User.adresse,25)#
#lJustify(tx_plz & ":",32)# #lJustify(Local.User.plz,25)#
#lJustify(tx_ort & ":",32)# #lJustify(Local.User.ort,25)#

How to determine if a full name has a space in it?

I have a field that a user can input first and last name to fill out my form. Sometimes, users put on their first name and that results in empty fields in my database. PLEASE keep in mind that I cannot change this method completely because this form is part of a bigger project and it is being used by other websites of my company.
This is the part of the code that i need the validation around it. I already have a validation that ensures that the filed is not empty but I need on more to ensure that the field has two items in it separated by space.
<input name="fullname" class="fullname" type="text" value="#fullname#" maxlength="150"/>
<cfif fullname eq '' and check2 eq 'check2'>
<br /><span style="color:red">*you must enter your full name</span></cfif>
The check2 eq 'check2' is checking if the form was submitted already to ensure a user submitting their data twice.
I thought of using regular expressions to do that but unfortunately I am not very familiar with how to use regx in CF9 and the documentation online through me off a bit.
I was also thinking to use "Find" or "FindOneOF", any thoughts on that?
Also, I am trying to avoid using JQ,JS etc, so please try to keep your suggestions based on CF code IF possible.
Any help or different suggestions on how to tackle this issue will be very appreciated.
No regex is needed for this. A slightly simpler solution:
<cfset form.fullname = "Dave " />
<cfif listLen(form.fullname," ") GT 1> <!--- space-delimited list, no need for trimming or anything --->
<!--- name has more than one 'piece' -- is good --->
<cfelse>
<!--- name has only one 'piece' -- bad --->
</cfif>
You could do something like this for server side validation:
<cfscript>
TheString = "ronger ddd";
TheString = trim(TheString); // get rid of beginning and ending spaces
SpaceAt = reFind(" ", TheString); // find the index of a space
// no space found -- one word
if (SpaceAt == 0) {
FullNameHasSpace = false;
// at least one space was found -- more than one word
} else {
FullNameHasSpace = true;
}
</cfscript>
<cfoutput>
<input type="input" value="#TheString#">
<cfif FullNameHasSpace eq true>
<p>found space at position #SpaceAt#</p>
<p>Your data is good.</p>
<cfelse>
<p>Did not find a space.</p>
<p>Your data is bad.</p>
</cfif>
</cfoutput>

How can I strip this URL of everything before "http://"?

I'm doing some web scraping with ColdFusion and mostly everything is working fine. The only other issues I'm getting is that some URL's come through with text behind them that is now causing errors.
Not sure what's causing it, but it's probably my regex. Anyhow, there's a distinct pattern where text appears before the "http://". I'd like to simply remove everything before it.
Any chance you could help?
Take this string for example:
"I'M OBSESSED WITH MY BEAUTIFUL FRIEND" src="http://scs.viceland.com/feed/images/uk_970014338_300.jpg
I'd much appreciate your help as regex isn't something I've managed to make time for - hopefully I will some day!
Thanks.
EDIT:
Hi,
I thought it might be helpful to post my entire function, since it could be my initial REGEX that is causing the issue. Basically, the funcion takes one argument. In this case, it's the contents of a HTML file (via CFHTTP).
In some cases, every URL looks and works fine. If I try digg.com for example it works...but it dies on something like youtube.com. I guess this would be down to their specific HTML formatting. Either way, all I ever need is the value of the SRC attribute on image tags.
Here's what I have so far:
<cffunction name="extractImages" returntype="array" output="false" access="public" displayname="extractImages">
<cfargument name="fileContent" type="string" />
<cfset var local = {} />
<cfset local.images = [] />
<cfset local.imagePaths = [] />
<cfset local.temp = [] />
<cfset local.images = reMatchNoCase("<img([^>]*[^/]?)>", arguments.fileContent) />
<cfloop array="#local.images#" index="local.i">
<cfset local.temp = reMatchNoCase("(""|')(.*)(gif|jpg|jpeg|png)", local.i) />
<cfset local.path = local.temp />
<cfif not arrayIsEmpty(local.path)>
<cfset local.path = trim(replace(local.temp[1],"""","","all")) />
<cfset arrayAppend(local.imagePaths, local.path) />
</cfif>
<cfif isValid("url", local.path)>
<cftry>
<cfif fileExists(local.path)>
<cfset arrayAppend(local.imagePaths, local.path) />
</cfif>
<cfcatch type="any">
<cfset application.messagesObject.addMessage("error","We were not able to obtain all available images on this page.") />
</cfcatch>
</cftry>
</cfif>
</cfloop>
<cfset local.imagePaths = application.udfObject.removeArrayDuplicates(local.imagePaths) />
<cfreturn local.imagePaths />
</cffunction>
This function WORKS. However, on some sites, not so. It looks a bit over the top but much of it is just certain safeguards to make sure I get valid image paths.
Hope you can help.
Many thanks again.
Michael
Take a look at ReFind() or REFindNoCase() - http://cfquickdocs.com/cf9/#refindnocase
Here is a regex that will work.
<cfset string = 'IM OBSESSED WITH MY BEAUTIFUL FRIEND" src="http://scs.viceland.com/feed/images/uk_970014338_300.jpg' />
<cfdump var="#refindNoCase('https?://[-\w.]+(:\d+)?(/([\w/_.]*)?)?',string, 1, true)#">
You will see a structure returned with a POS and LEN keys. Use the first element in the POS array to see where the match starts, and the first element in the LEN array to see how long it is. You can then use these values in the Mid() function to grab just that matching URL.
I'm not familiar with ColdFusion, but it seems to me that you just need a regex that looks for http://, then any number of characters, then the end of the string.

Resolving variables inside a Coldfusion string

My client has a database table of email bodies that get sent at certain times to customers. The text for the emails contains ColdFusion expressions like Dear #firstName# and so on. These emails are HTML - they also contain all sorts of HTML mark-up. What I'd like to do is read that text from the database into a string and then have ColdFusion Evaluate() that string to resolve the variables. When I do that, Evaluate() throws an exception because it doesn't like the HTML markup in there (I also tried filtering the string through HTMLEditFormat() as an intermediate step for grins but it didn't like the entities in there).
My predecessor solved this problem by writing the email text out to a file and then cfincluding that. It works. It's seems really hacky though. Is there a more elegant way to handle this using something like Evaluate that I'm not seeing?
What other languages often do that seems to work very well is just have some kind of token within your template that can be easily replaced by a regular expression. So you might have a template like:
Dear {{name}}, Thanks for trying {{product_name}}. Etc...
And then you can simply:
<cfset str = ReplaceNoCase(str, "{{name}}", name, "ALL") />
And when you want to get fancier you could just write a method to wrap this:
<cffunction name="fillInTemplate" access="public" returntype="string" output="false">
<cfargument name="map" type="struct" required="true" />
<cfargument name="template" type="string" required="true" />
<cfset var str = arguments.template />
<cfset var k = "" />
<cfloop list="#StructKeyList(arguments.map)#" index="k">
<cfset str = ReplaceNoCase(str, "{{#k#}}", arguments.map[k], "ALL") />
</cfloop>
<cfreturn str />
</cffunction>
And use it like so:
<cfset map = { name : "John", product : "SpecialWidget" } />
<cfset filledInTemplate = fillInTemplate(map, someTemplate) />
Not sure you need rereplace, you could brute force it with a simple replace if you don't have too many fields to merge
How about something like this (not tested)
<cfset var BaseTemplate = "... lots of html with embedded tokens">
<cfloop (on whatever)>
<cfset LoopTemplate = replace(BaseTemplate, "#firstName#", myvarforFirstName, "All">
<cfset LoopTemplate = replace(LoopTemplate, "#lastName#", myvarforLastName, "All">
<cfset LoopTemplate = replace(LoopTemplate, "#address#", myvarforAddress, "All">
</cfloop>
Just treat the html block as a simple string.
CF 7+: You may use regular expression, REReplace()?
CF 9: use Virtual File System
If the variable is in a structure from, something like a form post, then you can use "StructFind". It does exactly as you request. I ran into this issue when processing a form with dynamic inputs.
Ex.
StructFind(FORM, 'WhatYouNeed')