bad code warning: grouping an array for nested a menu - coldfusion

Wow. I have been asked to add a 3rd level to a tree menu for a company. Below is what I found had created about 6 months ago. And while it works, there is no way I am going to add another cfloop hack to it. All my feelings tell me this just bad code and to redo.
I cannot change the databased nor have the results returned in anything else other than a query. now you know the way it has to be, what are my options here???
Each record in the DB has NO relationship, the code below just looks at the name of the widget and works out if it is a parent or not based on the :
examples of widgetName
1= Compliance:
2= Compliance: Document Library:
3= Compliance: Document Library: All Firms
so above says that widget 2 should be 2 levels down. look at the code you will see in a very bad way what has been done....
<cfoutput>
<ul id="root" class="menu">
<cfloop array="#rc.widgets.getPageRecords()#" index="local.widgets">
<cfif listLen(local.widgets.getwidgetName(),":") eq 1>
<li>
<a href='javascript:void(0);' childid = '#local.widgets.getwidgetID()#' class='cat_close category'> </a>
<a href='javascript:void(0);'>#local.widgets.getwidgetName()# - #listLen(local.widgets.getwidgetName(),":")#</li></a>
<cfset wordToFind = local.widgets.getwidgetName()>
<ul id='#local.widgets.getwidgetID()#'>
<cfloop array="#rc.widgets.getPageRecords()#" index="local.widgets">
<cfif listLen(local.widgets.getwidgetName(),":") eq 2 AND #find(wordToFind,local.widgets.getwidgetName())#>
<li>
<a href='javascript:void(0);' childid = '#local.widgets.getwidgetID()#' class='cat_close category'> </a>
<a href='javascript:void(0);'>#local.widgets.getwidgetName()# - #listLen(local.widgets.getwidgetName(),":")#</li></a>
</li>
</cfif>
</cfloop>
</ul>
</cfif>
</cfloop>
</ul>
</cfoutput>

I would use a recursive function to do this, that would give you unlimited future compatibility. I'm not sure what the data you are working with looks like, but it seems as though it should be possible.

Related

Coldfusion Multi-Select box without CFSelect

How would one go about building a multiselect box in Coldfusion without using CFForm or CFSelect?
This is to pull values from a DB so its not just a static select box it is dynamic.
This is my first time every trying to code in ColdFusion, I have always been a .Net person so this is a bit of a change for me.
The reason why I am needing this is because I've gotten hired into a department at work that uses Coldfusion but from what the Lead developer told me is they do not use CFForm and seeing as how CFSelect requires to be inside CFForm I need a different way of doing this.
Use plain old HTML, for example:
<cfquery name="qryUsers" datasource="datasourcename">
SELECT [User].[UserID], [User].[FirstName]
FROM [User]
</cfquery>
<cfoutput>
<form ...>
<select name="users" multiple="multiple">
<option value="">- please select -</option>
<cfloop query="qryUsers">
<option value="#UserID#">#FirstName#</option>
</cfloop>
</select>
</form>
</cfoutput>

CFWheels - DeObfuscate ID on query paging?

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).

Problems with setting up a CFIF

<cfoutput query="allOutcomes" maxrows="10">
<div class="galleryOutcome">
<cfset thisPhoto = uploads.listPhotobyOutcomeID(#outcomeID#)>
<h3>#lastname#, #firstname#</h3>
<cfloop query="thisPhoto" >
<cfif isdefined(filename)>
<div class="gallerythumb">
<img src="documents/uploads/PHOTOS/#filename#" alt="#filename#" border="0" width="200"/>
</div>
<cfelse>
<p> NO PHOTOS </p>
</cfif>
</cfloop>
</div><div class="clear"></div><br /><br />
<div onClick="javascript: thumbHide()" id="thumbexpand" style="display:none; left:670px;; height:0px; position:fixed; top:100px;">
</div>
</cfoutput>
I have been trying to make it so that #lastname# and #firstname# do not display if there are no photos associated to them. I tried doing a cfif that checks to see if the filename is defined, but it didn't seem to work. It returns an error saying:
"Parameter 1 of function IsDefined, which is now (filepath to image), must be a syntactically valid variable name. "
Any tips?
Thanks
First, IsDefined expects the name of a variable. When you omit quotes, or use # signs, you are passing in the variable value instead. The correct syntax is:
<cfif IsDefined("variableName")>
However, query columns always exist. So it will not yield the correct result anyway. Instead you should test if the FileExists. If needed, use expandPath to generate an absolute physical path
<cfif FileExists( ExpandPath("/path/to/images/"& thisPhoto.fileName) )>
it exists. do something ...
<cfelse>
no photo
</cfif>
Edit: As Busches mentioned in the comments, generally structKeyExists is preferred over IsDefined because its results are more precise. Some may argue it also has better performance. But in most cases, any differences are negligible. Increased accuracy is the more compelling reason IMO.
<cfif structKeyExists( scopeOrStruct, "variableName")>
isDefined takes the name of the variable as a string, not the variable itself. change
<cfif isdefined(filename)>
to
<cfif isdefined("filename")>
use <cfif len(filename)>
I guess filename is one of the columns? In a query object, null is represented with empty string, so len() would work.

Display a link only if no link is present using ColdFusion?

So the announcement functionality on our site displays a "read more" link by default using the following code (in part):
<cfif announcement.recordCount gt 0>
<cfloop query="announcement">
<cfoutput>
<td colspan="2"><span class="left">#teaser_text# Read more »
</cfoutput>
</cfloop>
(Note, there is a cfquery statement prior to that, which I excluded for brevity in the code)
What I'm trying to do here is get the "Read More" link to show after the #teaser_text# only if no link is contained within #teaser_text#, so that I can manually add links in if needed and remove the automatically generated link.
Any thoughts on a cfif statement that would do this?
Thanks.
EDIT: To clarify, I want to remove "Read more" if ANY link is found within teaser_text.
To only show the read more link if no hyperlink is found within teaser_text, this check is likely to be good enough:
<cfif NOT refindNoCase('<a\s[^>]*?\bhref\s*=',teaser_text) >
Read more »
</cfif>
If you want to check for URLs, not for hyperlinks, you need to get more fancy.
You also need to remember that this is treating teaser_text as text (not as HTML), so commenting out a link will not prevent it from being found (if that matters, you need to investigate HTML DOM parsers; and there aren't any for CF so you'd need to look at the Java ones).
This should work:
<cfif findnocase('http://', teaser_text) eq 0>
Read more »
</cfif>
If you are placing the links in manually just change the first parameter of the findnocase() function [i.e. htp/https] or use a regex to figure out if it is a url [via: refindnocse() ]
-sean
Something like this should do what you want
<cfif announcement.recordCount gt 0>
<cfloop query="announcement">
<cfif findnocase("href",anouncement.teaser_text) >
#anouncement.teaser_text#
<cfelse>
<a href="/announcements/?id=#announcement.id#" > #anouncement.teaser_text# Read more </a>
</cfif>
</cfloop>
</cfif>

use a startdate on a <cfinput type= "datefield"> in ColdFusion

I am using the <cfinput type="datefield"> and I need to limit the dates that can be chosen. I cannot allow a date that can be selected in the past. Any help here?
Thanks!
Well you have two options validating it strictly server side or additionally adding a client side javascript check.
For the serverside, your cfm processing page could use the DateCompare function like so:
<cfif DateCompare(Date1,Date2) EQUAL -1>
<!--- Date 1 < Date 2 --->
<cfelseif DateCompare(Date1,Date2) EQUAL 0>
<!--- Date 1 = Date 2 --->
<cfelseif DateCompare(Date1,Date2) EQUAL 1>
<!--- Date 1 > Date 2 --->
</cfif>
Documentation and sample code is available for this function at:
http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=functions_m-r_11.html
Another post suggested using the range functionality, however I was not able to get this to work because the Adobe documentation said it was only supported on type numeric.
Alternatively you can use javascript to check if the date is within range live on the page:
<html>
<head>
<script type="text/javascript">
function withinRange(submittedDate) {
var start = Date.parse("08/10/2009");
var end = Date.parse("09/11/2009");
var userDate = Date.parse(document.getElementById('leaveDate').value);
var previousDate = document.getElementById('calendarWorkaround').value;
if (!isNaN(userDate) && ( previousDate != userDate)) {
document.getElementById('calendarWorkaround').value = userDate;
if (start <= userDate && end >= userDate) {
alert("Your date is correct!");
} else {
alert("Date is incorrect.");
}
}
}
</script>
</head>
<body>
<cfform name="testing" id="vacationForm"action="testprocess">
<cfinput name="date" type="datefield" id="leaveDate">
<input type="hidden" name="workaround" id="calendarWorkaround" value="-1">
</cfform>
<!--- To get around onChange bug with cfinput type datefield --->
<cfajaxproxy bind="javaScript:withinRange({leaveDate})">
</body>
</html>
Now I would use the onChange attribute for cfinput on the datefield, however there is a bug that it does not call the javascript function that seems to be documented on a couple of other pages online. The workaround that I used was the cfajaxproxy call with the bind attribute. If you are using cfeclipse, it will give you an error that the syntax is not correct however it worked fine for me.
The hidden input type is necessary because with the cfajaxproxy tag, everytime you click the calendar icon it will call the javascript function which is not what we want. So I store the last value chosen by the user (or -1 on page load) so that the javascript to check if it is within range is only executed if the value changes. It is a bit of a hack, however the implementation of <cfinput type="datefield"> does not work as documented.
You can also modify this example so that coldfusion dynamical generates the start date and end date so it is not hard coded.
Just remember that whichever way you choose, you should always check the value server side just in case the user has javascript disabled.
Try this:
Three datefields.
One is hidden by the hieght = 0. It is your starting point. I used today plus one so the user can select today in the Date Requested field.
<cfformgroup type="horizontal" height="0">
<cfinput type="datefield" name="firstAvailableDate" value="#dateAdd('d', +1,now())#"
</cfformgroup>
The requested date will be only today or before.
<cfformgroup type="horizontal">
<cfinput type="datefield" name="request_date" id="request_date" mask="mm/dd/yyyy" width="100" label=”Date Requested” onFocus="request_date.disabledRanges=[{rangeStart:firstAvailableDate.selectedDate}];" />
The Due Date can only be after the requested date selected.
<cfinput type="datefield" name="datedue" id="datedue" mask="mm/dd/yyyy" width="100" label=”Due Date” onFocus="datedue.disabledRanges=[{rangeEnd:request_date.selectedDate}];" />
</cfformgroup type="horizontal" >
AFAIK, there is no easy way to prevent the client from picking a day in the past. We have fields like that on a few of our forms that we check against the current date on the server side, though. Just remember to generate the date without the time for comparison, or you end up with [today's date] < now().
At first, I thought <cfcalendar> would help, but seems like the startrage and endrange is for disabled range.
I guess if you Really want to limit the range, use something else like jQuery UI Datepicker
Through experimentation, I was able to customize the supporting javascript files to restrict the selectable dates. However the jQuery option sounds good as well.
I just added 100 years on to the endrange attribute for the cfcalendar component, thinking no one will sit and click through that far to beat your validation :
<cfset startRange = #dateformat(now(), 'dd/mm/yyyy')# >
<cfset endRange = dateadd("yyyy", 100, #Now()#)>
<cfset selectedDate = dateadd("d", -1, #Now()#)>
<cfcalendar name="start_period" mask="dd/mm/yyyy" selecteddate="#selectedDate#" startRange="#startRange#" endrange="#endRange#">
Simon.
You can't make a datefield limit the dates that can be chosen. CFINPUT doesn't have any attribute to define this type of rule. You have to go for Jquery datepicker. If you want to use cfinput, you can work around that.
<script language="JavaScript">
$(function() {
$( "#date" ).datepicker({
changeMonth: true,
changeYear: true,
minDate: new Date(1939, 10 - 1, 25),
maxDate: '-1D'
});
});
</script>
<cfinput name="date" type="text" size="30" id="date" >
You should use server side validation as well, but I believe you can use the "range" attribute of cfinput to limit the valid dates.