I have a url.LoginID, and I'd like to remove it from the address bar when the user clicks on the link to login. It has to be a bookmark, it can't be a form submit.
Q: How do I remove ?LoginID from Index.cfm?LoginID=XYZ&AssignmentID=123
It's probably something along the lines of:
<cflocation url="#cgi.SCRIPT_NAME#?#cgi.QUERY_STRING#" addtoken="no">
Looks like you are on the right track.
If loginID is the only thing in the query string, you can simply cflocation to the destination page without the query string.
If there is other data in the query string, then you can do something like this:
<cfset q = reReplaceNoCase(cgi.query_string, "LOGINID=[^&]+&?", "")>
<cflocation url="#cgi.SCRIPT_NAME#?#q#">
This essentially removes loginid and everything until either the en of the string or the next URL variable.
As usual, there's already a UDF that someone has written available on CFLIB: queryStringDeleteVar
You can then do like so
<cflocation
url="#cgi.SCRIPT_NAME#?#queryStringDeleteVar("LoginID",cgi.QUERY_STRING)#"
addtoken="no"
>
CGI.QUERY_STRING is actually the default for the second arg, so this will work just as well
<cflocation
url="#cgi.SCRIPT_NAME#?#queryStringDeleteVar("LoginID")#"
addtoken="no"
>
Here's the code for queryStringDeleteVar:
<cfscript>
/**
* Deletes a var from a query string.
* Idea for multiple args from Michael Stephenson (michael.stephenson#adtran.com)
*
* #param variable A variable, or a list of variables, to delete from the query string.
* #param qs Query string to modify. Defaults to CGI.QUERY_STRING.
* #return Returns a string.
* #author Nathan Dintenfass (michael.stephenson#adtran.comnathan#changemedia.com)
* #version 1.1, February 24, 2002
*/
function queryStringDeleteVar(variable){
//var to hold the final string
var string = "";
//vars for use in the loop, so we don't have to evaluate lists and arrays more than once
var ii = 1;
var thisVar = "";
var thisIndex = "";
var array = "";
//if there is a second argument, use that as the query string, otherwise default to cgi.query_string
var qs = cgi.query_string;
if(arrayLen(arguments) GT 1)
qs = arguments[2];
//put the query string into an array for easier looping
array = listToArray(qs,"&");
//now, loop over the array and rebuild the string
for(ii = 1; ii lte arrayLen(array); ii = ii + 1){
thisIndex = array[ii];
thisVar = listFirst(thisIndex,"=");
//if this is the var, edit it to the value, otherwise, just append
if(not listFind(variable,thisVar))
string = listAppend(string,thisIndex,"&");
}
//return the string
return string;
}
</cfscript>
There are number of ways to do this, here is one way using a list loop to read through your existing parameters and check for the one you want to ignore:
<cfset newParams = "" />
<cfloop list="#cgi.query_string#" delimiters="&" index="i">
<cfif listFirst(i, "=") neq "loginID">
<cfset newParams = listAppend(newParams, i, "&") />
</cfif>
</cfloop>
<cflocation url="#cgi.script_name#?#newParams#" addtoken="no">
Hope that helps!
Suppose you don't really want to remove the ? to keep the URL valid, so simple regex should work:
QUERY_STRING = ReReplaceNoCase(cgi.QUERY_STRING, "LoginID=.+\&", "");
BTW, I'm not sure why do you keep LoginID in URL at all, it may be insecure approach. Using sessions sounds like a better idea.
Edit: Ben's regex is better, because my version is so simple that will "eat" all key=value pairs before last one.
Insert famous Zawinski two problem regex quote and solve differently:
<cfset copy = duplicate(url)>
<cfset structDelete(copy, "loginid")>
<cfset entries = []>
<cfloop collection="#copy#" item="key">
<cfset arrayAppend(entries, "#key#=#copy[key]#")>
</cfloop>
<cfoutput>#arrayToList(entries, "&")#</cfoutput>
Related
I have a list like:
<cfset list ="group 1:1; group 2:4; group a:7; group 1:3; group a:1;">
What I want to do is count (sum up) how many people are in group 1, group 2, group a, etc.. Now these fields are dynamic, so I don't know the exact name of the groups.
So the end result in this example is:
group 1: 4
group 2: 4
group a: 8
So the list is just an example, in reality it's much bigger with more groups (dynamic names). I'm using Coldfusion/Lucee. Can someone help me with this?
Just one of numerous of possible alternative ways of doing this. Starting from using a query instead of a list as the starting value.
What I am doing is to loop the list with ; as delimiter and adding the values to a structure. I later use that structure to loop and list the final total.
<cfset list ="group 1:1; group 2:4; group a:7; group 1:3; group a:1;">
<cfset totalStruct = {}>
<cfloop list="#list#" item="group" delimiters=';'>
<cfset groupName = listFirst(group, ':')>
<cfset groupNameKey = replace(groupName, ' ', '', 'all')>
<cfset groupValue = val(listLast(group, ':'))>
<cfif !structKeyExists(totalStruct, groupNameKey)>
<cfset totalStruct[groupNameKey] = {name:groupName, total=groupValue}>
<cfelse>
<cfset totalStruct[groupNameKey].total += groupValue>
</cfif>
</cfloop>
<cfoutput>
<ul>
<cfloop collection="#totalStruct#" item="group">
<li>#totalStruct[group].name# : #totalStruct[group].total#</li>
</cfloop>
</ul>
</cfoutput>
DEMO
Another alternative is to use the map and/or reduce closure functions to parse your list.
NOTE 1: I generally find cfscript to be much easier for most things for parsing text or "looping".
NOTE 2: I'm not a huge fan of loops, especially around large text values. Closure functions will likely be much more performant.
<cfset lst ="group 1:1; group 2:4; group a:7; group 1:3; group a:1;">
First, with a map() inside of a function:
<cfscript>
public Struct function parseLst (required String lst) {
var retval = {} ; //// Default return variable.
//// https://docs.lucee.org/reference/functions/listmap.html
arguments.lst.listmap(
function(el) {
var k = el.listFirst(":").ltrim() ; /// Get the "key" and strip extra leading space.
var v = el.listLast(":") ; /// Get the "value".
var p = retval["#k#"]?:0 ; /// Use Elvis to default to 0 if no struct Key exists.
retval["#k#"] = v + p ; /// Set the value of the key. NOTE: A struck key with the same name will generally overwrite itself. We want to add it.
}
,";" /// Specify the delimiter of the list.
) ;
return retval ;
}
writeDump(parseLst(lst));
</cfscript>
Then with a reduce() without being inside a function.
<cfscript>
//// https://docs.lucee.org/reference/functions/listreduce.html
r = listReduce(lst,
function(prev,nxt){
k = nxt.listFirst(":").ltrim() ; /// Get the "key" and strip extra leading space.
/// To shorten it, I just skipped setting the value beforehand and just did it while setting the struct value. Same method as above.
prev["#k#"] = (nxt.listLast(":"))+(prev["#k#"]?:0) ;
return prev ;
}
,
{} // Initial value
,";" // Delimiter
) ;
writedump(r) ;
</cfscript>
Both could (and probably should) be inside a function, and then you could just send your list variable to it.
And if possible, it might be a lot easier to fix the original list to be simpler to work with.
https://trycf.com/gist/dda51d88504a625fce5548142d73edb3/lucee5?theme=monokai
=======================================================
EDIT:
An alternative to using listFirst/Last() functions would be to just convert the "list" to an array and then use those pieces to get your "key" and "value".
<cfscript>
//// https://docs.lucee.org/reference/functions/listreduce.html
s = listReduce(lst,
function(prev,nxt){
var elem = nxt.listToArray(":") ;
prev["#elem[1].ltrim()#"] = elem[2] + (prev["#elem[1].ltrim()#"]?:0) ;
return prev ;
}
,
{} // Initial value
,";" // Delimiter
) ;
writedump(s) ;
</cfscript>
I have a list of keywords that I am looking for their position in a text file. All is good until I have a keyword that has multiple words.
So for example I have a string that I am searching:
Hello I am a string of wonderful words that will blow your mind time again. Here are some values:
Value 1: this is the value for one
Value 2: this is the value for two
Value 3: this is the value for three
So I have a list of keywords: list = "wonderful words, value 1, value 2, value 3"
So I need to loop through the list and find the word/phrase, then get it's position so I can perform a mid function to get the data.
The problem is when a keyword is a collection of words it is ignored.
Any help would be great. Thanks!
<cfset stringToSearch = "INCIDENT # 12345
LONG TERM SYS# C12345
REPORTED: 15:08:34 05/21/19
Nature: MEDICAL
Address: 34234 W Test ST; 312S
City: Testville
Responding Units: F32423
Cross Streets:
Between: Test ST & N Tester ST
Lat= 39.429019 Lon= -86.432111
Comments: This is a comment">
<cfset list_to_search = 'INCIDENT:,LONG TERM SYS:,REPORTED:,Nature:,Address:,City:,Responding Units:,Cross Streets:,Lat=,Lon=,Comments'>
<cfoutput>
<cfset x = '1'>
<cfset last_keyword = "">
<cfloop list="#list_to_search#" index="keyword" delimiters=",">
<cfif #x# neq '1'>
#x#. #last_keyword# - #keyword# <br><br>
1st: #REFind(last_keyword,stringToSearch)#<br>
2nd: #REFind(keyword,stringToSearch)#<br>
#x#. #last_keyword#: #Mid(stringToSearch,
FindNoCase(last_keyword,stringToSearch)+len(last_keyword),
FindNoCase(keyword,stringToSearch)-
FindNoCase(last_keyword,stringToSearch))#<br><br>
</cfif>
<cfset last_keyword = #keyword#>
<cfset #x# = #x# + 1>
</cfloop>
I'm trying to get the starting position at the end of a keyword all the way to the beginning of the next keyword to get the data in between. Like I said all is good until multiple word keyword.
I need to display an output from a data record that is formatted similar to this: XXXX:12345 (Xxxxxxxxx)
However, the only data I want to output is the "12345" and with two preceding zeros, i.e. the output should look like "0012345". The "12345" in the record is example only, each record has a unique number assigned. An example record looks like this: CAST:98765 (RPOS1234-XY)
Can I use the ReplaceNoCase() to pull only that data out of the record? If so, how would I write the code to remove the unwanted characters?
You can do this in one line of code using a few functions.
str = 'CAST:98765 (RPOS1234-XY)';
projectCode = '00' & listLast( listFirst( str, ' ' ), ':' );
writeDump( projectCode );
To explain this code from the inner most function going out.
ListFirst() gets the first element in an a list based on the delimiter you specify, in this case the delimiter is ' ' - a space - yes, you can use a space as a delimiter.
ListLast() gets the last element in a list based on the delimiter you specify, in this case the delimiter is ':'
The first part simplt appends '00' to the result of the above function calls.
If I had to use reReplaceNoCase or reFindNoCase this is how I would do it.
function parseTokenUsingReFindNoCase(token) {
var local = {};
// use regex to locate position of number (see only set of parentheses in regex pattern)
local.positions = reFindNoCase("^.+:(\d+).+$", arguments.token, 1, true);
// obtain the token substring and ensure at least 7 digits with preceding 0's
local.result = numberFormat( mid(arguments.token, local.positions.pos[2], local.positions.len[2]), repeatString(0, 7));
return local.result;
}
function parseTokenUsingReReplaceNoCase(token) {
var local = {};
// use regex to strip away text before and after the token
local.result = reReplaceNoCase(arguments.token, "(^\D+|\s.+$)", "", "all");
// ensure at least 7 digits with preceding 0's
local.result = numberFormat(local.result, repeatString(0, 7));
return local.result;
}
<h1>ParseToken</h1>
<h2>Using ReFindNoCase</h2>
<cfdump var="#parseTokenUsingReFindNoCase("CAST:98765 (RPOS1234-XY)")#" /><br>
<cfdump var="#parseTokenUsingReFindNoCase("CAST:591498 (FUBAR56-XE)")#" /><br>
<cfdump var="#parseTokenUsingReFindNoCase("CAST:784 (RFP4542-LL)")#" /><br>
<h2>Using ReReplaceNoCase</h2>
<cfdump var="#parseTokenUsingReReplaceNoCase("CAST:98765 (RPOS1234-XY)")#" /><br>
<cfdump var="#parseTokenUsingReReplaceNoCase("CAST:591498 (FUBAR56-XE)")#" /><br>
<cfdump var="#parseTokenUsingReReplaceNoCase("CAST:784 (RFP4542-LL)")#" /><br>
ParseToken
Using ReFindNoCase
0098765
0591498
0000784
Using ReReplaceNoCase
0098765
0591498
0000784
It doesn't use replaceNoCase, but based on your comments this will work:
<cfset castTicket = projectCode>
<!--- strip the first 5 characters, since it is always "CAST " --->
<cfset castTicket = removechars(castTicket, 1,5)>
<!--- now return the leftmost characters, up to the space --->
<cfset castTicket = left(castTicket, find(" ", castTicket) )>
<!--- format the number so it has 7 digits (2 leading zeros in this case) --->
<cfset castTicket = NumberFormat(castTicket, 0000000)>
<cfoutput>#castTicket#</cfoutput>
Returns:
0012345
I've been cracking my head over this for quite some time.
I currently build a custom BB-Code function as part of a project at work. But I don't get it to work at one point: a [code] block.
Using ColdFusion regex, I want to replace the < and > characters with < and >, but only on HTML betweeen the [code] blocks.
So, how can I restrict a regex expression to the part of the string which is between the [code] blocks.
Thanks in advance for any help.
For those who stumble upon this question and could use an answer as well, I'll provide an example for parsing HTML in [code] blocks. Looks somewhat messy though:
<cfset contents = form.yourString />
<cfset substring1= "[code]" />
<cfset occurrences1 = ( Len(contents) - Len(Replace(contents,substring1,"","ALL"))) / Len(substring1) />
<cfset substring2= "[/code]" />
<cfset occurrences2 = ( Len(contents) - Len(Replace(contents,substring2,"","ALL"))) / Len(substring2) />
<cfif occurrences1 EQ occurrences2 AND occurrences1 GT 0 AND occurrences2 GT 0>
<cfset loopinstance = occurrences1 />
<cfelse>
<cfif occurrences1 LT occurrences2>
<cfset loopinstance = occurrences1 />
<cfelse>
<cfset loopinstance = occurrences2 />
</cfif>
</cfif>
<cfloop index="code_loop" from="1" to="#loopinstance#">
<cfscript>
// prepare variables //
code_string = contents;
startpos = FindNoCase("[code]", code_string);
startpos = Evaluate(startpos + 6); // adjust the correct position of string in question
endpos = FindNoCase("[/code]", code_string);
code_string = Mid(code_string, startpos, Evaluate(endpos - startpos)); // extract the string between code brackets
//** Replace-Codes are extensible depending on the used programming languages **//
code_string = ReplaceNoCase(code_string, "<","<", "ALL");
code_string = ReplaceNoCase(code_string, ">",">", "ALL");
//** process conversion of [code] block **//
startpos = FindNoCase("[code]", contents); // reevaluating the start and end positions for main string
startpos = Evaluate(startpos + 6); // adjust the correct position of form string
endpos = FindNoCase("[/code]", contents);
contents = RemoveChars(contents, startpos, Evaluate(endpos - startpos)); // remove the extracted string
contents = Insert(code_string, contents, Evaluate(startpos - 1)); // insert the processed code block to the original position
contents = ReplaceNoCase(contents, "[code]", "[coded]", "ONE"); // "flagging" the processed [code] block as finished by adding a "d"
contents = ReplaceNoCase(contents, "[/code]", "[/coded]", "ONE"); // "flagging" the processed [code] block as finished by adding a "d"
</cfscript>
</cfloop>
<!--- This is the regex to turn a written [code] block into an escaped HTML block --->
<cfscript>
contents = REReplaceNoCase(contents, "\[coded\](.*?)\[/coded\]", "<div id =""code_test"">Escaped Code: <br />\1</div>", "ALL");
</cfscript>
I hope this will help some people who have a hard time following #David Faber's help.
I think in ColdFusion you'll have to iterate over the string, searching for occurrences of "[code]". When you find such an occurrence, read in the string until you hit "[/code]". Take that string and do a replaceList to replace the characters. Use the removeChars and insert functions to replace the old string with the new. One problem with using regular expressions in this context is that the CF function REReplace can't replace a pattern with another pattern, only with a string.
I have a url.LoginID, and I'd like to remove it from the address bar when the user clicks on the link to login. It has to be a bookmark, it can't be a form submit.
Q: How do I remove ?LoginID from Index.cfm?LoginID=XYZ&AssignmentID=123
It's probably something along the lines of:
<cflocation url="#cgi.SCRIPT_NAME#?#cgi.QUERY_STRING#" addtoken="no">
Looks like you are on the right track.
If loginID is the only thing in the query string, you can simply cflocation to the destination page without the query string.
If there is other data in the query string, then you can do something like this:
<cfset q = reReplaceNoCase(cgi.query_string, "LOGINID=[^&]+&?", "")>
<cflocation url="#cgi.SCRIPT_NAME#?#q#">
This essentially removes loginid and everything until either the en of the string or the next URL variable.
As usual, there's already a UDF that someone has written available on CFLIB: queryStringDeleteVar
You can then do like so
<cflocation
url="#cgi.SCRIPT_NAME#?#queryStringDeleteVar("LoginID",cgi.QUERY_STRING)#"
addtoken="no"
>
CGI.QUERY_STRING is actually the default for the second arg, so this will work just as well
<cflocation
url="#cgi.SCRIPT_NAME#?#queryStringDeleteVar("LoginID")#"
addtoken="no"
>
Here's the code for queryStringDeleteVar:
<cfscript>
/**
* Deletes a var from a query string.
* Idea for multiple args from Michael Stephenson (michael.stephenson#adtran.com)
*
* #param variable A variable, or a list of variables, to delete from the query string.
* #param qs Query string to modify. Defaults to CGI.QUERY_STRING.
* #return Returns a string.
* #author Nathan Dintenfass (michael.stephenson#adtran.comnathan#changemedia.com)
* #version 1.1, February 24, 2002
*/
function queryStringDeleteVar(variable){
//var to hold the final string
var string = "";
//vars for use in the loop, so we don't have to evaluate lists and arrays more than once
var ii = 1;
var thisVar = "";
var thisIndex = "";
var array = "";
//if there is a second argument, use that as the query string, otherwise default to cgi.query_string
var qs = cgi.query_string;
if(arrayLen(arguments) GT 1)
qs = arguments[2];
//put the query string into an array for easier looping
array = listToArray(qs,"&");
//now, loop over the array and rebuild the string
for(ii = 1; ii lte arrayLen(array); ii = ii + 1){
thisIndex = array[ii];
thisVar = listFirst(thisIndex,"=");
//if this is the var, edit it to the value, otherwise, just append
if(not listFind(variable,thisVar))
string = listAppend(string,thisIndex,"&");
}
//return the string
return string;
}
</cfscript>
There are number of ways to do this, here is one way using a list loop to read through your existing parameters and check for the one you want to ignore:
<cfset newParams = "" />
<cfloop list="#cgi.query_string#" delimiters="&" index="i">
<cfif listFirst(i, "=") neq "loginID">
<cfset newParams = listAppend(newParams, i, "&") />
</cfif>
</cfloop>
<cflocation url="#cgi.script_name#?#newParams#" addtoken="no">
Hope that helps!
Suppose you don't really want to remove the ? to keep the URL valid, so simple regex should work:
QUERY_STRING = ReReplaceNoCase(cgi.QUERY_STRING, "LoginID=.+\&", "");
BTW, I'm not sure why do you keep LoginID in URL at all, it may be insecure approach. Using sessions sounds like a better idea.
Edit: Ben's regex is better, because my version is so simple that will "eat" all key=value pairs before last one.
Insert famous Zawinski two problem regex quote and solve differently:
<cfset copy = duplicate(url)>
<cfset structDelete(copy, "loginid")>
<cfset entries = []>
<cfloop collection="#copy#" item="key">
<cfset arrayAppend(entries, "#key#=#copy[key]#")>
</cfloop>
<cfoutput>#arrayToList(entries, "&")#</cfoutput>