Modify FieldDefinitions programmatically - c#

There is currently a need within our shop to be able to add/modify/delete the FieldDefinitions on a WorkItemStore programmatically, without/before messing with the WorkItemType definitions. Is this possible? I know that i can GET all the fields on a project and see them, but what about adding new ones, or modifying existing ones ? (delete not as important).
i've been searching google for the past 2 days and couldn't find what i'm looking for...
Right now, this is how i am reading all the fields from the server.
List<FieldDefinition> all_defs = new List<FieldDefinition>();
FieldDefinitionCollection defs = wis.FieldDefinitions;
foreach(FieldDefinition def in defs)
all_defs.Add(def);

I was able to do a workaround. Basically, in code, I was able to get all of the fields on the server, create all of the fields that I needed to add or modify and then export one WIT as XML into a separate string.
From there, I extracted the new fields that I needed by comparison, modified the fields that I needed to modify (in Xml), changed the name of the WIT (in Xml) into a temporary one (e.g.: Missing_fields), then modified the WIT Xml to include both the new fields and the modified ones inside the Fields tag...
Then I used the Import WIT method on the temporary WIT I just created...
Then I refreshed the connection cache for the API... (this ensures the API has access to the new WIT so we can delete it)
Then I used the DestroyWIT action package to destroy the temporary WIT from the server, which essentially destroys the WIT itself, but not the fields as those are a separate entity on the server, and voilà! You've got yourself a FieldDefinition import and modify tool.
I still can't delete but that's not too bad...

Related

C# Outlook Add-in: How can I delete a User-defined property programmatically?

I have tried finding an answer to this question practically everywhere I could imagine, including here on StackOverflow. Unfortunately to no avail. So here it is.
I'm working on an Outlook Add-in (with Outlook 2021), and have developed some code that creates some ItemProperties specifically for use with that add-in. Now, when those properties are created, I can see them when I go to View->Settings->Advanced View Settings->Columns, as illustrated in the screenshot.
Screenshot of User-defined fields in Outlook
In some cases, though, I want to completely delete the properties. And as I know how to do that manually, as pointed out in the figure, I can't find out how to do that programmatically via C#. I have gone that far as to remove the properties from each mail containing that kind of property, like this:
IEnumerable<MailItem> listOfAssignedEmails = itemsToProcess.Where(
t => t.ItemProperties[MailExpiration.ExpirationDatePropertyName] != null);
foreach (MailItem email in listOfAssignedEmails)
{
// Note: The Delete() operation is deprecated. A more up-to-date method must be found.
email.ItemProperties[MailExpiration.ExpirationDatePropertyName].Delete();
email.Save();
}
... and yes, I know that the Delete() operation is deprecated; however, I couldn't find another method for removing the ItemProperty from the email (any suggestions are welcome).
Basically, the deletion of this Property is only going to be done very rarely (t. ex. if the user chooses to uninstall the Add-in. However, if there's any way to remove that property automatically, I would be happy to know.
Any suggestions will be greatly appreciated.
It is really a bad idea to remove a custom property from all emails that already have it: there is really no point since the user will never see them, but you will have to retouch (and thus change the last modified date) of a large number of emails.
Also note that named properties in MAPI are a finite resource - you can have at most 64k of them in a mailbox. Once a particular property mapping is used, you can never unmap it, even if there are no items that use that property.
Thirdly, doing anything Outlook related from an installer (rather than a VSTO addin) is a really bad idea - Windows installer runs in a service.
If you want to make sure the user no longer sees your custom fields as available properties in a view, you need to deal with the folder fields - they ar stored in a blob in a hidden (associated) message in that folder. OOM does not expose folder fields at all (if you don't count the AddToFolderFields parameter when calling UserProperties.Add). If using Redemption is an option (I am its author), it exposed RDOFolderFields object (accessible from RDOFolder2.FolderFields property) that allows to add or delete folder fields.
The list of properties shown on the screenshot belongs to the Folder.UserDefinedProperties property which returns a UserDefinedProperties object that represents the user-defined custom properties for the Folder object.
Use the ItemProperties.Remove method removes an object from the collection (from an item).
Use the ItemProperties property to return the ItemProperties collection. Use ItemProperties.Item(index), where index is the name of the object or the numeric position of the item within the collection, to return a single ItemProperty object.

Sitecore - Programatically modify the source field of a template

Currently, all the templates that we have created have source fields whose path is a string.
e.g. :
"sitecore/content/Test"
Now if I want to move the Test folder to
sitecore/content/Shared/Tags/Test
the links are broken.
If i manually change this to use the GUID (Using the build option), I get :
datasource={62CF8494-B148-4B2E-9D36-52EC4CD75E13}&database=master
If i now move the test folder around, my links remain as is.
I wanted to write a routine that runs through the tree and updates all the source fields for my templates (in a particular folder only), to contain the GUID and db name.
Is this possible?
I tried doing this in the Process method of a class that inherits from PublishItemProcessor and added the appropriate entry in the web.config. This method is called, but the Source property of the field is read only and cannpt be modified.
Any ideas?
Thanks in advance.
The best/most efficient option here would be to use Sitecore Powershell Extensions to modify the items.
This is a good reference point: https://sitecorepowershell.gitbooks.io/sitecore-powershell-extensions/content/working-with-items.html
You could also do this in code.
You need to write a routine (code or SPE) that starts with the /sitecore/templates/user defined or whatever your root folder is.
Recurse thru the tree and get all items that have the template: Template Field. Then you can check value of the the Source field. If it is the one you want to change, update the value and save the item.
Remember to publish the templates tree after updating all the values.

Can't query/order on built-in rally fields "could not read all instances of class com.f4tech.slm.domain.Artifact"

I'm using v2.0 of the API via the C# dll. But this problem also happens when I pass a Query String to the v2.0 API via https://rally1.rallydev.com/slm/doc/webservice/
I'm querying at the Artifact level because I need both Defects and Stories. I tried to see what kind of query string the Rally front end is using, and it passes custom fields and built-in fields to the artifact query. I am doing the same thing, but am not finding any luck getting it to work.
I need to be able to filter out the released items from my query. Furthermore, I also need to sort by the custom c_ReleaseType field as well as the built-in DragAndDropRank field. I'm guessing this is a problem because those built-in fields are not actually on the Artifact object, but why would the custom fields work? They're not on the Artifact object either. It might just be a problem I'm not able to guess at hidden in the API. If I can query these objects based on custom fields, I would expect the ability would exist to query them by built-in fields as well, even if those fields don't exist on the Ancestor object.
For the sake of the example, I am leaving out a bunch of the setup code... and only leaving in the code that causes the issues.
var request = new Request("Artifact");
request.Order = "DragAndDropRank";
//"Could not read: could not read all instances of class com.f4tech.slm.domain.Artifact"
When I comment the Order by DragAndDropRank line, it works.
var request = new Request("Artifact");
request.Query = (new Query("c_SomeCustomField", Query.Operator.Equals, "somevalue").
And(new Query("Release", Query.Operator.Equals, "null")));
//"Could not read: could not read all instances of class com.f4tech.slm.domain.Artifact"
When I take the Release part out of the query, it works.
var request = new Request("Artifact");
request.Query = (((new Query("TypeDefOid", Query.Operator.Equals, "someID").
And(new Query("c_SomeCustomField", Query.Operator.Equals, "somevalue"))).
And(new Query("DirectChildrenCount", Query.Operator.Equals, "0"))));
//"Could not read: could not read all instances of class com.f4tech.slm.domain.Artifact"
When I take the DirectChildrenCount part out of the query, it works.
Here's an example of the problem demonstrated by an API call.
https://rally1.rallydev.com/slm/webservice/v2.0/artifact?query=(c_KanbanState%20%3D%20%22Backlog%22)&order=DragAndDropRank&start=1&pagesize=20
When I remove the Order by DragAndDropRank querystring, it works.
I think most of your trouble is due to the fact that in order to use the Artifact endpoint you need to specify a types parameter so it knows which artifact sub classes to include.
Simply adding that to your example WSAPI query above causes it to return successfully:
https://rally1.rallydev.com/slm/webservice/v2.0/artifact?query=(c_KanbanState = "Backlog")&order=DragAndDropRank&start=1&pagesize=20&types=hierarchicalrequirement,defect
However I'm not tally sure if the C# API allows you to encode additional custom parameters onto the request...
Your question already contains the answer.
UserStory (HierarchicalRequirement in WS API) and Defect inherit some of their fields from Artifact, e.g. FormattedID, Name, Description, LastUpdateDate, etc. You may use those fields in the context of Artifact type.
The fields that you are trying to access on Artifact object do not exist on it. They exist on a child level, e.g. DragAndDropRank, Release, Iteration. It is not possible to use those fields in the context of Artifact type.
Parent objects don't have access to attributes specific to child object.
Artifact is an abstract type.
If you need to filter by Release, you need to make two separate requests - one for stories, the other for defects.

Winforms - Dynamic Load / Save Settings

I have a "settings file" in my Winforms application called Settings.settings with a partial class for custom methods, etc. Is there a way to load / save dynamic settings based on arbitrary keys?
For example, I have some ListViews in my application in which I want to save / load the column widths; Instead of creating a width setting for each column for each list view I would like a simple method to load / save the widths automatically.
Below is an example of the save method I have tried:
internal sealed partial class Settings
{
public void SetListViewColumnWidths(ListView listView)
{
String baseKey = listView.Name;
foreach (ColumnHeader h in listView.Columns)
{
String key = String.Format("{0}-{1}", baseKey, h.Index);
this[key] = h.Width;
}
}
}
When running that code I get the error "The settings property 'TestsListView-0' was not found." Is there something I am missing?
Store your column width settings in an Xml Serializable object. Ie, something that implements IXmlSerializable then create a single setting entry of that type in Settings.settings.
A good option would probably be an Xml Serializable Dictionary. A quick google search found quite a few different blog posts that describe how to implement that.
As mentioned in other answers you'll need to ensure that this object is a User setting. You may also need to initialize the setting instance. Ie, create a XmlSerializableDictionary() instance and assign it to the setting if the setting is null. The settings subsystem doesn't create default instances of complex setting objects.
Also, if you want these settings to persist between assembly versions (ie, be upgradable) you will need to upgrade the settings on application startup. This is described in detail on Miha Markič's blog and Raghavendra Prabhu's blog.
I think the error
The settings property
'key' was not found.
occurs because the 'key' value does not exist in your settings file (fairly self-explanatory).
As far as I am aware, you can't add settings values programmatically, you might need to investigate adding all of the settings you need to the file after all, although once they are there, I think you'll be able to use the sort of code you've given to save changes.
To Save changes, you'll need to make sure they are 'User' settings, not 'Application'.
The Settings file is quite simple XML, so you might be able to attack the problem by writing the XML directly to the file, but I've never done it, so can't be sure it would work, or necessarily recommend that approach.
http://msdn.microsoft.com/en-us/library/cftf714c.aspx is the MSDN link to start with.
You can do Settings.Save() or similar on user settings, but note that such settings would NOT get persisted to the xxx.exe.config file in your app directory as you'd expect. They actually go somewhere deep inside the user folder (search your drive for xxx.exe.config to find it). Next time that you manually change xxx.exe.config in your app directory, the change will mysteriously not apply (the system is still using the saved one from the user directory).

Properties embedded within another object

I am trying to use a business object that I am passing to the report.rdlc. The properties in my object are not directly exposed. The properties I require are embedded within another object inside the top level object. As this is a WCF project I can't control what goes on at the server end. I am just able to request these objects or Insert/Update/Delete their info from the database. It is done in this way as the back end can use multiple flavors of database.
Here is what I can see after adding my business object as a DataSource:
-BusinessObject
-CustomerInfo
-ClientName
-ColumnName
-DisplayName
-FieldName
-IsNull
-KeyColumn
-SenondKeyColumn
-StringValue
-ClientID
-ColumnName
-DisplayName
-FieldName
-IntValue
-IsNull
-KeyColumn
-SenondKeyColumn
+ClientAddress
+Instrument
+Telephone
etc etc
I need to be able to display, for example, the ClientName.StringValue field.
If I drag the field I want onto the report I get:
=First(Fields!StringValue.Value)
This doesn't display anything when the report is run, I assume because it can't qualify what StringValue it is talking about and there could be many.
If I try dragging the ClientName object I get:
=First(Fields!ContactName.Value)
However this gives:
#ERROR
When the report is run.
I would have thought you could use:
=First(Fields!ClientName.StringValue.Value)
but this won't even let me build.
The problem was that the info wasn't at the root level. I worked it out though.
=First(Fields!ClientName.Value.StringValue, "BusinessObject_CustomerInfo")
I've got a pretty good grip of the ReportViewer component now cheers.
If you set the data source to the CustomerInfo instance (or list) returned from the service it should work. The ReportViewer control can be a little complicated when you start dealing with object hierarchies, but you don't have to do anything crazy or special if all the information is at the root level.

Categories

Resources