├── .vscode └── tasks.json ├── README.md ├── SECURITY.md ├── extension ├── .gitignore ├── details.md ├── gruntfile.js ├── img │ ├── Settings-hub-screenshot.png │ ├── Work-item-screenshot.png │ └── logo.png ├── package.json ├── readme.md ├── tsconfig.json ├── typings │ ├── jquery │ │ └── jquery.d.ts │ ├── knockout │ │ └── knockout.d.ts │ ├── q │ │ └── q.d.ts │ ├── requirejs │ │ └── require.d.ts │ └── tsd.d.ts ├── vss-extension.json ├── vsts-uservoice-ui-settings-hub │ ├── settings.html │ ├── settings.js │ └── settings.ts └── vsts-uservoice-ui-wi-group │ ├── UV.js │ ├── UV.ts │ ├── UVizGroup.html │ ├── addlink.gif │ ├── app.css │ ├── app.js │ └── app.ts ├── readme.png └── web-api ├── .gitignore ├── UViz-Api.sln ├── UViz-Api ├── .gitignore ├── App_Start │ └── WebApiConfig.cs ├── Controllers │ └── SuggestionController.cs ├── Global.asax ├── Global.asax.cs ├── Models │ ├── Comment.cs │ ├── Status.cs │ ├── Suggestion.cs │ └── UVApi │ │ ├── Comment.cs │ │ ├── Creator.cs │ │ ├── Forum.cs │ │ ├── RawCommentResponse.cs │ │ ├── RawSuggestionResponse.cs │ │ ├── ResponseData.cs │ │ ├── Suggestion.cs │ │ ├── SuggestionResponse.cs │ │ ├── SuggestionStatus.cs │ │ └── SuggestionTopic.cs ├── Properties │ ├── AssemblyInfo.cs │ └── PublishProfiles │ │ └── uviz - Web Deploy.pubxml ├── UViz-Api.csproj ├── UViz-Api.csproj.user ├── Web.Debug.config ├── Web.Release.config ├── Web.config └── packages.config └── packages ├── Microsoft.AspNet.WebApi.5.2.3 └── Microsoft.AspNet.WebApi.5.2.3.nupkg ├── Microsoft.AspNet.WebApi.Client.5.2.3 ├── Microsoft.AspNet.WebApi.Client.5.2.3.nupkg └── lib │ ├── net45 │ ├── System.Net.Http.Formatting.dll │ └── System.Net.Http.Formatting.xml │ └── portable-wp8%2Bnetcore45%2Bnet45%2Bwp81%2Bwpa81 │ ├── System.Net.Http.Formatting.dll │ └── System.Net.Http.Formatting.xml ├── Microsoft.AspNet.WebApi.Core.5.2.3 ├── Content │ └── web.config.transform ├── Microsoft.AspNet.WebApi.Core.5.2.3.nupkg └── lib │ └── net45 │ ├── System.Web.Http.dll │ └── System.Web.Http.xml ├── Microsoft.AspNet.WebApi.WebHost.5.2.3 ├── Microsoft.AspNet.WebApi.WebHost.5.2.3.nupkg └── lib │ └── net45 │ ├── System.Web.Http.WebHost.dll │ └── System.Web.Http.WebHost.xml ├── Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0 ├── Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0.nupkg ├── build │ └── Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props ├── content │ ├── web.config.install.xdt │ └── web.config.uninstall.xdt ├── lib │ └── net45 │ │ ├── Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll │ │ └── Microsoft.CodeDom.Providers.DotNetCompilerPlatform.xml └── tools │ ├── init.ps1 │ └── uninstall.ps1 ├── Microsoft.Net.Compilers.1.0.0 ├── Microsoft.Net.Compilers.1.0.0.nupkg ├── ThirdPartyNotices.rtf ├── build │ └── Microsoft.Net.Compilers.props └── tools │ ├── Microsoft.Build.Tasks.CodeAnalysis.dll │ ├── Microsoft.CSharp.Core.targets │ ├── Microsoft.CodeAnalysis.CSharp.dll │ ├── Microsoft.CodeAnalysis.VisualBasic.dll │ ├── Microsoft.CodeAnalysis.dll │ ├── Microsoft.VisualBasic.Core.targets │ ├── System.Collections.Immutable.dll │ ├── System.Reflection.Metadata.dll │ ├── VBCSCompiler.exe │ ├── VBCSCompiler.exe.config │ ├── csc.exe │ └── vbc.exe └── Newtonsoft.Json.6.0.4 ├── Newtonsoft.Json.6.0.4.nupkg ├── lib ├── net20 │ ├── Newtonsoft.Json.dll │ └── Newtonsoft.Json.xml ├── net35 │ ├── Newtonsoft.Json.dll │ └── Newtonsoft.Json.xml ├── net40 │ ├── Newtonsoft.Json.dll │ └── Newtonsoft.Json.xml ├── net45 │ ├── Newtonsoft.Json.dll │ └── Newtonsoft.Json.xml ├── netcore45 │ ├── Newtonsoft.Json.dll │ └── Newtonsoft.Json.xml ├── portable-net40%2Bsl5%2Bwp80%2Bwin8%2Bwpa81 │ ├── Newtonsoft.Json.dll │ └── Newtonsoft.Json.xml └── portable-net45%2Bwp80%2Bwin8%2Bwpa81 │ ├── Newtonsoft.Json.dll │ └── Newtonsoft.Json.xml └── tools └── install.ps1 /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // A task runner configuration. 2 | { 3 | "version": "0.1.0", 4 | "command": "grunt", 5 | "isShellCommand": true, 6 | "tasks": [ 7 | { 8 | "taskName": "build", 9 | "isBuildCommand": true, 10 | "problemMatcher": "$msCompile" 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sample extension: User Voice UI 2 | This repo is a sample for how to add contributions to the work item form using the extensibility model of Visual Studio Team Services. You can find the result of this extension in the [VSTS marketplace](https://marketplace.visualstudio.com/items?itemName=ms-devlabs.vsts-uservoice-ui). You can learn more about about the integration cababilities of VSTS in [our documentation](https://www.visualstudio.com/integrate/explore/explore-vso-vsi). 3 | 4 | ![Customer Feedback group on work item form](https://github.com/Microsoft/vsts-uservoice-ui-extension/blob/master/readme.png) 5 | 6 | This sample shows a couple of concepts that can help you to understand better how to integrate with VSTS. The concepts that are showcased in this sample are the following: 7 | * Add a group to the work item form 8 | * Add configuration options to your extensions, including a page in the admin area 9 | * Use a Web API project to allow calling an external resource that doesn't allow Cross Domain 10 | 11 | The sample contains the extension itself and a Web API project. The Web API project is hosted in the web-api folder and is using Visual Studio, and the extension itself is in the extension folder. 12 | 13 | ### Web API 14 | The project contains one controller which basically is nothing more than a proxy to call the User Voice APIs. User Voice has a v2 set of APIs, but they don't allow read-only access yet. And since the key that is used to call these APIs is not secured you don't want to use an api key that allows read/write operations. You can find more information about the User Voice APIs at https://developer.uservoice.com/docs/api/reference/. The Web API needs to be hosted in an environment which is publicly accessible when you install the extension on VSTS, and I chose to use Azure. You can publish a Web API in many different ways, including Continuous Deployment with [Release Management in VSTS](https://www.visualstudio.com/en-us/features/release-management-vs.aspx). In this sample I publish right from Visual Studio. 15 | 16 | To create an API key for your User Voice account, go to https://[account name].uservoice.com/admin/settings/api (don't forget to replace the [account name]). 17 | 18 | ### Extension 19 | The extension consists of a manifest (vss-extension.json) to describe the extension. That is basically the only file that is in the installed package. All other files are again hosted on Azure. You could have chosen to include them in the package so you don't have to host them yourself, but since I needed the Web API project anyway I chose to host the files on Azure too. When there is an update to the extension, I only need to update the files on Azure. 20 | 21 | The extension contains a couple of folders for the plumbing (like the images that you see in the marketplace), but the most important ones are vsts-extension-uviz-settings-hub and vsts-extension-uviz-wi-group. These folders contain the files that are used by the contribution points in the extension. The code should be pretty self explanatory, but if you have questions, don't hestitate to reach out to me. 22 | 23 | Ewald Hofman 24 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /extension/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | scripts/*.js 3 | *.vsix 4 | .ftppass -------------------------------------------------------------------------------- /extension/details.md: -------------------------------------------------------------------------------- 1 | # User Voice UI # 2 | 3 | As a product owner you want to add the features to your product that are most valuable to your customers. There are multiple channels to get feedback from your customers and one of them that scales to thousands of users is [User Voice](http://www.uservoice.com). The Microsoft Visual Studio Cloud Services organization is responsible for Visual Studio Team Services, and uses User Voice intensily as described in the blog post [How we use User Voice to make a better product](https://blogs.msdn.microsoft.com/visualstudioalm/2015/10/08/how-we-use-user-voice-to-make-a-better-product/). 4 | 5 | ![Customer Feedback group on work item form](https://raw.githubusercontent.com/Microsoft/vsts-uservoice-ui-extension/master/readme.png) 6 | 7 | In our backlogs, we add links to our Feature work items to these user voice items so we are aware of the customer value during prioritization, and to remember that we need to close the User Voice suggestion once it is deployed. Going through the links of all work items doesn't work, and that is where this extension comes in. It reads the work item links and parses the url. If the url is a User Voice suggestion, it will retrieve the title, votes and the state from User Voice and shows that data on the front page of your work item. 8 | 9 | As we will add more contribution points in the future that user voice data will show up on more experiences, like on the cards on the Kanban board and the results of the query. 10 | 11 | You can find the source code of this extension in [a GitHub repository](https://github.com/Microsoft/vsts-uservoice-ui-extension). 12 | 13 | Ewald Hofman 14 | 15 | > Microsoft DevLabs is an outlet for experiments from Microsoft, experiments that represent some of the latest ideas around developer tools. Solutions in this category are designed for broad usage and you are encouraged to use and provide feedback on them; however, these extensions are not supported nor are any commitments made as to their longevity. -------------------------------------------------------------------------------- /extension/gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | grunt.initConfig({ 3 | ts: { 4 | build: { 5 | src: ["scripts/**/*.ts"], 6 | tsconfig: true 7 | }, 8 | options: { 9 | fast: 'never' 10 | } 11 | }, 12 | exec: { 13 | package: { 14 | command: "tfx extension create --manifest-globs vss-extension.json", 15 | stdout: true, 16 | stderr: true 17 | }, 18 | publish: { 19 | command: "tfx extension publish --manifest-globs vss-extension.json --share-with ", 20 | stdout: true, 21 | stderr: true 22 | } 23 | }, 24 | copy: { 25 | scripts: { 26 | files: [{ 27 | expand: true, 28 | flatten: true, 29 | src: ["node_modules/vss-web-extension-sdk/lib/VSS.SDK.js"], 30 | dest: "scripts", 31 | filter: "isFile" 32 | }] 33 | } 34 | }, 35 | 36 | 'ftp-deploy': { 37 | build: { 38 | auth: { 39 | host: 'waws-prod-sn1-001.ftp.azurewebsites.windows.net', 40 | port: 21, 41 | authKey: 'uviz' 42 | }, 43 | src: '.', 44 | dest: 'site/wwwroot', 45 | exclusions: ['img', 'node_modules', "typings", ".ftppass", ".gitignore", "*.vsix", "*.md", "*.json", "gruntfile.js", "*.ts"] 46 | } 47 | }, 48 | 49 | clean: ["scripts/**/*.js", "*.vsix"] 50 | }); 51 | 52 | grunt.loadNpmTasks("grunt-ts"); 53 | grunt.loadNpmTasks("grunt-exec"); 54 | grunt.loadNpmTasks("grunt-contrib-copy"); 55 | grunt.loadNpmTasks('grunt-contrib-clean'); 56 | grunt.loadNpmTasks('grunt-ftp-deploy'); 57 | 58 | grunt.registerTask("build", ["ts:build", "copy:scripts"]); 59 | grunt.registerTask("package", ["clean", "build", "exec:package"]); 60 | grunt.registerTask("publish", ["default", "exec:publish"]); 61 | grunt.registerTask("deploy", ["ftp-deploy"]); 62 | 63 | grunt.registerTask("default", ["package"]); 64 | }; 65 | -------------------------------------------------------------------------------- /extension/img/Settings-hub-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/extension/img/Settings-hub-screenshot.png -------------------------------------------------------------------------------- /extension/img/Work-item-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/extension/img/Work-item-screenshot.png -------------------------------------------------------------------------------- /extension/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/extension/img/logo.png -------------------------------------------------------------------------------- /extension/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "grunt": "~0.4.5", 4 | "grunt-contrib-clean": "^1.0.0", 5 | "grunt-contrib-copy": "~0.8.2", 6 | "grunt-exec": "~0.4.6", 7 | "grunt-ftp-deploy": "^0.1.10", 8 | "tfx-cli": "^0.3.13", 9 | "tsd": "~0.6.5", 10 | "vss-web-extension-sdk": "^1.95.2" 11 | }, 12 | "name": "tags-mru", 13 | "private": true, 14 | "version": "0.0.0", 15 | "dependencies": { 16 | "grunt-ts": "^5.3.2" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /extension/readme.md: -------------------------------------------------------------------------------- 1 | This extension scans the relations in your work item for links to your User Voice account, and shows these suggestions as annotations on the work item form. Utilizes Web API, Azure, Typescript, grunt, and tsd. 2 | 3 | ### Structure ### 4 | 5 | ``` 6 | /scripts - VSS SDK files 7 | /img - Image assets for extension and description 8 | /typings - Typescript typings 9 | /vsts-uservoice-ui-settings-hub - files for the contribution to the Admin area to configure the settings 10 | /vsts-uservoice-ui-wi-group - files for the contribution on the work item form 11 | 12 | details.md - Description to be shown in marketplace 13 | vss-extension.json - Extension manifest 14 | ``` 15 | 16 | ### Usage ### 17 | 18 | 1. Clone the repository 19 | 2. Install node.js 20 | 3. `npm install` to install required dependencies 21 | 4. 'npm install -g grunt' to install grunt 22 | 5. 'npm install -g tfx-cli' 23 | 5. create ".ftppass" file if grunt needs to deploy the app using FTP (https://github.com/zonak/grunt-ftp-deploy) 24 | 6. `grunt` to build and package the application 25 | 26 | #### Grunt #### 27 | 28 | Three basic `grunt` tasks are defined: 29 | 30 | * `build` - Compiles TS files in `scripts` folder 31 | * `package` - Builds the vsix package 32 | * `publish` - Publishes the extension to the marketplace using `tfx-cli` 33 | * `deploy` - Uploads the files to Azure using FTP (add your credentials to the Azure FTP site in the file .ftppass in the extension folder) 34 | 35 | Note: To avoid `tfx` prompting for your token when publishing, login in beforehand using `tfx login` and the service uri of ` https://marketplace.visualstudio.com`. 36 | 37 | #### Including framework modules #### 38 | 39 | The VSTS framework is setup to initalize the requirejs AMD loader, so just use `import Foo = require("foo")` to include framework modules. 40 | -------------------------------------------------------------------------------- /extension/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "amd", 4 | "sourceMap": false 5 | }, 6 | "exclude": [ 7 | "node_modules" 8 | ] 9 | } -------------------------------------------------------------------------------- /extension/typings/q/q.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for Q 2 | // Project: https://github.com/kriskowal/q 3 | // Definitions by: Barrie Nemetchek , Andrew Gaspar , John Reilly 4 | // Definitions: https://github.com/borisyankov/DefinitelyTyped 5 | 6 | /** 7 | * If value is a Q promise, returns the promise. 8 | * If value is a promise from another library it is coerced into a Q promise (where possible). 9 | */ 10 | declare function Q(promise: Q.IPromise): Q.Promise; 11 | /** 12 | * If value is not a promise, returns a promise that is fulfilled with value. 13 | */ 14 | declare function Q(value: T): Q.Promise; 15 | 16 | declare module Q { 17 | interface IPromise { 18 | then(onFulfill?: (value: T) => U | IPromise, onReject?: (error: any) => U | IPromise): IPromise; 19 | } 20 | 21 | interface Deferred { 22 | promise: Promise; 23 | resolve(value: T): void; 24 | reject(reason: any): void; 25 | notify(value: any): void; 26 | makeNodeResolver(): (reason: any, value: T) => void; 27 | } 28 | 29 | interface Promise { 30 | /** 31 | * Like a finally clause, allows you to observe either the fulfillment or rejection of a promise, but to do so without modifying the final value. This is useful for collecting resources regardless of whether a job succeeded, like closing a database connection, shutting a server down, or deleting an unneeded key from an object. 32 | 33 | * finally returns a promise, which will become resolved with the same fulfillment value or rejection reason as promise. However, if callback returns a promise, the resolution of the returned promise will be delayed until the promise returned from callback is finished. 34 | */ 35 | fin(finallyCallback: () => any): Promise; 36 | /** 37 | * Like a finally clause, allows you to observe either the fulfillment or rejection of a promise, but to do so without modifying the final value. This is useful for collecting resources regardless of whether a job succeeded, like closing a database connection, shutting a server down, or deleting an unneeded key from an object. 38 | 39 | * finally returns a promise, which will become resolved with the same fulfillment value or rejection reason as promise. However, if callback returns a promise, the resolution of the returned promise will be delayed until the promise returned from callback is finished. 40 | */ 41 | finally(finallyCallback: () => any): Promise; 42 | 43 | /** 44 | * The then method from the Promises/A+ specification, with an additional progress handler. 45 | */ 46 | then(onFulfill?: (value: T) => U | IPromise, onReject?: (error: any) => U | IPromise, onProgress?: Function): Promise; 47 | 48 | /** 49 | * Like then, but "spreads" the array into a variadic fulfillment handler. If any of the promises in the array are rejected, instead calls onRejected with the first rejected promise's rejection reason. 50 | * 51 | * This is especially useful in conjunction with all 52 | */ 53 | spread(onFulfill: (...args: any[]) => IPromise | U, onReject?: (reason: any) => IPromise | U): Promise; 54 | 55 | fail(onRejected: (reason: any) => U | IPromise): Promise; 56 | 57 | /** 58 | * A sugar method, equivalent to promise.then(undefined, onRejected). 59 | */ 60 | catch(onRejected: (reason: any) => U | IPromise): Promise; 61 | 62 | /** 63 | * A sugar method, equivalent to promise.then(undefined, undefined, onProgress). 64 | */ 65 | progress(onProgress: (progress: any) => any): Promise; 66 | 67 | /** 68 | * Much like then, but with different behavior around unhandled rejection. If there is an unhandled rejection, either because promise is rejected and no onRejected callback was provided, or because onFulfilled or onRejected threw an error or returned a rejected promise, the resulting rejection reason is thrown as an exception in a future turn of the event loop. 69 | * 70 | * This method should be used to terminate chains of promises that will not be passed elsewhere. Since exceptions thrown in then callbacks are consumed and transformed into rejections, exceptions at the end of the chain are easy to accidentally, silently ignore. By arranging for the exception to be thrown in a future turn of the event loop, so that it won't be caught, it causes an onerror event on the browser window, or an uncaughtException event on Node.js's process object. 71 | * 72 | * Exceptions thrown by done will have long stack traces, if Q.longStackSupport is set to true. If Q.onerror is set, exceptions will be delivered there instead of thrown in a future turn. 73 | * 74 | * The Golden Rule of done vs. then usage is: either return your promise to someone else, or if the chain ends with you, call done to terminate it. 75 | */ 76 | done(onFulfilled?: (value: T) => any, onRejected?: (reason: any) => any, onProgress?: (progress: any) => any): void; 77 | 78 | /** 79 | * If callback is a function, assumes it's a Node.js-style callback, and calls it as either callback(rejectionReason) when/if promise becomes rejected, or as callback(null, fulfillmentValue) when/if promise becomes fulfilled. If callback is not a function, simply returns promise. 80 | */ 81 | nodeify(callback: (reason: any, value: any) => void): Promise; 82 | 83 | /** 84 | * Returns a promise to get the named property of an object. Essentially equivalent to 85 | * 86 | * promise.then(function (o) { 87 | * return o[propertyName]; 88 | * }); 89 | */ 90 | get(propertyName: String): Promise; 91 | set(propertyName: String, value: any): Promise; 92 | delete(propertyName: String): Promise; 93 | /** 94 | * Returns a promise for the result of calling the named method of an object with the given array of arguments. The object itself is this in the function, just like a synchronous method call. Essentially equivalent to 95 | * 96 | * promise.then(function (o) { 97 | * return o[methodName].apply(o, args); 98 | * }); 99 | */ 100 | post(methodName: String, args: any[]): Promise; 101 | /** 102 | * Returns a promise for the result of calling the named method of an object with the given variadic arguments. The object itself is this in the function, just like a synchronous method call. 103 | */ 104 | invoke(methodName: String, ...args: any[]): Promise; 105 | fapply(args: any[]): Promise; 106 | fcall(...args: any[]): Promise; 107 | 108 | /** 109 | * Returns a promise for an array of the property names of an object. Essentially equivalent to 110 | * 111 | * promise.then(function (o) { 112 | * return Object.keys(o); 113 | * }); 114 | */ 115 | keys(): Promise; 116 | 117 | /** 118 | * A sugar method, equivalent to promise.then(function () { return value; }). 119 | */ 120 | thenResolve(value: U): Promise; 121 | /** 122 | * A sugar method, equivalent to promise.then(function () { throw reason; }). 123 | */ 124 | thenReject(reason: any): Promise; 125 | 126 | /** 127 | * Attaches a handler that will observe the value of the promise when it becomes fulfilled, returning a promise for that same value, perhaps deferred but not replaced by the promise returned by the onFulfilled handler. 128 | */ 129 | tap(onFulfilled: (value: T) => any): Promise; 130 | 131 | timeout(ms: number, message?: string): Promise; 132 | /** 133 | * Returns a promise that will have the same result as promise, but will only be fulfilled or rejected after at least ms milliseconds have passed. 134 | */ 135 | delay(ms: number): Promise; 136 | 137 | /** 138 | * Returns whether a given promise is in the fulfilled state. When the static version is used on non-promises, the result is always true. 139 | */ 140 | isFulfilled(): boolean; 141 | /** 142 | * Returns whether a given promise is in the rejected state. When the static version is used on non-promises, the result is always false. 143 | */ 144 | isRejected(): boolean; 145 | /** 146 | * Returns whether a given promise is in the pending state. When the static version is used on non-promises, the result is always false. 147 | */ 148 | isPending(): boolean; 149 | 150 | valueOf(): any; 151 | 152 | /** 153 | * Returns a "state snapshot" object, which will be in one of three forms: 154 | * 155 | * - { state: "pending" } 156 | * - { state: "fulfilled", value: } 157 | * - { state: "rejected", reason: } 158 | */ 159 | inspect(): PromiseState; 160 | } 161 | 162 | interface PromiseState { 163 | /** 164 | * "fulfilled", "rejected", "pending" 165 | */ 166 | state: string; 167 | value?: T; 168 | reason?: any; 169 | } 170 | 171 | // If no value provided, returned promise will be of void type 172 | export function when(): Promise; 173 | 174 | // if no fulfill, reject, or progress provided, returned promise will be of same type 175 | export function when(value: T | IPromise): Promise; 176 | 177 | // If a non-promise value is provided, it will not reject or progress 178 | export function when(value: T | IPromise, onFulfilled: (val: T) => U | IPromise, onRejected?: (reason: any) => U | IPromise, onProgress?: (progress: any) => any): Promise; 179 | 180 | /** 181 | * Currently "impossible" (and I use the term loosely) to implement due to TypeScript limitations as it is now. 182 | * See: https://github.com/Microsoft/TypeScript/issues/1784 for discussion on it. 183 | */ 184 | // export function try(method: Function, ...args: any[]): Promise; 185 | 186 | export function fbind(method: (...args: any[]) => T | IPromise, ...args: any[]): (...args: any[]) => Promise; 187 | 188 | export function fcall(method: (...args: any[]) => T, ...args: any[]): Promise; 189 | 190 | export function send(obj: any, functionName: string, ...args: any[]): Promise; 191 | export function invoke(obj: any, functionName: string, ...args: any[]): Promise; 192 | export function mcall(obj: any, functionName: string, ...args: any[]): Promise; 193 | 194 | export function denodeify(nodeFunction: Function, ...args: any[]): (...args: any[]) => Promise; 195 | export function nbind(nodeFunction: Function, thisArg: any, ...args: any[]): (...args: any[]) => Promise; 196 | export function nfbind(nodeFunction: Function, ...args: any[]): (...args: any[]) => Promise; 197 | export function nfcall(nodeFunction: Function, ...args: any[]): Promise; 198 | export function nfapply(nodeFunction: Function, args: any[]): Promise; 199 | 200 | export function ninvoke(nodeModule: any, functionName: string, ...args: any[]): Promise; 201 | export function npost(nodeModule: any, functionName: string, args: any[]): Promise; 202 | export function nsend(nodeModule: any, functionName: string, ...args: any[]): Promise; 203 | export function nmcall(nodeModule: any, functionName: string, ...args: any[]): Promise; 204 | 205 | /** 206 | * Returns a promise that is fulfilled with an array containing the fulfillment value of each promise, or is rejected with the same rejection reason as the first promise to be rejected. 207 | */ 208 | export function all(promises: IPromise[]): Promise; 209 | 210 | /** 211 | * Returns a promise that is fulfilled with an array of promise state snapshots, but only after all the original promises have settled, i.e. become either fulfilled or rejected. 212 | */ 213 | export function allSettled(promises: IPromise[]): Promise[]>; 214 | 215 | export function allResolved(promises: IPromise[]): Promise[]>; 216 | 217 | /** 218 | * Like then, but "spreads" the array into a variadic fulfillment handler. If any of the promises in the array are rejected, instead calls onRejected with the first rejected promise's rejection reason. 219 | * This is especially useful in conjunction with all. 220 | */ 221 | export function spread(promises: IPromise[], onFulfilled: (...args: T[]) => U | IPromise, onRejected?: (reason: any) => U | IPromise): Promise; 222 | 223 | /** 224 | * Returns a promise that will have the same result as promise, except that if promise is not fulfilled or rejected before ms milliseconds, the returned promise will be rejected with an Error with the given message. If message is not supplied, the message will be "Timed out after " + ms + " ms". 225 | */ 226 | export function timeout(promise: Promise, ms: number, message?: string): Promise; 227 | 228 | /** 229 | * Returns a promise that will have the same result as promise, but will only be fulfilled or rejected after at least ms milliseconds have passed. 230 | */ 231 | export function delay(promise: Promise, ms: number): Promise; 232 | /** 233 | * Returns a promise that will have the same result as promise, but will only be fulfilled or rejected after at least ms milliseconds have passed. 234 | */ 235 | export function delay(value: T, ms: number): Promise; 236 | /** 237 | * Returns a promise that will be fulfilled with undefined after at least ms milliseconds have passed. 238 | */ 239 | export function delay(ms: number): Promise ; 240 | /** 241 | * Returns whether a given promise is in the fulfilled state. When the static version is used on non-promises, the result is always true. 242 | */ 243 | export function isFulfilled(promise: Promise): boolean; 244 | /** 245 | * Returns whether a given promise is in the rejected state. When the static version is used on non-promises, the result is always false. 246 | */ 247 | export function isRejected(promise: Promise): boolean; 248 | /** 249 | * Returns whether a given promise is in the pending state. When the static version is used on non-promises, the result is always false. 250 | */ 251 | export function isPending(promise: Promise): boolean; 252 | 253 | /** 254 | * Returns a "deferred" object with a: 255 | * promise property 256 | * resolve(value) method 257 | * reject(reason) method 258 | * notify(value) method 259 | * makeNodeResolver() method 260 | */ 261 | export function defer(): Deferred; 262 | 263 | /** 264 | * Returns a promise that is rejected with reason. 265 | */ 266 | export function reject(reason?: any): Promise; 267 | 268 | export function Promise(resolver: (resolve: (val: T | IPromise) => void , reject: (reason: any) => void , notify: (progress: any) => void ) => void ): Promise; 269 | 270 | /** 271 | * Creates a new version of func that accepts any combination of promise and non-promise values, converting them to their fulfillment values before calling the original func. The returned version also always returns a promise: if func does a return or throw, then Q.promised(func) will return fulfilled or rejected promise, respectively. 272 | * 273 | * This can be useful for creating functions that accept either promises or non-promise values, and for ensuring that the function always returns a promise even in the face of unintentional thrown exceptions. 274 | */ 275 | export function promised(callback: (...args: any[]) => T): (...args: any[]) => Promise; 276 | 277 | /** 278 | * Returns whether the given value is a Q promise. 279 | */ 280 | export function isPromise(object: any): boolean; 281 | /** 282 | * Returns whether the given value is a promise (i.e. it's an object with a then function). 283 | */ 284 | export function isPromiseAlike(object: any): boolean; 285 | /** 286 | * Returns whether a given promise is in the pending state. When the static version is used on non-promises, the result is always false. 287 | */ 288 | export function isPending(object: any): boolean; 289 | 290 | /** 291 | * This is an experimental tool for converting a generator function into a deferred function. This has the potential of reducing nested callbacks in engines that support yield. 292 | */ 293 | export function async(generatorFunction: any): (...args: any[]) => Promise; 294 | export function nextTick(callback: Function): void; 295 | 296 | /** 297 | * A settable property that will intercept any uncaught errors that would otherwise be thrown in the next tick of the event loop, usually as a result of done. Can be useful for getting the full stack trace of an error in browsers, which is not usually possible with window.onerror. 298 | */ 299 | export var onerror: (reason: any) => void; 300 | /** 301 | * A settable property that lets you turn on long stack trace support. If turned on, "stack jumps" will be tracked across asynchronous promise operations, so that if an uncaught error is thrown by done or a rejection reason's stack property is inspected in a rejection callback, a long stack trace is produced. 302 | */ 303 | export var longStackSupport: boolean; 304 | 305 | /** 306 | * Calling resolve with a pending promise causes promise to wait on the passed promise, becoming fulfilled with its fulfillment value or rejected with its rejection reason (or staying pending forever, if the passed promise does). 307 | * Calling resolve with a rejected promise causes promise to be rejected with the passed promise's rejection reason. 308 | * Calling resolve with a fulfilled promise causes promise to be fulfilled with the passed promise's fulfillment value. 309 | * Calling resolve with a non-promise value causes promise to be fulfilled with that value. 310 | */ 311 | export function resolve(object: IPromise): Promise; 312 | /** 313 | * Calling resolve with a pending promise causes promise to wait on the passed promise, becoming fulfilled with its fulfillment value or rejected with its rejection reason (or staying pending forever, if the passed promise does). 314 | * Calling resolve with a rejected promise causes promise to be rejected with the passed promise's rejection reason. 315 | * Calling resolve with a fulfilled promise causes promise to be fulfilled with the passed promise's fulfillment value. 316 | * Calling resolve with a non-promise value causes promise to be fulfilled with that value. 317 | */ 318 | export function resolve(object: T): Promise; 319 | 320 | /** 321 | * Resets the global "Q" variable to the value it has before Q was loaded. 322 | * This will either be undefined if there was no version or the version of Q which was already loaded before. 323 | * @returns { The last version of Q. } 324 | */ 325 | export function noConflict(): typeof Q; 326 | } 327 | 328 | declare module "q" { 329 | export = Q; 330 | } 331 | -------------------------------------------------------------------------------- /extension/typings/requirejs/require.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for RequireJS 2.1.8 2 | // Project: http://requirejs.org/ 3 | // Definitions by: Josh Baldwin 4 | // Definitions: https://github.com/borisyankov/DefinitelyTyped 5 | 6 | /* 7 | require-2.1.8.d.ts may be freely distributed under the MIT license. 8 | 9 | Copyright (c) 2013 Josh Baldwin https://github.com/jbaldwin/require.d.ts 10 | 11 | Permission is hereby granted, free of charge, to any person 12 | obtaining a copy of this software and associated documentation 13 | files (the "Software"), to deal in the Software without 14 | restriction, including without limitation the rights to use, 15 | copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | copies of the Software, and to permit persons to whom the 17 | Software is furnished to do so, subject to the following conditions: 18 | 19 | The above copyright notice and this permission notice shall be 20 | included in all copies or substantial portions of the Software. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 23 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 24 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 25 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 26 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 27 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 28 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 29 | OTHER DEALINGS IN THE SOFTWARE. 30 | */ 31 | 32 | declare module 'module' { 33 | var mod: { 34 | config: () => any; 35 | id: string; 36 | uri: string; 37 | } 38 | export = mod; 39 | } 40 | 41 | interface RequireError extends Error { 42 | 43 | /** 44 | * The error ID that maps to an ID on a web page. 45 | **/ 46 | requireType: string; 47 | 48 | /** 49 | * Required modules. 50 | **/ 51 | requireModules: string[]; 52 | 53 | /** 54 | * The original error, if there is one (might be null). 55 | **/ 56 | originalError: Error; 57 | } 58 | 59 | interface RequireShim { 60 | 61 | /** 62 | * List of dependencies. 63 | **/ 64 | deps?: string[]; 65 | 66 | /** 67 | * Name the module will be exported as. 68 | **/ 69 | exports?: string; 70 | 71 | /** 72 | * Initialize function with all dependcies passed in, 73 | * if the function returns a value then that value is used 74 | * as the module export value instead of the object 75 | * found via the 'exports' string. 76 | * @param dependencies 77 | * @return 78 | **/ 79 | init?: (...dependencies: any[]) => any; 80 | } 81 | 82 | interface RequireConfig { 83 | 84 | // The root path to use for all module lookups. 85 | baseUrl?: string; 86 | 87 | // Path mappings for module names not found directly under 88 | // baseUrl. 89 | paths?: { [key: string]: any; }; 90 | 91 | // Dictionary of Shim's. 92 | // does not cover case of key->string[] 93 | shim?: { [key: string]: RequireShim; }; 94 | 95 | /** 96 | * For the given module prefix, instead of loading the 97 | * module with the given ID, substitude a different 98 | * module ID. 99 | * 100 | * @example 101 | * requirejs.config({ 102 | * map: { 103 | * 'some/newmodule': { 104 | * 'foo': 'foo1.2' 105 | * }, 106 | * 'some/oldmodule': { 107 | * 'foo': 'foo1.0' 108 | * } 109 | * } 110 | * }); 111 | **/ 112 | map?: { 113 | [id: string]: { 114 | [id: string]: string; 115 | }; 116 | }; 117 | 118 | /** 119 | * AMD configurations, use module.config() to access in 120 | * define() functions 121 | **/ 122 | config?: { [id: string]: {}; }; 123 | 124 | /** 125 | * Configures loading modules from CommonJS packages. 126 | **/ 127 | packages?: {}; 128 | 129 | /** 130 | * The number of seconds to wait before giving up on loading 131 | * a script. The default is 7 seconds. 132 | **/ 133 | waitSeconds?: number; 134 | 135 | /** 136 | * A name to give to a loading context. This allows require.js 137 | * to load multiple versions of modules in a page, as long as 138 | * each top-level require call specifies a unique context string. 139 | **/ 140 | context?: string; 141 | 142 | /** 143 | * An array of dependencies to load. 144 | **/ 145 | deps?: string[]; 146 | 147 | /** 148 | * A function to pass to require that should be require after 149 | * deps have been loaded. 150 | * @param modules 151 | **/ 152 | callback?: (...modules: any[]) => void; 153 | 154 | /** 155 | * If set to true, an error will be thrown if a script loads 156 | * that does not call define() or have shim exports string 157 | * value that can be checked. 158 | **/ 159 | enforceDefine?: boolean; 160 | 161 | /** 162 | * If set to true, document.createElementNS() will be used 163 | * to create script elements. 164 | **/ 165 | xhtml?: boolean; 166 | 167 | /** 168 | * Extra query string arguments appended to URLs that RequireJS 169 | * uses to fetch resources. Most useful to cachce bust when 170 | * the browser or server is not configured correcty. 171 | * 172 | * @example 173 | * urlArgs: "bust= + (new Date()).getTime() 174 | **/ 175 | urlArgs?: string; 176 | 177 | /** 178 | * Specify the value for the type="" attribute used for script 179 | * tags inserted into the document by RequireJS. Default is 180 | * "text/javascript". To use Firefox's JavasScript 1.8 181 | * features, use "text/javascript;version=1.8". 182 | **/ 183 | scriptType?: string; 184 | 185 | } 186 | 187 | // todo: not sure what to do with this guy 188 | interface RequireModule { 189 | 190 | /** 191 | * 192 | **/ 193 | config(): {}; 194 | 195 | } 196 | 197 | /** 198 | * 199 | **/ 200 | interface RequireMap { 201 | 202 | /** 203 | * 204 | **/ 205 | prefix: string; 206 | 207 | /** 208 | * 209 | **/ 210 | name: string; 211 | 212 | /** 213 | * 214 | **/ 215 | parentMap: RequireMap; 216 | 217 | /** 218 | * 219 | **/ 220 | url: string; 221 | 222 | /** 223 | * 224 | **/ 225 | originalName: string; 226 | 227 | /** 228 | * 229 | **/ 230 | fullName: string; 231 | } 232 | 233 | interface Require { 234 | 235 | /** 236 | * Configure require.js 237 | **/ 238 | config(config: RequireConfig): Require; 239 | 240 | /** 241 | * CommonJS require call 242 | * @param module Module to load 243 | * @return The loaded module 244 | */ 245 | (module: string): any; 246 | 247 | /** 248 | * Start the main app logic. 249 | * Callback is optional. 250 | * Can alternatively use deps and callback. 251 | * @param modules Required modules to load. 252 | **/ 253 | (modules: string[]): void; 254 | 255 | /** 256 | * @see Require() 257 | * @param ready Called when required modules are ready. 258 | **/ 259 | (modules: string[], ready: Function): void; 260 | 261 | /** 262 | * @see http://requirejs.org/docs/api.html#errbacks 263 | * @param ready Called when required modules are ready. 264 | **/ 265 | (modules: string[], ready: Function, errback: Function): void; 266 | 267 | /** 268 | * Generate URLs from require module 269 | * @param module Module to URL 270 | * @return URL string 271 | **/ 272 | toUrl(module: string): string; 273 | 274 | /** 275 | * Returns true if the module has already been loaded and defined. 276 | * @param module Module to check 277 | **/ 278 | defined(module: string): boolean; 279 | 280 | /** 281 | * Returns true if the module has already been requested or is in the process of loading and should be available at some point. 282 | * @param module Module to check 283 | **/ 284 | specified(module: string): boolean; 285 | 286 | /** 287 | * On Error override 288 | * @param err 289 | **/ 290 | onError(err: RequireError, errback?: (err: RequireError) => void): void; 291 | 292 | /** 293 | * Undefine a module 294 | * @param module Module to undefine. 295 | **/ 296 | undef(module: string): void; 297 | 298 | /** 299 | * Semi-private function, overload in special instance of undef() 300 | **/ 301 | onResourceLoad(context: Object, map: RequireMap, depArray: RequireMap[]): void; 302 | } 303 | 304 | interface RequireDefine { 305 | 306 | /** 307 | * Define Simple Name/Value Pairs 308 | * @param config Dictionary of Named/Value pairs for the config. 309 | **/ 310 | (config: { [key: string]: any; }): void; 311 | 312 | /** 313 | * Define function. 314 | * @param func: The function module. 315 | **/ 316 | (func: () => any): void; 317 | 318 | /** 319 | * Define function with dependencies. 320 | * @param deps List of dependencies module IDs. 321 | * @param ready Callback function when the dependencies are loaded. 322 | * callback param deps module dependencies 323 | * callback return module definition 324 | **/ 325 | (deps: string[], ready: Function): void; 326 | 327 | /** 328 | * Define module with simplified CommonJS wrapper. 329 | * @param ready 330 | * callback require requirejs instance 331 | * callback exports exports object 332 | * callback module module 333 | * callback return module definition 334 | **/ 335 | (ready: (require: Require, exports: { [key: string]: any; }, module: RequireModule) => any): void; 336 | 337 | /** 338 | * Define a module with a name and dependencies. 339 | * @param name The name of the module. 340 | * @param deps List of dependencies module IDs. 341 | * @param ready Callback function when the dependencies are loaded. 342 | * callback deps module dependencies 343 | * callback return module definition 344 | **/ 345 | (name: string, deps: string[], ready: Function): void; 346 | 347 | /** 348 | * Define a module with a name. 349 | * @param name The name of the module. 350 | * @param ready Callback function when the dependencies are loaded. 351 | * callback return module definition 352 | **/ 353 | (name: string, ready: Function): void; 354 | 355 | /** 356 | * Used to allow a clear indicator that a global define function (as needed for script src browser loading) conforms 357 | * to the AMD API, any global define function SHOULD have a property called "amd" whose value is an object. 358 | * This helps avoid conflict with any other existing JavaScript code that could have defined a define() function 359 | * that does not conform to the AMD API. 360 | * define.amd.jQuery is specific to jQuery and indicates that the loader is able to account for multiple version 361 | * of jQuery being loaded simultaneously. 362 | */ 363 | amd: Object; 364 | } 365 | 366 | // Ambient declarations for 'require' and 'define' 367 | declare var requirejs: Require; 368 | declare var require: Require; 369 | declare var define: RequireDefine; 370 | -------------------------------------------------------------------------------- /extension/typings/tsd.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | -------------------------------------------------------------------------------- /extension/vss-extension.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifestVersion": 1.0, 3 | "id": "vsts-uservoice-ui", 4 | "version": "1.0.3", 5 | "name": "User Voice UI", 6 | "description": "Shows rich data of the linked User Voice suggestions on the work item form", 7 | "longDescription": "With User Voice you can turn customer engagement and feedback from thousands, or even millions, of users into smart product decisions - all without turning your backlog into a hurricane. With this extension you will be able to show rich data (state, votes) of the linked user voice items on your work item form to seamlessly connect your community with your engineering team.", 8 | "baseUri": "https://uviz.azurewebsites.net", 9 | "publisher": "ms-devlabs", 10 | "public": true, 11 | "icons": { 12 | "default": "img/logo.png" 13 | }, 14 | "content": { 15 | "details": { 16 | "path": "details.md" 17 | } 18 | }, 19 | "tags": [ 20 | "customer", 21 | "feedback" 22 | ], 23 | "categories": [ 24 | "Plan and track" 25 | ], 26 | "links": { 27 | "getstarted": { 28 | "uri": "https://www.uservoice.com/for/visual-studio-online/" 29 | }, 30 | "support": { 31 | "uri": "mailto://ewald.hofman@microsoft.com?subject=Support requested for vsts-uservoice-ui" 32 | }, 33 | "learn": { 34 | "uri": "https://www.uservoice.com/tour/helpdesk/" 35 | }, 36 | "pricing": { 37 | "uri": "https://www.uservoice.com/plans/product-management/" 38 | } 39 | }, 40 | "branding": { 41 | "color": "rgb(218, 118, 27)", 42 | "theme": "dark" 43 | }, 44 | "targets": [ 45 | { 46 | "id": "Microsoft.VisualStudio.Services" 47 | } 48 | ], 49 | "scopes": ["vso.work", "vso.work_write"], 50 | "contributions": [ 51 | { 52 | "id": "vsts-uservoice-ui-wi-group", 53 | "type": "ms.vss-work-web.work-item-form-group", 54 | "description": "Shows User Voice details on the work item form", 55 | "targets": [ 56 | "ms.vss-work-web.work-item-form" 57 | ], 58 | "properties": { 59 | "name": "Customer feedback", 60 | "uri": "vsts-uservoice-ui-wi-group/UVizGroup.html" 61 | } 62 | }, 63 | { 64 | "id": "vsts-uservoice-ui-settings-hub", 65 | "type": "ms.vss-web.hub", 66 | "description": "Settings for the User Voice Visualization extension", 67 | "targets": [ 68 | "ms.vss-web.collection-admin-hub-group" 69 | ], 70 | "properties": { 71 | "name": "UserVoice UI settings", 72 | "order": 100, 73 | "uri": "vsts-uservoice-ui-settings-hub/settings.html" 74 | } 75 | } 76 | ] 77 | } -------------------------------------------------------------------------------- /extension/vsts-uservoice-ui-settings-hub/settings.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 33 | 34 | 35 | 51 |
52 |
53 | 54 | 55 | 56 | 57 |
58 | 59 |
60 | 61 | 62 | 63 | 64 |
65 | 66 |
67 | 68 | 69 | 70 | 71 |
72 | 73 |
74 | 75 | 76 | 77 | Retrieving settings... 78 | 79 |
80 | 81 | -------------------------------------------------------------------------------- /extension/vsts-uservoice-ui-settings-hub/settings.js: -------------------------------------------------------------------------------- 1 | define(["require", "exports", "q"], function (require, exports, Q) { 2 | "use strict"; 3 | var UVizSettings = (function () { 4 | function UVizSettings(accountName, apiKey, tag) { 5 | this.accountName = accountName; 6 | this.apiKey = apiKey; 7 | this.tag = tag; 8 | } 9 | return UVizSettings; 10 | }()); 11 | exports.UVizSettings = UVizSettings; 12 | var Settings = (function () { 13 | function Settings() { 14 | this.scope = "Default"; 15 | this.accountNameSetting = "accountname"; 16 | this.apiKeySetting = "apikey"; 17 | this.tagSetting = "tag"; 18 | this.unknownValue = ""; 19 | this.accountNameSelector = "#account-name"; 20 | this.apiKeySelector = "#api-key"; 21 | this.tagSelector = "#tag"; 22 | this.saveButtonSelector = "#save-button"; 23 | this.saveResultSelector = "#save-result"; 24 | } 25 | Settings.prototype.initializeUI = function () { 26 | var _this = this; 27 | $(this.saveButtonSelector).on('click', function (eventObject) { 28 | _this._saveSettings(); 29 | }); 30 | this._populate(); 31 | }; 32 | Settings.prototype.urlToConfigureSettings = function () { 33 | return VSS.getWebContext().collection.uri + "_admin/_apps/hub/ms-devlabs.vsts-uservoice-ui.vsts-uservoice-ui-settings-hub"; 34 | }; 35 | Settings.prototype.getSettings = function (rejectOnSettingNotAvailable) { 36 | var _this = this; 37 | var defer = Q.defer(); 38 | var resetUnknownValue = function (value) { return value === _this.unknownValue ? "" : value; }; 39 | VSS.getService(VSS.ServiceIds.ExtensionData).then(function (dataService) { 40 | Q.all([ 41 | dataService.getValue(_this.accountNameSetting, { scopeType: _this.scope, defaultValue: _this.unknownValue }), 42 | dataService.getValue(_this.apiKeySetting, { scopeType: _this.scope, defaultValue: _this.unknownValue }), 43 | dataService.getValue(_this.tagSetting, { scopeType: _this.scope, defaultValue: "" }) 44 | ]).spread(function (accountName, apiKey, tag) { 45 | if (rejectOnSettingNotAvailable && (accountName === _this.unknownValue || apiKey === _this.unknownValue)) { 46 | defer.reject("You need to configure this extension before you can use it."); 47 | } 48 | else { 49 | defer.resolve(new UVizSettings(resetUnknownValue(accountName), resetUnknownValue(apiKey), tag)); 50 | } 51 | }).fail(function (reason) { 52 | defer.reject(reason); 53 | }); 54 | }); 55 | return defer.promise; 56 | }; 57 | Settings.prototype._saveSettings = function () { 58 | var _this = this; 59 | $(this.saveResultSelector).text("Saving settings..."); 60 | var accountName = $(this.accountNameSelector).val(); 61 | var apiKey = $(this.apiKeySelector).val(); 62 | var tag = $(this.tagSelector).val(); 63 | VSS.getService(VSS.ServiceIds.ExtensionData).then(function (dataService) { 64 | dataService.setValue(_this.accountNameSetting, accountName, { scopeType: _this.scope }).then(function (value) { }); 65 | dataService.setValue(_this.apiKeySetting, apiKey, { scopeType: _this.scope }).then(function (value) { }); 66 | dataService.setValue(_this.tagSetting, tag, { scopeType: _this.scope }).then(function (value) { }); 67 | $(_this.saveResultSelector).text("The settings are saved."); 68 | setTimeout(function () { $(_this.saveResultSelector).text(""); }, 2000); 69 | }); 70 | }; 71 | Settings.prototype._populate = function () { 72 | var _this = this; 73 | this.getSettings(false).done(function (settings) { 74 | $(_this.accountNameSelector).val(settings.accountName).prop("disabled", false); 75 | $(_this.apiKeySelector).val(settings.apiKey).prop("disabled", false); 76 | $(_this.tagSelector).val(settings.tag).prop("disabled", false); 77 | $(_this.saveResultSelector).text(""); 78 | $(_this.saveButtonSelector).prop("disabled", false); 79 | }); 80 | }; 81 | return Settings; 82 | }()); 83 | exports.Settings = Settings; 84 | }); 85 | -------------------------------------------------------------------------------- /extension/vsts-uservoice-ui-settings-hub/settings.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import Extension_Data = require("VSS/SDK/Services/ExtensionData"); 4 | import Q = require("q"); 5 | 6 | export class UVizSettings { 7 | 8 | constructor( 9 | public accountName: string, 10 | public apiKey: string, 11 | public tag: string 12 | ) { } 13 | 14 | } 15 | 16 | export class Settings { 17 | 18 | private scope = "Default"; // can either be User or Default 19 | private accountNameSetting = "accountname"; 20 | private apiKeySetting = "apikey"; 21 | private tagSetting = "tag"; 22 | private unknownValue = ""; 23 | private accountNameSelector = "#account-name"; 24 | private apiKeySelector = "#api-key"; 25 | private tagSelector = "#tag"; 26 | private saveButtonSelector = "#save-button"; 27 | private saveResultSelector = "#save-result"; 28 | 29 | /** 30 | * Register the eventhandler for the save button and populate the input controls 31 | * with the previously stored settings 32 | */ 33 | public initializeUI(): void { 34 | // register the event handler for the Save button 35 | $(this.saveButtonSelector).on('click', (eventObject) => { 36 | this._saveSettings(); 37 | }); 38 | 39 | // populate the input controls when the page loads 40 | this._populate(); 41 | } 42 | 43 | public urlToConfigureSettings(): string { 44 | return `${VSS.getWebContext().collection.uri}_admin/_apps/hub/ms-devlabs.vsts-uservoice-ui.vsts-uservoice-ui-settings-hub`; 45 | } 46 | 47 | /** 48 | * Get the retrieval of the settings started and return the promise. 49 | * 50 | * @rejectOnSettingNotAvailable: when true, the promise is rejected when the setting is not configured yet. Else just give back the value. 51 | */ 52 | public getSettings(rejectOnSettingNotAvailable: boolean): Q.Promise { 53 | var defer = Q.defer(); 54 | 55 | // function that return the empty string when the setting wasn't set yet 56 | var resetUnknownValue = (value: string): string => {return value === this.unknownValue ? "" : value} 57 | 58 | VSS.getService(VSS.ServiceIds.ExtensionData).then((dataService: Extension_Data.ExtensionDataService) => { 59 | 60 | // get the value of both settings, and when the value for the setting isn't set yet, then return the default value which is 61 | Q.all([ 62 | dataService.getValue(this.accountNameSetting, {scopeType: this.scope, defaultValue: this.unknownValue}), 63 | dataService.getValue(this.apiKeySetting, {scopeType: this.scope, defaultValue: this.unknownValue}), 64 | dataService.getValue(this.tagSetting, {scopeType: this.scope, defaultValue: ""}) 65 | ]).spread((accountName: string, apiKey: string, tag: string) => { 66 | 67 | // when the caller wants to reject the promise when the setting is not configured yet (which is the contribution on the work item form) 68 | if (rejectOnSettingNotAvailable && (accountName === this.unknownValue || apiKey === this.unknownValue)) { 69 | 70 | // then reject the promise and provide actionable information, to navigate to the admin page to configure the settings 71 | defer.reject(`You need to configure this extension before you can use it.`) 72 | 73 | } else { 74 | 75 | // if either the caller wants to get settings even when it doesn't exist yet (which is the admin page to configure the 76 | // settings), or when the settings are successfully retrieved, resolve the promise 77 | defer.resolve(new UVizSettings(resetUnknownValue(accountName), resetUnknownValue(apiKey), tag)); 78 | } 79 | }).fail((reason: any): void => { 80 | defer.reject(reason); 81 | }); 82 | }); 83 | 84 | return defer.promise; 85 | } 86 | 87 | /** 88 | * Save the settings in the data storage 89 | */ 90 | private _saveSettings() { 91 | 92 | // give the user feedback the settings are being saved 93 | $(this.saveResultSelector).text("Saving settings..."); 94 | 95 | // get the value of the input controls 96 | const accountName = $(this.accountNameSelector).val(); 97 | const apiKey = $(this.apiKeySelector).val(); 98 | const tag = $(this.tagSelector).val(); 99 | 100 | // store the values 101 | VSS.getService(VSS.ServiceIds.ExtensionData).then((dataService: Extension_Data.ExtensionDataService) => { 102 | dataService.setValue(this.accountNameSetting, accountName, {scopeType: this.scope}).then((value: string) => {}); 103 | dataService.setValue(this.apiKeySetting, apiKey, {scopeType: this.scope}).then((value: string) => {}); 104 | dataService.setValue(this.tagSetting, tag, {scopeType: this.scope}).then((value: string) => {}); 105 | 106 | $(this.saveResultSelector).text("The settings are saved."); 107 | setTimeout(() => {$(this.saveResultSelector).text(""); }, 2000); 108 | }); 109 | } 110 | 111 | /** 112 | * Get the settings from the data storage. When the settings are retrieved, populate the 113 | * input controls and enable them as well as the save button 114 | */ 115 | private _populate() { 116 | // retrieve the settings async, and when retrieved ... 117 | this.getSettings(false).done((settings: UVizSettings) => { 118 | 119 | // ... then set the values and enable the inputs ... 120 | $(this.accountNameSelector).val(settings.accountName).prop("disabled", false); 121 | $(this.apiKeySelector).val(settings.apiKey).prop("disabled", false); 122 | $(this.tagSelector).val(settings.tag).prop("disabled", false); 123 | 124 | // ... and enable the save button 125 | $(this.saveResultSelector).text(""); 126 | $(this.saveButtonSelector).prop("disabled", false); 127 | }); 128 | } 129 | } -------------------------------------------------------------------------------- /extension/vsts-uservoice-ui-wi-group/UV.js: -------------------------------------------------------------------------------- 1 | define(["require", "exports", "q", "vsts-uservoice-ui-settings-hub/settings"], function (require, exports, Q, Settings) { 2 | "use strict"; 3 | var UserVoiceSuggestion = (function () { 4 | function UserVoiceSuggestion(id, title, description, description_html, url, votes, status, response, response_html, response_date, total_comments, most_recent_comments) { 5 | this.id = id; 6 | this.title = title; 7 | this.description = description; 8 | this.description_html = description_html; 9 | this.url = url; 10 | this.votes = votes; 11 | this.status = status; 12 | this.response = response; 13 | this.response_html = response_html; 14 | this.response_date = response_date; 15 | this.total_comments = total_comments; 16 | this.most_recent_comments = most_recent_comments; 17 | } 18 | return UserVoiceSuggestion; 19 | }()); 20 | exports.UserVoiceSuggestion = UserVoiceSuggestion; 21 | var UserVoiceComment = (function () { 22 | function UserVoiceComment(created_by, created_at, text, html) { 23 | this.created_by = created_by; 24 | this.created_at = created_at; 25 | this.text = text; 26 | this.html = html; 27 | } 28 | return UserVoiceComment; 29 | }()); 30 | exports.UserVoiceComment = UserVoiceComment; 31 | var Services = (function () { 32 | function Services(workItemFormService) { 33 | this.workItemFormService = workItemFormService; 34 | } 35 | Services.prototype.linkedUVSuggestions = function () { 36 | var self = this; 37 | var defer = Q.defer(); 38 | var settings = new Settings.Settings(); 39 | var q = Q.all([ 40 | settings.getSettings(true), 41 | this.workItemFormService.getWorkItemRelations() 42 | ]) 43 | .spread(function (settings, relations) { 44 | var userVoiceItems = relations 45 | .map(function (relation) { 46 | return Services.extractIdFromUrl(relation.url); 47 | }) 48 | .filter(function (item) { 49 | return item.id !== null; 50 | }) 51 | .map(function (item) { 52 | return self._getUserVoiceInfo(settings.accountName, settings.apiKey, item.id); 53 | }); 54 | Q.all(userVoiceItems).then(function (suggestions) { 55 | defer.resolve(suggestions); 56 | }).fail(function (reason) { 57 | defer.reject(reason); 58 | }); 59 | }) 60 | .fail(function (reason) { 61 | defer.reject(reason); 62 | }); 63 | return defer.promise; 64 | }; 65 | Services.extractIdFromUrl = function (url) { 66 | var pattern = "/forums/[0-9]+.*/suggestions/([0-9]+)|/admin/v[23]/suggestions/([0-9]+)"; 67 | var matches = url.match(pattern); 68 | if (matches && matches.length > 2) { 69 | return { id: matches[1] || matches[2] }; 70 | } 71 | else { 72 | return { id: null }; 73 | } 74 | }; 75 | Services.prototype.idExists = function (id) { 76 | var self = this; 77 | var defer = Q.defer(); 78 | var settings = new Settings.Settings(); 79 | var q = Q.all([ 80 | settings.getSettings(true) 81 | ]) 82 | .spread(function (settings) { 83 | self._getUserVoiceInfo(settings.accountName, settings.apiKey, id) 84 | .done(function (suggestion) { defer.resolve(suggestion); }, function () { defer.resolve(null); }); 85 | }) 86 | .fail(function (reason) { 87 | defer.reject(reason); 88 | }); 89 | return defer.promise; 90 | }; 91 | Services.prototype._getUserVoiceInfo = function (accountName, apiKey, id) { 92 | var defer = Q.defer(); 93 | $.ajax({ 94 | type: "GET", 95 | url: "../api/Suggestion/" + id + "?accountName=" + accountName + "&apikey=" + apiKey 96 | }).done(function (data) { 97 | if (!data.url) { 98 | defer.reject("item is not found for id " + id); 99 | } 100 | else if (data.id) { 101 | defer.resolve(new UserVoiceSuggestion(data.id, data.title, data.description, data.description_html, data.url, data.votes, { 102 | name: data.status.name, 103 | hex_color: data.status.hex_color 104 | }, data.response, data.response_html, data.response_date, data.total_comments, data.most_recent_comments)); 105 | } 106 | else { 107 | var settings = new Settings.Settings; 108 | var reason = "Unable to retrieve the data from User Voice (reason: " + data.title + "). Please make sure the api key is configured correctly"; 109 | defer.reject(reason); 110 | } 111 | }).fail(function (error) { 112 | defer.reject(error.responseText); 113 | }); 114 | return defer.promise; 115 | }; 116 | return Services; 117 | }()); 118 | exports.Services = Services; 119 | }); 120 | -------------------------------------------------------------------------------- /extension/vsts-uservoice-ui-wi-group/UV.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | import WitServices = require("TFS/WorkItemTracking/Services"); 5 | import WitContracts = require("TFS/WorkItemTracking/Contracts"); 6 | import Q = require("q"); 7 | import Settings = require("vsts-uservoice-ui-settings-hub/settings"); 8 | 9 | export class UserVoiceSuggestion { 10 | 11 | public constructor( 12 | public id: number, 13 | public title: string, 14 | public description: string, 15 | public description_html: string, 16 | public url: string, 17 | public votes: number, 18 | public status: { 19 | name: string; 20 | hex_color: string; 21 | }, 22 | public response: string, 23 | public response_html: string, 24 | public response_date: string, 25 | public total_comments: number, 26 | public most_recent_comments: UserVoiceComment[] 27 | ) { } 28 | 29 | } 30 | 31 | export class UserVoiceComment { 32 | 33 | public constructor( 34 | public created_by: string, 35 | public created_at: string, 36 | public text: string, 37 | public html: string 38 | ) { } 39 | 40 | } 41 | 42 | export class Services { 43 | 44 | constructor(public workItemFormService?: WitServices.IWorkItemFormService) { 45 | } 46 | 47 | public linkedUVSuggestions(): Q.Promise { 48 | var self = this; 49 | var defer = Q.defer(); 50 | var settings = new Settings.Settings(); 51 | 52 | // load the settings and get the work item relations 53 | var q = Q.all([ 54 | settings.getSettings(true), 55 | (this.workItemFormService.getWorkItemRelations()) 56 | ]) 57 | // when both are done then 58 | .spread((settings: Settings.UVizSettings, relations: WitContracts.WorkItemRelation[]) => { 59 | 60 | var userVoiceItems = relations 61 | // extract the id from the url. when the url is not a valid uservoice url, then the id will be null 62 | .map((relation: WitContracts.WorkItemRelation): {id: string} => { 63 | return Services.extractIdFromUrl(relation.url); 64 | }) 65 | // exclude invalid uservoice urls 66 | .filter((item: {id: string}) => { 67 | return item.id !== null; 68 | }) 69 | // retrieve the data from the user voice site (which is a promise) 70 | .map((item: {id: string}) => { 71 | return self._getUserVoiceInfo(settings.accountName, settings.apiKey, item.id); 72 | }); 73 | 74 | // wait until the data for all user voice items is retrieved 75 | Q.all(userVoiceItems).then((suggestions: UserVoiceSuggestion[]) => { 76 | // and then resolve the promise to inform the caller to show the user voice data in the UI 77 | defer.resolve(suggestions); 78 | }).fail((reason: any): void => { 79 | // the call to get the user voice information failed 80 | defer.reject(reason); 81 | }) 82 | }) 83 | .fail((reason: any): void => { 84 | // the work item relations could not be retrieved 85 | defer.reject(reason); 86 | }); 87 | 88 | return defer.promise; 89 | } 90 | 91 | /** 92 | * Parses the url and returns the User Voice suggestion ID from 93 | * the url. Will return null when the url is not a valid User Voice 94 | * url. 95 | */ 96 | public static extractIdFromUrl(url: string): {id: string} { 97 | var pattern = `\/forums/[0-9]+.*/suggestions/([0-9]+)|/admin/v[23]/suggestions/([0-9]+)`; 98 | var matches = url.match(pattern); 99 | if ( matches && matches.length > 2 ) { 100 | // matches[1] is for the public facing url 101 | // matches[2] is for the admin url 102 | return {id: matches[1]||matches[2]}; 103 | } else { 104 | return {id: null}; 105 | } 106 | } 107 | 108 | /** 109 | * Validates if the given ID is a valid User Voice item in the forum. It returns the 110 | * UserVoiceSuggestion object when found, else null. 111 | */ 112 | public idExists(id: string): Q.Promise { 113 | var self = this; 114 | var defer = Q.defer(); 115 | var settings = new Settings.Settings(); 116 | 117 | // load the settings and get the work item relations 118 | var q = Q.all([ 119 | settings.getSettings(true) 120 | ]) 121 | // when both are done then 122 | .spread((settings: Settings.UVizSettings) => { 123 | self._getUserVoiceInfo(settings.accountName, settings.apiKey, id) 124 | .done( 125 | (suggestion: UserVoiceSuggestion) => {defer.resolve(suggestion)}, // the _getUserVoiceInfo will resolve the promise when the id is found, then return suggestion 126 | () => {defer.resolve(null)}); // the _getUserVoiceInfo will reject the promise when the id is NOT found, then return null 127 | }) 128 | .fail((reason: any): void => { 129 | // the work item relations could not be retrieved 130 | defer.reject(reason); 131 | }); 132 | 133 | return defer.promise; 134 | 135 | } 136 | 137 | private _getUserVoiceInfo(accountName: string, apiKey: string, id: string): Q.Promise { 138 | var defer = Q.defer(); 139 | 140 | // call the UViz-API web api (which is hosted in the api subfolder in Azure) to overcome Cross-Domain scripting 141 | // which is not supported in the User Voice API v1 142 | $.ajax({ 143 | type: "GET", 144 | url: `../api/Suggestion/${id}?accountName=${accountName}&apikey=${apiKey}` 145 | }).done((data: any) => { 146 | 147 | if (!data.url) { 148 | // the item is not found 149 | defer.reject(`item is not found for id ${id}`); 150 | } else if (data.id) { 151 | // when the id is set, the call was successful and return the data 152 | defer.resolve(new UserVoiceSuggestion( 153 | data.id, 154 | data.title, 155 | data.description, 156 | data.description_html, 157 | data.url, 158 | data.votes, 159 | { 160 | name: data.status.name, 161 | hex_color: data.status.hex_color 162 | }, 163 | data.response, 164 | data.response_html, 165 | data.response_date, 166 | data.total_comments, 167 | data.most_recent_comments) 168 | ); 169 | } else { 170 | // something went wrong, and inform the user 171 | var settings = new Settings.Settings; 172 | var reason = `Unable to retrieve the data from User Voice (reason: ${data.title}). Please make sure the api key is configured correctly`; 173 | defer.reject(reason); 174 | } 175 | }).fail((error: any) => { 176 | defer.reject(error.responseText); 177 | }); 178 | 179 | return defer.promise; 180 | } 181 | 182 | } 183 | -------------------------------------------------------------------------------- /extension/vsts-uservoice-ui-wi-group/UVizGroup.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 27 | 28 |
29 | 30 |
31 | 35 |
37 | 38 |
39 |
40 |
41 | 42 | -------------------------------------------------------------------------------- /extension/vsts-uservoice-ui-wi-group/addlink.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/extension/vsts-uservoice-ui-wi-group/addlink.gif -------------------------------------------------------------------------------- /extension/vsts-uservoice-ui-wi-group/app.css: -------------------------------------------------------------------------------- 1 | .suggestion { 2 | border-bottom: 7px solid transparent; 3 | } 4 | 5 | .votes-status { 6 | float: left; 7 | width: 70px; 8 | } 9 | 10 | .votes { 11 | width: 100%; 12 | text-align: center; 13 | border: 1px solid rgb(207, 215, 230); 14 | border-radius: 3px; 15 | font-size: 18px; 16 | font-weight: bold; 17 | color: #292C33; 18 | padding: 4px 10px; 19 | } 20 | 21 | .status { 22 | color: white; 23 | text-align: center; 24 | padding: 3px 0; 25 | border-radius: 3px; 26 | } 27 | 28 | .status:hover + .status-response { 29 | display: block; 30 | } 31 | 32 | .status-response-date { 33 | font-weight: bold; 34 | } 35 | 36 | .title-cell { 37 | padding-left: 10px; 38 | width: 100%; 39 | vertical-align: top; 40 | } 41 | 42 | .title:hover + .suggestion-description { 43 | display: block; 44 | } 45 | 46 | .no-linked-suggestions { 47 | color: #999; 48 | display: block; 49 | } 50 | 51 | .comments { 52 | margin-top: 10px; 53 | } 54 | 55 | .commentcount { 56 | margin-left: 5px; 57 | display: inline; 58 | } 59 | 60 | .comments:hover > .comments-tooltip { 61 | display: block; 62 | } 63 | 64 | .comment-created-by { 65 | font-weight: bold; 66 | margin-right: 2px; 67 | } 68 | 69 | .tooltip { 70 | border: 1px solid grey; 71 | background: #fafafa; 72 | display: none; 73 | padding: 0 5px; 74 | margin-left: 10px; 75 | width: 350px; 76 | position: absolute; 77 | } -------------------------------------------------------------------------------- /extension/vsts-uservoice-ui-wi-group/app.js: -------------------------------------------------------------------------------- 1 | define(["require", "exports", "TFS/WorkItemTracking/Services", "vsts-uservoice-ui-wi-group/UV", "vsts-uservoice-ui-settings-hub/settings", "VSS/Controls", "VSS/Controls/StatusIndicator"], function (require, exports, WitServices, UV, Settings, Controls, StatusIndicator) { 2 | "use strict"; 3 | VSS.register("vsts-uservoice-ui-wi-group", function () { 4 | return { 5 | onLoaded: function (args) { 6 | render(); 7 | $("#add-item-id").keypress(function (e) { 8 | var key = e.which; 9 | if (key === 13) { 10 | var suggestionIdOrUrl = $("#add-item-id").val(); 11 | addingIds = true; 12 | addUserVoiceSuggestion(suggestionIdOrUrl); 13 | addingIds = false; 14 | addTag(); 15 | render(); 16 | } 17 | }); 18 | }, 19 | onSaved: function (args) { 20 | render(); 21 | }, 22 | onReset: function (args) { 23 | render(); 24 | }, 25 | onRefreshed: function (args) { 26 | render(); 27 | }, 28 | onFieldChanged: function (args) { 29 | if (!addingIds && args.changedFields["System.LinkedFiles"] === "") { 30 | addTag(); 31 | render(); 32 | } 33 | } 34 | }; 35 | }); 36 | function addUserVoiceSuggestion(idOrUrls) { 37 | var waitcontrol = startWaitControl(); 38 | var returnInvalidId = function (reason, clearValue) { 39 | $("#invalid-item-id").text(reason).show(); 40 | setTimeout(function () { 41 | $("#invalid-item-id").hide(); 42 | }, 2000); 43 | if (clearValue) { 44 | $("#add-item-id").val(""); 45 | } 46 | endWaitControl(waitcontrol); 47 | }; 48 | var addLink = function (suggestion) { 49 | WitServices.WorkItemFormService.getService().then(function (wi) { 50 | wi.getWorkItemRelations().then(function (relations) { 51 | var linkExists = relations.some(function (relation) { 52 | return relation.url === suggestion.url; 53 | }); 54 | if (!linkExists) { 55 | wi.addWorkItemRelations([ 56 | { 57 | attributes: { 58 | key: suggestion.title 59 | }, 60 | rel: "Hyperlink", 61 | title: undefined, 62 | url: suggestion.url 63 | }]); 64 | $("#invalid-item-id").hide(); 65 | $("#add-item-id").val(""); 66 | endWaitControl(waitcontrol); 67 | } 68 | else { 69 | returnInvalidId("User Voice suggestion is already linked", true); 70 | } 71 | }); 72 | }); 73 | }; 74 | $.each(idOrUrls.split(","), function (idx, idOrUrl) { 75 | var id = parseInt(idOrUrl) || UV.Services.extractIdFromUrl(idOrUrl).id; 76 | if (!id) { 77 | returnInvalidId("ID is not a number or a valid URL", false); 78 | } 79 | else { 80 | var UVServices = new UV.Services(); 81 | UVServices.idExists(id.toString()).done(function (suggestion) { 82 | if (suggestion) { 83 | addLink(suggestion); 84 | } 85 | else { 86 | returnInvalidId("Not a valid User Voice item ID", false); 87 | } 88 | }, function (reason) { 89 | returnInvalidId(reason ? reason.toString() : "Unknown error", false); 90 | }); 91 | } 92 | }); 93 | } 94 | function addTag() { 95 | var settings = new Settings.Settings(); 96 | WitServices.WorkItemFormService.getService().then(function (wi) { 97 | var UVServices = new UV.Services(wi); 98 | UVServices.linkedUVSuggestions().then(function (linkedUVSuggestions) { 99 | if (linkedUVSuggestions.length > 0) { 100 | settings.getSettings(false).done(function (settings) { 101 | if (settings.tag) { 102 | wi.getFieldValue("System.Tags").then(function (tagString) { 103 | var tags = tagString 104 | .split(";") 105 | .map(function (t) { return t.trim(); }) 106 | .filter(function (t) { return !!t; }); 107 | if (tags.indexOf(settings.tag) === -1) { 108 | wi.setFieldValue("System.Tags", tags.concat([settings.tag]) 109 | .join(";")); 110 | } 111 | }); 112 | } 113 | }); 114 | } 115 | }); 116 | }); 117 | } 118 | var addingIds = false; 119 | function render() { 120 | var waitcontrol = startWaitControl(); 121 | WitServices.WorkItemFormService.getService().then(function (service) { 122 | var UVServices = new UV.Services(service); 123 | UVServices.linkedUVSuggestions() 124 | .then(function (linkedUVSuggestions) { 125 | if (linkedUVSuggestions.length === 0) { 126 | var settings = new Settings.Settings(); 127 | settings.getSettings(true).then(function (settings) { 128 | $("#items").empty(); 129 | $("#message").empty().append($("\n ").html("\n

\n No suggestions linked to this work item. Find some in your User Voice forum. \n

")); 130 | }); 131 | } 132 | else { 133 | $("#message").empty(); 134 | $("#items").empty().append(""); 135 | linkedUVSuggestions = linkedUVSuggestions.sort(function (a, b) { return b.votes - a.votes; }); 136 | $.each(linkedUVSuggestions, function (idx, UVSuggestion) { 137 | $("#items table").append($("").html("\n ")); 140 | }); 141 | var newHeight = $("#add-item").height() + $("#message").height(); 142 | $("#items table td").each(function (idx, elt) { 143 | newHeight += $(elt).height(); 144 | }); 145 | VSS.resize(null, newHeight); 146 | } 147 | }) 148 | .fail(function (reason) { 149 | $("#items").empty().append($("
").html(reason ? reason.toString() : "Unknown error")); 150 | }) 151 | .finally(function () { 152 | endWaitControl(waitcontrol); 153 | }); 154 | }); 155 | } 156 | function renderComments(UVSuggestion) { 157 | if (UVSuggestion.total_comments === 0) { 158 | return ""; 159 | } 160 | else { 161 | var ret = ""; 162 | for (var i = 0; i < UVSuggestion.most_recent_comments.length; i++) { 163 | if (i > 0) { 164 | ret += " "; 165 | } 166 | ret += ("" + UVSuggestion.most_recent_comments[i].created_by) + 167 | (" (" + UVSuggestion.most_recent_comments[i].created_at + ")") + 168 | (": " + UVSuggestion.most_recent_comments[i].text); 169 | } 170 | return ret; 171 | } 172 | } 173 | function formatNumber(value) { 174 | var stringValue = value.toFixed(0); 175 | var rgx = /(\d+)(\d{3})/; 176 | while (rgx.test(stringValue)) { 177 | stringValue = stringValue.replace(rgx, '$1' + ',' + '$2'); 178 | } 179 | return stringValue; 180 | } 181 | function startWaitControl() { 182 | var isAlreadyRunning = $("#waitcontrol").attr("running") === "true"; 183 | if (isAlreadyRunning) { 184 | return null; 185 | } 186 | else { 187 | var waitControlOptions = { 188 | target: $("#container"), 189 | message: "Reaching out to UserVoice.com...", 190 | backgroundColor: "transparent" 191 | }; 192 | var waitcontrol = Controls.create(StatusIndicator.WaitControl, $("#container"), waitControlOptions); 193 | waitcontrol.startWait(); 194 | $("#waitcontrol").attr("running", "true"); 195 | return waitcontrol; 196 | } 197 | } 198 | function endWaitControl(waitcontrol) { 199 | if (waitcontrol) { 200 | waitcontrol.endWait(); 201 | $("#waitcontrol").attr("running", "false"); 202 | } 203 | } 204 | }); 205 | -------------------------------------------------------------------------------- /extension/vsts-uservoice-ui-wi-group/app.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import WitServices = require("TFS/WorkItemTracking/Services"); 4 | import WitContracts = require("TFS/WorkItemTracking/Contracts"); 5 | import UV = require("vsts-uservoice-ui-wi-group/UV"); 6 | import Settings = require("vsts-uservoice-ui-settings-hub/settings"); 7 | import Controls = require("VSS/Controls"); 8 | import StatusIndicator = require("VSS/Controls/StatusIndicator"); 9 | 10 | // Register a listener for the work item group contribution. 11 | VSS.register("vsts-uservoice-ui-wi-group", function () { 12 | return { 13 | // Called when a new work item is being loaded in the UI 14 | onLoaded: function (args) { 15 | render(); 16 | 17 | // Register the event to add a link to the entered the User Voice item ID 18 | // when hitting {Enter} 19 | $("#add-item-id").keypress((e) => { 20 | 21 | // when it is the Enter key 22 | var key = e.which; 23 | if (key === 13) { 24 | 25 | // read the value from the input control 26 | var suggestionIdOrUrl = $("#add-item-id").val(); 27 | 28 | // set the flag to indicate the form should not render on every 29 | // link that is added. We will render the changes explicitly 30 | addingIds = true; 31 | 32 | // verify the input and add the link 33 | addUserVoiceSuggestion(suggestionIdOrUrl) 34 | 35 | // reset the flag so any link that is added to the work item will 36 | // result in a rerender of the control 37 | addingIds = false; 38 | 39 | // add the UserVoice tag and render the control to reflect the changes 40 | addTag(); 41 | render(); 42 | } 43 | }) 44 | }, 45 | 46 | // Called after the work item has been saved 47 | onSaved: function (args) { 48 | render(); 49 | }, 50 | 51 | // Called when the work item is reset to its unmodified state (undo) 52 | onReset: function (args) { 53 | render(); 54 | }, 55 | 56 | // Called when the work item has been refreshed from the server 57 | onRefreshed: function (args) { 58 | render(); 59 | }, 60 | 61 | // Called when a field is changed on the work item 62 | onFieldChanged: function (args) { 63 | // When adding a link 64 | if (!addingIds && args.changedFields["System.LinkedFiles"] === "") { 65 | // add the defined tag when one of the links is a user voice suggestion 66 | addTag(); 67 | render(); 68 | } 69 | } 70 | } 71 | }); 72 | 73 | /** 74 | * Adds a link to the work item based on the ID of the User Voice item entered through the add-item input 75 | */ 76 | function addUserVoiceSuggestion(idOrUrls: string): void { 77 | var waitcontrol = startWaitControl(); 78 | 79 | // function to show something went wrong, and return false 80 | var returnInvalidId = (reason: string, clearValue: boolean) => { 81 | 82 | // show the error message 83 | $("#invalid-item-id").text(reason).show(); 84 | 85 | // hide the message after 2 seconds 86 | setTimeout(() => { 87 | $("#invalid-item-id").hide(); 88 | }, 2000); 89 | 90 | // when the link was added succesfully, clear the input control 91 | if (clearValue) { 92 | $("#add-item-id").val(""); 93 | } 94 | 95 | endWaitControl(waitcontrol); 96 | } 97 | 98 | // function to add the UV suggestion as a link 99 | var addLink = (suggestion: UV.UserVoiceSuggestion) => { 100 | WitServices.WorkItemFormService.getService().then((wi: WitServices.IWorkItemFormService) => { 101 | 102 | // get the relations of the work item 103 | wi.getWorkItemRelations().then((relations: WitContracts.WorkItemRelation[]) => { 104 | 105 | // don't add the link if it already exists 106 | var linkExists = relations.some((relation: WitContracts.WorkItemRelation) => { 107 | return relation.url === suggestion.url; 108 | }); 109 | 110 | if (!linkExists) { 111 | 112 | // Add a hyperlink work item link 113 | wi.addWorkItemRelations([ 114 | { 115 | attributes: { 116 | key: suggestion.title // the comment of the work item link 117 | }, 118 | rel: "Hyperlink", // valid values are: 'Hyperlink', 'ArtifactLink' (e.g. git commit), any valid linktype name (e.g. 'Child', 'Parent') 119 | title: undefined, // the title is ignored for work item links 120 | url: suggestion.url // the url for the Hyperlink, git commit, or work item 121 | }]); 122 | 123 | // the link has been added succesfully, hide any error message and clear the input control 124 | $("#invalid-item-id").hide(); 125 | $("#add-item-id").val(""); 126 | 127 | endWaitControl(waitcontrol); 128 | } else { 129 | 130 | returnInvalidId("User Voice suggestion is already linked", true); 131 | 132 | } 133 | 134 | }); 135 | }); 136 | } 137 | 138 | // when the idOrUrl is a valid number (parseInt), then use it. Else try to 139 | // extract the id from the url. 140 | $.each(idOrUrls.split(","), (idx:number, idOrUrl: string) => { 141 | var id = parseInt(idOrUrl) || UV.Services.extractIdFromUrl(idOrUrl).id; 142 | 143 | if (!id) { 144 | returnInvalidId("ID is not a number or a valid URL", false); 145 | } else { 146 | var UVServices = new UV.Services(); 147 | UVServices.idExists(id.toString()).done( 148 | (suggestion: UV.UserVoiceSuggestion) => { 149 | if (suggestion) { 150 | // when the id is a valid User Voice ID, then hide the error 151 | // text, add the link and indicate it was succesful to clear 152 | // the value from the input 153 | addLink(suggestion); 154 | } else { 155 | // Not a valid User Voice ID 156 | returnInvalidId("Not a valid User Voice item ID", false); 157 | } 158 | }, 159 | (reason: string) => { 160 | // Something went wrong 161 | returnInvalidId(reason ? reason.toString() : "Unknown error", false); 162 | } 163 | ); 164 | } 165 | }); 166 | } 167 | 168 | /** 169 | * Adds the tag as specified in the settings to the current work item. If no tag is configured, nothing happens. 170 | */ 171 | function addTag() { 172 | var settings = new Settings.Settings(); 173 | 174 | WitServices.WorkItemFormService.getService().then(wi => { 175 | var UVServices = new UV.Services(wi); 176 | UVServices.linkedUVSuggestions().then(linkedUVSuggestions => { 177 | // when there are linked user voice suggestions 178 | if (linkedUVSuggestions.length > 0) { 179 | settings.getSettings(false).done((settings: Settings.UVizSettings) => { 180 | // when the tag is specified in the settings 181 | if (settings.tag) { 182 | // get the current value of the tags which gives back the list of tags in a ;-separated string 183 | (>wi.getFieldValue("System.Tags")).then(tagString => { 184 | // convert the string into an array 185 | let tags = tagString 186 | .split(";") 187 | .map(t => t.trim()) 188 | .filter(t => !!t); 189 | 190 | // add the tag as specified in the settings to the array when it doesn't exist yet and convert it into a ;-separated list again 191 | // and finally add the tags to the work item 192 | if (tags.indexOf(settings.tag) === -1) { 193 | wi.setFieldValue("System.Tags", 194 | tags.concat([settings.tag]) 195 | .join(";")); 196 | } 197 | }); 198 | } 199 | }) 200 | } 201 | }) 202 | }) 203 | } 204 | 205 | var addingIds: boolean = false; 206 | 207 | function render() { 208 | var waitcontrol = startWaitControl(); 209 | 210 | WitServices.WorkItemFormService.getService().then(service => { 211 | 212 | var UVServices = new UV.Services(service); 213 | 214 | UVServices.linkedUVSuggestions() 215 | .then(linkedUVSuggestions => { 216 | 217 | // when there are no items linked provide a help text 218 | if (linkedUVSuggestions.length === 0) { 219 | var settings = new Settings.Settings(); 220 | settings.getSettings(true).then((settings: Settings.UVizSettings) => { 221 | $("#items").empty(); 222 | $("#message").empty().append($(` 223 | `).html(` 224 |

225 | No suggestions linked to this work item. Find some in your User Voice forum. 226 |

`)); 227 | }); 228 | } else { 229 | 230 | $("#message").empty(); 231 | // start a table 232 | $("#items").empty().append("
\n
\n " + formatNumber(UVSuggestion.votes) + "\n
\n
\n " + (UVSuggestion.status.name || "no state") + "\n
\n
\n \n " + UVSuggestion.title + "\n \n " + (UVSuggestion.total_comments === 0 138 | ? "" 139 | : "
\n \n
\n " + UVSuggestion.total_comments + "\n
") + "\n
"); 233 | 234 | // sort the suggestion descending by its votes 235 | linkedUVSuggestions = linkedUVSuggestions.sort((a: UV.UserVoiceSuggestion, b: UV.UserVoiceSuggestion): number => { return b.votes - a.votes}) 236 | 237 | // show all user voice items sorted by most votes 238 | $.each(linkedUVSuggestions, (idx: number, UVSuggestion: UV.UserVoiceSuggestion) => { 239 | $("#items table").append( 240 | $(``).html( 241 | ` 252 | ` 265 | ) 266 | ); 267 | }); 268 | 269 | var newHeight = $("#add-item").height() + $("#message").height(); 270 | $("#items table td").each((idx: number, elt: Element) => { 271 | newHeight += $(elt).height(); 272 | }); 273 | // resize the control to fit the contents 274 | VSS.resize(null, newHeight); 275 | } 276 | }) 277 | .fail((reason: any): void => { 278 | 279 | // remove all existing items and show error 280 | $("#items").empty().append($("
").html(reason ? reason.toString() : "Unknown error")); 281 | 282 | }) 283 | .finally(() => { 284 | endWaitControl(waitcontrol); 285 | }); 286 | 287 | }); 288 | } 289 | 290 | function renderComments(UVSuggestion: UV.UserVoiceSuggestion): string { 291 | if (UVSuggestion.total_comments === 0) { 292 | return ""; 293 | } else { 294 | var ret: string = ``; 295 | 296 | for (var i = 0; i < UVSuggestion.most_recent_comments.length; i++ ){ 297 | if (i > 0) { 298 | ret += " "; 299 | } 300 | ret += `${UVSuggestion.most_recent_comments[i].created_by}` + 301 | ` (${UVSuggestion.most_recent_comments[i].created_at})` + 302 | `: ${UVSuggestion.most_recent_comments[i].text}` 303 | } 304 | 305 | return ret; 306 | } 307 | } 308 | 309 | function formatNumber(value: number): string { 310 | var stringValue = value.toFixed(0); 311 | var rgx = /(\d+)(\d{3})/; 312 | while (rgx.test(stringValue)) { 313 | stringValue = stringValue.replace(rgx, '$1' + ',' + '$2'); 314 | } 315 | return stringValue; 316 | } 317 | 318 | /** 319 | * Starts the wait control. When there is already a wait control running, null is returned 320 | */ 321 | function startWaitControl(): StatusIndicator.WaitControl { 322 | var isAlreadyRunning = $("#waitcontrol").attr("running") === "true"; 323 | 324 | if (isAlreadyRunning) { 325 | return null; 326 | } else { 327 | // create and start the wait control 328 | var waitControlOptions: StatusIndicator.IWaitControlOptions = { 329 | target: $("#container"), 330 | message: "Reaching out to UserVoice.com...", 331 | backgroundColor: "transparent" 332 | }; 333 | var waitcontrol = Controls.create(StatusIndicator.WaitControl, $("#container"), waitControlOptions); 334 | waitcontrol.startWait(); 335 | 336 | // indicate the wait control is running 337 | $("#waitcontrol").attr("running", "true") 338 | 339 | return waitcontrol; 340 | } 341 | } 342 | 343 | /** 344 | * Ends the wait control. 345 | */ 346 | function endWaitControl(waitcontrol: StatusIndicator.WaitControl) { 347 | if (waitcontrol) { 348 | waitcontrol.endWait(); 349 | 350 | // indicate the wait control is running 351 | $("#waitcontrol").attr("running", "false"); 352 | } 353 | } -------------------------------------------------------------------------------- /readme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/readme.png -------------------------------------------------------------------------------- /web-api/.gitignore: -------------------------------------------------------------------------------- 1 | .vs -------------------------------------------------------------------------------- /web-api/UViz-Api.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.24720.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UViz-Api", "UViz-Api\UViz-Api.csproj", "{C26F5BAD-043F-4788-949E-3D0CF61BE615}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {C26F5BAD-043F-4788-949E-3D0CF61BE615}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {C26F5BAD-043F-4788-949E-3D0CF61BE615}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {C26F5BAD-043F-4788-949E-3D0CF61BE615}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {C26F5BAD-043F-4788-949E-3D0CF61BE615}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /web-api/UViz-Api/.gitignore: -------------------------------------------------------------------------------- 1 | obj 2 | bin 3 | *.pubxml.user -------------------------------------------------------------------------------- /web-api/UViz-Api/App_Start/WebApiConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web.Http; 5 | 6 | namespace UViz_Api 7 | { 8 | public static class WebApiConfig 9 | { 10 | public static void Register(HttpConfiguration config) 11 | { 12 | // Web API configuration and services 13 | 14 | // Web API routes 15 | config.MapHttpAttributeRoutes(); 16 | 17 | config.Routes.MapHttpRoute( 18 | name: "DefaultApi", 19 | routeTemplate: "api/{controller}/{id}", 20 | defaults: new { id = RouteParameter.Optional } 21 | ); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /web-api/UViz-Api/Controllers/SuggestionController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Net.Http; 6 | using System.Threading.Tasks; 7 | using System.Web.Http; 8 | using System.Web.Script.Serialization; 9 | using UViz_Api.Models; 10 | using UVApi = UViz_Api.Models.UVApi; 11 | 12 | namespace UViz_Api.Controllers 13 | { 14 | public class SuggestionController : ApiController 15 | { 16 | 17 | public async Task getItem(int id, string accountName, string apikey) 18 | { 19 | var url = $"https://{accountName}.uservoice.com/api/v1/suggestions/{id}.json?client={apikey}"; 20 | 21 | using (var client = new HttpClient()) 22 | { 23 | var response = await client.GetAsync(url); 24 | if (response.IsSuccessStatusCode) 25 | { 26 | 27 | var json = await response.Content.ReadAsStringAsync(); 28 | var jsonObject = new JavaScriptSerializer().Deserialize(json); 29 | 30 | return new Suggestion() 31 | { 32 | id = id, 33 | title = jsonObject.suggestion.title, 34 | description = jsonObject.suggestion.text, 35 | description_html = jsonObject.suggestion.formatted_text, 36 | url = jsonObject.suggestion.url, 37 | votes = jsonObject.suggestion.vote_count, 38 | status = new Status() 39 | { 40 | name = jsonObject.suggestion.status?.name, 41 | hex_color = jsonObject.suggestion.status?.hex_color 42 | }, 43 | response = jsonObject.suggestion.response?.text, 44 | response_html = jsonObject.suggestion.response?.formatted_text, 45 | response_date = FormatDate(jsonObject.suggestion.response?.created_at), 46 | total_comments = jsonObject.suggestion.comments_count, 47 | most_recent_comments = await getCommentsOfItem(jsonObject.suggestion.topic.forum.id, id, accountName, apikey) 48 | }; 49 | 50 | } 51 | else 52 | { 53 | 54 | return new Suggestion() 55 | { 56 | id = id, 57 | title = response.ReasonPhrase, 58 | votes = 0, 59 | status = new Status() 60 | { 61 | name = "Error", 62 | hex_color = "#CC293D" 63 | } 64 | }; 65 | 66 | } 67 | 68 | } 69 | } 70 | 71 | private async Task> getCommentsOfItem(int forumId, int id, string accountName, string apikey) 72 | { 73 | var url = $"https://{accountName}.uservoice.com/api/v1/forums/{forumId}/suggestions/{id}/comments.json?client={apikey}"; 74 | 75 | using (var client = new HttpClient()) 76 | { 77 | var response = await client.GetAsync(url); 78 | if (response.IsSuccessStatusCode) 79 | { 80 | 81 | var json = await response.Content.ReadAsStringAsync(); 82 | var jsonObject = new JavaScriptSerializer().Deserialize(json); 83 | 84 | return from c in jsonObject.comments 85 | select new Comment() 86 | { 87 | text = c.text, 88 | html = c.formatted_text, 89 | created_at = FormatDate(c.created_at), 90 | created_by = c.creator.name 91 | }; 92 | } 93 | 94 | else 95 | { 96 | 97 | return null; 98 | 99 | } 100 | 101 | } 102 | } 103 | 104 | private static string FormatDate(string dateString) 105 | { 106 | return !string.IsNullOrWhiteSpace(dateString) 107 | ? DateTime.ParseExact(dateString, "yyyy/MM/dd HH:mm:ss +0000", CultureInfo.InvariantCulture).ToString("MMM dd, yyyy") 108 | : ""; 109 | } 110 | 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /web-api/UViz-Api/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="UViz_Api.WebApiApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /web-api/UViz-Api/Global.asax.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Http; 6 | using System.Web.Routing; 7 | 8 | namespace UViz_Api 9 | { 10 | public class WebApiApplication : System.Web.HttpApplication 11 | { 12 | protected void Application_Start() 13 | { 14 | GlobalConfiguration.Configure(WebApiConfig.Register); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /web-api/UViz-Api/Models/Comment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | 6 | namespace UViz_Api.Models 7 | { 8 | public class Comment 9 | { 10 | public string created_by { get; set; } 11 | public string created_at { get; set; } 12 | public string html { get; set; } 13 | public string text { get; set; } 14 | 15 | } 16 | } -------------------------------------------------------------------------------- /web-api/UViz-Api/Models/Status.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | 6 | namespace UViz_Api.Models 7 | { 8 | public class Status 9 | { 10 | 11 | public string name { get; set; } 12 | public string hex_color { get; set; } 13 | } 14 | } -------------------------------------------------------------------------------- /web-api/UViz-Api/Models/Suggestion.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace UViz_Api.Models 4 | { 5 | public class Suggestion 6 | { 7 | /// 8 | /// The id of the suggestion 9 | /// 10 | public int id { get; set; } 11 | 12 | /// 13 | /// The title of the suggestion 14 | /// 15 | public string title { get; set; } 16 | 17 | /// 18 | /// The unformatted (plain text) description of the suggestion 19 | /// 20 | public string description { get; set; } 21 | 22 | /// 23 | /// The html-formatted description of the suggestion 24 | /// 25 | public string description_html { get; set; } 26 | 27 | /// 28 | /// The public facing url to the suggestion 29 | /// 30 | public string url { get; set; } 31 | 32 | /// 33 | /// The number of votes for the suggestion 34 | /// 35 | public int votes { get; set; } 36 | 37 | /// 38 | /// The current status of the suggestion 39 | /// 40 | public Status status { get; set; } 41 | 42 | /// 43 | /// The list of the 10 most recently added comments 44 | /// 45 | public IEnumerable most_recent_comments { get; set; } 46 | 47 | /// 48 | /// The unformatted (plain text) response that goes with the status 49 | /// 50 | public string response { get; set; } 51 | 52 | /// 53 | /// The last html-formatted response that goes with the status 54 | /// 55 | public string response_html { get; set; } 56 | 57 | /// 58 | /// The date when the status was changed last 59 | /// 60 | public string response_date { get; set; } 61 | 62 | /// 63 | /// The total number of comments on this suggestion 64 | /// 65 | public int total_comments { get; set; } 66 | } 67 | } -------------------------------------------------------------------------------- /web-api/UViz-Api/Models/UVApi/Comment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | 6 | namespace UViz_Api.Models.UVApi 7 | { 8 | public class Comment 9 | { 10 | public string id { get; set; } 11 | public string text { get; set; } 12 | public string formatted_text { get; set; } 13 | public string created_at { get; set; } 14 | public string updated_at { get; set; } 15 | public Creator creator { get; set; } 16 | public Suggestion suggestion { get; set; } 17 | } 18 | } -------------------------------------------------------------------------------- /web-api/UViz-Api/Models/UVApi/Creator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | 6 | namespace UViz_Api.Models.UVApi 7 | { 8 | public class Creator 9 | { 10 | public string name { get; set; } 11 | public string email { get; set; } 12 | 13 | } 14 | } -------------------------------------------------------------------------------- /web-api/UViz-Api/Models/UVApi/Forum.cs: -------------------------------------------------------------------------------- 1 | namespace UViz_Api.Models.UVApi 2 | { 3 | public class Forum 4 | { 5 | public int id { get; set; } 6 | public string name { get; set; } 7 | } 8 | } -------------------------------------------------------------------------------- /web-api/UViz-Api/Models/UVApi/RawCommentResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace UViz_Api.Models.UVApi 4 | { 5 | public class RawCommentResponse 6 | { 7 | public IEnumerable comments { get; set; } 8 | public ResponseData response_data { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /web-api/UViz-Api/Models/UVApi/RawSuggestionResponse.cs: -------------------------------------------------------------------------------- 1 | namespace UViz_Api.Models.UVApi 2 | { 3 | public class RawSuggestionResponse 4 | { 5 | public Suggestion suggestion { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /web-api/UViz-Api/Models/UVApi/ResponseData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | 6 | namespace UViz_Api.Models.UVApi 7 | { 8 | public class ResponseData 9 | { 10 | public string total_records { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /web-api/UViz-Api/Models/UVApi/Suggestion.cs: -------------------------------------------------------------------------------- 1 | namespace UViz_Api.Models.UVApi 2 | { 3 | public class Suggestion 4 | { 5 | public string created_at { get; set; } 6 | public int comments_count { get; set; } 7 | public string formatted_text { get; set; } 8 | public int id { get; set; } 9 | public SuggestionResponse response { get; set; } 10 | public SuggestionStatus status { get; set; } 11 | public string text { get; set; } 12 | public string title { get; set; } 13 | public SuggestionTopic topic { get; set; } 14 | public string url { get; set; } 15 | public int vote_count { get; set; } 16 | } 17 | } -------------------------------------------------------------------------------- /web-api/UViz-Api/Models/UVApi/SuggestionResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | 6 | namespace UViz_Api.Models.UVApi 7 | { 8 | public class SuggestionResponse 9 | { 10 | public string created_at { get; set; } 11 | public string formatted_text { get; set; } 12 | public string text { get; set; } 13 | } 14 | } -------------------------------------------------------------------------------- /web-api/UViz-Api/Models/UVApi/SuggestionStatus.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | 6 | namespace UViz_Api.Models.UVApi 7 | { 8 | public class SuggestionStatus 9 | { 10 | public string hex_color { get; set; } 11 | public string name { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /web-api/UViz-Api/Models/UVApi/SuggestionTopic.cs: -------------------------------------------------------------------------------- 1 | namespace UViz_Api.Models.UVApi 2 | { 3 | public class SuggestionTopic 4 | { 5 | public Forum forum { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /web-api/UViz-Api/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("UViz_Api")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("UViz_Api")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("c26f5bad-043f-4788-949e-3d0cf61be615")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /web-api/UViz-Api/Properties/PublishProfiles/uviz - Web Deploy.pubxml: -------------------------------------------------------------------------------- 1 |  2 | 6 | 7 | 8 | MSDeploy 9 | AzureWebSite 10 | Release 11 | Any CPU 12 | 13 | 14 | True 15 | False 16 | uviz.scm.azurewebsites.net:443 17 | uviz 18 | 19 | True 20 | WMSVC 21 | True 22 | $uviz 23 | <_SavePWD>True 24 | <_DestinationType>AzureWebSite 25 | False 26 | 27 | -------------------------------------------------------------------------------- /web-api/UViz-Api/UViz-Api.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Debug 8 | AnyCPU 9 | 10 | 11 | 2.0 12 | {C26F5BAD-043F-4788-949E-3D0CF61BE615} 13 | {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} 14 | Library 15 | Properties 16 | UViz_Api 17 | UViz-Api 18 | v4.5.2 19 | true 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | true 30 | full 31 | false 32 | bin\ 33 | DEBUG;TRACE 34 | prompt 35 | 4 36 | false 37 | 38 | 39 | pdbonly 40 | true 41 | bin\ 42 | TRACE 43 | prompt 44 | 4 45 | false 46 | 47 | 48 | 49 | ..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll 50 | True 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | ..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll 74 | 75 | 76 | ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll 77 | 78 | 79 | ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll 80 | 81 | 82 | ..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | Global.asax 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | Web.config 115 | 116 | 117 | Web.config 118 | 119 | 120 | 121 | 122 | 123 | 124 | 10.0 125 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | True 135 | True 136 | 1292 137 | / 138 | http://localhost:1292/ 139 | False 140 | False 141 | 142 | 143 | False 144 | 145 | 146 | 147 | 148 | 149 | 150 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 151 | 152 | 153 | 154 | 155 | 162 | -------------------------------------------------------------------------------- /web-api/UViz-Api/UViz-Api.csproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 600 5 | True 6 | False 7 | True 8 | 9 | False 10 | uviz - Web Deploy 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | NoStartPage 19 | True 20 | False 21 | False 22 | False 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | True 32 | True 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /web-api/UViz-Api/Web.Debug.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | -------------------------------------------------------------------------------- /web-api/UViz-Api/Web.Release.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | -------------------------------------------------------------------------------- /web-api/UViz-Api/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 42 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /web-api/UViz-Api/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /web-api/packages/Microsoft.AspNet.WebApi.5.2.3/Microsoft.AspNet.WebApi.5.2.3.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/web-api/packages/Microsoft.AspNet.WebApi.5.2.3/Microsoft.AspNet.WebApi.5.2.3.nupkg -------------------------------------------------------------------------------- /web-api/packages/Microsoft.AspNet.WebApi.Client.5.2.3/Microsoft.AspNet.WebApi.Client.5.2.3.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/web-api/packages/Microsoft.AspNet.WebApi.Client.5.2.3/Microsoft.AspNet.WebApi.Client.5.2.3.nupkg -------------------------------------------------------------------------------- /web-api/packages/Microsoft.AspNet.WebApi.Client.5.2.3/lib/net45/System.Net.Http.Formatting.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/web-api/packages/Microsoft.AspNet.WebApi.Client.5.2.3/lib/net45/System.Net.Http.Formatting.dll -------------------------------------------------------------------------------- /web-api/packages/Microsoft.AspNet.WebApi.Client.5.2.3/lib/portable-wp8%2Bnetcore45%2Bnet45%2Bwp81%2Bwpa81/System.Net.Http.Formatting.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/web-api/packages/Microsoft.AspNet.WebApi.Client.5.2.3/lib/portable-wp8%2Bnetcore45%2Bnet45%2Bwp81%2Bwpa81/System.Net.Http.Formatting.dll -------------------------------------------------------------------------------- /web-api/packages/Microsoft.AspNet.WebApi.Core.5.2.3/Content/web.config.transform: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /web-api/packages/Microsoft.AspNet.WebApi.Core.5.2.3/Microsoft.AspNet.WebApi.Core.5.2.3.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/web-api/packages/Microsoft.AspNet.WebApi.Core.5.2.3/Microsoft.AspNet.WebApi.Core.5.2.3.nupkg -------------------------------------------------------------------------------- /web-api/packages/Microsoft.AspNet.WebApi.Core.5.2.3/lib/net45/System.Web.Http.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/web-api/packages/Microsoft.AspNet.WebApi.Core.5.2.3/lib/net45/System.Web.Http.dll -------------------------------------------------------------------------------- /web-api/packages/Microsoft.AspNet.WebApi.WebHost.5.2.3/Microsoft.AspNet.WebApi.WebHost.5.2.3.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/web-api/packages/Microsoft.AspNet.WebApi.WebHost.5.2.3/Microsoft.AspNet.WebApi.WebHost.5.2.3.nupkg -------------------------------------------------------------------------------- /web-api/packages/Microsoft.AspNet.WebApi.WebHost.5.2.3/lib/net45/System.Web.Http.WebHost.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/web-api/packages/Microsoft.AspNet.WebApi.WebHost.5.2.3/lib/net45/System.Web.Http.WebHost.dll -------------------------------------------------------------------------------- /web-api/packages/Microsoft.AspNet.WebApi.WebHost.5.2.3/lib/net45/System.Web.Http.WebHost.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | System.Web.Http.WebHost 5 | 6 | 7 | 8 | Provides a global for ASP.NET applications. 9 | 10 | 11 | 12 | 13 | 14 | Gets the global . 15 | 16 | 17 | Extension methods for 18 | 19 | 20 | Maps the specified route template. 21 | A reference to the mapped route. 22 | A collection of routes for the application. 23 | The name of the route to map. 24 | The route template for the route. 25 | 26 | 27 | Maps the specified route template and sets default route. 28 | A reference to the mapped route. 29 | A collection of routes for the application. 30 | The name of the route to map. 31 | The route template for the route. 32 | An object that contains default route values. 33 | 34 | 35 | Maps the specified route template and sets default route values and constraints. 36 | A reference to the mapped route. 37 | A collection of routes for the application. 38 | The name of the route to map. 39 | The route template for the route. 40 | An object that contains default route values. 41 | A set of expressions that specify values for routeTemplate. 42 | 43 | 44 | Maps the specified route template and sets default route values, constraints, and end-point message handler. 45 | A reference to the mapped route. 46 | A collection of routes for the application. 47 | The name of the route to map. 48 | The route template for the route. 49 | An object that contains default route values. 50 | A set of expressions that specify values for routeTemplate. 51 | The handler to which the request will be dispatched. 52 | 53 | 54 | A that passes ASP.NET requests into the pipeline and write the result back. 55 | 56 | 57 | Initializes a new instance of the class. 58 | The route data. 59 | 60 | 61 | Initializes a new instance of the class. 62 | The route data. 63 | The message handler to dispatch requests to. 64 | 65 | 66 | Provides code that handles an asynchronous task 67 | The asynchronous task. 68 | The HTTP context. 69 | 70 | 71 | A that returns instances of that can pass requests to a given instance. 72 | 73 | 74 | Initializes a new instance of the class. 75 | 76 | 77 | Provides the object that processes the request. 78 | An object that processes the request. 79 | An object that encapsulates information about the request. 80 | 81 | 82 | Gets the singleton instance. 83 | 84 | 85 | Provides the object that processes the request. 86 | An object that processes the request. 87 | An object that encapsulates information about the request. 88 | 89 | 90 | Provides a registration point for the simple membership pre-application start code. 91 | 92 | 93 | Registers the simple membership pre-application start code. 94 | 95 | 96 | Represents the web host buffer policy selector. 97 | 98 | 99 | Initializes a new instance of the class. 100 | 101 | 102 | Gets a value that indicates whether the host should buffer the entity body of the HTTP request. 103 | true if buffering should be used; otherwise a streamed request should be used. 104 | The host context. 105 | 106 | 107 | Uses a buffered output stream for the web host. 108 | A buffered output stream. 109 | The response. 110 | 111 | 112 | Provides the catch blocks used within this assembly. 113 | 114 | 115 | Gets the label for the catch block in System.Web.Http.WebHost.HttpControllerHandler.WriteBufferedResponseContentAsync. 116 | The label for the catch block in System.Web.Http.WebHost.HttpControllerHandler.WriteBufferedResponseContentAsync. 117 | 118 | 119 | Gets the label for the catch block in System.Web.Http.WebHost.HttpControllerHandler.WriteErrorResponseContentAsync. 120 | The label for the catch block in System.Web.Http.WebHost.HttpControllerHandler.WriteErrorResponseContentAsync. 121 | 122 | 123 | Gets the label for the catch block in System.Web.Http.WebHost.HttpControllerHandler.ComputeContentLength. 124 | The label for the catch block in System.Web.Http.WebHost.HttpControllerHandler.ComputeContentLength. 125 | 126 | 127 | Gets the label for the catch block in System.Web.Http.WebHost.HttpControllerHandler.WriteStreamedResponseContentAsync. 128 | The label for the catch block in System.Web.Http.WebHost.HttpControllerHandler.WriteStreamedResponseContentAsync. 129 | 130 | 131 | Gets the label for the catch block in System.Web.Http.WebHost.WebHostExceptionCatchBlocks.HttpWebRoute.GetRouteData. 132 | The catch block in System.Web.Http.WebHost.WebHostExceptionCatchBlocks.HttpWebRoute.GetRouteData. 133 | 134 | 135 | -------------------------------------------------------------------------------- /web-api/packages/Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0/Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/web-api/packages/Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0/Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0.nupkg -------------------------------------------------------------------------------- /web-api/packages/Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0/build/Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | roslyn\%(RecursiveDir)%(Filename)%(Extension) 5 | PreserveNewest 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /web-api/packages/Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0/content/web.config.install.xdt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /web-api/packages/Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0/content/web.config.uninstall.xdt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /web-api/packages/Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0/lib/net45/Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/web-api/packages/Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0/lib/net45/Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll -------------------------------------------------------------------------------- /web-api/packages/Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0/lib/net45/Microsoft.CodeDom.Providers.DotNetCompilerPlatform.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Microsoft.CodeDom.Providers.DotNetCompilerPlatform 5 | 6 | 7 | 8 | 9 | Provides access to instances of the .NET Compiler Platform C# code generator and code compiler. 10 | 11 | 12 | 13 | 14 | Default Constructor 15 | 16 | 17 | 18 | 19 | Gets an instance of the .NET Compiler Platform C# code compiler. 20 | 21 | An instance of the .NET Compiler Platform C# code compiler 22 | 23 | 24 | 25 | Provides access to instances of the .NET Compiler Platform VB code generator and code compiler. 26 | 27 | 28 | 29 | 30 | Default Constructor 31 | 32 | 33 | 34 | 35 | Gets an instance of the .NET Compiler Platform VB code compiler. 36 | 37 | An instance of the .NET Compiler Platform VB code compiler 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /web-api/packages/Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0/tools/init.ps1: -------------------------------------------------------------------------------- 1 | param($installPath, $toolsPath, $package, $project) 2 | 3 | $compilerPackageName = 'Microsoft.Net.Compilers' 4 | $roslynSubFolder = 'roslyn' 5 | 6 | if ($project -eq $null) { 7 | $project = Get-Project 8 | } 9 | 10 | $libDirectory = Join-Path $installPath 'lib\net45' 11 | $packageDirectory = Split-Path $installPath 12 | $compilerPackage = Get-ChildItem $packageDirectory | Where-Object {$_.Name.StartsWith($compilerPackageName)} 13 | $compilerPackageDirectory = Join-Path $packageDirectory $compilerPackage.Name 14 | $compilerPackageToolsDirectory = Join-Path $compilerPackageDirectory 'tools' 15 | 16 | $projectRoot = $project.Properties.Item('FullPath').Value 17 | $binDirectory = Join-Path $projectRoot 'bin' 18 | 19 | # We need to copy the provider assembly into the bin\ folder, otherwise 20 | # Microsoft.VisualStudio.Web.Host.exe cannot find the assembly. 21 | # However, users will see the error after they clean solutions. 22 | New-Item $binDirectory -type directory -force | Out-Null 23 | Copy-Item $libDirectory\* $binDirectory | Out-Null 24 | 25 | if ($project.Type -eq 'Web Site') { 26 | $roslynSubDirectory = Join-Path $binDirectory $roslynSubFolder 27 | New-Item $roslynSubDirectory -type directory -force 28 | Copy-Item $compilerPackageToolsDirectory\* $roslynSubDirectory 29 | 30 | # Generate a .refresh file for each dll/exe file. 31 | Push-Location 32 | Set-Location $projectRoot 33 | $relativeAssemblySource = Resolve-Path -relative $compilerPackageToolsDirectory 34 | Pop-Location 35 | 36 | Get-ChildItem -Path $roslynSubDirectory | ` 37 | Foreach-Object { 38 | if (($_.Extension -eq ".dll") -or ($_.Extension -eq ".exe")) { 39 | $refreshFile = $_.FullName 40 | $refreshFile += ".refresh" 41 | $refreshContent = Join-Path $relativeAssemblySource $_.Name 42 | Set-Content $refreshFile $refreshContent 43 | } 44 | } 45 | } 46 | # SIG # Begin signature block 47 | # MIIarQYJKoZIhvcNAQcCoIIanjCCGpoCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB 48 | # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR 49 | # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQU4NU63n4pEviX8BhDuZffBBeM 50 | # 0QegghWCMIIEwzCCA6ugAwIBAgITMwAAAHD0GL8jIfxQnQAAAAAAcDANBgkqhkiG 51 | # 9w0BAQUFADB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G 52 | # A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEw 53 | # HwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwHhcNMTUwMzIwMTczMjAy 54 | # WhcNMTYwNjIwMTczMjAyWjCBszELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp 55 | # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw 56 | # b3JhdGlvbjENMAsGA1UECxMETU9QUjEnMCUGA1UECxMebkNpcGhlciBEU0UgRVNO 57 | # OkY1MjgtMzc3Ny04QTc2MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT 58 | # ZXJ2aWNlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoxTZ7xygeRG9 59 | # LZoEnSM0gqVCHSsA0dIbMSnIKivzLfRui93iG/gT9MBfcFOv5zMPdEoHFGzcKAO4 60 | # Kgp4xG4gjguAb1Z7k/RxT8LTq8bsLa6V0GNnsGSmNAMM44quKFICmTX5PGTbKzJ3 61 | # wjTuUh5flwZ0CX/wovfVkercYttThkdujAFb4iV7ePw9coMie1mToq+TyRgu5/YK 62 | # VA6YDWUGV3eTka+Ur4S+uG+thPT7FeKT4thINnVZMgENcXYAlUlpbNTGNjpaMNDA 63 | # ynOJ5pT2Ix4SYFEACMHe2j9IhO21r9TTmjiVqbqjWLV4aEa/D4xjcb46Q0NZEPBK 64 | # unvW5QYT3QIDAQABo4IBCTCCAQUwHQYDVR0OBBYEFG3P87iErvfMdr24e6w9l2GB 65 | # dCsnMB8GA1UdIwQYMBaAFCM0+NlSRnAK7UD7dvuzK7DDNbMPMFQGA1UdHwRNMEsw 66 | # SaBHoEWGQ2h0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3Rz 67 | # L01pY3Jvc29mdFRpbWVTdGFtcFBDQS5jcmwwWAYIKwYBBQUHAQEETDBKMEgGCCsG 68 | # AQUFBzAChjxodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY3Jv 69 | # c29mdFRpbWVTdGFtcFBDQS5jcnQwEwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJKoZI 70 | # hvcNAQEFBQADggEBAF46KvVn9AUwKt7hue9n/Cr/bnIpn558xxPDo+WOPATpJhVN 71 | # 98JnglwKW8UK7lXwoy2Ooh2isywt0BHimioB0TAmZ6GmbokxHG7dxHFU8Ami3cHW 72 | # NnPADP9VCGv8oZT9XSwnIezRIwbcBCzvuQLbA7tHcxgK632ZzV8G4Ij3ipPFEhEb 73 | # 81KVo3Kg0ljZwyzia3931GNT6oK4L0dkKJjHgzvxayhh+AqIgkVSkumDJklct848 74 | # mn+voFGTxby6y9ErtbuQGQqmp2p++P0VfkZEh6UG1PxKcDjG6LVK9NuuL+xDyYmi 75 | # KMVV2cG6W6pgu6W7+dUCjg4PbcI1cMCo7A2hsrgwggTsMIID1KADAgECAhMzAAAB 76 | # Cix5rtd5e6asAAEAAAEKMA0GCSqGSIb3DQEBBQUAMHkxCzAJBgNVBAYTAlVTMRMw 77 | # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN 78 | # aWNyb3NvZnQgQ29ycG9yYXRpb24xIzAhBgNVBAMTGk1pY3Jvc29mdCBDb2RlIFNp 79 | # Z25pbmcgUENBMB4XDTE1MDYwNDE3NDI0NVoXDTE2MDkwNDE3NDI0NVowgYMxCzAJ 80 | # BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k 81 | # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xDTALBgNVBAsTBE1PUFIx 82 | # HjAcBgNVBAMTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjCCASIwDQYJKoZIhvcNAQEB 83 | # BQADggEPADCCAQoCggEBAJL8bza74QO5KNZG0aJhuqVG+2MWPi75R9LH7O3HmbEm 84 | # UXW92swPBhQRpGwZnsBfTVSJ5E1Q2I3NoWGldxOaHKftDXT3p1Z56Cj3U9KxemPg 85 | # 9ZSXt+zZR/hsPfMliLO8CsUEp458hUh2HGFGqhnEemKLwcI1qvtYb8VjC5NJMIEb 86 | # e99/fE+0R21feByvtveWE1LvudFNOeVz3khOPBSqlw05zItR4VzRO/COZ+owYKlN 87 | # Wp1DvdsjusAP10sQnZxN8FGihKrknKc91qPvChhIqPqxTqWYDku/8BTzAMiwSNZb 88 | # /jjXiREtBbpDAk8iAJYlrX01boRoqyAYOCj+HKIQsaUCAwEAAaOCAWAwggFcMBMG 89 | # A1UdJQQMMAoGCCsGAQUFBwMDMB0GA1UdDgQWBBSJ/gox6ibN5m3HkZG5lIyiGGE3 90 | # NDBRBgNVHREESjBIpEYwRDENMAsGA1UECxMETU9QUjEzMDEGA1UEBRMqMzE1OTUr 91 | # MDQwNzkzNTAtMTZmYS00YzYwLWI2YmYtOWQyYjFjZDA1OTg0MB8GA1UdIwQYMBaA 92 | # FMsR6MrStBZYAck3LjMWFrlMmgofMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9j 93 | # cmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY0NvZFNpZ1BDQV8w 94 | # OC0zMS0yMDEwLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6 95 | # Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljQ29kU2lnUENBXzA4LTMx 96 | # LTIwMTAuY3J0MA0GCSqGSIb3DQEBBQUAA4IBAQCmqFOR3zsB/mFdBlrrZvAM2PfZ 97 | # hNMAUQ4Q0aTRFyjnjDM4K9hDxgOLdeszkvSp4mf9AtulHU5DRV0bSePgTxbwfo/w 98 | # iBHKgq2k+6apX/WXYMh7xL98m2ntH4LB8c2OeEti9dcNHNdTEtaWUu81vRmOoECT 99 | # oQqlLRacwkZ0COvb9NilSTZUEhFVA7N7FvtH/vto/MBFXOI/Enkzou+Cxd5AGQfu 100 | # FcUKm1kFQanQl56BngNb/ErjGi4FrFBHL4z6edgeIPgF+ylrGBT6cgS3C6eaZOwR 101 | # XU9FSY0pGi370LYJU180lOAWxLnqczXoV+/h6xbDGMcGszvPYYTitkSJlKOGMIIF 102 | # vDCCA6SgAwIBAgIKYTMmGgAAAAAAMTANBgkqhkiG9w0BAQUFADBfMRMwEQYKCZIm 103 | # iZPyLGQBGRYDY29tMRkwFwYKCZImiZPyLGQBGRYJbWljcm9zb2Z0MS0wKwYDVQQD 104 | # EyRNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMTAwODMx 105 | # MjIxOTMyWhcNMjAwODMxMjIyOTMyWjB5MQswCQYDVQQGEwJVUzETMBEGA1UECBMK 106 | # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 107 | # IENvcnBvcmF0aW9uMSMwIQYDVQQDExpNaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBD 108 | # QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJyWVwZMGS/HZpgICBC 109 | # mXZTbD4b1m/My/Hqa/6XFhDg3zp0gxq3L6Ay7P/ewkJOI9VyANs1VwqJyq4gSfTw 110 | # aKxNS42lvXlLcZtHB9r9Jd+ddYjPqnNEf9eB2/O98jakyVxF3K+tPeAoaJcap6Vy 111 | # c1bxF5Tk/TWUcqDWdl8ed0WDhTgW0HNbBbpnUo2lsmkv2hkL/pJ0KeJ2L1TdFDBZ 112 | # +NKNYv3LyV9GMVC5JxPkQDDPcikQKCLHN049oDI9kM2hOAaFXE5WgigqBTK3S9dP 113 | # Y+fSLWLxRT3nrAgA9kahntFbjCZT6HqqSvJGzzc8OJ60d1ylF56NyxGPVjzBrAlf 114 | # A9MCAwEAAaOCAV4wggFaMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMsR6MrS 115 | # tBZYAck3LjMWFrlMmgofMAsGA1UdDwQEAwIBhjASBgkrBgEEAYI3FQEEBQIDAQAB 116 | # MCMGCSsGAQQBgjcVAgQWBBT90TFO0yaKleGYYDuoMW+mPLzYLTAZBgkrBgEEAYI3 117 | # FAIEDB4KAFMAdQBiAEMAQTAfBgNVHSMEGDAWgBQOrIJgQFYnl+UlE/wq4QpTlVnk 118 | # pDBQBgNVHR8ESTBHMEWgQ6BBhj9odHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtp 119 | # L2NybC9wcm9kdWN0cy9taWNyb3NvZnRyb290Y2VydC5jcmwwVAYIKwYBBQUHAQEE 120 | # SDBGMEQGCCsGAQUFBzAChjhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2Nl 121 | # cnRzL01pY3Jvc29mdFJvb3RDZXJ0LmNydDANBgkqhkiG9w0BAQUFAAOCAgEAWTk+ 122 | # fyZGr+tvQLEytWrrDi9uqEn361917Uw7LddDrQv+y+ktMaMjzHxQmIAhXaw9L0y6 123 | # oqhWnONwu7i0+Hm1SXL3PupBf8rhDBdpy6WcIC36C1DEVs0t40rSvHDnqA2iA6VW 124 | # 4LiKS1fylUKc8fPv7uOGHzQ8uFaa8FMjhSqkghyT4pQHHfLiTviMocroE6WRTsgb 125 | # 0o9ylSpxbZsa+BzwU9ZnzCL/XB3Nooy9J7J5Y1ZEolHN+emjWFbdmwJFRC9f9Nqu 126 | # 1IIybvyklRPk62nnqaIsvsgrEA5ljpnb9aL6EiYJZTiU8XofSrvR4Vbo0HiWGFzJ 127 | # NRZf3ZMdSY4tvq00RBzuEBUaAF3dNVshzpjHCe6FDoxPbQ4TTj18KUicctHzbMrB 128 | # 7HCjV5JXfZSNoBtIA1r3z6NnCnSlNu0tLxfI5nI3EvRvsTxngvlSso0zFmUeDord 129 | # EN5k9G/ORtTTF+l5xAS00/ss3x+KnqwK+xMnQK3k+eGpf0a7B2BHZWBATrBC7E7t 130 | # s3Z52Ao0CW0cgDEf4g5U3eWh++VHEK1kmP9QFi58vwUheuKVQSdpw5OPlcmN2Jsh 131 | # rg1cnPCiroZogwxqLbt2awAdlq3yFnv2FoMkuYjPaqhHMS+a3ONxPdcAfmJH0c6I 132 | # ybgY+g5yjcGjPa8CQGr/aZuW4hCoELQ3UAjWwz0wggYHMIID76ADAgECAgphFmg0 133 | # AAAAAAAcMA0GCSqGSIb3DQEBBQUAMF8xEzARBgoJkiaJk/IsZAEZFgNjb20xGTAX 134 | # BgoJkiaJk/IsZAEZFgltaWNyb3NvZnQxLTArBgNVBAMTJE1pY3Jvc29mdCBSb290 135 | # IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0wNzA0MDMxMjUzMDlaFw0yMTA0MDMx 136 | # MzAzMDlaMHcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD 137 | # VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xITAf 138 | # BgNVBAMTGE1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQTCCASIwDQYJKoZIhvcNAQEB 139 | # BQADggEPADCCAQoCggEBAJ+hbLHf20iSKnxrLhnhveLjxZlRI1Ctzt0YTiQP7tGn 140 | # 0UytdDAgEesH1VSVFUmUG0KSrphcMCbaAGvoe73siQcP9w4EmPCJzB/LMySHnfL0 141 | # Zxws/HvniB3q506jocEjU8qN+kXPCdBer9CwQgSi+aZsk2fXKNxGU7CG0OUoRi4n 142 | # rIZPVVIM5AMs+2qQkDBuh/NZMJ36ftaXs+ghl3740hPzCLdTbVK0RZCfSABKR2YR 143 | # JylmqJfk0waBSqL5hKcRRxQJgp+E7VV4/gGaHVAIhQAQMEbtt94jRrvELVSfrx54 144 | # QTF3zJvfO4OToWECtR0Nsfz3m7IBziJLVP/5BcPCIAsCAwEAAaOCAaswggGnMA8G 145 | # A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFCM0+NlSRnAK7UD7dvuzK7DDNbMPMAsG 146 | # A1UdDwQEAwIBhjAQBgkrBgEEAYI3FQEEAwIBADCBmAYDVR0jBIGQMIGNgBQOrIJg 147 | # QFYnl+UlE/wq4QpTlVnkpKFjpGEwXzETMBEGCgmSJomT8ixkARkWA2NvbTEZMBcG 148 | # CgmSJomT8ixkARkWCW1pY3Jvc29mdDEtMCsGA1UEAxMkTWljcm9zb2Z0IFJvb3Qg 149 | # Q2VydGlmaWNhdGUgQXV0aG9yaXR5ghB5rRahSqClrUxzWPQHEy5lMFAGA1UdHwRJ 150 | # MEcwRaBDoEGGP2h0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1 151 | # Y3RzL21pY3Jvc29mdHJvb3RjZXJ0LmNybDBUBggrBgEFBQcBAQRIMEYwRAYIKwYB 152 | # BQUHMAKGOGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljcm9z 153 | # b2Z0Um9vdENlcnQuY3J0MBMGA1UdJQQMMAoGCCsGAQUFBwMIMA0GCSqGSIb3DQEB 154 | # BQUAA4ICAQAQl4rDXANENt3ptK132855UU0BsS50cVttDBOrzr57j7gu1BKijG1i 155 | # uFcCy04gE1CZ3XpA4le7r1iaHOEdAYasu3jyi9DsOwHu4r6PCgXIjUji8FMV3U+r 156 | # kuTnjWrVgMHmlPIGL4UD6ZEqJCJw+/b85HiZLg33B+JwvBhOnY5rCnKVuKE5nGct 157 | # xVEO6mJcPxaYiyA/4gcaMvnMMUp2MT0rcgvI6nA9/4UKE9/CCmGO8Ne4F+tOi3/F 158 | # NSteo7/rvH0LQnvUU3Ih7jDKu3hlXFsBFwoUDtLaFJj1PLlmWLMtL+f5hYbMUVbo 159 | # nXCUbKw5TNT2eb+qGHpiKe+imyk0BncaYsk9Hm0fgvALxyy7z0Oz5fnsfbXjpKh0 160 | # NbhOxXEjEiZ2CzxSjHFaRkMUvLOzsE1nyJ9C/4B5IYCeFTBm6EISXhrIniIh0EPp 161 | # K+m79EjMLNTYMoBMJipIJF9a6lbvpt6Znco6b72BJ3QGEe52Ib+bgsEnVLaxaj2J 162 | # oXZhtG6hE6a/qkfwEm/9ijJssv7fUciMI8lmvZ0dhxJkAj0tr1mPuOQh5bWwymO0 163 | # eFQF1EEuUKyUsKV4q7OglnUa2ZKHE3UiLzKoCG6gW4wlv6DvhMoh1useT8ma7kng 164 | # 9wFlb4kLfchpyOZu6qeXzjEp/w7FW1zYTRuh2Povnj8uVRZryROj/TGCBJUwggSR 165 | # AgEBMIGQMHkxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD 166 | # VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xIzAh 167 | # BgNVBAMTGk1pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBAhMzAAABCix5rtd5e6as 168 | # AAEAAAEKMAkGBSsOAwIaBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw 169 | # HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFDvK 170 | # fpycwfPatt+qgg/wt8sn9WuHME4GCisGAQQBgjcCAQwxQDA+oCSAIgBNAGkAYwBy 171 | # AG8AcwBvAGYAdAAgAEEAUwBQAC4ATgBFAFShFoAUaHR0cDovL3d3dy5hc3AubmV0 172 | # LyAwDQYJKoZIhvcNAQEBBQAEggEAY89COpl+7GxCYosYAwhnzs2yzV6UWzYCxH8J 173 | # UbpBaEDarn16Gyh5RjjwLCI8hilUTfZd7hvpbwLc7dyt6JrB3jdt8AfaKwU4kQDw 174 | # NWixHxclnR3HuCE6nTSEd6O7ZPWYvTKx6ItmGH8MT5zX52tpylcEGk2VBKWP90OV 175 | # c8zS6Bdm/p4UdWfEtczmm79MssvhRJLKS81QbcNIY/fSClFDCbWzMg9jER83szK5 176 | # siCFvZpM3if3jdoSBlm24LAbWSdMEfdAhTpiZ9qMkWuk19ZmtflMbIsRXVEKPyPp 177 | # h2s/Uu2H6tOjh/dy0J55I9Yjb9N2k99teflCevXLd/j0xDMGTKGCAigwggIkBgkq 178 | # hkiG9w0BCQYxggIVMIICEQIBATCBjjB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMK 179 | # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 180 | # IENvcnBvcmF0aW9uMSEwHwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EC 181 | # EzMAAABw9Bi/IyH8UJ0AAAAAAHAwCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJAzEL 182 | # BgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTE1MDYxNTA5MDUxNFowIwYJKoZI 183 | # hvcNAQkEMRYEFKlGGP6GbAyMgc6+TnqAi9roXL4mMA0GCSqGSIb3DQEBBQUABIIB 184 | # AAKAFMGUisPm9qW//v6e+PI2UMjKPQOi7L7KBXb0LNalFyoUDXGgP34ACCjSVxsi 185 | # jesrpqklUJ9wOiDdM9ulSEluZCYD0FrWAtiXACo9DZaqDTRp+DxyZ7hLoG87eya1 186 | # dptFzXJQZPbpr8NOJxUhRsoIiWh7/MgZ3J13OuF8u61nOmjQSfmXWNvJJnBRiVGE 187 | # XOh2Rkxr5KO2zjPiNSNooLZcW0tVdnysrzhAbJicHAnzk24LIYouMB+Azs9O04pj 188 | # ve6O5WDeFdr8Fjvf8TuPfyli9mm40kQGROr6Rc3zhXQoG88ffGLQyaVRDrLFCvbi 189 | # kac5LhN/Bj1kfZ9XCh2pXfI= 190 | # SIG # End signature block 191 | -------------------------------------------------------------------------------- /web-api/packages/Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0/tools/uninstall.ps1: -------------------------------------------------------------------------------- 1 | param($installPath, $toolsPath, $package, $project) 2 | 3 | $roslynSubFolder = 'roslyn' 4 | 5 | if ($project -eq $null) { 6 | $project = Get-Project 7 | } 8 | 9 | $projectRoot = $project.Properties.Item('FullPath').Value 10 | $binDirectory = Join-Path $projectRoot 'bin' 11 | $targetDirectory = Join-Path $binDirectory $roslynSubFolder 12 | 13 | if (Test-Path $targetDirectory) { 14 | Remove-Item $targetDirectory -Force -Recurse 15 | } 16 | # SIG # Begin signature block 17 | # MIIarQYJKoZIhvcNAQcCoIIanjCCGpoCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB 18 | # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR 19 | # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUBnzRb+A5zqyBejTiJB6CJOry 20 | # jZ6gghWCMIIEwzCCA6ugAwIBAgITMwAAAHGzLoprgqofTgAAAAAAcTANBgkqhkiG 21 | # 9w0BAQUFADB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G 22 | # A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEw 23 | # HwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwHhcNMTUwMzIwMTczMjAz 24 | # WhcNMTYwNjIwMTczMjAzWjCBszELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp 25 | # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw 26 | # b3JhdGlvbjENMAsGA1UECxMETU9QUjEnMCUGA1UECxMebkNpcGhlciBEU0UgRVNO 27 | # OkI4RUMtMzBBNC03MTQ0MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT 28 | # ZXJ2aWNlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6pG9soj9FG8h 29 | # NigDZjM6Zgj7W0ukq6AoNEpDMgjAhuXJPdUlvHs+YofWfe8PdFOj8ZFjiHR/6CTN 30 | # A1DF8coAFnulObAGHDxEfvnrxLKBvBcjuv1lOBmFf8qgKf32OsALL2j04DROfW8X 31 | # wG6Zqvp/YSXRJnDSdH3fYXNczlQqOVEDMwn4UK14x4kIttSFKj/X2B9R6u/8aF61 32 | # wecHaDKNL3JR/gMxR1HF0utyB68glfjaavh3Z+RgmnBMq0XLfgiv5YHUV886zBN1 33 | # nSbNoKJpULw6iJTfsFQ43ok5zYYypZAPfr/tzJQlpkGGYSbH3Td+XA3oF8o3f+gk 34 | # tk60+Bsj6wIDAQABo4IBCTCCAQUwHQYDVR0OBBYEFPj9I4cFlIBWzTOlQcJszAg2 35 | # yLKiMB8GA1UdIwQYMBaAFCM0+NlSRnAK7UD7dvuzK7DDNbMPMFQGA1UdHwRNMEsw 36 | # SaBHoEWGQ2h0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3Rz 37 | # L01pY3Jvc29mdFRpbWVTdGFtcFBDQS5jcmwwWAYIKwYBBQUHAQEETDBKMEgGCCsG 38 | # AQUFBzAChjxodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY3Jv 39 | # c29mdFRpbWVTdGFtcFBDQS5jcnQwEwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJKoZI 40 | # hvcNAQEFBQADggEBAC0EtMopC1n8Luqgr0xOaAT4ku0pwmbMa3DJh+i+h/xd9N1P 41 | # pRpveJetawU4UUFynTnkGhvDbXH8cLbTzLaQWAQoP9Ye74OzFBgMlQv3pRETmMaF 42 | # Vl7uM7QMN7WA6vUSaNkue4YIcjsUe9TZ0BZPwC8LHy3K5RvQrumEsI8LXXO4FoFA 43 | # I1gs6mGq/r1/041acPx5zWaWZWO1BRJ24io7K+2CrJrsJ0Gnlw4jFp9ByE5tUxFA 44 | # BMxgmdqY7Cuul/vgffW6iwD0JRd/Ynq7UVfB8PDNnBthc62VjCt2IqircDi0ASh9 45 | # ZkJT3p/0B3xaMA6CA1n2hIa5FSVisAvSz/HblkUwggTsMIID1KADAgECAhMzAAAB 46 | # Cix5rtd5e6asAAEAAAEKMA0GCSqGSIb3DQEBBQUAMHkxCzAJBgNVBAYTAlVTMRMw 47 | # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN 48 | # aWNyb3NvZnQgQ29ycG9yYXRpb24xIzAhBgNVBAMTGk1pY3Jvc29mdCBDb2RlIFNp 49 | # Z25pbmcgUENBMB4XDTE1MDYwNDE3NDI0NVoXDTE2MDkwNDE3NDI0NVowgYMxCzAJ 50 | # BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k 51 | # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xDTALBgNVBAsTBE1PUFIx 52 | # HjAcBgNVBAMTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjCCASIwDQYJKoZIhvcNAQEB 53 | # BQADggEPADCCAQoCggEBAJL8bza74QO5KNZG0aJhuqVG+2MWPi75R9LH7O3HmbEm 54 | # UXW92swPBhQRpGwZnsBfTVSJ5E1Q2I3NoWGldxOaHKftDXT3p1Z56Cj3U9KxemPg 55 | # 9ZSXt+zZR/hsPfMliLO8CsUEp458hUh2HGFGqhnEemKLwcI1qvtYb8VjC5NJMIEb 56 | # e99/fE+0R21feByvtveWE1LvudFNOeVz3khOPBSqlw05zItR4VzRO/COZ+owYKlN 57 | # Wp1DvdsjusAP10sQnZxN8FGihKrknKc91qPvChhIqPqxTqWYDku/8BTzAMiwSNZb 58 | # /jjXiREtBbpDAk8iAJYlrX01boRoqyAYOCj+HKIQsaUCAwEAAaOCAWAwggFcMBMG 59 | # A1UdJQQMMAoGCCsGAQUFBwMDMB0GA1UdDgQWBBSJ/gox6ibN5m3HkZG5lIyiGGE3 60 | # NDBRBgNVHREESjBIpEYwRDENMAsGA1UECxMETU9QUjEzMDEGA1UEBRMqMzE1OTUr 61 | # MDQwNzkzNTAtMTZmYS00YzYwLWI2YmYtOWQyYjFjZDA1OTg0MB8GA1UdIwQYMBaA 62 | # FMsR6MrStBZYAck3LjMWFrlMmgofMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9j 63 | # cmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY0NvZFNpZ1BDQV8w 64 | # OC0zMS0yMDEwLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6 65 | # Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljQ29kU2lnUENBXzA4LTMx 66 | # LTIwMTAuY3J0MA0GCSqGSIb3DQEBBQUAA4IBAQCmqFOR3zsB/mFdBlrrZvAM2PfZ 67 | # hNMAUQ4Q0aTRFyjnjDM4K9hDxgOLdeszkvSp4mf9AtulHU5DRV0bSePgTxbwfo/w 68 | # iBHKgq2k+6apX/WXYMh7xL98m2ntH4LB8c2OeEti9dcNHNdTEtaWUu81vRmOoECT 69 | # oQqlLRacwkZ0COvb9NilSTZUEhFVA7N7FvtH/vto/MBFXOI/Enkzou+Cxd5AGQfu 70 | # FcUKm1kFQanQl56BngNb/ErjGi4FrFBHL4z6edgeIPgF+ylrGBT6cgS3C6eaZOwR 71 | # XU9FSY0pGi370LYJU180lOAWxLnqczXoV+/h6xbDGMcGszvPYYTitkSJlKOGMIIF 72 | # vDCCA6SgAwIBAgIKYTMmGgAAAAAAMTANBgkqhkiG9w0BAQUFADBfMRMwEQYKCZIm 73 | # iZPyLGQBGRYDY29tMRkwFwYKCZImiZPyLGQBGRYJbWljcm9zb2Z0MS0wKwYDVQQD 74 | # EyRNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMTAwODMx 75 | # MjIxOTMyWhcNMjAwODMxMjIyOTMyWjB5MQswCQYDVQQGEwJVUzETMBEGA1UECBMK 76 | # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 77 | # IENvcnBvcmF0aW9uMSMwIQYDVQQDExpNaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBD 78 | # QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJyWVwZMGS/HZpgICBC 79 | # mXZTbD4b1m/My/Hqa/6XFhDg3zp0gxq3L6Ay7P/ewkJOI9VyANs1VwqJyq4gSfTw 80 | # aKxNS42lvXlLcZtHB9r9Jd+ddYjPqnNEf9eB2/O98jakyVxF3K+tPeAoaJcap6Vy 81 | # c1bxF5Tk/TWUcqDWdl8ed0WDhTgW0HNbBbpnUo2lsmkv2hkL/pJ0KeJ2L1TdFDBZ 82 | # +NKNYv3LyV9GMVC5JxPkQDDPcikQKCLHN049oDI9kM2hOAaFXE5WgigqBTK3S9dP 83 | # Y+fSLWLxRT3nrAgA9kahntFbjCZT6HqqSvJGzzc8OJ60d1ylF56NyxGPVjzBrAlf 84 | # A9MCAwEAAaOCAV4wggFaMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMsR6MrS 85 | # tBZYAck3LjMWFrlMmgofMAsGA1UdDwQEAwIBhjASBgkrBgEEAYI3FQEEBQIDAQAB 86 | # MCMGCSsGAQQBgjcVAgQWBBT90TFO0yaKleGYYDuoMW+mPLzYLTAZBgkrBgEEAYI3 87 | # FAIEDB4KAFMAdQBiAEMAQTAfBgNVHSMEGDAWgBQOrIJgQFYnl+UlE/wq4QpTlVnk 88 | # pDBQBgNVHR8ESTBHMEWgQ6BBhj9odHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtp 89 | # L2NybC9wcm9kdWN0cy9taWNyb3NvZnRyb290Y2VydC5jcmwwVAYIKwYBBQUHAQEE 90 | # SDBGMEQGCCsGAQUFBzAChjhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2Nl 91 | # cnRzL01pY3Jvc29mdFJvb3RDZXJ0LmNydDANBgkqhkiG9w0BAQUFAAOCAgEAWTk+ 92 | # fyZGr+tvQLEytWrrDi9uqEn361917Uw7LddDrQv+y+ktMaMjzHxQmIAhXaw9L0y6 93 | # oqhWnONwu7i0+Hm1SXL3PupBf8rhDBdpy6WcIC36C1DEVs0t40rSvHDnqA2iA6VW 94 | # 4LiKS1fylUKc8fPv7uOGHzQ8uFaa8FMjhSqkghyT4pQHHfLiTviMocroE6WRTsgb 95 | # 0o9ylSpxbZsa+BzwU9ZnzCL/XB3Nooy9J7J5Y1ZEolHN+emjWFbdmwJFRC9f9Nqu 96 | # 1IIybvyklRPk62nnqaIsvsgrEA5ljpnb9aL6EiYJZTiU8XofSrvR4Vbo0HiWGFzJ 97 | # NRZf3ZMdSY4tvq00RBzuEBUaAF3dNVshzpjHCe6FDoxPbQ4TTj18KUicctHzbMrB 98 | # 7HCjV5JXfZSNoBtIA1r3z6NnCnSlNu0tLxfI5nI3EvRvsTxngvlSso0zFmUeDord 99 | # EN5k9G/ORtTTF+l5xAS00/ss3x+KnqwK+xMnQK3k+eGpf0a7B2BHZWBATrBC7E7t 100 | # s3Z52Ao0CW0cgDEf4g5U3eWh++VHEK1kmP9QFi58vwUheuKVQSdpw5OPlcmN2Jsh 101 | # rg1cnPCiroZogwxqLbt2awAdlq3yFnv2FoMkuYjPaqhHMS+a3ONxPdcAfmJH0c6I 102 | # ybgY+g5yjcGjPa8CQGr/aZuW4hCoELQ3UAjWwz0wggYHMIID76ADAgECAgphFmg0 103 | # AAAAAAAcMA0GCSqGSIb3DQEBBQUAMF8xEzARBgoJkiaJk/IsZAEZFgNjb20xGTAX 104 | # BgoJkiaJk/IsZAEZFgltaWNyb3NvZnQxLTArBgNVBAMTJE1pY3Jvc29mdCBSb290 105 | # IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0wNzA0MDMxMjUzMDlaFw0yMTA0MDMx 106 | # MzAzMDlaMHcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD 107 | # VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xITAf 108 | # BgNVBAMTGE1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQTCCASIwDQYJKoZIhvcNAQEB 109 | # BQADggEPADCCAQoCggEBAJ+hbLHf20iSKnxrLhnhveLjxZlRI1Ctzt0YTiQP7tGn 110 | # 0UytdDAgEesH1VSVFUmUG0KSrphcMCbaAGvoe73siQcP9w4EmPCJzB/LMySHnfL0 111 | # Zxws/HvniB3q506jocEjU8qN+kXPCdBer9CwQgSi+aZsk2fXKNxGU7CG0OUoRi4n 112 | # rIZPVVIM5AMs+2qQkDBuh/NZMJ36ftaXs+ghl3740hPzCLdTbVK0RZCfSABKR2YR 113 | # JylmqJfk0waBSqL5hKcRRxQJgp+E7VV4/gGaHVAIhQAQMEbtt94jRrvELVSfrx54 114 | # QTF3zJvfO4OToWECtR0Nsfz3m7IBziJLVP/5BcPCIAsCAwEAAaOCAaswggGnMA8G 115 | # A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFCM0+NlSRnAK7UD7dvuzK7DDNbMPMAsG 116 | # A1UdDwQEAwIBhjAQBgkrBgEEAYI3FQEEAwIBADCBmAYDVR0jBIGQMIGNgBQOrIJg 117 | # QFYnl+UlE/wq4QpTlVnkpKFjpGEwXzETMBEGCgmSJomT8ixkARkWA2NvbTEZMBcG 118 | # CgmSJomT8ixkARkWCW1pY3Jvc29mdDEtMCsGA1UEAxMkTWljcm9zb2Z0IFJvb3Qg 119 | # Q2VydGlmaWNhdGUgQXV0aG9yaXR5ghB5rRahSqClrUxzWPQHEy5lMFAGA1UdHwRJ 120 | # MEcwRaBDoEGGP2h0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1 121 | # Y3RzL21pY3Jvc29mdHJvb3RjZXJ0LmNybDBUBggrBgEFBQcBAQRIMEYwRAYIKwYB 122 | # BQUHMAKGOGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljcm9z 123 | # b2Z0Um9vdENlcnQuY3J0MBMGA1UdJQQMMAoGCCsGAQUFBwMIMA0GCSqGSIb3DQEB 124 | # BQUAA4ICAQAQl4rDXANENt3ptK132855UU0BsS50cVttDBOrzr57j7gu1BKijG1i 125 | # uFcCy04gE1CZ3XpA4le7r1iaHOEdAYasu3jyi9DsOwHu4r6PCgXIjUji8FMV3U+r 126 | # kuTnjWrVgMHmlPIGL4UD6ZEqJCJw+/b85HiZLg33B+JwvBhOnY5rCnKVuKE5nGct 127 | # xVEO6mJcPxaYiyA/4gcaMvnMMUp2MT0rcgvI6nA9/4UKE9/CCmGO8Ne4F+tOi3/F 128 | # NSteo7/rvH0LQnvUU3Ih7jDKu3hlXFsBFwoUDtLaFJj1PLlmWLMtL+f5hYbMUVbo 129 | # nXCUbKw5TNT2eb+qGHpiKe+imyk0BncaYsk9Hm0fgvALxyy7z0Oz5fnsfbXjpKh0 130 | # NbhOxXEjEiZ2CzxSjHFaRkMUvLOzsE1nyJ9C/4B5IYCeFTBm6EISXhrIniIh0EPp 131 | # K+m79EjMLNTYMoBMJipIJF9a6lbvpt6Znco6b72BJ3QGEe52Ib+bgsEnVLaxaj2J 132 | # oXZhtG6hE6a/qkfwEm/9ijJssv7fUciMI8lmvZ0dhxJkAj0tr1mPuOQh5bWwymO0 133 | # eFQF1EEuUKyUsKV4q7OglnUa2ZKHE3UiLzKoCG6gW4wlv6DvhMoh1useT8ma7kng 134 | # 9wFlb4kLfchpyOZu6qeXzjEp/w7FW1zYTRuh2Povnj8uVRZryROj/TGCBJUwggSR 135 | # AgEBMIGQMHkxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD 136 | # VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xIzAh 137 | # BgNVBAMTGk1pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBAhMzAAABCix5rtd5e6as 138 | # AAEAAAEKMAkGBSsOAwIaBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw 139 | # HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFB3V 140 | # 8I1bJ4SWcN/s+tSEZJkvxFs8ME4GCisGAQQBgjcCAQwxQDA+oCSAIgBNAGkAYwBy 141 | # AG8AcwBvAGYAdAAgAEEAUwBQAC4ATgBFAFShFoAUaHR0cDovL3d3dy5hc3AubmV0 142 | # LyAwDQYJKoZIhvcNAQEBBQAEggEACHMr42Y7++5pqm2UMWEhTJNHE4CE0MCnOBBB 143 | # 3QRg/Om9NWcbyNDTcv+m/BEjSqw3DntgyesjLyNItIjHS2USsGwx2Pu9rGSoyf4B 144 | # 0P7+hQ/SESgyjlJKOYPVY3f+LgsJnm1Ul7thPkqMFLNxc0ZfDL1isLo7+fS4sog1 145 | # oFlMATgsvEh6gIWXCwmA+nePp41mi+XTzITV45Tmfnp5RC7fmGSATUJjZkX5mSI/ 146 | # vMzLvtrd9xOwZ6LRIAWTsP2LWLkrcLfHI44ScewmsVYwZ6gUHiNRZiFgFtI2KLNJ 147 | # DACTa81SyuKzvcPNEOagBfxBGuvUx0FS+132MqtgP2+WMoEZjKGCAigwggIkBgkq 148 | # hkiG9w0BCQYxggIVMIICEQIBATCBjjB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMK 149 | # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 150 | # IENvcnBvcmF0aW9uMSEwHwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EC 151 | # EzMAAABxsy6Ka4KqH04AAAAAAHEwCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJAzEL 152 | # BgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTE1MDYxNTA5MDUxNFowIwYJKoZI 153 | # hvcNAQkEMRYEFIVN6iX2CUsMgdCMMcY6FnEVYStsMA0GCSqGSIb3DQEBBQUABIIB 154 | # AFa8SHBubu7B/dlmeOswQecmfudrcda3BgEJZ3tboYHprUJqiFCxqRgdm/eiDOwp 155 | # PSXHtkkU6Sa3+/ozfrIhqQTvCEAVdVLzwY00fSNbBDs+WAEtp3ev0Z82OnYWAT66 156 | # YqE9yaV2MONgDbp141b4q5mOjQ7wcfmR6cj26q8XCS52HdtKW71D/plzOTzLu6Yk 157 | # y9L+g+MNBIrAwriCzRY81av1H+d1v4XTXFsWs+OAMHRlj2CIDTli9fXGrp8/EEMI 158 | # 7HPMzAINAge09jeZdnjVtAjQ5ibo9MsDXfzfGf97sPYCN8jMh5IH9Enipds0OP+M 159 | # r1ZCMQaDnwYmOaombAp9QuI= 160 | # SIG # End signature block 161 | -------------------------------------------------------------------------------- /web-api/packages/Microsoft.Net.Compilers.1.0.0/Microsoft.Net.Compilers.1.0.0.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/web-api/packages/Microsoft.Net.Compilers.1.0.0/Microsoft.Net.Compilers.1.0.0.nupkg -------------------------------------------------------------------------------- /web-api/packages/Microsoft.Net.Compilers.1.0.0/ThirdPartyNotices.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang1033\deflangfe1033{\fonttbl{\f0\fswiss\fprq2\fcharset0 Tahoma;}} 2 | {\colortbl ;\red0\green0\blue255;} 3 | {\*\generator Riched20 10.0.10037}{\*\mmathPr\mnaryLim0\mdispDef1\mwrapIndent1440 }\viewkind4\uc1 4 | \pard\nowidctlpar\sb120\sa120\f0\fs19 THIRD-PARTY SOFTWARE NOTICES AND INFORMATION\par 5 | Do Not Translate or Localize\par 6 | \par 7 | This file provides information regarding components that are being relicensed to you by Microsoft Corporation under Microsoft's software licensing terms. Microsoft Corporation reserves all rights not expressly granted herein.\par 8 | \par 9 | %% \caps .NET Compiler Platform\caps0 NOTICES AND INFORMATION BEGIN HERE\par 10 | =========================================\par 11 | Copyright (C) .NET Foundation. All rights reserved.\par 12 | \par 13 | Apache License, Version 2.0\par 14 | Apache License\par 15 | Version 2.0, January 2004\par 16 | {{\field{\*\fldinst{HYPERLINK http://www.apache.org/licenses/ }}{\fldrslt{http://www.apache.org/licenses/\ul0\cf0}}}}\f0\fs19\par 17 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\par 18 | 1. Definitions.\par 19 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.\par 20 | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.\par 21 | "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.\par 22 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.\par 23 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.\par 24 | "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.\par 25 | "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).\par 26 | "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.\par 27 | "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."\par 28 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.\par 29 | 2. Grant of Copyright License.\par 30 | Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.\par 31 | 3. Grant of Patent License.\par 32 | Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.\par 33 | 4. Redistribution.\par 34 | You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:\par 35 | 1. You must give any other recipients of the Work or Derivative Works a copy of this License; and\par 36 | 2. You must cause any modified files to carry prominent notices stating that You changed the files; and\par 37 | 3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and\par 38 | 4. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.\par 39 | You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.\par 40 | 5. Submission of Contributions.\par 41 | Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.\par 42 | 6. Trademarks.\par 43 | This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.\par 44 | 7. Disclaimer of Warranty.\par 45 | Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.\par 46 | 8. Limitation of Liability.\par 47 | In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.\par 48 | 9. Accepting Warranty or Additional Liability.\par 49 | While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.\par 50 | END OF TERMS AND CONDITIONS\par 51 | =========================================\par 52 | END OF \caps .NET Compiler Platform\caps0 NOTICES AND INFORMATION\par 53 | } 54 | -------------------------------------------------------------------------------- /web-api/packages/Microsoft.Net.Compilers.1.0.0/build/Microsoft.Net.Compilers.props: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 12 | 15 | 18 | 19 | 21 | false 23 | $(MSBuildThisFileDirectory)..\tools\Microsoft.CSharp.Core.targets 24 | $(MSBuildThisFileDirectory)..\tools\Microsoft.VisualBasic.Core.targets 25 | 26 | 27 | 29 | 30 | $(MSBuildThisFileDirectory)..\tools 31 | csc.exe 32 | $(MSBuildThisFileDirectory)..\tools 33 | vbc.exe 34 | 35 | 36 | -------------------------------------------------------------------------------- /web-api/packages/Microsoft.Net.Compilers.1.0.0/tools/Microsoft.Build.Tasks.CodeAnalysis.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/web-api/packages/Microsoft.Net.Compilers.1.0.0/tools/Microsoft.Build.Tasks.CodeAnalysis.dll -------------------------------------------------------------------------------- /web-api/packages/Microsoft.Net.Compilers.1.0.0/tools/Microsoft.CSharp.Core.targets: -------------------------------------------------------------------------------- 1 |  2 | 25 | 28 | 29 | $(NoWarn);1701;1702 30 | 31 | 32 | 33 | 34 | $(NoWarn);2008 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | $(AppConfig) 47 | 48 | 49 | $(IntermediateOutputPath)$(TargetName).compile.pdb 50 | 51 | 52 | 53 | 54 | false 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | true 64 | 65 | 66 | 67 | 128 | 129 | 130 | <_CoreCompileResourceInputs Remove="@(_CoreCompileResourceInputs)" /> 131 | 132 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /web-api/packages/Microsoft.Net.Compilers.1.0.0/tools/Microsoft.CodeAnalysis.CSharp.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/web-api/packages/Microsoft.Net.Compilers.1.0.0/tools/Microsoft.CodeAnalysis.CSharp.dll -------------------------------------------------------------------------------- /web-api/packages/Microsoft.Net.Compilers.1.0.0/tools/Microsoft.CodeAnalysis.VisualBasic.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/web-api/packages/Microsoft.Net.Compilers.1.0.0/tools/Microsoft.CodeAnalysis.VisualBasic.dll -------------------------------------------------------------------------------- /web-api/packages/Microsoft.Net.Compilers.1.0.0/tools/Microsoft.CodeAnalysis.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/web-api/packages/Microsoft.Net.Compilers.1.0.0/tools/Microsoft.CodeAnalysis.dll -------------------------------------------------------------------------------- /web-api/packages/Microsoft.Net.Compilers.1.0.0/tools/Microsoft.VisualBasic.Core.targets: -------------------------------------------------------------------------------- 1 |  2 | 25 | 26 | <_NoWarnings Condition=" '$(WarningLevel)' == '0' ">true 27 | <_NoWarnings Condition=" '$(WarningLevel)' == '1' ">false 28 | 29 | 30 | 31 | 32 | $(IntermediateOutputPath)$(TargetName).compile.pdb 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | false 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | true 53 | 54 | 55 | 56 | 126 | 127 | 128 | <_CoreCompileResourceInputs Remove="@(_CoreCompileResourceInputs)" /> 129 | 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /web-api/packages/Microsoft.Net.Compilers.1.0.0/tools/System.Collections.Immutable.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/web-api/packages/Microsoft.Net.Compilers.1.0.0/tools/System.Collections.Immutable.dll -------------------------------------------------------------------------------- /web-api/packages/Microsoft.Net.Compilers.1.0.0/tools/System.Reflection.Metadata.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/web-api/packages/Microsoft.Net.Compilers.1.0.0/tools/System.Reflection.Metadata.dll -------------------------------------------------------------------------------- /web-api/packages/Microsoft.Net.Compilers.1.0.0/tools/VBCSCompiler.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/web-api/packages/Microsoft.Net.Compilers.1.0.0/tools/VBCSCompiler.exe -------------------------------------------------------------------------------- /web-api/packages/Microsoft.Net.Compilers.1.0.0/tools/VBCSCompiler.exe.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /web-api/packages/Microsoft.Net.Compilers.1.0.0/tools/csc.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/web-api/packages/Microsoft.Net.Compilers.1.0.0/tools/csc.exe -------------------------------------------------------------------------------- /web-api/packages/Microsoft.Net.Compilers.1.0.0/tools/vbc.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/web-api/packages/Microsoft.Net.Compilers.1.0.0/tools/vbc.exe -------------------------------------------------------------------------------- /web-api/packages/Newtonsoft.Json.6.0.4/Newtonsoft.Json.6.0.4.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/web-api/packages/Newtonsoft.Json.6.0.4/Newtonsoft.Json.6.0.4.nupkg -------------------------------------------------------------------------------- /web-api/packages/Newtonsoft.Json.6.0.4/lib/net20/Newtonsoft.Json.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/web-api/packages/Newtonsoft.Json.6.0.4/lib/net20/Newtonsoft.Json.dll -------------------------------------------------------------------------------- /web-api/packages/Newtonsoft.Json.6.0.4/lib/net35/Newtonsoft.Json.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/web-api/packages/Newtonsoft.Json.6.0.4/lib/net35/Newtonsoft.Json.dll -------------------------------------------------------------------------------- /web-api/packages/Newtonsoft.Json.6.0.4/lib/net40/Newtonsoft.Json.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/web-api/packages/Newtonsoft.Json.6.0.4/lib/net40/Newtonsoft.Json.dll -------------------------------------------------------------------------------- /web-api/packages/Newtonsoft.Json.6.0.4/lib/net45/Newtonsoft.Json.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/web-api/packages/Newtonsoft.Json.6.0.4/lib/net45/Newtonsoft.Json.dll -------------------------------------------------------------------------------- /web-api/packages/Newtonsoft.Json.6.0.4/lib/netcore45/Newtonsoft.Json.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/web-api/packages/Newtonsoft.Json.6.0.4/lib/netcore45/Newtonsoft.Json.dll -------------------------------------------------------------------------------- /web-api/packages/Newtonsoft.Json.6.0.4/lib/portable-net40%2Bsl5%2Bwp80%2Bwin8%2Bwpa81/Newtonsoft.Json.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/web-api/packages/Newtonsoft.Json.6.0.4/lib/portable-net40%2Bsl5%2Bwp80%2Bwin8%2Bwpa81/Newtonsoft.Json.dll -------------------------------------------------------------------------------- /web-api/packages/Newtonsoft.Json.6.0.4/lib/portable-net45%2Bwp80%2Bwin8%2Bwpa81/Newtonsoft.Json.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vsts-uservoice-ui-extension/a29a86d22490c8e2f03d475864981dce4132b50c/web-api/packages/Newtonsoft.Json.6.0.4/lib/portable-net45%2Bwp80%2Bwin8%2Bwpa81/Newtonsoft.Json.dll -------------------------------------------------------------------------------- /web-api/packages/Newtonsoft.Json.6.0.4/tools/install.ps1: -------------------------------------------------------------------------------- 1 | param($installPath, $toolsPath, $package, $project) 2 | 3 | # open json.net splash page on package install 4 | # don't open if json.net is installed as a dependency 5 | 6 | try 7 | { 8 | $url = "http://james.newtonking.com/json" 9 | $dte2 = Get-Interface $dte ([EnvDTE80.DTE2]) 10 | 11 | if ($dte2.ActiveWindow.Caption -eq "Package Manager Console") 12 | { 13 | # user is installing from VS NuGet console 14 | # get reference to the window, the console host and the input history 15 | # show webpage if "install-package newtonsoft.json" was last input 16 | 17 | $consoleWindow = $(Get-VSComponentModel).GetService([NuGetConsole.IPowerConsoleWindow]) 18 | 19 | $props = $consoleWindow.GetType().GetProperties([System.Reflection.BindingFlags]::Instance -bor ` 20 | [System.Reflection.BindingFlags]::NonPublic) 21 | 22 | $prop = $props | ? { $_.Name -eq "ActiveHostInfo" } | select -first 1 23 | if ($prop -eq $null) { return } 24 | 25 | $hostInfo = $prop.GetValue($consoleWindow) 26 | if ($hostInfo -eq $null) { return } 27 | 28 | $history = $hostInfo.WpfConsole.InputHistory.History 29 | 30 | $lastCommand = $history | select -last 1 31 | 32 | if ($lastCommand) 33 | { 34 | $lastCommand = $lastCommand.Trim().ToLower() 35 | if ($lastCommand.StartsWith("install-package") -and $lastCommand.Contains("newtonsoft.json")) 36 | { 37 | $dte2.ItemOperations.Navigate($url) | Out-Null 38 | } 39 | } 40 | } 41 | else 42 | { 43 | # user is installing from VS NuGet dialog 44 | # get reference to the window, then smart output console provider 45 | # show webpage if messages in buffered console contains "installing...newtonsoft.json" in last operation 46 | 47 | $instanceField = [NuGet.Dialog.PackageManagerWindow].GetField("CurrentInstance", [System.Reflection.BindingFlags]::Static -bor ` 48 | [System.Reflection.BindingFlags]::NonPublic) 49 | $consoleField = [NuGet.Dialog.PackageManagerWindow].GetField("_smartOutputConsoleProvider", [System.Reflection.BindingFlags]::Instance -bor ` 50 | [System.Reflection.BindingFlags]::NonPublic) 51 | if ($instanceField -eq $null -or $consoleField -eq $null) { return } 52 | 53 | $instance = $instanceField.GetValue($null) 54 | if ($instance -eq $null) { return } 55 | 56 | $consoleProvider = $consoleField.GetValue($instance) 57 | if ($consoleProvider -eq $null) { return } 58 | 59 | $console = $consoleProvider.CreateOutputConsole($false) 60 | 61 | $messagesField = $console.GetType().GetField("_messages", [System.Reflection.BindingFlags]::Instance -bor ` 62 | [System.Reflection.BindingFlags]::NonPublic) 63 | if ($messagesField -eq $null) { return } 64 | 65 | $messages = $messagesField.GetValue($console) 66 | if ($messages -eq $null) { return } 67 | 68 | $operations = $messages -split "==============================" 69 | 70 | $lastOperation = $operations | select -last 1 71 | 72 | if ($lastOperation) 73 | { 74 | $lastOperation = $lastOperation.ToLower() 75 | 76 | $lines = $lastOperation -split "`r`n" 77 | 78 | $installMatch = $lines | ? { $_.StartsWith("------- installing...newtonsoft.json ") } | select -first 1 79 | 80 | if ($installMatch) 81 | { 82 | $dte2.ItemOperations.Navigate($url) | Out-Null 83 | } 84 | } 85 | } 86 | } 87 | catch 88 | { 89 | # stop potential errors from bubbling up 90 | # worst case the splash page won't open 91 | } 92 | 93 | # yolo --------------------------------------------------------------------------------
242 |
243 | ${formatNumber(UVSuggestion.votes)} 244 |
245 |
249 | ${UVSuggestion.status.name || "no state"} 250 |
251 |
253 | 254 | ${UVSuggestion.title} 255 | 256 | ${UVSuggestion.total_comments === 0 257 | ? "" 258 | : `
259 | 260 |
261 | ${UVSuggestion.total_comments} 262 |
` 263 | } 264 |