Skip to content

Commit

Permalink
feat(vsix): Update error handling on startup (#416)
Browse files Browse the repository at this point in the history
* feat(vsix): Add wider exception handling on startup

* feat(vsix): Add alert for unauthorized file delete
  • Loading branch information
Plac3hold3r committed Jun 1, 2019
1 parent efc18da commit fe62fef
Show file tree
Hide file tree
Showing 11 changed files with 128 additions and 34 deletions.
3 changes: 3 additions & 0 deletions src/MvxScaffolding.Core/Contexts/MvxScaffoldingContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System;
using System.Diagnostics;
using MvxScaffolding.Core.Dialogs.Interfaces;
using MvxScaffolding.Core.Files;
using MvxScaffolding.Core.Properties;
using MvxScaffolding.Core.Template;

Expand All @@ -31,6 +32,8 @@ public static class MvxScaffoldingContext

public static string SolutionName { get; set; } = "SolutionProject";

public static FileDeleteStatus RemoveOldSolutionDirectoryStatus { get; set; } = FileDeleteStatus.Unknown;

public static bool CanCreateSolutionDirectory { get; set; }

public static Version LastKnownVersion
Expand Down
15 changes: 15 additions & 0 deletions src/MvxScaffolding.Core/Files/FileDeleteStatus.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//---------------------------------------------------------------------------------
// Copyright © 2018, Jonathan Froon, Plac3hold3r+github@outlook.com
// MvxScaffolding is licensed using the MIT License
//---------------------------------------------------------------------------------

namespace MvxScaffolding.Core.Files
{
public enum FileDeleteStatus
{
Unknown,
Success,
UnauthorizedAccessError,
Error
}
}
15 changes: 13 additions & 2 deletions src/MvxScaffolding.Core/Files/FileSystemUtils.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
// Copyright © 2018, Jonathan Froon, Plac3hold3r+github@outlook.com
// MvxScaffolding is licensed using the MIT License
//---------------------------------------------------------------------------------
Expand All @@ -12,19 +12,30 @@ namespace MvxScaffolding.Core.Files
{
public static class FileSystemUtils
{
public static void SafeDeleteDirectory(string dir)
public static FileDeleteStatus SafeDeleteDirectory(string dir)
{
try
{
if (Directory.Exists(dir))
{
Directory.Delete(dir, true);
}

return FileDeleteStatus.Success;
}
catch (UnauthorizedAccessException ex)
{
Logger.Current.Info.TrackAsync("Unauthorized access to deleting directory", ex)
.FireAndForget();

return FileDeleteStatus.UnauthorizedAccessError;
}
catch (Exception ex)
{
Logger.Current.Exception.TrackAsync(ex, "Error deleting directory")
.FireAndForget();

return FileDeleteStatus.Error;
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/MvxScaffolding.Core/MvxScaffolding.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@
<Compile Include="Extensions\GitHubDownloadExtensions.cs" />
<Compile Include="Extensions\TaskExtensions.cs" />
<Compile Include="Extensions\WebClientExtensions.cs" />
<Compile Include="Files\FileDeleteStatus.cs" />
<Compile Include="Files\FileSystemUtils.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Properties\Settings.Designer.cs">
Expand Down
4 changes: 3 additions & 1 deletion src/MvxScaffolding.Core/Template/TemplateLinks.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
// Copyright © 2018, Jonathan Froon, Plac3hold3r+github@outlook.com
// MvxScaffolding is licensed using the MIT License
//---------------------------------------------------------------------------------
Expand All @@ -25,6 +25,8 @@ public static class TemplateLinks

public const string EditorConfig = "EditorConfig";

public const string UnauthorizedAccessError = "UnauthorizedAccessError";

public const string FluentLayout = "FluentLayout";

public const string HyperioniOS = "HyperioniOS";
Expand Down
10 changes: 10 additions & 0 deletions src/MvxScaffolding.Core/ViewModels/Dialogs/SimpleInfoViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,15 @@ public static SimpleInfoViewModel EditorConfigInfo()
TemplateLink = TemplateLinks.EditorConfig
};
}

public static SimpleInfoViewModel UnauthorizedAccessErrorInfo()
{
return new SimpleInfoViewModel
{
Title = LocalResources.SimpleInfo_UnauthorizedAccess_Title,
Message = LocalResources.SimpleInfo_UnauthorizedAccess_Message,
TemplateLink = TemplateLinks.UnauthorizedAccessError
};
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 24 additions & 18 deletions src/MvxScaffolding.Localization/Resources/LocalResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -59,46 +59,46 @@
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="mimetype" type="xsd:string"/>
<xsd:attribute ref="xml:space"/>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string"/>
<xsd:attribute name="name" type="xsd:string"/>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
<xsd:attribute ref="xml:space"/>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
Expand Down Expand Up @@ -453,4 +453,10 @@
<data name="AppDetails_Create_Solution_Directory" xml:space="preserve">
<value>Create directory for solution</value>
</data>
</root>
<data name="SimpleInfo_UnauthorizedAccess_Message" xml:space="preserve">
<value>MvxScaffolding can not delete the default Visual Studio generated project folder due to the choice of file location requiring elevated privileges. You will need to either run Visual Studio as an Administrator or manually clean up the directory after project has been generated.</value>
</data>
<data name="SimpleInfo_UnauthorizedAccess_Title" xml:space="preserve">
<value>Unauthorized Access Error</value>
</data>
</root>
13 changes: 13 additions & 0 deletions src/MvxScaffolding.UI/Views/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
// MvxScaffolding is licensed using the MIT License
//---------------------------------------------------------------------------------

using System;
using System.Windows.Input;
using System.Windows.Media;
using MahApps.Metro.Controls;
using MaterialDesignThemes.Wpf;
using MvxScaffolding.Core.Contexts;
using MvxScaffolding.Core.Files;
using MvxScaffolding.Core.Template;
using MvxScaffolding.Core.ViewModels;
using MvxScaffolding.Core.ViewModels.Dialogs;
Expand Down Expand Up @@ -37,6 +39,17 @@ public MainWindow()
#endif
}

protected override void OnContentRendered(EventArgs e)
{
base.OnContentRendered(e);

if (MvxScaffoldingContext.RemoveOldSolutionDirectoryStatus == FileDeleteStatus.UnauthorizedAccessError
&& DataContext is MainViewModel mainViewModel)
{
mainViewModel.ShowDialogCommand.Execute(SimpleInfoViewModel.UnauthorizedAccessErrorInfo());
}
}

private void OnDialogClosing(object sender, DialogClosingEventArgs eventArgs)
{
if (eventArgs.Parameter is SimpleInfoViewModel infoViewModel
Expand Down
1 change: 0 additions & 1 deletion src/MvxScaffolding.Vsix/MvxScaffolding.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@
"Type": "Blank",
"Platforms": [ "iOS", "Android", "UWP" ]
}

]
}
}
38 changes: 27 additions & 11 deletions src/MvxScaffolding.Vsix/Wizards/MvxScaffoldingBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows;
using EnvDTE;
using EnvDTE80;
using Microsoft.Internal.VisualStudio.PlatformUI;
Expand Down Expand Up @@ -54,17 +55,17 @@ public void RunStarted(object automationObject, Dictionary<string, string> repla
{
if (runKind == WizardRunKind.AsNewProject || runKind == WizardRunKind.AsMultiProject)
{
MvxScaffoldingContext.WizardVersion = new Version(ThisAssembly.Vsix.Version);
MvxScaffoldingContext.WizardName = ThisAssembly.Vsix.Name;
MvxScaffoldingContext.ProjectName = replacementsDictionary[VSTemplateKeys.ProjectName];
MvxScaffoldingContext.SafeProjectName = replacementsDictionary[VSTemplateKeys.SafeProjectName];
MvxScaffoldingContext.CanCreateSolutionDirectory = !string.IsNullOrWhiteSpace(replacementsDictionary[VSTemplateKeys.SpecifiedSolutionName]);
MvxScaffoldingContext.SolutionName = replacementsDictionary[VSTemplateKeys.SpecifiedSolutionName];

RemoveOldSolutionDirectory(automationObject, replacementsDictionary);

try
{
MvxScaffoldingContext.WizardVersion = new Version(ThisAssembly.Vsix.Version);
MvxScaffoldingContext.WizardName = ThisAssembly.Vsix.Name;
MvxScaffoldingContext.ProjectName = replacementsDictionary[VSTemplateKeys.ProjectName];
MvxScaffoldingContext.SafeProjectName = replacementsDictionary[VSTemplateKeys.SafeProjectName];
MvxScaffoldingContext.CanCreateSolutionDirectory = !string.IsNullOrWhiteSpace(replacementsDictionary[VSTemplateKeys.SpecifiedSolutionName]);
MvxScaffoldingContext.SolutionName = replacementsDictionary[VSTemplateKeys.SpecifiedSolutionName];

MvxScaffoldingContext.RemoveOldSolutionDirectoryStatus = RemoveOldSolutionDirectory(automationObject, replacementsDictionary);

ShowModal(Startup.FirstView());

MvxScaffoldingContext.RunningTimer.Stop();
Expand All @@ -87,6 +88,19 @@ public void RunStarted(object automationObject, Dictionary<string, string> repla
MvxScaffoldingContext.UserSelectedOptions = null;
}
}
catch (Exception ex) when (ex.GetType() != typeof(WizardBackoutException))
{
Logger.Current.Exception.TrackAsync(ex, "Error running wizard")
.FireAndForget();

var messageBoxText = $"There was an error trying to run MvxScaffolding. Please log an issue on GitHub if the error persists.\n\nError Message: {ex.Message}\nError StackTrace:{ex.StackTrace}";
var caption = "MvxScaffolding error";
MessageBoxButton button = MessageBoxButton.OK;
MessageBoxImage icon = MessageBoxImage.Error;
MessageBox.Show(messageBoxText, caption, button, icon);

throw new WizardBackoutException("Error running wizard, closing application", ex);
}
finally
{
Logger.Current.Telemetry.TrackEndSessionAsync()
Expand All @@ -95,7 +109,7 @@ public void RunStarted(object automationObject, Dictionary<string, string> repla
}
}

private static void RemoveOldSolutionDirectory(object automationObject, Dictionary<string, string> replacementsDictionary)
private static FileDeleteStatus RemoveOldSolutionDirectory(object automationObject, Dictionary<string, string> replacementsDictionary)
{
var dte = (DTE)automationObject;
var solution = (Solution2)dte.Solution;
Expand All @@ -107,11 +121,13 @@ private static void RemoveOldSolutionDirectory(object automationObject, Dictiona
? oldDestinationDirectory
: Path.GetFullPath(Path.Combine(oldDestinationDirectory, @"..\"));

FileSystemUtils.SafeDeleteDirectory(solutionRootDirectory);
FileDeleteStatus deleteStatus = FileSystemUtils.SafeDeleteDirectory(solutionRootDirectory);

var rootFolderDictionary = Path.GetFullPath(Path.Combine(solutionRootDirectory, @"..\"));
replacementsDictionary[VSTemplateKeys.DestinationDirectory] = rootFolderDictionary;
replacementsDictionary[VSTemplateKeys.SolutionDirectory] = rootFolderDictionary;

return deleteStatus;
}

public bool ShouldAddProjectItem(string filePath)
Expand Down

0 comments on commit fe62fef

Please sign in to comment.