<cfparam name="Mode" default="">
<cfif (Mode is "Add") or (Mode is "Edit")>
<cfinclude template="sequence.cfm">
<cfif Mode is "Add">
<cfinclude template="add.cfm">
<cfinclude template="input.cfm">
<cfabort>
<cfelse>
<cfinclude template="edit.cfm">
<!--- (Stay here; show the list) --->
</cfif>
<cfelseif Mode is "Delete">
<form action="delete.cfm" method="post">
<input type="hidden" name="Item"
value=<cfoutput>"#urlDecode(url.Item)#"</cfoutput>>
Are you sure you want to delete
<cfoutput>#urlDecode(url.Item)#</cfoutput>?
<input type="submit" name="Mode" value="Yes">
<input type="submit" name="Mode" value="No">
</form>
<cfabort>
</cfif>
<cfinclude template="list.cfm">
<cfif Mode is "Yes"> <cfquery name="deleteItem" datasource="context"> delete * from OrdItem where Item = '#form.Item#' </cfquery> </cfif> <cfinclude template="work.cfm">
You need an item query to fill the form. If you're editing, you want to bring back the data for the row you want to edit. If you're adding records, you want an item query OF THE SAME NAME to bring back nothing; so, set an impossible condition in the where clause and add an empty row.
<!--- Set defaults ---> <cfparam Name="Mode" default=""> <cfinclude template="ItemListQuery.cfm"> <!--- Query to fill the form ---> <cfif Mode is "Edit"> <cfquery name="ItemGet" datasource="context"> select * from OrdItem where Item = '#Item#' </cfquery> <cfelse> <!--- Mode is assumed to be "Add" ---> <cfquery name="ItemGet" datasource="context"> select * from OrdItem where 1 = 0 </cfquery> <cfset dummy=queryAddRow(ItemGet)> </cfif>
For the form itself, consider that you'll need a text field for the item name and a select control for the sequence. The select box should let you specify which item this item must follow. To make this item the first item, it should follow none. However, "none" looks very odd in a select box; "top" is less correct but more acceptable. Select "top" to move this item to the top.
For each item in the list, see if the value the item had when you came here matches the item for which you're building an option in the list. You won't get a match on Add - "top" will be the default for Add - but for Edit, one of these items should match and should therefore have the attribute "selected" in the option tag.
<form name="Change" action="work.cfm" method="post"> Item: <input name="Item" type="text" value=<cfoutput>"#ItemGet.Item#"</cfoutput>><br> Immediately follow... <select name="MySpot"> <option value="0">top <cfoutput query=ItemList> <cfif ItemList.Item is not ItemGet.Item> <cfset chosen=""> <cfelse> <cfset chosen="selected"> </cfif> <option value="#ItemList.CurrentRow#" #chosen#>#Item# </cfoutput> </select>
Time to set controls and hidden values. To remember what the item was before you messed with it, store it in OldItem. If you're adding, the first submit button will say "Add". Otherwise, it will say "Edit". The second submit button says "List" and corresponds to a "cancel" operation. Items contains the entire list of items, separated by ^. Numbers contains the entire list of sequence numbers (ListOrder numbers in the table), separated by ^. Close the form.
<input name="OldItem" type="hidden" value=<cfoutput>"#ItemGet.Item#"</cfoutput>> <input name="Mode" type="submit" value="<cfoutput>#Mode#"</cfoutput>> <input name="Mode" type="submit" value="List"> <input name="Items" type="hidden" value= <cfoutput>"#valueList(ItemList.Item, '^')#"</cfoutput>> <input name="Numbers" type="hidden" value= <cfoutput>"#valueList(ItemList.ListOrder, '^')#"</cfoutput>> </form>
<cfquery name="ItemList" datasource="context"> select * from OrdItem order by ListOrder </cfquery>
Otherwise, determine Item and ListOrder for the items surrounding the position where you want this item to wind up. Usually, you can infer the previous item from the select box's current position. If you're at the top, use 0 and "_top" as the previous item. Usually, you can infer the next item by adding one to the select box's current position. If you're at the bottom, use 1 and "_bottom" as the next item. (ListOrder numbers are decimals between zero and one so that you can almost always squeeze in between two of them.) You won't actually use the names that begin with underscores; they just help you remember what's going on.
<cfloop from="1" to="1" index="dummy"> <cfparam name="MySpot" default=""> <cfif (MySpot gt 0) and (listGetAt(Items,MySpot,"^") is form.Item)> <cfset NewListOrd=listGetAt(Numbers,MySpot,"^")> <cfbreak> </cfif> <cfif MySpot ge 1> <cfset PrevItem=listGetAt(Items,MySpot,"^")> <cfset PrevNr=listGetAt(Numbers,MySpot,"^")> <cfelse> <cfset PrevItem="_top"> <cfset PrevNr=0> </cfif> <cfif MySpot+1 le listLen(Items,"^")> <cfset NextItem=listGetAt(Items,MySpot+1,"^")> <cfset NextNr=listGetAt(Numbers,MySpot+1,"^")> <cfelse> <cfset NextItem="_bottom"> <cfset NextNr=1> </cfif>
Once you've determined which neighbors should bracket the item, take the difference of their ListOrder values to see how far apart they are. If you have to squeeze between items that are very close together, you might eventually exceed the accuracy of the database. To avoid this, if the item are very close together, include ordfix.cfm to do a massive query to spread all the items out before continuing. Ordfix.cfm will also find out the neighbors' new ListOrder values and their difference so the rest of the code won't have to change. If ordfix.cfm is called even once in six months, I'll be surprised; but it's nice to know you can handle the situation.
Split the difference, add it to the ListOrder value of the previous item, and assign it as the new ListOrder value for the item you just added or edited. Close the loop.
<cfset Diff=NextNr-PrevNr> <cfif Diff lt 0.0001> <cfinclude template="ordfix.cfm"> </cfif> <cfset NewListOrd=PrevNr+(Diff/2)> </cfloop>
<cfquery name="ItemGet" datasource="context">
select * from OrdItem
where Item = '#form.Item#'
</cfquery>
<cfif ItemGet.recordCount>
The item already exists in the database;
no action was taken.
<cfelse>
<cfquery name="ItemAdd" datasource="context">
insert into OrdItem
(Item, ListOrder)
values
('#form.Item#', #NewListOrd#)
</cfquery>
<cfoutput>#form.Item#</cfoutput>
has been sent to the database
</cfif>
<cfif form.OldItem is not form.Item>
<cfquery name="ItemGet" datasource="context">
select * from OrdItem
where Item = '#form.Item#'
</cfquery>
<cfif ItemGet.recordCount>
The item already exists in the database;
no action was taken.
Press your back button to continue.
<cfabort>
</cfif>
</cfif>
<cfquery name="Change" datasource="context">
update OrdItem set
Item = '#form.Item#',
ListOrder = #NewListOrd#
where Item = '#form.OldItem#'
</cfquery>
<cfinclude template="ItemListQuery"> <cfset Spread=1/(itemList.recordcount+1)> <cfset Row=0> <cfloop query="itemList"> <cfset Row=Row+1> <cfset NewSeq=Spread*Row> <cfquery name="ordSpread" datasource="context"> update OrdItem set ListOrder = #NewSeq# where Item = '#itemList.Item#' </cfquery> </cfloop>
Now you have to cope with unfinished business. Spreading out the items changed their ListOrder, and you need those values for the previous and next items in order to give the item to be moved a new ListOrder. Therefore, if the previous item's ListOrder isn't zero - your item won't be at the top, find its new value. If the next item's ListOrder isn't one - your item won't be at the bottom - find its new value. Get their new difference, and let the code in sequence.cfm pick up where it left off.
<cfif PrevNr is not 0> <cfquery name="getOne" datasource="context"> select ListOrder from OrdItem where Item = '#PrevItem#' </cfquery> <cfset PrevNr = getOne.ListOrder> </cfif> <cfif NextNr is not 1> <cfquery name="getOne" datasource="context"> select ListOrder from OrdItem where Item = '#NextItem#' </cfquery> <cfset NextNr = getOne.ListOrder> </cfif> <cfset Diff=NextNr-PrevNr>
<!--- Fill and display the list and controls ---> <cfinclude template="ItemListQuery.cfm"> <a href="input.cfm?Mode=Add">Add</a> <table border="1"> <tr><td>Edit</td><td>Item</td><td>Delete</td></tr> <cfoutput query="ItemList"> <tr><td><a href= "input.cfm?Mode=Edit&Item=#urlEncodedFormat(Item)#"> Edit</a></td> <td>#Item#</td> <td><a href= "work.cfm?Mode=Delete&Item=#urlEncodedFormat(Item)#"> Delete</a></td></tr> </cfoutput> </table>