How to set order of new sharepoint list columns programmatically [duplicate] - sharepoint

I'm adding in two new fields into an already existing Sharepoint list programmatically through a feature. The fields are being added successfully but I have been unable to adjust the column order.
This task is done simply through the UI by going to List Settings and then Column Ordering, but I have been unable to achieve the task programmatically.
Through some research I've seen that you can use the SPContentType of the form to change the ordering of the FieldLinks (as follows):
SPList list = web.Lists["Example List"];
if (list.ContentTypes.Count > 0) {
SPContentType ct = list.ContentTypes[0];
string[] names = {"Example_x0020_One", "Example_x0020_Two", "Example_x0020_Three"};
ct.FieldLinks.Reorder(names);
ct.Update();
}
In this example, I the list would already have "Example One" and "Example Three" columns, and I would add "Example Two" later and then try to order them.
However this approach did not work for me, so if anyone has input on it, that would be appreciated.
The next item I saw is manually changing the SchemaXml of the list to have the proper order of the fields, but I wanted to see if this was the best method.
Any input would be appreciated, thank you for your help.

I took a look at the source of the Column ordering page (formEdt.aspx), it looks like they use web services, not the object model:
function DoBuildAndSubmit()
{
var numFound, currentSelect, selectValue;
var form = document.forms.aspnetForm;
var numFields = form["numSelects"].value;
var xml = "<Fields>";
numFound = 0;
while(numFound < numFields)
{
for(x = 0; x < numFields; x++)
{
currentSelect = form["FormPosition" + x];
if(currentSelect.selectedIndex == numFound)
{
selectValue = currentSelect.options[numFound].value;
xml = xml + "<Field Name=\"" + selectValue + "\"/>" + "\n";
numFound++;
}
}
}
for(x = numFields ; x < 67; x++)
xml = xml + "<Field Name=\"" + form["FormPosition" + x].value + "\"/>" + "\n";
xml = xml + "</Fields>";
document.frmLayoutSubmit["ReorderedFields"].value=xml;
document.frmLayoutSubmit.action = "http://local/_vti_bin/owssvr.dll?CS=65001";
document.frmLayoutSubmit.submit();
}
Now, it might be possible to do through the object model, but I don't have a good feeling about it when the UI is punting.

Here's a powershell version:
# Moves "FieldToBeMoved" after "Description" field
$list = $web.Lists["Documents"]
$ct = $list.ContentTypes[0] # Or find the desired CT
$newOrder = #()
foreach ($field in $ct.Fields)
{
if ($field.StaticName -ne "FieldToBeMoved")
{
$newOrder += $field.StaticName
}
if ($field.StaticName -eq "Description")
{
$newOrder += "FieldToBeMoved"
}
}
$ct.FieldLinks.Reorder($newOrder)
$ct.Update();

I used the code from your answer, except I programmatically examined the content types and fields for the list I wanted to re-order.
//Step 1 (optional): List out the content types and fields for your list to see what is in the list
SPList list = web.Lists[strListName];
string strRet="";
foreach (SPContentType spct in list.ContentTypes)
{
strRet += "<strong>Content Type: </strong>" + spct.Name + ", <strong>Fields</strong>: <br />";
foreach (SPField field in spct.Fields)
{
if (strFieldInfo != "")
{
strFieldInfo += ", ";
}
strFieldInfo += "\"" + field.StaticName + "\"";
}
strRet += strFieldInfo + "<br />-----<br />";
}
//Output the results
lblOutput.Text = strRet;
Now, you'll have an idea of how many content types your list has and what fields are in the list.
By default, if content type management is not enabled, you'll have one content type that has all the fields.
Sample output from the above code:
Content Type: Event, Fields:
"ContentType", "Title", "Location", "EventDate", "EndDate", "Description", "fAllDayEvent", "fRecurrence", "WorkspaceLink", "EventType", "UID", "RecurrenceID", "EventCanceled", "Duration", "RecurrenceData", "TimeZone", "XMLTZone", "MasterSeriesItemID", "Workspace", "Course", "CourseLocation"
Next Step 2 is to change the order of the content type. You can cut and paste from the output from step 1, re-order it, and add "{" and "};" around it to create the string array for the ordering you want.
if (list.ContentTypes.Count > 0)
{
SPContentType ct = list.ContentTypes[0]; //Specify the content type here, if you have more than one content type in your list.
string[] fieldnames = { "ContentType", "Title", "Course", "CourseLocation", "EventDate", "EndDate", "Description", "fAllDayEvent", "fRecurrence", "WorkspaceLink", "EventType", "UID", "RecurrenceID", "EventCanceled", "Duration", "RecurrenceData", "TimeZone", "XMLTZone", "MasterSeriesItemID", "Workspace", "Location"};
ct.FieldLinks.Reorder(fieldnames);
web.AllowUnsafeUpdates = true;
ct.Update(true);
web.AllowUnsafeUpdates = false;
}

Related

Using add or push to add items to an array in groovy

I am getting the following error while push/add items to an array in groovy.
$groovy main.groovy
Caught: groovy.lang.MissingMethodException: No signature of method: [LProgressNotes;.push() is applicable for argument types: (ProgressNotes) values: [ProgressNotes#d35dea7]
Possible solutions: sum(), plus(java.util.Collection), plus([Ljava.lang.Object;), plus(java.lang.Object), use([Ljava.lang.Object;), plus(java.lang.Iterable)
groovy.lang.MissingMethodException: No signature of method: [LProgressNotes;.push() is applicable for argument types: (ProgressNotes) values: [ProgressNotes#d35dea7]
Possible solutions: sum(), plus(java.util.Collection), plus([Ljava.lang.Object;), plus(java.lang.Object), use([Ljava.lang.Object;), plus(java.lang.Iterable)
at main$_buildOutNotes_closure2.doCall(main.groovy:82)
at main.buildOutNotes(main.groovy:75)
at main$buildOutNotes.callCurrent(Unknown Source)
at main.run(main.groovy:64)
Here is the function:
def buildOutNotes(incomingNotes, systemNotes) {
ProgressNotes[] outNotes = systemNotes;
//iterate incoming chares
incomingNotes.each { incoming ->
//split the note further
def iNote = splitIncoming(incoming);
//check that the incoming note is in the system note
def foundNotes = systemNotes.findAll { it.ProgressNote == iNote.ProgressNote }
if(!foundNotes){
//add the incoming note to the outNote
outNotes.push(iNote);
}
}
return outNotes;
}
Here are the articles that show push and add use
https://mrhaki.blogspot.com/2015/01/groovy-goodness-pop-and-push-items-in.html
def list = ['Groovy', 'is', 'great!']
list.push('rocks!')
http://docs.groovy-lang.org/next/html/documentation/working-with-collections.html
def list = [5, 6, 7, 8]
emptyList.add(5)
I am building the example code on https://www.tutorialspoint.com/execute_groovy_online.php.
You can view the example here
http://tpcg.io/NGw4szCv
Here is the full code as well:
//package com.javacodegeeks.groovy.date;
//import static java.util.Calendar.*;
//import groovy.json.*;
//import java.util.Properties;
//import java.util.List;
//progress notes object
class ProgressNotes {
def ActionDate
String ActionBy
String Status
String ProgressNote
ProgressNotes(inActionDate, inActionBy, inStatus, inNote){
this.ActionDate = inActionDate
this.ActionBy = inActionBy
this.Status = inStatus
this.ProgressNote = inNote
}
}
//delimiter
String delimiter = "##";
//out notes
ProgressNotes[] outNotes;
//date patterns
def dateInSystemPattern = "yyyy-MM-dd HH:mm:ss";
def dateIncomingPattern = "MM/dd/yyyy hh:mm ";
/************** SAMPLE DATA START ****************/
//incoming note string
String incomingNote = "2019-12-15T01:29:44 User1: December 13 went to pickup the toilet at the wholesaler " +
"then went to site then remove and replace the toilet then found out that there is a " +
"fruit inside the toilet then clean up the site and silicone around the toilet then " +
"throw the old toilet at dumpster." + delimiter +
"2019-12-13T10:43:05 User2: applied 3 bottles of urinal treatment. let sit for an " +
"hour. augered out urinal main. draining excellent. tried augering toilet. object stuck in " +
"toilet. will not come out. Don will replace." + delimiter +
"2019-12-13T09:18:51 user3: PO 508758 - unclog Washroom " +
"Details: " +
"Unclog toilet bowl and urinal in. Room 116.";
//in system notes
ProgressNotes[] systemNotes = [
["2012-01-26T14:52:50", "User1", "DISPATCHED", "reassign to Space Planning to confirm space availability"],
["2012-02-01T12:23:05", "User2", "DISPATCHED", "spoke to requestor and she has a few relocations and POD requirements."],
["2012-02-01T12:23:45", "User3", "DISPATCHED", "Contacted Customer for clarification spreadsheet is forthcoming for this request."],
["2012-02-03T18:45:00", "User1", "DISPATCHED", "Extending date to allow for clean-up of backlog."]
];
/************** SPLIT incomingNote ****************/
def incomingNotes = [];
if (incomingNote != ""){
incomingNotes = incomingNote.split(delimiter);
}
/************** PICK NOTES ****************/
if (!incomingNotes){
//No incoming notes push the system notes out
outNotes = systemNotes;
}
else{
//check and build the outnotes
outNotes = buildOutNotes(incomingNotes, systemNotes);
}
println("OUTNOTES Length: " + outNotes.length)
println(" ");
/************** HELPER METHODS ****************/
def buildOutNotes(incomingNotes, systemNotes) {
ProgressNotes[] outNotes = systemNotes;
//iterate incoming chares
incomingNotes.each { incoming ->
//split the note further
def iNote = splitIncoming(incoming);
//check that the incoming note is in the system note
def foundNotes = systemNotes.findAll { it.ProgressNote == iNote.ProgressNote }
if(!foundNotes){
//add the incoming note to the outNote
outNotes.push(iNote);
}
}
return outNotes;
}
def splitIncoming(incoming){
//date time characters
int dateTimeChars = 20;
def dateAndTimePart = incoming.substring(0,dateTimeChars).trim();
String remainingNote = incoming.substring(dateTimeChars);
String userPart = "";
String notePart = "";
def remainingNotes = remainingNote.split(":");
if(remainingNotes){
userPart = remainingNotes.getAt(0);
notePart = incoming.substring(dateTimeChars+userPart.length()+1).trim();
}
//build the object
def outNote = new ProgressNotes(dateAndTimePart, userPart, "", notePart);
return outNote;
}
You use an array in your code (ProgressNotes[]), not a list (List<ProgressNotes>). Any of the mentioned methods (add and push) does not exist for Java (and thus Groovy) arrays. An array is fixed size, so once initialized, you can't add any new elements to it - you can only replace existing elements. If you try to add a new element to the array, you will get IndexOutOfBoundsException. Just look at this simple example:
String[] list = ["foo", "bar"]
assert list[0] == "foo"
assert list[1] == "bar"
try {
list[2] = "new"
} catch (IndexOutOfBoundsException e) {
println "Caught!"
}
list[1] = "abc"
println list
Output:
Caught!
[foo, abc]
If you want to use List.add() or List.push() (or event groovier leftShift like [] << "elem") you need to use a list instead of an array. Arrays are a good choice if you know the size of the collection is fixed.
//out notes
List<ProgressNotes> outNotes;

Xpages, Compute combo box value

I have a combo box its values are 10, 20, 30, 40, 50 when I create a document and display it in a view the number is appeared but I want to display its labels (labels are text) not numbers.
Can someone please help me find out how to solve this issue.
Thanks
I tend to add values of a given checkbox into a Resource Bundle, and then access that Resource Bundle both from the xp:selectItems and the xp:viewColumn values.
First, I'll create a Resource Bundle which I set as "label", with the following values:
itemsList_status=Draft|0,Published|1,Archived|-1
itemsList_delimiter=,
Here's an example of the xp:selectItems for an xp:combobox:
<xp:selectItems>
<xp:this.value>
<![CDATA[#{javascript:return getSelectItems('status');}]
</xp:this.value>
</xp:selectItems>
Here's my getSelectItems() SSJS function:
var getSelectItems : function(key) {
try {
var returnObj = new Array();
var items = null;
switch(key) {
case "status" :
items = #Text(label.itemsList_status).split(label.itemsList_delimiter);
break
}
for( var n=0, l=items.length; n < l; n++) {
returnObj.push(items[n]);
}
return returnObj;
} catch(e) {
print(database.getTitle() + " SSJS Error for getSelectItems()");
print(e.toString());
}
}
Run this function, and it'll return 3 options that read Draft, Published, and Archived while actually storing 0, 1, and -1 respectively. The problem that you're running into - and what using this abstracted method of storing your labels:values in a Resource Bundle + SSJS to address - is when you go to read the value but actually want to display the label...
Here's my getSelectedValue() function:
var getSelectedValue : function(key, thisValue) {
try {
var returnObj = new Array();
var items = null;
switch(key) {
case "status" :
items = #Text(label.itemsList_status).split(label.itemsList_delimiter);
break
}
var s = "";
var l = "";
for( var n=0, i=items.length; n < i; n++) {
if(items[n].indexOf("|") == -1) {
s = items[n];
l = items[n];
} else {
s = items[n].split("|").pop();
l = items[n].split("|").shift();
}
if(thisValue == s) {
return l;
}
}
return thisValue;
} catch(e) {
print(database.getTitle() + " SSJS Error for getSelectedValue()");
print(e.toString());
}
}
Now you'd pass the current value and the desired label:value Resource Bundle item handle, and this function spits out the correlating label. Here is how I use it in an xp:viewColumn:
<xp:viewColumn
id="viewColumn_status">
<xp:this.value><![CDATA[#{javascript:return ""}]]></xp:this.value>
<xp:text
value="#{javascript: return
getSelectedValue('status',thisEntry.getDocument().getItemValueString('status'))}" />
<xp:viewColumnHeader
value="#{label.viewColumnHeader_posts_status}"
id="viewColumnHeader_status">
</xp:viewColumnHeader>
</xp:viewColumn>
... and that's about it - the Resource Bundle entry to "itemList_status" acts as the authoritative master for the label:value pairs, so you just check that for the input building and deconstruct it for the labeling of stored values.
Hope this helps!
Using aliases is analog to the usage in a normal form.
Use a formula item for the values:
return ["- Please select -|", "ten|10", "twenty|20", "thirty|30"];
But keep in mind that the values that is stored is always TEXT and not a number.

How to pass multiple list types as a parameter using the same method variable

I'm trying to pass multiple list types as a parameter using the same method variable and then loop through the types based on which type as been past. I tried using a generic method but it's not working. Below are pseudo/example codes. The List SAS_F_DISAGG_F and List SAS_C_DISAGG_C are SQL/Entity, and the List DisaggReportGroups is a class object. I'm trying to pass the entity lists.
protected void GetReportGroup()
{
DisaggReportGroups rptGroup = new DisaggReportGroups();
List<DisaggReportGroups> disagreportGroup = new List<DisaggReportGroups>();
disagreportGroup.Add(rptGroup);
DisaggregatedReportData disagReportData = new DisaggregatedReportData();
foreach (var reportGroup in disagreportGroup)
{
if (reportGroup.FuturesOnly == "Futures Only, " & reportGroup.Agriculture == "Agriculture")
{
List<SAS_F_DISAGG_F> futONlyDisagReportData = disagReportData.GetFuturesOnlyReportData(reportGroup.Agriculture).ToList();
CreateLongFormatReport<List<SAS_F_DISAGG_F>>(reportGroup.AgricultureFilenameFOLF, reportGroup.FuturesOnly, reportGroup.Agriculture, futONlyDisagReportData);
}
else if (reportGroup.FOCombined == "Futures and Options Combined, " & reportGroup.Agriculture == "Agriculture")
{
List<SAS_C_DISAGG_C> combinedDisagReportData = disagReportData.GetFOCombinedReportData(reportGroup.Agriculture).ToList();
CreateLongFormatReport<List<SAS_C_DISAGG_C>>(reportGroup.AgricultureFilenameFOCombinedLF, reportGroup.FOCombined, reportGroup.Agriculture, combinedDisagReportData);
}
}
}
protected void CreateFormatReport<T>(string filename, string disagCategory, string commSubGp, List<T> reportData)
{
using (FileStream fileStream = new FileStream(Server.MapPath(#"~/Includes/") + filename, FileMode.Create))
{
using (StreamWriter writer = new StreamWriter(fileStream))
{
foreach (var value in reportData)
{
string FuturesOnly = "Futures Only, ";
string FOCombined = "Futures and Options Combined, ";
string reportCategory = "";
if (disagCategory == FuturesOnly)
{
reportCategory = FuturesOnly;
}
else if (disagCategory == FOCombined)
{
reportCategory = FOCombined;
}
string row01 = String.Format("{0, -10}{1, 29}{2, 8}", value.MKTTITL.PadRight(120), "Code -", value.Conmkt);
string row02 = String.Format("{0, -10}{1, 7}{2, 14}", "Blah Blah - ", reportCategory, value.DAT1TITL);
string row03 = String.Format("{0, 3}{1, 3}{2, 8:0,0}{3, 3}{4, 8:0,0}{5, 11:0,0}{6, 11:0,0}{7, 11:0,0}{8, 11:0,0}{9, 13:0,0}{10, 11:0,0}{11, 11:0,0}{12, 13:0,0}{13, 10:0,0}{14, 9:0,0}{15, 3}{16, 8:0,0}{17, 10:0,0}", "All",
colon, value.TA01, colon, value.TA02, value.TA03, value.TA04, value.TA05, value.TA06, value.TA07, value.TA08, value.TA09, value.TA10, value.TA11, value.TA12, colon, value.TA15, value.TA16);
string row04 = String.Format("{0, 3}{1, 3}{2, 8:0,0}{3, 3}{4, 8:0,0}{5, 11:0,0}{6, 11:0,0}{7, 11:0,0}{8, 11:0.##}{9, 13:0,0}{10, 11:0,0}{11, 11:0,0}{12, 13:0,0}{13, 10:0,0}{14, 9:0,0}{15, 3}{16, 8:0,0}{17, 10:0,0}", "Old",
colon, value.TO01, colon, value.TO02, value.TO03, value.TO04, value.TO05, value.TO06, value.TO07, value.TO08, value.TO09, value.TO10, value.TO11, value.TO12, colon, value.TO15, value.TO16);
writer.Write(row01);
writer.WriteLine(row02);
writer.WriteLine(row03);
writer.WriteLine(row04);
} //end foreach
writer.Close();
} //end of stream writer
}
}
Thanks for your help.
I managed to solve this problem myself so I'm posting my solution for others that may need the same type of help. The solution is to use Reflection within the foreach iteration.
foreach (var value in ReportData)
{
//Reflection can be used
string TA01 = value.GetType().GetProperty("TA01").GetValue(value).ToString();
//...
//...
//do more stuff/coding...
}
Then in the String.Format change "value.TA01" to "TA01". Do the same for all other variables.
Hope this help.

Getting ViewPanel Headers programmatically

This code does provide me the values I am wanting in my comboBox, but I am wondering if there is a way to get the viewColumn ID, viewColumnHeader ID, and number of columns in the viewPanel programmatically. The viewPanel is using a JDBCQuery as the datasource.
var itemList:java.util.Vector = new java.util.Vector;
var colID = "viewColumn"; //default id assigned
var colHeaderID = "viewColumnHeader"; //default id assigned
var end = 10; //max # of viewPanel columns
itemList.add("Select Column");
for(x=1;x<end;x++) {
try {
if(getComponent(colID + x) == null) {
throw ("Only " + (x-1) + " columns in ViewPanel");
x=end;
} else {
var disColID = getComponent(colID + x).getColumnName();
}
var disColHeaderID = getComponent(colHeaderID + x).getValue();
itemList.add(disColHeaderID + "|" + disColID);
} catch (e) {
dBar.info(e.toString());
}
}
itemList
The current way is obviously restricted to only 9 columns and ensuring the viewColumn ID and viewColumnHeader ID have a specific naming structure which would be nice to get away from.
All the columns of a view panel can be accessed by getting the child components of view panel using viewPanelObj.getChildren(). The column headers are a part of view column and after you get the handle of view column you can access the header using viewColumnObj.getHeader().
So a sample SSJS code to access all the view columns and view headers would look something like this:
var viewPnl:com.ibm.xsp.component.xp.XspViewPanel = getComponent("viewPanel1");
var list:java.util.List = viewPnl.getChildren();
for (var i=0 ; i<list.size() ; i++) {
var viewCol:com.ibm.xsp.component.xp.XspViewColumn = list.get(i);
var viewHdr:com.ibm.xsp.component.xp.XspViewColumnHeader = viewCol.getHeader();
// Perform required operations on objects of viewCol & viewHdr
}

Building string programatically and dealing with trailing or leading boolean operators

I am using Lucene in an application. As such I have a form that lets users build a query by selecting what they want to search from dropdowns.
Once a user submits, I build the query and it comes down to something like this:
var formedQuery= string.Empty;
foreach(var field in fields)
{
if (field.name != 'condition so you never know which field from fields will be 1st')
formedQuery += " AND" + field.name + ":" field.value;
}
Now the problem with this is that the statement will begin with ' AND'
Now I usually finish with:
formedQuery = formedQuery.Substring(4) //Trim the first 4 characters
Would fellow programmers usually prefer to do:
var formedQuery= string.Empty;
var i = false;
foreach(var field in fields)
{
if (false &&
field.name != 'condition so you never know which field from fields will be 1st')
{
formedQuery += " AND" + field.name + ":" field.value;
i = true;
}
else
formedQuery += " " + field.name + ":" field.value;
}
Is there another technique people like to use for this sort of thing I am not thinking of?
I prefer the former.
There are two other solutions I use, depending a little on the language. The first is similar to your second one, but just changes the "first-field" check.
var formedQuery = string.Empty;
var and = string.Empty;
foreach(var field in fields)
{
if (field.name != 'condition so you never know which field from fields will be 1st')
{
formedQuery += and + field.name + ":" field.value;
and = " AND";
}
}
But the solution I usually use involves an ordered list. Assuming I can extend your example code in any way that looks sensible:
var formedQuery = list.Empty;
foreach(var field in fields)
{
if (field.name != 'condition so you never know which field from fields will be 1st')
{
formedQuery.push(field.name + ":" field.value);
}
}
formedQuery = formedQuery.join(" AND ");
This also has the advantage of not making lots of unnecessary string copies as your assemble the string (in some languages, this is expensive).
I have always used the former. Mostly because it looks cleaner to me.
Another approach:
query = first_field_name
for every other field besides first:
query = " AND " + field_name

Resources