├── images ├── menu.PNG └── window.PNG ├── Sitefinity-VSIX.Shared ├── Shared │ ├── ExitCode.cs │ ├── InputDialog.xaml │ ├── ConfigParser.cs │ ├── Constants.cs │ ├── VSHelpers.cs │ ├── InputDialog.xaml.cs │ └── CliDownloader.cs ├── VSCommandTable.cs ├── Commands │ ├── DynamicCommand.cs │ └── AboutCommand.cs ├── Sitefinity-VSIX.Shared.shproj ├── Sitefinity-VSIX.Shared.projitems ├── VSPackage.resx └── vNextPackage.cs ├── Sitefinity-VSIX ├── app.config ├── Properties │ └── AssemblyInfo.cs ├── source.extension.vsixmanifest └── Sitefinity-VSIX.csproj ├── LICENSE.md ├── Sitefinity-VSIX.VS22 ├── Properties │ └── AssemblyInfo.cs ├── source.extension.vsixmanifest └── Sitefinity-VSIX.VS22.csproj ├── Sitefinity-VSIX.sln ├── SharedFiles └── VSCommandTable.vsct ├── NOTICE.txt ├── CONTRIBUTING.md ├── README.md └── .gitignore /images/menu.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sitefinity/Sitefinity-VSIX/HEAD/images/menu.PNG -------------------------------------------------------------------------------- /images/window.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sitefinity/Sitefinity-VSIX/HEAD/images/window.PNG -------------------------------------------------------------------------------- /Sitefinity-VSIX.Shared/Shared/ExitCode.cs: -------------------------------------------------------------------------------- 1 | namespace Sitefinity_VSIX.Shared 2 | { 3 | public enum ExitCode 4 | { 5 | OK = 0, 6 | GeneralError = 1, 7 | InsufficientPermissions = 2 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Sitefinity-VSIX/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright © 2022 Progress Software Corporation and/or one of its subsidiaries or affiliates. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /Sitefinity-VSIX.Shared/VSCommandTable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Design; 3 | 4 | namespace Sitefinity_VSIX 5 | { 6 | internal sealed partial class PackageGuids 7 | { 8 | public const string guidPackageString = "c65a7dcd-f890-4bd5-9d62-c032cf4358da"; 9 | public const string guidPackageCommandSetString = "668f2cd7-b6fd-416b-a58a-a80112fd33f7"; 10 | public static readonly Guid guidPackage = new Guid("c65a7dcd-f890-4bd5-9d62-c032cf4358da"); 11 | public static readonly Guid guidPackageCommandSet = new Guid("668f2cd7-b6fd-416b-a58a-a80112fd33f7"); 12 | } 13 | 14 | internal sealed partial class PackageIds 15 | { 16 | public const int DynamicCommandId = 0x0100; 17 | public const int AboutCommandId = 0x200; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Sitefinity-VSIX.Shared/Commands/DynamicCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Design; 3 | using Microsoft.VisualStudio.Shell; 4 | 5 | namespace Sitefinity_VSIX.Commands 6 | { 7 | internal sealed class DynamicCommand : OleMenuCommand 8 | { 9 | private Predicate matches; 10 | 11 | public DynamicCommand(CommandID rootId, Predicate matches, EventHandler invokeHandler, EventHandler beforeQueryStatusHandler) 12 | : base(invokeHandler, null, beforeQueryStatusHandler, rootId) 13 | { 14 | if (matches == null) 15 | { 16 | throw new ArgumentNullException("matches"); 17 | } 18 | 19 | this.matches = matches; 20 | } 21 | 22 | public override bool DynamicItemMatch(int cmdId) 23 | { 24 | if (this.matches(cmdId)) 25 | { 26 | this.MatchedCommandId = cmdId; 27 | return true; 28 | } 29 | 30 | this.MatchedCommandId = 0; 31 | return false; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sitefinity-VSIX.Shared/Sitefinity-VSIX.Shared.shproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 55098d12-d255-4a29-83b2-e9b4b13cdfa5 5 | 14.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Sitefinity-VSIX.Shared/Shared/InputDialog.xaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 41 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | ========================================================================= 2 | ==  NOTICE file corresponding to section 4 d of            == 3 | ==  the Apache License, Version 2.0,                                  == 4 | ==  in this case for Progress Sitefinity VSIX v1                   == 5 | ========================================================================= 6 | 7 | Progress Sitefinity VSIX v1 (the “Product”) 8 | 9 | Copyright © 2018-2019, 2022 Progress Software Corporation and/or one of its subsidiaries or affiliates. All rights reserved. 10 | 11 | For license information see the LICENSE.md file which accompanies this NOTICE.txt file. 12 | 13 | Portions of the Product include certain open source and commercial third-party components listed below (“Third-Party Components”). The authors of the Third-Party Components require Progress Software Corporation (“PSC”) to include the following notices and additional licensing terms as a condition of PSC’s use of such Third-Party Components. You acknowledge that the authors of the Third-Party Components have no obligation to provide support to you for the Third-Party Components or the Product. You hereby undertake to comply with all licenses related to the applicable Third-Party Components. Notwithstanding anything to the contrary, to the extent that any of the terms and conditions of the Product Agreement conflict, vary, or are in addition to the terms and conditions of the aforementioned third-party licenses for these technologies, such terms and conditions are offered by PSC alone and not by any other party. 14 | 15 | ------------------------------------------------------------------------- 16 | SUMMARY OF COMPONENTS: 17 | 18 | VendorName | ComponentName | VersionName | LicenseType 19 | 20 | James Newton-King | Newtonsoft.Json | 13.0.1 - Open Source | MIT-style License 21 | James Newton-King | Newtonsoft.Json | 10.0.3 - Open Source | MIT-style License 22 | ------------------------------------------------------------------------- 23 | 24 | 1. Special Notices Regarding Open-Source Third-Party Components incorporated in the Product: 25 | 26 | Progress Sitefinity VSIX v1 may incorporate Newtonsoft.Json v13.0.1 or Newtonsoft.Json 10.0.3. Such technologies are subject to the following terms and conditions: 27 | The MIT License (MIT) 28 | Copyright (c) 2007 James Newton-King 29 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 30 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 32 | 33 | 2. Special Notices Regarding Commercially Licensed Third-Party Components incorporated in the Product: None 34 | 35 | 36 | 37 | NOTICE FROM PROGRESS SOFTWARE CORPORATION: Additional notices may be included in the release notes or other documentation that accompanies updates received in connection with support of the Product. 38 | 39 | 40 | Updated 6.June.2022 41 | -------------------------------------------------------------------------------- /Sitefinity-VSIX.Shared/Shared/InputDialog.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | 5 | namespace Sitefinity_VSIX.Shared 6 | { 7 | public partial class InputDialog : Window 8 | { 9 | private const int ElementHeight = 23; 10 | private const int LabelWidth = 110; 11 | private const int InputWidth = 210; 12 | private const int LabelMarginLeft = 25; 13 | private const int InputMarginLeft = LabelMarginLeft + 120; 14 | private const int ElementInitialMarginTop = 50; 15 | private const int ElementAdditionalMarginTop = 30; 16 | private const int ElementInitialMarginBottom = 50; 17 | 18 | public InputDialog(Command commandConfig) 19 | { 20 | InitializeComponent(); 21 | this.Title = commandConfig.Title; 22 | int numberOfArgs = 0; 23 | for (int i = 0; i < commandConfig.Args.Count; i++, numberOfArgs++) 24 | { 25 | var argument = commandConfig.Args[i]; 26 | this.CreateInput(i, null); 27 | this.CreateLabel(i, argument); 28 | } 29 | 30 | for (int i = 0; i < commandConfig.Options.Count; i++) 31 | { 32 | this.CreateInput(i + numberOfArgs, commandConfig.Options[i].DefaultValue); 33 | this.CreateLabel(i + numberOfArgs, commandConfig.Options[i].Title); 34 | } 35 | } 36 | 37 | public List ResponseText { get; set; } 38 | 39 | private void Ok_Click(object sender, RoutedEventArgs e) 40 | { 41 | if (this.ResponseText == null) 42 | { 43 | this.ResponseText = new List(); 44 | } 45 | 46 | foreach (var element in this.Grid.Children) 47 | { 48 | if (element.GetType() == typeof(TextBox)) 49 | { 50 | this.ResponseText.Add(((TextBox)element).Text); 51 | } 52 | } 53 | 54 | this.DialogResult = true; 55 | } 56 | 57 | private void Cancel_Click(object sender, RoutedEventArgs e) 58 | { 59 | this.DialogResult = false; 60 | } 61 | 62 | private void CreateInput(int index, string defaultValue) 63 | { 64 | TextBox newInput = new TextBox(); 65 | 66 | newInput.HorizontalAlignment = HorizontalAlignment.Left; 67 | newInput.Height = ElementHeight; 68 | newInput.Width = InputWidth; 69 | newInput.Margin = new Thickness(InputMarginLeft, ElementInitialMarginTop + index * ElementAdditionalMarginTop, 0, ElementInitialMarginBottom); 70 | newInput.TextWrapping = TextWrapping.Wrap; 71 | newInput.VerticalAlignment = VerticalAlignment.Top; 72 | 73 | if (defaultValue != null) 74 | { 75 | newInput.Text = defaultValue; 76 | } 77 | 78 | this.Grid.Children.Add(newInput); 79 | } 80 | 81 | private void CreateLabel(int index, string text) 82 | { 83 | TextBlock newLabel = new TextBlock(); 84 | 85 | newLabel.Text = text; 86 | newLabel.HorizontalAlignment = HorizontalAlignment.Left; 87 | newLabel.Height = ElementHeight; 88 | newLabel.Width = LabelWidth; 89 | newLabel.Margin = new Thickness(LabelMarginLeft, ElementInitialMarginTop + index * ElementAdditionalMarginTop, 0, ElementInitialMarginBottom); 90 | newLabel.VerticalAlignment = VerticalAlignment.Top; 91 | 92 | this.Grid.Children.Add(newLabel); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Sitefinity VSIX 2 | 3 | ## Before You Start 4 | 5 | Anyone wishing to contribute to the Sitefinity VSIX project MUST read & sign the [Sitefinity VSIX Contribution License Agreement](https://progress.co1.qualtrics.com/jfe/form/SV_ahKtkYhyns5jeuN). The Sitefinity CMS team cannot accept pull requests from users who have not signed the CLA first. 6 | 7 | ## Introduction 8 | 9 | These guidelines are here to facilitate your contribution and streamline the process of getting changes merged into this project and released. Any contributions you can make will help tremendously, even if only in the form of an issue report. Following these guidelines will help to streamline the pull request and change submission process. 10 | 11 | ## Report an Issue 12 | 13 | If you find a bug in the source code or a mistake in the documentation, you can submit an issue to our [GitHub Repository](https://github.com/Sitefinity/Sitefinity-VSIX). 14 | Before you submit your issue, search the archive to check if a similar issues has been logged or addressed. This will let us focus on fixing issues and adding new features. 15 | If your issue appears to be a bug, and hasn't been reported, open a new issue. To help us investigate your issue and respond in a timely manner, you can provide is with the following details. 16 | 17 | * **Overview of the issue:** Provide a short description of the visible symptoms. If applicable, include error messages, screen shots, and stack traces. 18 | * **Motivation for or use case:** Let us know how this particular issue affects your work. 19 | * **Sitefinity VSIX version:** Always update to the most recent master release; the bug may already be resolved. 20 | * **Steps to reproduce:** If applicable, submit a step-by-step walkthrough of how to reproduce the issue. 21 | * **Related issues:** If you discover a similar issue in our archive, give us a heads up - it might help us identify the culprit. 22 | * **Suggest a fix:** You are welcome to suggest a bug fix or pinpoint the line of code or the commit that you believe has introduced the issue. 23 | 24 | ## Requesting New Features 25 | 26 | You can request a new feature by submitting an issue to our GitHub Repository or visit the [Sitefinity Feedback portal](https://feedback.telerik.com/Project/153), and search this list for similar feature requests. 27 | 28 | ## Code Fixes and Enhancements 29 | 30 | ### 1. Log an Issue 31 | 32 | Before doing anything else, we ask that you file an issue in the Issues list for this project. First, be sure to check the list to ensure that your issue hasn't already been logged. If you're free and clear, file an issue and provide a detailed description of the bug or feature you're interested in. If you're also planning to work on the issue you're creating, let us know so that we can help and provide feedback. 33 | 34 | ### 2. Fork and Branch 35 | 36 | #### Fork Us, Then Create A Topic Branch For Your Work 37 | 38 | The work you are doing for your pull request should not be done in the master branch of your forked repository. Create a topic branch for your work. This allows you to isolate the work you are doing from other changes that may be happening. 39 | 40 | Github is a smart system, too. If you submit a pull request from a topic branch and we ask you to fix something, pushing a change to your topic branch will automatically update the pull request. 41 | 42 | #### Isolate Your Changes For The Pull Request 43 | 44 | See the previous item on creating a topic branch. 45 | 46 | If you don't use a topic branch, we may ask you to re-do your pull request on a topic branch. If your pull request contains commits or other changes that are not related to the pull request, we will ask you to re-do your pull request. 47 | 48 | #### (optional) Squash your commits 49 | 50 | When you've completed your work on a topic branch, you may squash your work down into fewer commits to make the merge process easier. For information on squashing via an interactive rebase, see [the rebase documentation on GitHub](https://help.github.com/articles/interactive-rebase) 51 | 52 | ### 3. Submit a Pull Request 53 | 54 | See [Github's documentation for pull requests](https://help.github.com/articles/using-pull-requests). 55 | 56 | Pull requests are the preferred way to contribute to Sitefinity VSIX. Any time you can send us a pull request with the changes that you want, we will have an easier time seeing what you are trying to do. It is very important to provide a meaningful description with your pull requests that alter any code. 57 | -------------------------------------------------------------------------------- /Sitefinity-VSIX.Shared/Shared/CliDownloader.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.IO; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.IO.Compression; 6 | using System; 7 | using System.Reflection; 8 | using Newtonsoft.Json; 9 | using System.Diagnostics; 10 | using System.Text.RegularExpressions; 11 | 12 | namespace Sitefinity_VSIX.Shared 13 | { 14 | internal class CliDownloader 15 | { 16 | public static void SetUp(string cliPath) 17 | { 18 | // Get current version 19 | var currentVersion = Assembly.GetExecutingAssembly().GetName().Version; 20 | 21 | // Get CLI latest version for this major 22 | var releasesResponse = Get(Constants.CliReleasesUrl); 23 | var releases = Newtonsoft.Json.JsonConvert.DeserializeObject>(releasesResponse); 24 | var latestRelease = releases.Where(x => !x.Prerelease && ParseReleaseMajorVersion(x.Version).Major == currentVersion.Major).OrderByDescending(x => x.PublishDate).FirstOrDefault(); 25 | 26 | if (latestRelease != null) 27 | { 28 | var latestReleaseVersion = ParseReleaseMajorVersion(latestRelease.Version); 29 | 30 | // check if cli is already downloaded or its current version is lower than the latest release version 31 | if (File.Exists(cliPath)) 32 | { 33 | // get cli version 34 | var versionInfo = FileVersionInfo.GetVersionInfo(cliPath); 35 | Version cliVersion = Version.Parse(versionInfo.FileVersion); 36 | 37 | if (cliVersion.CompareTo(latestReleaseVersion) < 0) 38 | { 39 | HandleCliDownloadAndExtraction(latestRelease, cliPath); 40 | } 41 | } 42 | else 43 | { 44 | HandleCliDownloadAndExtraction(latestRelease, cliPath); 45 | } 46 | } 47 | } 48 | 49 | private static string Get(string uri) 50 | { 51 | HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri); 52 | 53 | // Error occurs if this is not set because of GitHub API 54 | request.UserAgent = "some agent"; 55 | 56 | using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) 57 | using (Stream stream = response.GetResponseStream()) 58 | using (StreamReader reader = new StreamReader(stream)) 59 | { 60 | return reader.ReadToEnd(); 61 | } 62 | } 63 | 64 | private static void Download(string downloadUri, string targetLocation) 65 | { 66 | using (var client = new WebClient()) 67 | { 68 | client.DownloadFile(downloadUri, targetLocation); 69 | } 70 | } 71 | 72 | private static void HandleCliDownloadAndExtraction(Release release, string cliPath) 73 | { 74 | var asset = release.Assets.Where(x => x.Name == Constants.WindowsReleaseName).FirstOrDefault(); 75 | if (asset != null) 76 | { 77 | var cliDirectoryName = Path.GetDirectoryName(cliPath); 78 | 79 | if (Directory.Exists(cliDirectoryName)) 80 | { 81 | Directory.Delete(cliDirectoryName, true); 82 | } 83 | 84 | Directory.CreateDirectory(cliDirectoryName); 85 | var releaseZipPath = Path.Combine(cliDirectoryName, Constants.WindowsReleaseName); 86 | Download(asset.DownloadUrl, releaseZipPath); 87 | 88 | // extract and delete the zip 89 | ZipFile.ExtractToDirectory(releaseZipPath, cliDirectoryName); 90 | File.Delete(releaseZipPath); 91 | } 92 | } 93 | 94 | private static Version ParseReleaseMajorVersion(string version) 95 | { 96 | var regex = new Regex(@"(\d+\.)(\d+\.)(\d+\.)(\d)"); 97 | return Version.Parse(regex.Match(version).Value); 98 | } 99 | } 100 | 101 | internal class Release 102 | { 103 | [JsonProperty(PropertyName = "tag_name")] 104 | public string Version; 105 | 106 | [JsonProperty(PropertyName = "published_at")] 107 | public DateTime PublishDate; 108 | 109 | public List Assets; 110 | 111 | public bool Prerelease; 112 | } 113 | 114 | internal class Asset 115 | { 116 | public string Name; 117 | 118 | [JsonProperty(PropertyName = "browser_download_url")] 119 | public string DownloadUrl; 120 | } 121 | } -------------------------------------------------------------------------------- /Sitefinity-VSIX.VS22/Sitefinity-VSIX.VS22.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | 10 | Debug 11 | AnyCPU 12 | 2.0 13 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | {248BB80D-4109-41E8-A758-B4E23D527F7F} 15 | Library 16 | Properties 17 | Sitefinity_VSIX.VS22 18 | Sitefinity-VSIX.VS22 19 | v4.8 20 | true 21 | true 22 | true 23 | false 24 | false 25 | true 26 | true 27 | Program 28 | $(DevEnvDir)devenv.exe 29 | /rootsuffix Exp 30 | 31 | 32 | true 33 | full 34 | false 35 | bin\Debug\ 36 | TRACE;DEBUG 37 | prompt 38 | 4 39 | 40 | 41 | pdbonly 42 | true 43 | bin\Release\ 44 | TRACE 45 | prompt 46 | 4 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 13.0.1 65 | 66 | 67 | 68 | 69 | VSCommandTable.vsct 70 | Menus.ctmenu 71 | Designer 72 | 73 | 74 | 75 | 76 | Designer 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | compile; build; native; contentfiles; analyzers; buildtransitive 85 | 86 | 87 | runtime; build; native; contentfiles; analyzers; buildtransitive 88 | all 89 | 90 | 91 | 92 | 93 | 94 | 101 | -------------------------------------------------------------------------------- /Sitefinity-VSIX/Sitefinity-VSIX.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 15.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | true 9 | 10 | 11 | 12 | false 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | Debug 21 | AnyCPU 22 | 2.0 23 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 24 | {4476C8C5-5A49-42DB-AD78-B1C7D787D4F4} 25 | Library 26 | Properties 27 | Sitefinity_VSIX 28 | Sitefinity-VSIX 29 | v4.8 30 | true 31 | true 32 | true 33 | true 34 | true 35 | false 36 | Program 37 | $(DevEnvDir)devenv.exe 38 | /rootsuffix Exp 39 | 40 | 41 | true 42 | full 43 | false 44 | bin\Debug\ 45 | TRACE;DEBUG 46 | prompt 47 | 4 48 | True 49 | 50 | 51 | pdbonly 52 | true 53 | bin\Release\ 54 | TRACE 55 | prompt 56 | 4 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | Designer 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 8.0.0 81 | 82 | 83 | 14.3.25407 84 | 85 | 86 | 10.0.30319 87 | 88 | 89 | 10.0.3 90 | 91 | 92 | 93 | 94 | VSCommandTable.vsct 95 | Menus.ctmenu 96 | Designer 97 | 98 | 99 | 100 | 101 | 102 | 109 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sitefinity VSIX 2 | Sitefinity VSIX is a Visual Studio extension that allows you to create Sitefinity CMS related resources. This way you can easily scaffold Sitefinity CMS projects. The tool is focused on facilitating the development of MVC widgets and templates. 3 | 4 | ## Installation 5 | Supported Visual Studio Versions: 2017, 2019 and 2022. 6 | 7 | **NOTE**: For Visual Studio 2017 (up to Update 15.3), you first need to install [.NET Core](https://www.microsoft.com/net/download/windows) before using the extension. 8 | 9 | You can download the extension from Visual Studio Marketplace >> [Sitefinity VSIX](https://marketplace.visualstudio.com/items?itemName=vs-publisher-443.Sitefinity-VSIX).
10 | For Visual Studio 2022, there is a separate extension available from Visual Studio Marketplace >> [Sitefinity VSIX for VS 2022](https://marketplace.visualstudio.com/items?itemName=vs-publisher-443.Sitefinity-VSIX-VS22). 11 | 12 | ## Architecture 13 | Sitefinity VSIX is based on Sitefinity CLI. After you install the extension, during the initial load of the first solution, the extension downloads Sitefinity CLI from GitHub, extracts it, and creates a configuration file. 14 | 15 | You can also download and use Sitefinity CLI independently, without installing Sitefinity VSIX. 16 | For more information, see [Sitefinity CLI](https://github.com/Sitefinity/Sitefinity-CLI). 17 | 18 | ## Use Sitefinity VSIX 19 | 20 | 1. Open your Sitefinity CMS solution in Visual Studio. 21 | 2. In the context menu of the SitefinityWebApp project, click *Add >> Sitefinity*. 22 | 23 | A submenu with available options expands. 24 | 3. Click the resource that you want to create. 25 | 4. A dialog appears, where you must fill out the information needed to create the resource. 26 | ![Menu](images/menu.PNG) 27 | 28 | ## Available commands 29 | #### Resource package 30 | Adds a new Resource package with some basic content in it. If the `ResourcePackages` folder does not exist it will be created. 31 | 32 | Parameters: 33 | - *Name* - Enter the name of the resource package that you want to create. 34 | - *TemplateName* - Enter the name of the template you want to use for the creation. The default value is *Bootstrap5*. 35 | 36 | **Note**: Depending on the version of Sitefinity CMS your project is using, the resource packages use different default versions of Bootstrap, per the following table: 37 | 38 | | Sitefinity version | Bootstrap version | 39 | |--------------------|-------------------| 40 | | 13.3 | v4 | 41 | | 14.0 | v4 | 42 | | 14.4 | v4, v5 | 43 | | 15.0 | v4, v5 | 44 | 45 | #### Page template 46 | Adds a new Page template. 47 | 48 | Parameters: 49 | - *Name* - Enter the name of the page template that you want to create. 50 | - *ResourcePackage* - Enter the name of the resource package where the template will be created. The default value is *Bootstrap5*. 51 | - *TemplateName* - Enter the name of the template that you want to use in the creation. The default value is *Default*. 52 | 53 | #### Grid widget 54 | 55 | Adds a new Grid widget. 56 | 57 | Parameters: 58 | - *Name* - Enter the name of the grid widget that you want to create. 59 | - *ResourcePackage* - Enter the name of the resource package where the widget will be created. The default value is *Bootstrap5*. 60 | - *TemplateName* - Enter the name of the template you want to use for the creation. The default value is *grid-6+6*. 61 | 62 | #### Widget 63 | Adds a new custom widget. 64 | 65 | Parameters: 66 | - *Name* - Enter the name of the widget that you want to create. 67 | - *TemplateName* - Enter the name of the template that you want to use in the creation. The default value is *Default*. 68 | 69 | #### Module 70 | Adds a new custom module. The generated structure demonstrates how a custom module can be implemented and integrated with Sitefinity CMS. 71 | Parameters: 72 | - *Name* - Enter the name of the custom module that you want to create. 73 | - *Description* – Enter the description of the custom module that you want to create. 74 | - *TemplateName* - Enter the name of the template that you want to use for creating your module. Different templates define different implementation structure for the sample module. The default value is *Default*. 75 | 76 | #### Integration tests 77 | Adds a new integration tests project, customized to work with Sitefinity CMS. Contains the integration test classes and instructions for implementation. 78 | Parameters: 79 | - *Name* - Enter the name of the integration tests project that you want to create. 80 | - *TemplateName* - Enter the name of the template that you want to use for creating your integration tests project. Different templates define different implementation structure for the sample project. The default value is *Default*. 81 | 82 | #### About 83 | Provides information about the currently installed version of Sitefinity VSIX 84 | 85 | ## Sitefinity version 86 | Sitefinity VSIX will automatically try to detect the version of your Sitefinity CMS project and use the respective templates. If the version cannot be detected, Sitefinity VSIX uses the latest version of the templates. 87 | 88 | ## Template generation and custom templates 89 | For more information about templates generation and custom templates, see the [Sitefinity CLI repository](https://github.com/Sitefinity/Sitefinity-CLI) 90 | 91 | ## Known issues 92 | #### Visual Studio 2015 integration 93 | Sitefinity VSIX/CLI correctly updates the csproj and sln files, but Visual Studio 2015 won't refresh the solution correctly. 94 | 95 | The workaround is to reopen the solution. 96 | 97 | #### Visual Studio appears frozen or crashes 98 | When Sitefinity VSIX is installed for the first time or there is a newer version of Sitefinity CLI, Visual Studio freezes until the CLI is downloaded and unzipped and, in some cases, crashes. 99 | 100 | After Sitefinity CLI is downloaded and unzipped, Visual Studio restores its normal behaviour. 101 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # Benchmark Results 46 | BenchmarkDotNet.Artifacts/ 47 | 48 | # .NET Core 49 | project.lock.json 50 | project.fragment.lock.json 51 | artifacts/ 52 | **/Properties/launchSettings.json 53 | 54 | *_i.c 55 | *_p.c 56 | *_i.h 57 | *.ilk 58 | *.meta 59 | *.obj 60 | *.pch 61 | *.pdb 62 | *.pgc 63 | *.pgd 64 | *.rsp 65 | *.sbr 66 | *.tlb 67 | *.tli 68 | *.tlh 69 | *.tmp 70 | *.tmp_proj 71 | *.log 72 | *.vspscc 73 | *.vssscc 74 | .builds 75 | *.pidb 76 | *.svclog 77 | *.scc 78 | 79 | # Chutzpah Test files 80 | _Chutzpah* 81 | 82 | # Visual C++ cache files 83 | ipch/ 84 | *.aps 85 | *.ncb 86 | *.opendb 87 | *.opensdf 88 | *.sdf 89 | *.cachefile 90 | *.VC.db 91 | *.VC.VC.opendb 92 | 93 | # Visual Studio profiler 94 | *.psess 95 | *.vsp 96 | *.vspx 97 | *.sap 98 | 99 | # TFS 2012 Local Workspace 100 | $tf/ 101 | 102 | # Guidance Automation Toolkit 103 | *.gpState 104 | 105 | # ReSharper is a .NET coding add-in 106 | _ReSharper*/ 107 | *.[Rr]e[Ss]harper 108 | *.DotSettings.user 109 | 110 | # JustCode is a .NET coding add-in 111 | .JustCode 112 | 113 | # TeamCity is a build add-in 114 | _TeamCity* 115 | 116 | # DotCover is a Code Coverage Tool 117 | *.dotCover 118 | 119 | # AxoCover is a Code Coverage Tool 120 | .axoCover/* 121 | !.axoCover/settings.json 122 | 123 | # Visual Studio code coverage results 124 | *.coverage 125 | *.coveragexml 126 | 127 | # NCrunch 128 | _NCrunch_* 129 | .*crunch*.local.xml 130 | nCrunchTemp_* 131 | 132 | # MightyMoose 133 | *.mm.* 134 | AutoTest.Net/ 135 | 136 | # Web workbench (sass) 137 | .sass-cache/ 138 | 139 | # Installshield output folder 140 | [Ee]xpress/ 141 | 142 | # DocProject is a documentation generator add-in 143 | DocProject/buildhelp/ 144 | DocProject/Help/*.HxT 145 | DocProject/Help/*.HxC 146 | DocProject/Help/*.hhc 147 | DocProject/Help/*.hhk 148 | DocProject/Help/*.hhp 149 | DocProject/Help/Html2 150 | DocProject/Help/html 151 | 152 | # Click-Once directory 153 | publish/ 154 | 155 | # Publish Web Output 156 | *.[Pp]ublish.xml 157 | *.azurePubxml 158 | # Note: Comment the next line if you want to checkin your web deploy settings, 159 | # but database connection strings (with potential passwords) will be unencrypted 160 | *.pubxml 161 | *.publishproj 162 | 163 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 164 | # checkin your Azure Web App publish settings, but sensitive information contained 165 | # in these scripts will be unencrypted 166 | PublishScripts/ 167 | 168 | # NuGet Packages 169 | *.nupkg 170 | # The packages folder can be ignored because of Package Restore 171 | **/packages/* 172 | # except build/, which is used as an MSBuild target. 173 | !**/packages/build/ 174 | # Uncomment if necessary however generally it will be regenerated when needed 175 | #!**/packages/repositories.config 176 | # NuGet v3's project.json files produces more ignorable files 177 | *.nuget.props 178 | *.nuget.targets 179 | 180 | # Microsoft Azure Build Output 181 | csx/ 182 | *.build.csdef 183 | 184 | # Microsoft Azure Emulator 185 | ecf/ 186 | rcf/ 187 | 188 | # Windows Store app package directories and files 189 | AppPackages/ 190 | BundleArtifacts/ 191 | Package.StoreAssociation.xml 192 | _pkginfo.txt 193 | *.appx 194 | 195 | # Visual Studio cache files 196 | # files ending in .cache can be ignored 197 | *.[Cc]ache 198 | # but keep track of directories ending in .cache 199 | !*.[Cc]ache/ 200 | 201 | # Others 202 | ClientBin/ 203 | ~$* 204 | *~ 205 | *.dbmdl 206 | *.dbproj.schemaview 207 | *.jfm 208 | *.pfx 209 | *.publishsettings 210 | orleans.codegen.cs 211 | 212 | # Since there are multiple workflows, uncomment next line to ignore bower_components 213 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 214 | #bower_components/ 215 | 216 | # RIA/Silverlight projects 217 | Generated_Code/ 218 | 219 | # Backup & report files from converting an old project file 220 | # to a newer Visual Studio version. Backup files are not needed, 221 | # because we have git ;-) 222 | _UpgradeReport_Files/ 223 | Backup*/ 224 | UpgradeLog*.XML 225 | UpgradeLog*.htm 226 | 227 | # SQL Server files 228 | *.mdf 229 | *.ldf 230 | *.ndf 231 | 232 | # Business Intelligence projects 233 | *.rdl.data 234 | *.bim.layout 235 | *.bim_*.settings 236 | 237 | # Microsoft Fakes 238 | FakesAssemblies/ 239 | 240 | # GhostDoc plugin setting file 241 | *.GhostDoc.xml 242 | 243 | # Node.js Tools for Visual Studio 244 | .ntvs_analysis.dat 245 | node_modules/ 246 | 247 | # Typescript v1 declaration files 248 | typings/ 249 | 250 | # Visual Studio 6 build log 251 | *.plg 252 | 253 | # Visual Studio 6 workspace options file 254 | *.opt 255 | 256 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 257 | *.vbw 258 | 259 | # Visual Studio LightSwitch build output 260 | **/*.HTMLClient/GeneratedArtifacts 261 | **/*.DesktopClient/GeneratedArtifacts 262 | **/*.DesktopClient/ModelManifest.xml 263 | **/*.Server/GeneratedArtifacts 264 | **/*.Server/ModelManifest.xml 265 | _Pvt_Extensions 266 | 267 | # Paket dependency manager 268 | .paket/paket.exe 269 | paket-files/ 270 | 271 | # FAKE - F# Make 272 | .fake/ 273 | 274 | # JetBrains Rider 275 | .idea/ 276 | *.sln.iml 277 | 278 | # CodeRush 279 | .cr/ 280 | 281 | # Python Tools for Visual Studio (PTVS) 282 | __pycache__/ 283 | *.pyc 284 | 285 | # Cake - Uncomment if you are using it 286 | # tools/** 287 | # !tools/packages.config 288 | 289 | # Tabs Studio 290 | *.tss 291 | 292 | # Telerik's JustMock configuration file 293 | *.jmconfig 294 | 295 | # BizTalk build output 296 | *.btp.cs 297 | *.btm.cs 298 | *.odx.cs 299 | *.xsd.cs 300 | -------------------------------------------------------------------------------- /Sitefinity-VSIX.Shared/VSPackage.resx: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | text/microsoft-resx 120 | 121 | 122 | 2.0 123 | 124 | 125 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 126 | 127 | 128 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 129 | 130 | 131 | 132 | CreatePackageCommand Extension 133 | 134 | 135 | CreatePackageCommand Visual Studio Extension Detailed Info 136 | 137 | -------------------------------------------------------------------------------- /Sitefinity-VSIX.Shared/vNextPackage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Design; 3 | using System.Runtime.InteropServices; 4 | using Microsoft.VisualStudio.Shell; 5 | using Sitefinity_VSIX.Commands; 6 | using System.Threading; 7 | using System.Windows.Threading; 8 | using System.IO; 9 | using Sitefinity_VSIX.Shared; 10 | using System.Diagnostics; 11 | 12 | namespace Sitefinity_VSIX 13 | { 14 | [PackageRegistration(AllowsBackgroundLoading = true, UseManagedResourcesOnly = true)] 15 | [InstalledProductRegistration("#110", "#112", "1.0")] 16 | [ProvideMenuResource("Menus.ctmenu", 1)] 17 | [Guid(PackageGuids.guidPackageString)] 18 | [ProvideAutoLoad("f1536ef8-92ec-443c-9ed7-fdadf150da82", PackageAutoLoadFlags.BackgroundLoad)] 19 | public sealed class vNextPackage : AsyncPackage 20 | { 21 | private ConfigParser configParser; 22 | 23 | protected async override System.Threading.Tasks.Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) 24 | { 25 | if (await GetServiceAsync(typeof(IMenuCommandService)) is OleMenuCommandService commandService) 26 | { 27 | ThreadHelper.Generic.BeginInvoke(DispatcherPriority.ContextIdle, () => 28 | { 29 | if (commandService != null) 30 | { 31 | var extentionPath = Path.GetDirectoryName(this.GetType().Assembly.Location); 32 | var dllPath = Path.Combine(extentionPath, Constants.CLIFolderName, Constants.CLIName); 33 | var configPath = Path.Combine(extentionPath, Constants.CLIFolderName, Constants.ConfigFileName); 34 | 35 | CliDownloader.SetUp(dllPath); 36 | 37 | var fileInfo = new FileInfo(configPath); 38 | 39 | if (!fileInfo.Exists) 40 | { 41 | var args = string.Format("{0} config", Constants.CLIName); 42 | var process = new Process(); 43 | process.StartInfo.WorkingDirectory = fileInfo.DirectoryName; 44 | process.StartInfo.RedirectStandardOutput = true; 45 | process.StartInfo.UseShellExecute = false; 46 | process.StartInfo.CreateNoWindow = true; 47 | process.StartInfo.FileName = Constants.DotNetCoreProcessName; 48 | process.StartInfo.Arguments = args; 49 | process.Start(); 50 | process.WaitForExit(); 51 | 52 | if (process.ExitCode != (int)ExitCode.OK) 53 | { 54 | string message; 55 | string title; 56 | 57 | switch (process.ExitCode) 58 | { 59 | case (int)ExitCode.InsufficientPermissions: 60 | message = Constants.ConfigPermissionsErrorMessage; 61 | title = Constants.ConfigPermissionsErrorTitle; 62 | break; 63 | case (int)ExitCode.GeneralError: 64 | default: 65 | message = Constants.ConfigGeneralErrorMessage; 66 | title = Constants.ConfigGeneralErrorTitle; 67 | break; 68 | } 69 | 70 | VSHelpers.ShowErrorMessage(this, message, title); 71 | return; 72 | } 73 | } 74 | 75 | this.configParser = new ConfigParser(configPath); 76 | var dynamicCommandRootId = new CommandID(PackageGuids.guidPackageCommandSet, PackageIds.DynamicCommandId); 77 | var dynamicCommand = new DynamicCommand(dynamicCommandRootId, IsValidDynamicItem, OnInvokedDynamicItem, OnBeforeQueryStatusDynamicItem); 78 | commandService.AddCommand(dynamicCommand); 79 | 80 | var aboutCommandRootId = new CommandID(PackageGuids.guidPackageCommandSet, PackageIds.AboutCommandId); 81 | var aboutCommand = new AboutCommand(OnInvokeAboutCommand, aboutCommandRootId, dllPath); 82 | commandService.AddCommand(aboutCommand); 83 | } 84 | }); 85 | } 86 | } 87 | 88 | private void OnInvokedDynamicItem(object sender, EventArgs e) 89 | { 90 | DynamicCommand invokedCommand = (DynamicCommand)sender; 91 | var commandConfig = this.configParser.Commands.Find(c => c.Title.Equals(invokedCommand.Text)); 92 | var currentProjectPath = VSHelpers.GetCurrentProjectPath(); 93 | 94 | var extentionPath = Path.GetDirectoryName(this.GetType().Assembly.Location); 95 | var exePath = Path.Combine(extentionPath, Constants.CLIFolderName, Constants.CLIName); 96 | var fileInfo = new FileInfo(exePath); 97 | 98 | if (!fileInfo.Exists) 99 | { 100 | string message = "File 'sf.exe' does not exist!"; 101 | VSHelpers.ShowErrorMessage(this, message, commandConfig.Title); 102 | return; 103 | } 104 | 105 | var args = String.Format("{0}", commandConfig.Name); 106 | 107 | // get arguments 108 | var dialog = new InputDialog(commandConfig); 109 | if (dialog.ShowDialog() == true) 110 | { 111 | for (int i = 0; i < dialog.ResponseText.Count; i++) 112 | { 113 | var input = dialog.ResponseText[i]; 114 | 115 | var isArgument = i < commandConfig.Args.Count; 116 | if (string.IsNullOrEmpty(input) || input.IndexOfAny(Path.GetInvalidFileNameChars()) > 0) 117 | { 118 | string message; 119 | 120 | if (isArgument) 121 | { 122 | message = string.Format("Invalid argument: {0}!", commandConfig.Args[i]); 123 | } 124 | else 125 | { 126 | message = string.Format("Invalid argument: {0}!", commandConfig.Options[i - commandConfig.Args.Count]); 127 | } 128 | 129 | VSHelpers.ShowErrorMessage(this, message, commandConfig.Title); 130 | return; 131 | } 132 | 133 | // response is argument, else - response is option 134 | if (isArgument) 135 | { 136 | args = String.Format("{0} \"{1}\"", args, input); 137 | } 138 | else 139 | { 140 | var optionIndex = i - commandConfig.Args.Count; 141 | args = String.Format("{0} {1} \"{2}\"", args, commandConfig.Options[optionIndex].Name, input); 142 | } 143 | } 144 | 145 | args = String.Format("{0} {1} -r \"{2}\"", Constants.CLIName, args, currentProjectPath); 146 | 147 | var process = new Process(); 148 | process.StartInfo.FileName = Constants.DotNetCoreProcessName; 149 | process.StartInfo.WorkingDirectory = fileInfo.DirectoryName; 150 | process.StartInfo.Arguments = args; 151 | process.Start(); 152 | } 153 | } 154 | 155 | private void OnBeforeQueryStatusDynamicItem(object sender, EventArgs args) 156 | { 157 | OleMenuCommand matchedCommand = (OleMenuCommand)sender; 158 | matchedCommand.Enabled = true; 159 | matchedCommand.Visible = true; 160 | 161 | bool isRootItem = (matchedCommand.MatchedCommandId == 0); 162 | int commandIndex = isRootItem ? 0 : (matchedCommand.MatchedCommandId - (int)PackageIds.DynamicCommandId); 163 | 164 | matchedCommand.Text = configParser.Commands[commandIndex].Title; 165 | 166 | // Clear the ID because we are done with this item. 167 | matchedCommand.MatchedCommandId = 0; 168 | } 169 | 170 | private void OnInvokeAboutCommand(object sender, EventArgs args) 171 | { 172 | if (sender is AboutCommand command) 173 | { 174 | VSHelpers.ShowMessage(this, command.AboutMessage, command.AboutTitle); 175 | } 176 | } 177 | 178 | private bool IsValidDynamicItem(int commandId) 179 | { 180 | return PackageIds.DynamicCommandId <= commandId && commandId <= (PackageIds.DynamicCommandId + configParser.Commands.Count - 1); 181 | } 182 | } 183 | } 184 | --------------------------------------------------------------------------------