I moved to again, this time to a place that I will stay at for a long time, see my new stuff at http://31og.com/ .
Cheers
I moved to again, this time to a place that I will stay at for a long time, see my new stuff at http://31og.com/ .
Cheers
Update: This post has been copied to http://31og.com/post/how-to-get-your-identity-tokens
I’ve always found that it’s slightly harder than it needs to be to get the keys for apps to authenticate against Twitter, Facebook, Google and Microsoft Account. Today I’ll show you where to go to get all these keys.
Go to the Windows Live application management site and sign in if you aren’t already. Click on create application, when the new page loads type an application name, select a language and click I accept. If your app is a web application enter the redirect domain otherwise select Yes under the mobile client app and then click Save.
At this point you should see a message on the top of the form that reads Your changes were saved. You can now use the Client ID and Client Secret that are displayed on screen.
Go to the Facebook developers apps page and sign in if you aren’t already, click on + Create new App and enter an application name into the popup, optionally enter an application namepsace and then select a category and optionally a sub category and the press continue. Enter the Captcha that is displayed and click continue.
At the top of the page you will see your App ID and App Secret, customise all the settings of your app as you feel needed and then take the app out of Sandbox mode to make it live.
Go to Twitter Devs Site and sign in if you aren’t already. Hover over your profile in the top right corner and then click on My applications.
Click on Create a new application. Enter a name, description and web address for your application, check the Yes, I agree checkbox, enter the Captha and then click on Create your Twitter application.
If you scroll down a bit you’ll see your Consumer Key and Consumer Secret.
Go to Google+ Sign-In, all instructions are under Step 1: Create a client ID and client secret, but I will place them here as well .
Go to Google APIs Console, select create from the drop down menu on the left and then enter a project and click Create Project. In the all services list switch the Google+ API on and then when the page is redirect check I agree to these terms and click Agree.
Next go to the API Access tab on the left and click on Create an OAuth 2.0 client ID…. Enter your product name and click Next. Select the application type that is applicable to you and then click Create client ID.
After the popup closes you will see your Client ID and Client secret on the page.
The steps above will give you all you need to setup Windows Azure Mobile Services Identity providers. Option 3 on the Get started with authentication in Mobile Services tutorial.
Sometimes you can’t always use the tools you want to use. This is especially true when there is already a bunch of stuff setup around existing tools. Lets say for example that you have an existing application doing your builds, for everything you need around TFS you can do it from within the Web Access although this is all fine and works well, when you want to see how the builds are running you need to go out to another tool to see this information.
Today I’m going to help you setup your builds external to TFS to send build information to TFS so that you can see this info in TFS, the purpose of this is just to give you visibility from TFS and is a very basic introduction that can be extended on a lot with bigger builds.
Before we dig into any code, you will need to create a Build Definition that we will use for our fake builds, if you don’t currently have any controllers setup for you specific collection you will need to create one, after you create the build definition you will be able to de register this build controller as it’s not needed for the manually builds which we will be using.
What we had initially was a build.bat file that has the basics in it (build the build.proj file and log to different files for multiple levels of logging and dumps the latest build in a folder) that our current build server would run that looked like below
1: @echo off
2: call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\VsDevCmd.bat"
3: msbuild build.proj /t:DoBuild /v:d /fl1 /fl2 /fl3 /fl4 /flp1:logfile=build.log /flp2:logfile=build.errors.log;errorsonly /flp3:logfile=build.warnings.log;warningsonly /flp4:logfile=build.details.log;detailsonly /p:OutputPath=\\GORDON-PC\Demos\drops\TfsFakeBuilds-Latest
and a build.proj file that basically just let use build multiple projects that could be part of multiple solutions (for the purpose of this post we only have 1)
1: <?xml version="1.0" encoding="utf-8"?>
2: <Project DefaultTargets="DoBuild" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3: <ItemGroup>
4: <BuildProject Include="TfsFakeBuilds.sln"/>
5: </ItemGroup>
6:
7: <Target Name="DoBuild">
8: <MSBuild Projects="@(BuildProject)">
9: </MSBuild>
10: </Target>
11: </Project>
The next steps was to create 2 msbuild tasks that we could use for the starting and stopping of our TFS Build. Create a new class library project and add a reference to the following assemblies
Microsoft.Build.Framework
Microsoft.Build.Utilities.v4.0
Microsoft.TeamFoundation.Build.Client
Microsoft.TeamFoundation.Client
Microsoft.TeamFoundation.Common
Microsoft.VisualStudio.Services.Common
Add 2 classes that each inherit from Microsoft.Build.Utilities.Task, you can call theses classes TfsFakeBuildStart and TfsFakeBuildFinish. All these 2 tasks are going to do is call a call into a 3rd class that is going to do all the work.
TfsFakeBuildStart.cs
1: namespace TfsBuildTask
2: {
3: using System;
4: using Microsoft.Build.Framework;
5: using Microsoft.Build.Utilities;
6: public class TfsFakeBuildStart : Task
7: {
8: [Required]
9: public string Architecture { get; set; }
10: [Required]
11: public string CollectionUri { get; set; }
12: [Required]
13: public string Configuration { get; set; }
14: [Required]
15: public string DefinitionName { get; set; }
16: [Required]
17: public string DetailedLogPath { get; set; }
18: [Required]
19: public string DropLocation { get; set; }
20: [Required]
21: public string ErrorLogPath { get; set; }
22: [Required]
23: public string PathFromBuildRoot { get; set; }
24: [Required]
25: public string RegularLogPath { get; set; }
26: [Required]
27: public string ServerPath { get; set; }
28: [Required]
29: public string TargetNames { get; set; }
30: [Required]
31: public string TeamProjectName { get; set; }
32: [Required]
33: public string WarningLogPath { get; set; }
34: public override bool Execute()
35: {
36: try
37: {
38: base.Log.LogMessage("Tfs Fake Build Starting on '" + this.DefinitionName + "'.");
39: FakeBuildObject.Instance.Start(this.DetailedLogPath, this.ErrorLogPath, this.WarningLogPath, this.DropLocation, this.RegularLogPath, this.CollectionUri, this.TeamProjectName, this.DefinitionName, this.Configuration, this.PathFromBuildRoot, this.Architecture, this.ServerPath, this.TargetNames);
40: base.Log.LogMessage("Tfs Fake Build Started on '" + this.DefinitionName + "'.");
41: return true;
42: }
43: catch (Exception ex)
44: {
45: base.Log.LogError("Failed to start Fake Build: " + ex);
46: return false;
47: }
48: }
49: public IBuildEngine BuildEngine { get; set; }
50: public ITaskHost HostObject { get; set; }
51: }
52: }
TfsFakeBuildFinish.cs
1: namespace TfsBuildTask
2: {
3: using System;
4: using Microsoft.Build.Framework;
5: using Microsoft.Build.Utilities;
6: public class TfsFakeBuildFinish : Task
7: {
8: public override bool Execute()
9: {
10: try
11: {
12: base.Log.LogMessage("Tfs Fake Build Stopping.");
13: FakeBuildObject.Instance.Stop();
14: base.Log.LogMessage("Tfs Fake Build Stopped.");
15: return true;
16: }
17: catch(Exception ex)
18: {
19: base.Log.LogError("Failed to stop Fake Build: " + ex);
20: return false;
21: }
22: }
23: public IBuildEngine BuildEngine { get; set; }
24: public ITaskHost HostObject { get; set; }
25: }
26: }
In our 3rd class we will be getting a reference to a build definition and then kicking off a manual build in our start method and then in our stop method we will collection all the information to from the build and finish off the TFS manual build. Create a 3rd class like below
FakeBuildObject.cs
1: namespace TfsBuildTask
2: {
3: using System;
4: using System.IO;
5: using System.Linq;
6: using Microsoft.TeamFoundation.Build.Client;
7: using Microsoft.TeamFoundation.Client;
8: public class FakeBuildObject
9: {
10: private static FakeBuildObject instance;
11: private string architecture;
12: private string collectionUri;
13: private string configuration;
14: private string definitionName;
15: private string detailedLogPath;
16: private string dropLocation;
17: private string errorLogPath;
18: private string pathFromBuildRoot;
19: private string regularLogPath;
20: private string serverPath;
21: private string targetNames;
22: private string teamProjectName;
23: private string warningLogPath;
24: private IBuildDefinition buildDefinition;
25: private IBuildProjectNode buildProjectNode;
26: private IBuildServer buildServer;
27: private TfsTeamProjectCollection collection;
28: private IBuildDetail detail;
29: private DateTime startTime;
30: private DateTime endTime;
31: public static FakeBuildObject Instance
32: {
33: get
34: {
35: if (instance == null)
36: {
37: instance = new FakeBuildObject();
38: }
39: return instance;
40: }
41: }
42: public void Start(string detailedLogPath, string errorLogPath, string warningLogPath, string dropLocation, string regularLogPath, string collectionUri, string teamProjectName, string definitionName, string configuration, string pathFromBuildRoot, string architecture, string serverPath, string targetNames)
43: {
44: this.detailedLogPath = detailedLogPath;
45: this.errorLogPath = errorLogPath;
46: this.warningLogPath = warningLogPath;
47: this.dropLocation = dropLocation;
48: this.regularLogPath = regularLogPath;
49: this.collectionUri = collectionUri;
50: this.teamProjectName = teamProjectName;
51: this.definitionName = definitionName;
52: this.configuration = configuration;
53: this.pathFromBuildRoot = pathFromBuildRoot;
54: this.architecture = architecture;
55: this.serverPath = serverPath;
56: this.targetNames = targetNames;
57: this.startTime = DateTime.Now;
58: this.collection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(this.collectionUri));
59: this.buildServer = this.collection.GetService<IBuildServer>();
60: this.buildDefinition = this.buildServer.GetBuildDefinition(this.teamProjectName, this.definitionName);
61: this.detail = this.buildDefinition.CreateManualBuild(this.definitionName + " - " + DateTime.Now.ToString("yyyyMMddHHmmss"), this.dropLocation);
62: this.buildProjectNode = this.detail.Information.AddBuildProjectNode(this.configuration, this.pathFromBuildRoot, this.architecture, this.serverPath, this.startTime, this.targetNames);
63: }
64: public void Stop()
65: {
66: this.endTime = DateTime.Now;
67: string[] regularLinesFromLog = ConvertToLines(this.ReadFile(this.regularLogPath));
68: string[] errorLinesFromLog = ConvertToLines(this.ReadFile(this.errorLogPath));
69: string[] warningLinesFromLog = ConvertToLines(this.ReadFile(this.warningLogPath));
70: this.buildProjectNode.CompilationErrors = 0;
71: this.buildProjectNode.CompilationWarnings = 0;
72: DateTime nextMessageLogTime = this.startTime;
73: int timeBetweenMessages = Convert.ToInt32((this.endTime - this.startTime).TotalMilliseconds / regularLinesFromLog.Length);
74: foreach (string line in regularLinesFromLog)
75: {
76: if (!string.IsNullOrEmpty(line))
77: {
78: if (errorLinesFromLog.Contains(line))
79: {
80: this.buildProjectNode.Node.Children.AddBuildError(line, nextMessageLogTime);
81: this.buildProjectNode.CompilationErrors++;
82: }
83: else if (warningLinesFromLog.Contains(line))
84: {
85: this.buildProjectNode.Node.Children.AddBuildWarning(line, nextMessageLogTime);
86: this.buildProjectNode.CompilationWarnings++;
87: }
88: else
89: {
90: this.buildProjectNode.Node.Children.AddBuildMessage(line, BuildMessageImportance.Normal, nextMessageLogTime);
91: }
92: }
93: nextMessageLogTime.AddMilliseconds(timeBetweenMessages);
94: }
95: this.buildProjectNode.Node.Children.AddExternalLink("Detailed Log File", new Uri(this.detailedLogPath));
96: this.buildProjectNode.Node.Children.AddExternalLink("Error Log File", new Uri(this.errorLogPath));
97: this.buildProjectNode.Node.Children.AddExternalLink("Warning Log File", new Uri(this.warningLogPath));
98: this.buildProjectNode.Save();
99: this.detail.Information.Save();
100: this.detail.FinalizeStatus(errorLinesFromLog.Length == 1 && string.IsNullOrEmpty(errorLinesFromLog[0]) ? BuildStatus.Succeeded : BuildStatus.Failed);
101: }
102: private static string[] ConvertToLines(string sr)
103: {
104: return sr.Replace("\r",string.Empty).Split('\n');
105: }
106: private string ReadFile(string logPath)
107: {
108: using (FileStream fs = new FileStream(logPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
109: {
110: using (StreamReader sr = new StreamReader(fs))
111: {
112: return sr.ReadToEnd();
113: }
114: }
115: }
116: }
117: }
The last parts is just to include this task in your build and then you will have the info in TFS to display on your dashboard and view without having to bounce out to another tool.
For this we created a folder called tfs_config along side our build.proj file and then placed the dll of our fake build tasks in that folder as well as 2 files as below that will be used to kick off and complete the TFS Manual Build
tfs.build.tasks
1: <?xml version="1.0" encoding="utf-8"?>
2: <Project DefaultTargets="Default" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3: <UsingTask AssemblyFile="TfsBuildTask.dll" TaskName="TfsFakeBuildStart" />
4: <UsingTask AssemblyFile="TfsBuildTask.dll" TaskName="TfsFakeBuildFinish" />
5: </Project>
tfs.build.targets
1: <?xml version="1.0" encoding="utf-8"?>
2: <Project DefaultTargets="Default" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3: <Import Project="tfs.build.tasks" />
4: <PropertyGroup>
5: <BuildRoot>\\GORDON-PC\Demos\TfsFakeBuilds\</BuildRoot>
6: <DropLocation>\\GORDON-PC\Demos\TfsFakeBuilds\</DropLocation>
7: </PropertyGroup>
8: <Target Name="StartTfsBuild">
9: <TfsFakeBuildStart Architecture="x86" CollectionUri="http://TfsServerUri:8080/tfs" Configuration="Debug" DefinitionName="Demo Definition" DetailedLogPath="$(BuildRoot)build.detailed.log" DropLocation="$(DropLocation)" ErrorLogPath="$(BuildRoot)build.errors.log" PathFromBuildRoot="$(BuildRoot)build.bat" RegularLogPath="$(BuildRoot)build.log" ServerPath="$/TfsFakeBuilds/build.bat" TargetNames="Defaults" TeamProjectName="Demos" WarningLogPath="$(BuildRoot)build.warnings.log">
10: </TfsFakeBuildStart>
11: </Target>
12: <Target Name="EndTfsBuild" DependsOnTargets="StartTfsBuild;DoBuild">
13: <TfsFakeBuildFinish />
14: </Target>
15: </Project>
next we opened the build.proj file and added the line below above the target DoBuild
1: <import Project="tfs_config\tfs.build.targets" />
and for the last piece we duplicated the build.bat file so that full builds on dev machines would not log to TFS, all that we changed in that build.bat file was the target that we use with the msdbuild command to EndTfsBuild.
Any finally we could stay in TFS for build info once again and we got all the info you see below . Hope this helps somebody else as well.
Before I start I just want to give credit to Tiago Pascal for his help in getting me started with some of the basics and tips and tricks that got me start with the JavaScript extensions and to Alexander Vanwynsberghe for a debugging tip that makes this all a lot easier.
This post will give you a basic starting point for creating TFS Web Access JavaScript extension with most of it in proper TypeScript. This post will give a brief overview of the whole process and supply enough to get you started. In later posts I will go into the different sections in more detail and explain what bits of code do or how I found I was able to know that bits of code could work in the web access.
First you need a name for your extension, I have choose to use B1n4ryD1g1t.Tfs.Extensions so when ever you see that you will replace it with your name but I will try remember to point out the couple of places required.
The easiest way I find to create these plugins is to create a new HTML Application with TypeScript, this provides a good starting point and also makes it more familiar to me as it’s a solution and will allow me to build the project and any TypeScript errors will be revealed to me through the normal error list window.
Create 2 files (and remember to do the name replacing here ), manifest.xml and B1n4ryD1g1t.Tfs.Extensions.min.js and then rename the solutions current app.ts to be B1n4ryD1g1t.Tfs.Extensions.debug.ts. If you build the solution and also show all files in the solution folder you will notice that there is now a B1n4ryD1g1t.Tfs.Extensions.debug.js and B1n4ryD1g1t.Tfs.Extensions.debug.js.map file, you can include those in your solution. The last step of setting up the solution is to remove the app.css, default.htm and web.config as we won’t be needing these.
Next you need to use NuGet to add a reference to JQuery to your project
Now right click on your jquery-{version}.js file and click on Search for TypeScript Typings… and install the jquery.TypeScript.DefinitelyTyped typing.
Your solution should end up looking something like below, we aren’t going to physically reference these jquery files in our extension they were purely used to get the typings.
The mainifest.xml has the basic information for your extension that TFS will show on the extensions page in the web access.
The plugin node is self explanatory and for now you will leave the module nodes intact (remember to replace the name though). The 2 modules below allow our extension to run on the task and portfolio boards.
1: <WebAccess version="12.0">
2: <plugin name="B1n4ryD1g1t Tfs Extensions - Web Access" vendor="Gordon Beeming" moreinfo="https://gbeeming.wordpress.com" version="1.7">
3: <modules>
4: <module namespace="B1n4ryD1g1t.Tfs.Extensions" loadAfter="TFS.Agile.TaskBoard.View"/>
5: <module namespace="B1n4ryD1g1t.Tfs.Extensions" loadAfter="TFS.Agile.Boards.Controls"/>
6: </modules>
7: </plugin>
8: </WebAccess>
The minimum content for our extension will be plain Js and will leave 4 errors in our solution that we can ignore
The content for the extension will look like below
1: /// <reference path="Scripts/typings/jquery/jquery.d.ts" />
2: var __extends = this.__extends || function (d, b) {
3: function __() { this.constructor = d; }
4: __.prototype = b.prototype;
5: d.prototype = new __();
6: };
7: define(["require", "exports", "Presentation/Scripts/TFS/TFS", "Presentation/Scripts/TFS/TFS.Core", "Presentation/Scripts/TFS/TFS.OM", "Presentation/Scripts/TFS/TFS.UI.Controls", "WorkItemTracking/Scripts/TFS.WorkItemTracking"],
8: function (require1, exports, tfs, core, tfsOM, tfsUiControls, tfsWorkItemTracking) {
9: var TFS = tfs;
10: var Core = core;
11: var TFS_OM = tfsOM;
12: var TFS_UI_Controls = tfsUiControls;
13: var TFS_WorkItemTracking = tfsWorkItemTracking;
14: var B1n4ryD1g1tTfsExtension = (function (_super) {
15: __extends(B1n4ryD1g1tTfsExtension, _super);
16: function B1n4ryD1g1tTfsExtension(options) {
17: _super.call(this, options);
18: }
19: B1n4ryD1g1tTfsExtension.prototype.initializeOptions = function (options) {
20: _super.prototype.initializeOptions.call(this, $.extend({
21: }, options));
22: };
23: B1n4ryD1g1tTfsExtension.prototype.initialize = function () {
24: alert('this is running');
25: };
26: B1n4ryD1g1tTfsExtension._typeName = "B1n4ryD1g1t.Tfs.Extensions";
27: return B1n4ryD1g1tTfsExtension;
28: })(TFS_UI_Controls.BaseControl);
29: TFS.initClassPrototype(B1n4ryD1g1tTfsExtension, {});
30: TFS_UI_Controls.Enhancement.registerEnhancement(B1n4ryD1g1tTfsExtension, ".taskboard");
31: TFS_UI_Controls.Enhancement.registerEnhancement(B1n4ryD1g1tTfsExtension, ".agile-board");
32: });
Open your debug.js file and copy all of its contents to the min.js file and minify it, the web essentials extension for Visual Studio will help with this as it’s as easy as ctrl + alt + x. Open the folder where your extension is and zip the debug.js, min.js and mainifest.xml files.
Now you will need to upload your extension into TFS. Browse to the TFS Server Home, click on the admin settings button and then on extensions tab.Click Install, browse for your newly created zip file and click ok. Now all you need to do it click enable and ok to the warning. Now just refresh any of the task boards or portfolio boards and you will see an alert from inside the extension.
As you can see this is a very lengthy process to add the extension and none of that changes for checking for changes unless you use a trick that Alexander blogged about Debugging TFS Web Access Extensions. This will basically using fiddler tell your machine that when it requests the js file for your extension it must use the version js file that is in your solution instead of the one from TFS, this will speed up development a lot as you can just save the file which will trigger Visual Studio to update the debug.js file which refreshing TFS will now be loaded.
For the Editor I used the values
regex:http://labtfs01/_static/tfs/12/_scripts/TFS/.+//_plugins/.+/B1n4ryD1g1t.Tfs.Extensions.js with header:CachControl=no-cache and regex:http://labtfs01/_static/tfs/12/_scripts/TFS/.+//_plugins/.+/B1n4ryD1g1t.Tfs.Extensions.js with C:\c\r\Apps\TfsJsExtensions\TfsJsExtensions\B1n4ryD1g1t.Tfs.Extensions.debug.js. Similar to the blog post just adding a wild card for the debug/min difference in TFS.
To the bottom of you .ts file add the TypeScript code below (sorry about no full TS highlighting).
1: module B1n4ryD1g1tModule {
2: export class Core {
3: Require: any;
4: Exports: any;
5: TFS: any;
6: Core: any;
7: TFS_OM: any;
8: TFS_WorkItemTracking: any;
9: WorkItemManager: any;
10: CurrentlyFetchingWorkItems: boolean;
11: constructor(require1, exports, tfs, core, tfsom, tfsWorkItemTracking) {
12: this.Require = require1;
13: this.Exports = exports;
14: this.TFS = tfs;
15: this.Core = core;
16: this.TFS_OM = tfsom;
17: this.TFS_WorkItemTracking = tfsWorkItemTracking;
18: }
19: public init(): any {
20: this.initWorkItemManagerEvents();
21: var that = this;
22: window.setTimeout(function () {
23: if (that.isAgileBoard()) {
24: that.setAgileBoardIDs();
25: }
26: if (that.isTaskBoard()) {
27: that.setTaskBoardIDs();
28: }
29: }, 100);
30: }
31: private getCurrentTeamName(): string {
32: return this.TFS.Host.TfsContext.getDefault().currentTeam.name;
33: }
34: private isTaskBoard(): boolean {
35: return $(".taskboard").length > 0;
36: }
37: private isAgileBoard(): boolean {
38: return $(".agile-board").length > 0;
39: }
40: private setAgileBoardIDs(): void {
41: var idsToFetch = [];
42: $(".board-tile").each(function () {
43: var id = $(this).attr("data-item-id");
44: idsToFetch.push(parseInt(id));
45: });
46: this.loadWorkItems(idsToFetch, this.setAgileBoardIDsWork);
47: }
48: private setAgileBoardIDsWork(index, row, that: Core): void {
49: that.workWithAgileBoard(row[0], that);
50: }
51: private workWithAgileBoard(id, that: Core): void {
52: if (that.workWithAgileBoard_WorkItem(id)) {
53: }
54: }
55: private setTaskBoardIDs(): void {
56: var idsToFetch = [];
57: $("#taskboard-table .tbTile").each(function () {
58: var id = $(this).attr("id");
59: id = id.split('-')[1];
60: idsToFetch.push(parseInt(id));
61: });
62: $("#taskboard-table .taskboard-row .taskboard-parent").each(function () {
63: var id = $(this).attr("id");
64: if (id != undefined) {
65: id = id.split('_')[1];
66: id = id.substring(1);
67: idsToFetch.push(parseInt(id));
68: }
69: });
70: this.loadWorkItems(idsToFetch, this.setTaskBoardIDsWork);
71: }
72: private setTaskBoardIDsWork(index, row, that: Core): void {
73: if (that.workWithTaskBoard(row[0], row[1], that)) {
74: }
75: }
76: private workWithTaskBoard(id, state, that: Core): void {
77: if (that.workWithTaskBoard_Task(id)) {
78: }
79: if (that.workWithTaskBoard_Requirement(id)) {
80: that.taskboard_setRequirementState(id, state);
81: }
82: }
83: private workWithTaskBoard_Requirement(id): boolean {
84: return this.boards_setID("taskboard-table_p" + id, id);
85: }
86: private workWithTaskBoard_Task(id): boolean {
87: return this.boards_setID("tile-" + id, id);
88: }
89: private workWithAgileBoard_WorkItem(id): boolean {
90: var titleObj = $(".board-tile[data-item-id='" + id + "'] .title");
91: if ($(titleObj).length > 0 && $(titleObj).find(".TitleAdded").length == 0) {
92: var idHtml = "<span class='TitleAdded' style='font-weight:bold;'>" + id + "</span> - ";
93: $(titleObj).html(idHtml + $(titleObj).html());
94: return true;
95: }
96: return false;
97: }
98: private boards_setID(idTagLookFor, id): boolean {
99: var titleObj = $("#" + idTagLookFor + " .witTitle");
100: if ($(titleObj).length > 0 && $(titleObj).find(".TitleAdded").length == 0) {
101: var idHtml = "<span class='TitleAdded' style='font-weight:bold;'>" + id + "</span> - ";
102: $(titleObj).html(idHtml + $(titleObj).html());
103: return true;
104: }
105: return false;
106: }
107: private taskboard_setRequirementState(id, state): boolean {
108: var titleObj = $("#taskboard-table_p" + id + " .witTitle");
109: if ($(titleObj).length > 0 && $(titleObj).find(".StateAdded").length == 0) {
110: var stateHtml = "<br/><br/><span class='StateAdded' style='color:#505050;font-size:smaller;font-weight:bold;'>" + state + "</span>";
111: $(titleObj).html($(titleObj).html() + stateHtml);
112: return true;
113: }
114: return false;
115: }
116: private workItemChanged(sender, workItemChangedArgs): void {
117: if (workItemChangedArgs.change === this.TFS_WorkItemTracking.WorkItemChangeType.Reset || workItemChangedArgs.change === this.TFS_WorkItemTracking.WorkItemChangeType.SaveCompleted) {
118: var that = this;
119: var id = workItemChangedArgs.workItem.id;
120: var state = workItemChangedArgs.workItem.getFieldValue("System.State");
121: if (that.isTaskBoard()) {
122: window.setTimeout(function () {
123: that.workWithTaskBoard(id, state, that);
124: }, 100);
125: } else if (that.isAgileBoard()) {
126: window.setTimeout(function () {
127: that.workWithAgileBoard(id, that);
128: }, 100);
129: }
130: }
131: }
132: private loadWorkItems(idsToFetch: Array, onComplete): void {
133: var that = this;
134: that.loadWorkItemsWork(idsToFetch, onComplete, that);
135: }
136: private loadWorkItemsWork(idsToFetch: Array, onComplete, that: Core): void {
137: var takeAmount = 100;
138: if (takeAmount >= idsToFetch.length) {
139: takeAmount = idsToFetch.length;
140: }
141: if (takeAmount > 0) {
142: that.WorkItemManager.store.beginPageWorkItems(idsToFetch.splice(0, takeAmount), [
143: "System.Id",
144: "System.State"
145: ], function (payload) {
146: that.loadWorkItemsWork(idsToFetch, onComplete, that);
147: $.each(payload.rows, function (index, row) {
148: onComplete(index, row, that);
149: });
150: }, function (err) {
151: that.loadWorkItemsWork(idsToFetch, onComplete, that);
152: alert(err);
153: });
154: }
155: }
156: private initWorkItemManagerEvents(): void {
157: var service = this.TFS_OM.TfsTeamProjectCollection.getDefaultConnection().getService(this.TFS_WorkItemTracking.WorkItemStore);
158: this.WorkItemManager = service.workItemManager;
159: var that = this;
160: this.WorkItemManager.attachWorkItemChanged(function (sender, workItemChangedArgs) {
161: that.workItemChanged(sender, workItemChangedArgs);
162: });
163: }
164: }
165: }
This is all the code we will need to do the magic, all that is left is to wire it up to the extension. This can be done by replacing the method below in the original snippet for the extension.
1: B1n4ryD1g1tTfsExtension.prototype.initialize = function () {
2: var bdCore = new B1n4ryD1g1tModule.Core(require1, exports, TFS, Core, TFS_OM, TFS_WorkItemTracking);
3: bdCore.init();
4: };
If you fresh your page now and you have the debug tip running you will see that you have IDs on your work items and on the task board the requirement states are showing.
Before
After
Hope this sparks something in others as it has in me, As said in the intro I will be extending on what is covered in this article in later posts to provide more details.
You have probably noticed that with windows 8 when you disable UAC it doesn’t fully turn off. You are able to completely turn it off but that then disables the ability to use any of the Modern UI applications. So today I dug a bit and eventually managed to get a solution to make your application run as admin when being run from a click once deploy.
The way a user would be able to enable admin mode for a regular application is to Right click on the application and click ‘Run as administrator’
With click once applications you aren’t able to do this. I suppose you could find one of the many ways to locate the actual executable and then run as administrator but then every time the application updates you will need to locate the application again and also many users (including myself) won’t see this as a suitable way to launch an application as administrator.
After trying all the regular methods of enabling an application to run as full administrator I eventually got to the solution below.
Add these usings into the program.cs file
1: using System.ComponentModel;
2: using System.Linq;
Add the member and method to the program.cs class
1: private const uint BCM_SETSHIELD = 0x160C;
2:
3: [DllImport("user32", CharSet = CharSet.Auto, SetLastError = true)]
4: private static extern int SendMessage(IntPtr hWnd, uint Msg, int wParam, IntPtr lParam);
Add the parameter string[] args to the main method if it does not already exist
1: public static void Main(string[] args)
And finally wrap all the code inside you main method with
1: if (string.IsNullOrEmpty((from o in args where o == "--engage" select o).FirstOrDefault()))
2: {
3: var btnElevate = new Button();
4: btnElevate.FlatStyle = FlatStyle.System;
5:
6: SendMessage(btnElevate.Handle, BCM_SETSHIELD, 0, (IntPtr) 1);
7:
8: var processInfo = new ProcessStartInfo();
9: processInfo.Verb = "runas";
10: processInfo.FileName = Application.ExecutablePath;
11: processInfo.Arguments = string.Join(" ", args.Concat(new[] { "--engage" }).ToArray());
12: try
13: {
14: Process p = Process.Start(processInfo);
15: p.WaitForExit();
16: }
17: catch (Win32Exception)
18: {
19: //Do nothing. Probably the user cancelled the UAC window or provided invalid credentials.
20: }
21:
22: Application.Exit();
23: }
24: else
25: {
26: // place code that was in the main method here
27: }
That’s all you need to be able to run your application as administrator on launch. Basically what is going to happen is your application will start up and see that there is no command argument for –engage, it will then get it’s own executable path and attempt to run itself again using administrator mode. If a user has UAC enabled they will be prompted as usual to allow the application to run in admin mode and if they have UAC disable in windows 8 the application will now run if real administrator mode.
Email From Microsoft
Important Notice to our Microsoft Tag Customers
To our valued Microsoft Tag Customer,
This August 19, 2013 notice is to inform you that the Microsoft Tag service will terminate in two years, on August 19, 2015. We are providing this two year termination notice in accordance with our Terms of Use for the Microsoft Tag Service located at this link: http://tag.microsoft.com/tag-terms-of-use.aspx See Section 2 – Availability of Service; Changes to the Agreement & Service, paragraph 2.1.
Through August 19, 2015, you will be able to continue to log into your existing Microsoft Tag service account, use existing Microsoft Tag codes, generate new Microsoft Tags, and run reports as usual.
To help you prepare for the termination of the Microsoft Tag service on August 19, 2015, Scanbuy has been selected to support Microsoft Tag technology on the ScanLife platform beginning no later than September 18th, 2013, and to offer transition and migration services to Microsoft TAG customers who choose to migrate to the ScanLife platform. This transition path will help you to continue running your campaigns using Microsoft Tags on the ScanLife platform.
Scanbuy is the largest provider of QR codes and runs ScanLife, a cloud-based mobile engagement platform for creating personalized, uniquely tailored experiences for consumers to digitally engage with brands in their everyday surroundings through smartphones.
If you wish to learn more about the ScanLife platform you may contact Adam Gold, VP of Sales at adamg@scanbuy.com or call 212-278-0178 x 400.
We thank you for allowing us to serve you as our Microsoft Tag customer. If you have questions that Microsoft can assist you with please contact taginfo@microsoft.com.
Respectfully,
Microsoft
Eric Engstrom,
General Manager, Microsoft
Have you recently tried embedding an image into your signature where you would normally in older outlook versions create a signature in html format and then use that as your signature.
As you know this worked as expected in the older version and now in 2013 it doesn’t work anymore.
The simple way to fix this is to add an entry into your registry using the below inside a .reg file.
1:
2: Windows Registry Editor Version 5.00
3: [HKEY_CURRENT_USER\Software\Microsoft\Office\15.0\Outlook\Options\Mail]
4: "Send
5: Pictures With Document"=dword:00000001
That’s all you need, restart outlook and now when you send an email the images will be embedded into the mail.
Today I received a random error, the error message read “The type caught or thrown must be derived from System.Exception”. The reason why this was to me a random error was because I was trying to catch a Microsoft.TeamFoundation.WorkItemTracking.Client.ServerRejectedChangesException exception.
To try see if maybe this was a bug or maybe my pc needed a reboot or something I started drilling into the definitions of the exception to try get all the way through to System.Exception.
basically this looked like below
public class ServerRejectedChangesException : ValidationException
into
public class ValidationException : ClientException
into
public class ClientException : TeamFoundationServerException
into
public class TeamFoundationServerException : Microsoft.VisualStudio.Services.Common.VssException
At the TeamFoundationServerException class I noticed that the VssException was not lit up by Visual Studio which to me meant that I didn’t have a reference added to be able to drill into it’s definition like I was for the previous levels.
I added a reference to Microsoft.VisualStudio.Services.Common and suddenly the error been thrown when trying to build my project went away. Basically this allowed the IDE to navigate through to System.Exception like below.
public abstract class VssException : ApplicationException
into
public class ApplicationException : Exception
into
public class Exception : ISerializable, _Exception
It would be cool if this extra reference was not needed but I understand why it is .
There is an update for the preview of Visual Studio 2013, this was release a couple days ago. There are 2 ways to get the update.
The first look at the notifications in VS
The other way is to browse to Microsoft Download Center using the link below
http://www.microsoft.com/en-za/download/details.aspx?id=39710
Once you have downloaded the exe file, the installation is the same as the updates for VS 2012, also remember if you are wanting to install this update on multiple machines you can download all the files using the /layout command argument (see below) and then copy the files to each machine.
Open cmd.exe and navigate to the folder where you downloaded the exe update file to, then run “VS2013 Preview Update.exe” /layout
This will launch the update application prompting for a location to download the installation files to. Next Click Download and wait until the download completes.
Now that the files are complete launch the installer again without /layout and then the installer will use the downloaded files instead of downloading each file each time you run the installer.
This method can be used with all VS bootstrap installers and is not only a VS 2013 installer feature.
Documentation for this release can be found on the link http://documentation.azurewebsites.net/TfsApi/1.2/Default.aspx.
Features adding to this release
Administration
This will probably be the last update for a while but feel free to contact me for additions that you would like.
The Release can be found using the below link on codeplex