I created an ActivityNode (an Entry) and I can add custom fields with the
setFields(List<Field> newListField)
fonction.
BUT
I am unable to modify these fields. (In this case I try to modify the value of the field named LIBENTITE)
FieldList list = myEntry.getTextFields();
List<Field> updatedList = new ArrayList<Field>();
//I add each old field in the new list, but I modify the field LIBENTITE
for(Field myField : list){
if(myField.getName().equals("LIBENTITE")){
((TextField)myField).setTextSummary("New value");
}
updatedList.add(myField);
}
myEntry.setFields(updatedList);
activityService.updateActivityNode(myEntry);
This code should replace the old list of fields with the new one, but I can't see any change in the custom field LIBENTITE of myEntry in IBM connections.
So I tried to create a new list of fields, not modifying my field but adding a new one :
for(Field myField:list){
if(!myField.getName().equals("LIBENTITE")){
updatedList.add(myField);
}
}
Field newTextField = new TextField("New Value");
newTextField .setFieldName("LIBENTITE");
updatedList.add(newTextField );
And this code is just adding the new field in myEntry. What I see is that the other custom fields did not change and I have now two custom fields named LIBENTITE, one with the old value and the second with the new value, in myEntry.
So I though that maybe if I clear the old list of Fields, and then I add the new one, it would work.
I tried the two fonctions
myEntry.clearFieldsMap();
and
myEntry.remove("LIBENTITE");
but none of them seems to work, I still can't remove a custom field from myEntry using SBT.
Any suggestions ?
I have two suggestions, as I had (or have) similar problems:
If you want to update an existing text field in an activity node, you have to call node.setField(fld) to update the field in the node object.
Code snippet from my working application, where I'm updating a text field containing a (computed) start time:
ActivityNode node = activityService.getActivityNode(id);
node.setTitle(formatTitle()); // add/update start and end time in title
boolean startFound = false;
// ...
FieldList textfields =node.getTextFields();
Iterator<Field> iterFields = textfields.iterator();
while (iterFields.hasNext()) {
TextField fld = (TextField) iterFields.next();
if (fld.getName().equals(Constants.FIELDNAME_STARTTIME)) {
fld.setTextSummary(this.getStartTimeString()); // NOTE: .setFieldValue does *not* work
node.setField(fld); // write updated field back. This seems to be the only way updating fields works
startFound=true;
}
}
If there is no field with that name, I create a new one (that's the reason I'm using the startFound boolean variable).
I think that the node.setField(fld) should do the trick. If not, there might be a way to sidestep the problem:
You have access to the underlying DOM object which was parsed in. You can use this to tweak the DOM object, which finally will be written back to Connections.
I had to use this as there seems to be another nasty bug in the SBT SDK: If you read in a text field which has no value, and write it back, an error will be thrown. Looks like the DOM object misses some required nodes, so you have to create them yourself to avoid the error.
Some code to demonstrate this:
// ....
} else if (null == fld.getTextSummary()) { // a text field without any contents. Which is BAD!
// there is a bug in the SBT API: if we read a field which has no value
// and try to write the node back (even without touching the field) a NullPointerException
// will be thrown. It seems that there is no value node set for the field. We
// can't set a value with fld.setTextSummary(), the error will still be thrown.
// therefore we have to remove the field, and - optionally - we set a defined "empty" value
// to avoid the problem.
// node.remove(fld.getName()); // remove the field -- this does *not* work! At least not for empty fields
// so we have to do it the hard way: we delete the node of the field in the cached dom structure
String fieldName = fld.getName();
DeferredElementNSImpl fldData = (DeferredElementNSImpl) fld.getDataHandler().getData();
fldData.getParentNode().removeChild(fldData); // remove the field from the cached dom structure, therefore delete it
// and create it again, but with a substitute value
Field newEmptyField = new TextField (Constants.FIELD_TEXTFIELD_EMPTY_VALUE); // create a field with a placeholder value
newEmptyField.setFieldName(fieldName);
node.setField(newEmptyField);
}
Hope that helps.
Just so that post does not stay unanswered I write the answer that was in a comment of the initial question :
"currently, there is no solution to this issue, the TextFields are read-only map. we have the issue recorded on github.com/OpenNTF/SocialSDK/issues/1657"
Related
I seem to be having a problem with assigning values to fields of a content item with a custom content part and the values not persisting.
I have to create the content item (OrchardServices.ContentManager.Create) first before calling the following code which modifies a field value:
var fields = contentItem.As<MyPart>().Fields;
var imageField = fields.FirstOrDefault(o => o.Name.Equals("Image"));
if (imageField != null)
{
((MediaLibraryPickerField)imageField).Ids = new int[] { imageId };
}
The above code works perfectly when against an item that already exists, but the imageId value is lost if this is done before creating it.
Please note, this is not exclusive to MediaLibraryPickerFields.
I noticed that other people have reported this aswell:
https://orchard.codeplex.com/workitem/18412
Is it simply the case that an item must be created prior to amending it's value field?
This would be a shame, as I'm assigning this fields as part of a large import process and would inhibit performance to create it and then modify the item only to update it again.
As the comments on this issue explain, you do need to call Create. I'm not sure I understand why you think that is an issue however.
I followed How do you copy a datetime field from the current document to a new document and I try something like this:
Cdoc.save();
Pdoc.copyItem(Cdoc.getDocument().getFirstItem("mytest1"));
getComponent('exampleDialog').show()
But I get a handling error message.
Thanks for your time!
Assuming Cdoc and Pdoc are defined as xp:dominoDocument data sources then you have to change your code to:
Cdoc.save();
Pdoc.getDocument().copyItem(Cdoc.getDocument().getFirstItem("mytest1"));
getComponent('exampleDialog').show()
So, you only need to add .getDocument() to Pdoc to get the Notes Document. Otherwise it fails and you get the error "Error calling method 'copyItem(lotus.domino.local.Item)' on an object of type 'NotesXspDocument'".
Keep in mind that you have to save Pdoc after copying item too if you want to show the copied item in your exampleDialog.
If you don't want to save document Pdoc at this point yet then you can copy the item on NotesXspDocument level with just:
Pdoc.replaceItemValue("mytest1", Cdoc.getItemValueDateTime("mytest1"));
getComponent('exampleDialog').show()
I do not often use "copyItem". You are not specifying if you are using NotesDocuments or NotesXspDocuments, so I will write a quick thing about both because they should be handled differently.
var currentDoc:NotesDocument = ....
var newDoc:NotesDocument= ...
newDoc.replaceItemValue("fldname", currentDoc.getItemValueDateTimeArray("fldname").elementAt(0))
if currentDoc is a NotesXspDocument, use the following
var currentDoc:NotesXspDocument = ...
var newDoc:NotesDocument=...
newDoc.replaceItemValue("fldname", currentDoc.getItemValueDateTime("fldname"))
Otherwise, you could continue trying with copyItem, I just lack experience with it.
EDIT
Just some things to add, remember that putting calling xspDoc.getDocument(true) will update the background document and this might be needed. Also, in the comments for that article you posted, they mentioned the possible need to put that document into another variable.
var docSource:NotesDocument = xspDoc.getDocument(true);
var docNew:NotesDocument = ...
docNew.copyItem(docSource.getItem("blah");
Also remember that copyItem is a function of NotesDocument and not NotesXspDocument.
The following code is a script object on an XPage in it I loop through an array of all the forms in a database, looking for all the forms that contain the field "ACIncludeForm". My method works but it takes 2 - 3 seconds to compute which really slows the load of the XPage. My question is - is there a better method to accomplish this. I added code to check to see if the sessionScope variable is null and only execute if needed and the second time the page loads it does so in under a second. So my method really consumes a lot of processor time.
var forms:Array = database.getForms();
var rtn = new Array;
for (i=0 ; i<forms.length; ++i){
var thisForm:NotesForm = forms[i];
var a = thisForm.getFields().indexOf("ACIncludeForm");
if (a >= 0){
if (!thisForm.isSubForm()) {
if (thisForm.getAliases()[0] == ""){
rtn.push(thisForm.getName() + "|" + thisForm.getName() );
}else{
rtn.push(thisForm.getName() + "|" + thisForm.getAliases()[0] );
}
}
}
thisForm.recycle()
}
sessionScope.put("ssAllFormNames",rtn)
One approach would be to build an index of forms by yourself. For example, create an agent (LotusScript or Java) that gets all forms and for each form, create a document with for example a field "form" containing the form name and and a field "fields" containing all field names (beware of 32K limit).
Then create a view that displays all these documents and contains the value of the "fields" field in the first column so that each value of this field creates one line in this view.
Having such a view, you can simply make a #DbLookup from your XPage.
If your forms are changed, you only need to re-run the agent to re-build your index. The #DbLookup should be pretty fast.
Place the form list in a static field of a Java class. It will stay there for a long time (maybe until http boot). In my experience applicationScope values dissappear in 15 minutes.
I have a custom entity named eld_timereg. This entity has an attribute named eld_name (which is the primary field). Records are created in a generic handler (after pushing a button in the ribbon). I'm using late binding.
var myService = ...;
var t = new Entity( "eld_timereg" );
t["field1"] = "abc";
.
.
.
t["eld_name"] = GenerateAnUniqueStringCode("ZXC");
// returns something like ZXC-16398-T1VC
return myService.Create(t);
The record is created without any error. Checking the entity in SSMS, the value is blank although it is a required field.
What is happening here?
Found the problem. You put me on the right track James. Thank you for that. The eld_name attribute was overwritten in the plug-in (I forgot). Here it should be filled with some information. This was going wrong and always returned a null.
How do you set a richText value with replaceItemValue from a rich tect control?
I found this bit of code here:
http://www.bleedyellow.com/blogs/martin/entry/save_a_richtext_field_from_a_xpage_to_a_document?lang=en_us
var doc = configuratieformulieren.getDocumentByKey("ConfiguratieIVNL", true);
if(doc == null){
return;
}else{
var titel = getComponent("inputTextIBPTitelIVNL").getValue();
doc.replaceItemValue("IBPTitel",titel);
var inhoud = getComponent("inputRichTextIBPInhoudIVNL").getValue();
if (inhoud != null){
var contentType = doc.getMIMEEntity("IBPInhoud").getContentType();
var encoding = doc.getMIMEEntity("IBPInhoud").getEncoding();
var str = session.createStream();
inhoud.toString();
str.writeText(inhoud.toString());
doc.getMIMEEntity("IBPInhoud").setContentFromText(str, contentType, encoding);
}
doc.save(true, true);
}
sessionScope.put("FormulierIVNLInfoBeschPG","Lezen");
Is it correct? It looks like this code depends on the fact that the field already exists. How id this handled if the field does not exist? Is there and easier way to set a field value to the contents of a rich text control?
Let data sources do the heavy lifting. For a long and boring (but thorough) explanation of why, read this article. But here's the quick version:
Don't use:
getComponent("someID").getValue()
Instead, use:
someDataSource.getValue("someFieldName")
This is always a more efficient way to access data: instead of having to spider through the component tree to locate a match, it goes straight to the data source, which the component would have to ask anyway if you asked it what its value is.
Similarly, don't use:
someDataSource.replaceItemValue("someFieldName", someValue)
Instead, use:
someDataSource.setValue("someFieldName", someValue)
The latter is much more flexible on input type. The data source already contains all the logic for determining what to do based on whether the value is text, date, number, rich text, file upload, etc. No need to duplicate any of that logic in your own code.
So if the goal is to update a separate document based on data in the current document, just define a separate document data source that points to the document you want to update. Then it's literally this simple:
configData.setValue("RichTextData", currentDocument.getValue("RichTextData"));
configData.save();
With the above code, if the field you specify on the current document is rich text, then the item it creates on the other document will be rich text. If it's any other type on the current document, it will be the same type on the other document. With getValue() and setValue(), you don't have to pay attention to the data type... the data source handles all of that for you.
For bonus points, scope configData to applicationScope so that any updates to it are immediately cached for all users... or sessionScope if the document you're updating is user-specific.
I was able to solve my orginal issue. To expand on my issue I was having problems with using a dialog box to create Form / Document B from Form / Document A using a dialog box on Form A. What was happening was any changes to Form B would be saved to Document A's datasource.
I found the ingoreRequestParams on Form B's datasource, set it and that solved my problem with Form B writing to Form A document.