TFS 2012 Pull Custom Mapped Fields Programmatically - azure-devops

I came across this tutorial on how to programmatically pull Iteration Paths and Area Paths from TFS 2012 using their API.
http://geekswithblogs.net/TarunArora/archive/2011/07/10/tfs-2010-sdk-get-projects-iterations-area-path-queries-and.aspx
However, my company has set up some custom fields, one being "Team" (not to be confused with Team Projects that is already specified by TFS). In our Feature Request template, the user will click a dropdown menu and select the team they want to assign a project to. However, I haven't found a way to pull our team names programmatically like I can iteration paths and area paths.
Any ideas on how to pull custom fields?

You need something like this:
var server = tfs.GetService<WorkItemStore>();
var projectName = "MyTeamProject";
var workItemTypeName = "Bug";
var fieldName = "Field.RefName";
var allowedValues =
server
.Projects
.Cast<Project>()
.Single(project => project.Name == projectName)
.WorkItemTypes
.Cast<WorkItemType>()
.Single(workItemType => workItemType.Name == workItemTypeName)
.FieldDefinitions
.Cast<FieldDefinition>()
.Single(field => field.ReferenceName == fieldName)
.AllowedValues;

Related

Get list of projects user has access to

I have a site for automation, and want to display to users a list of projects they have access to in a dropdown.
If I have a a PAT for an admin account in the org, how can I get the list of projects given a user's email?
Presumably the REST api is the best way to do this?
https://learn.microsoft.com/en-us/rest/api/azure/devops/?view=azure-devops-rest-7.1
I've found two ways to do this.
First method: list all projects, filter based on user list
projects = GET https://dev.azure.com/{orgname}/_apis/projects?api-version=6.0
results = []
foreach project in projects:
descriptor = GET https://vssps.dev.azure.com/{orgname}/_apis/graph/descriptors/{project.id}
members = GET https://vssps.dev.azure.com/{orgname}/_apis/graph/users?api-version=6.0-preview.1&scopeDescriptor={descriptor}
if userId in members:
results.push(project)
Second method: get user entitlements
This is better because this will show all projects that people have Reader (or higher) on, where the first method doesn't show projects where the user doesn't have explicit membership
users = GET https://vsaex.dev.azure.com/{orgname}/_apis/UserEntitlements?$filter=name eq '{userId}'&$orderBy=name Ascending&select=Projects
user = [x for x in users where x.userId == userId][0]
results = user.projectEntitlements
Note the select query parameter included in the second example, this is necessary for projectEntitlements to be included in the result.

Upload files to moodle under a selected course

I have created a particular course using the moodle rest api and i would like to add a files into that particular course. The course may take the week/topic form. I need to add the uploaded file under a selected topic/week how can i achieve that? .I used core_files_upload to upload files but how can i add it to a selected course?
Array
(
[contextid] => int
[component] => string
[filearea] => string
[itemid] => int
[filepath] => string
[filename] => string
[url] => string
)
In order to make a file appear in a course, you would need to create an instance of the 'mod_resource' activity in the course and then attach the relevant file to that resource.
I don't believe there is currently any webservice for creating activities within a course (https://tracker.moodle.org/browse/MDL-40779 appears to still be incomplete).
The best you can do at the moment would be to create a custom Moodle plugin (probably a local plugin) and then implement your own webservice in order to add this functionality.

issue changing properties on project items using multi-project vstemplate with IWizard

I am creating a VSIX extension that generates a multi-project solution to be distributed. I would like to do several things to individual items I include in each projects vstemplate but think I may be taking the wrong approach. I have created a IWizard and am able to debug the solution. I added a custom item type to the project items in vstemplate in order to identify which ones I want to flag. I am then doing the following to change a ProjectItems Build Action from Content to Compile. I am still learning Visual Studio Extensability and have very limited experience using the objects so I apologize if the below is horrendous!
public void ProjectFinishedGenerating(Project project)
{
var myproject = (VSProject)project.Object;
foreach (ProjectItem pi in myproject.ProjectItems)
{
var myBuildAction = pi.Properties.Item("ItemType").Value;
if myBuildAction == "CompileContent")
pi.Properties.Item("ItemType").Value = "Compile";
}
}
This is my item in vstemplate
<VSTemplate Version="3.0.0" Type="Project" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" xmlns:sdk="http://schemas.microsoft.com/developer/vstemplate-sdkextension/2010">
<ProjectItem TargetFileName="MyFolder\$ext_safeprojectname$\$ext_safeprojectname$_CodeFile.cs" ReplaceParameters="true" ItemType="CompileContent">CodeFile.cs</ProjectItem>

New & renamed workflows with existing content

I have a site with a custom content type Content, which initially had a single workflow attached, content_workflow. There are several thousand existing instances of Content.
I now have a need to add a second workflow to this type, content_beta_workflow. How can I update all existing content to be part of the new workflow?
On a related note: if I want rename the initial workflow to content_alpha_workflow, how can I update all existing content to reflect this change?
If you are simply changing from one workflow to the other, follow these steps:
Go to Site Setup > Types
Select your custom content type from the drop down menu, the page will update to display the current workflow
Select your new workflow from the dropdown, a map will be generated showing each state in the current workflow
For each state, select the state in your new workflow that most closely matches (or is most appropriate)
When you save, all objects of your custom site will be updated to use the new workflow. For each state in the map from the original workflow, existing content in that state will be put into the state you chose in step 4 above. Security settings will be re-indexed and you are done.
As for renaming the old workflow, you can do so in the portal_workflow tool in the ZMI. But only change the human-facing Title of the workflow. Changing the ID may have side effects for the workflow history of your content.
edited
Okay, I see from your comment that you are looking to add a new workflow to a type in addition to the one it already has. Here's a bit of sample code to accomplish that:
my_type = 'Content' # This is your content portal_type name
my_wf = 'content_workflow_beta'
wf_chain = list(wf_tool.getChainForPortalType(my_type))
if my_wf not in wf_chain:
wf_chain.append(my_wf)
wf_tool.setChainForPortalTypes([my_type], wf_chain)
You can add this code in an upgrade step for the package that defines your content type and workflows. Add a call to updateRoleMappings on the workflow tool and you'll be set to use the new workflow through the standard Plone UI in addition to your original workflow.
As you've already found, you can also manually update the workflow history of all objects to rename workflow ID, but that's a pretty invasive step.
As workflow_history is a dict property on each content item, it was a case of adding or updating suitable items as required. First, I copied the GenericSetup for content_workflow to content_alpha_workflow. Next, I created content_beta_workflow and added it to the profile. Then I wrote the following upgrade step:
import logging
from DateTime import DateTime
def modify_content_workflow_history(context, logger=None):
if logger is None: logger = logging.getLogger('my.product')
# import the new workflows
context.portal_setup.runImportStepFromProfile('profile-my.product:default', 'workflow')
# set up some defaults for the new records
_history_defaults = dict(
action = None,
actor = 'admin',
comments = 'automatically created by update v2',
time = DateTime(),
)
_alpha_defaults = dict(review_state = 'alpha_state_1', **_history_defaults)
_beta_defaults = dict(review_state = 'beta_state_1', **_history_defaults)
for parent in context.parents.values():
for content in parent.content.values():
# don't acquire the parent's history
if 'parent_workflow' in content.workflow_history:
content.workflow_history = {}
# copy content_workflow to content_alpha_workflow
if 'content_workflow' in content.workflow_history:
alpha_defaults = context.workflow_history['content_workflow']
del content.workflow_history['content_workflow']
else:
alpha_defaults = (_alpha_defaults,) # must be a tuple
content.workflow_history['ctcc_content_alpha_workflow'] = alpha_defaults
# create the beta workflow with a modified actor
beta_defaults = dict(**_beta_defaults)
beta_defaults['actor'] = u'%suser' % parent.id
content.workflow_history['ctcc_content_beta_workflow'] = (beta_defaults,)
logger.info('Content workflow history updated')

TFS 2010 API - Get work items from merge

I need to send an email on completion of a build in TFS 2010 which details the work items associated with check-ins that have been compiled as part of this build. This works no problem via use of the associatedChangesets variable available in the build workflow.
However, in a production situation, we will merge changes from our Development branch into a Release branch. At this point, the build considers there to have only been one change - which is the aforementioned merging of Development into Release. Obviously this is fairly useless as we need to find out which changes where made in the branch that was merged in, and the work items associated.
Does anyone know how to accomplish this using the TFS 2010 API? It seems to be fairly poorly documented from an API perspective. I know you can expand the merge history node in VS2010 but obviously this is no good as this data needs to be collected programatically so a report email can be sent.
OK... I think I found a solution to this although it's clunky and truth be told I'm not exactly sure how it works. But here goes - maybe it will point someone in the right direction.
var associatedWorkItems = new List<WorkItem>();
//Passed in from the build workflow (this variable is available under the 'Run On Agent' sequence as 'associatedChangesets'
IList<Changeset> associatedChangesets = context.GetValue(BuildAssociatedChangesets);
if (associatedChangesets.Count > 0)
{
var projectCollection =
new TfsTeamProjectCollection(new Uri("http://localhost:8080/tfs/DefaultCollection"));
VersionControlServer versionControlServer = projectCollection.GetService<VersionControlServer>();
foreach (var changeset in associatedChangesets)
{
//In order to view the individual changes, load the changeset directly from the VCS.
Changeset localChangeset = versionControlServer.GetChangeset(changeset.ChangesetId);
foreach (Change change in localChangeset.Changes)
{
//Find out what was merged in.
ChangesetMerge[] mergedChangesets = versionControlServer.QueryMerges(
null,
null,
change.Item.ServerItem,
new ChangesetVersionSpec(localChangeset.ChangesetId),
new ChangesetVersionSpec(localChangeset.ChangesetId),
null,
RecursionType.Full);
//Extract work item information from changesets being identified as merged.
foreach (var changesetMerge in mergedChangesets)
{
Changeset actualChange = versionControlServer.GetChangeset(changesetMerge.SourceVersion);
foreach (WorkItem item in actualChange.WorkItems)
{
if (!associatedWorkItems.Exists(w => w.Id == item.Id))
{
associatedWorkItems.Add(item);
}
}
}
}
}
}
Don't ask me exactly how QueryMerges works but all I'm doing here it saying show me what what merged as a part of a changeset checked in. You'll notice that the parameters ChangesetVersionSpec are the same - this means we're just looking at merges from this one changeset.
You'll get back an array of ChangesetMerge objects from QueryMerges(). In the ChangesetMerge class there is a property called SourceVersion - this is the ChangesetId of the original changeset merged in. Once we've got that we can use the VersionControlServer.GetChangeset() method to load the individual set and extract the WorkItem. This is then added to a list of WorkItems which can be manipulated in any way you want (in my case an email). I also used the .Exists() check to make sure the same WorkItem doesn't get recorded twice.
Note that even though you have the collection associatedChangesets from the build workflow, for some reason (for me at least), the Changes[] property inside associatedChangesets was never populated (hence loading each individual changeset using the VersionControlServer.GetChangeset() method as this seems to actually populate all the fields we need.
Like I say, 1. this is a clunky solution (lots of looping - some of which is probably unecessary), 2. I don't fully understand how this works although it seems to produce the required results - I came to this conclusion by doing a lot testing and debugging) and finally - it's the best I could come up with based on the woeful documentation provided by Microsoft.
Hope it helps someone!