From c286c385be51e55ab5f40720f23f1013059b5afd Mon Sep 17 00:00:00 2001 From: Andreas Weizel Date: Tue, 8 Jan 2013 22:19:28 +0100 Subject: [PATCH] Added AddInManager2 project, migrated to SharpDevelop 5. --- SharpDevelop.sln | 13 + .../AddInManager2/Project/AddInManager2.addin | 39 ++ .../Project/AddInManager2.csproj | 189 +++++ .../Project/Configuration/AssemblyInfo.cs | 15 + .../Project/Resources/StringResources.resx | 248 +++++++ .../Project/Resources/accept.png | Bin 0 -> 582 bytes .../Project/Resources/exclamation.png | Bin 0 -> 658 bytes .../Project/Resources/license.txt | 7 + .../Project/Resources/magnifier.png | Bin 0 -> 664 bytes .../Project/Resources/packageicon.png | Bin 0 -> 1865 bytes .../Project/Resources/packageicon_small.png | Bin 0 -> 938 bytes .../Project/Resources/resultset_next.png | Bin 0 -> 525 bytes .../Project/Resources/resultset_previous.png | Bin 0 -> 539 bytes .../Project/Resources/sd_packageicon.png | Bin 0 -> 2010 bytes .../AddInManager2/Project/Src/AddInManager.cs | 59 ++ .../Project/Src/AddInManagerTask.cs | 89 +++ .../Src/BooleanToFontWeightConverter.cs | 48 ++ .../AddInManager2/Project/Src/Commands.cs | 44 ++ .../Project/Src/DelegateCommand.cs | 51 ++ .../Project/Src/EnumerableExtensions.cs | 34 + .../Src/Model/AcceptLicensesEventArgs.cs | 29 + .../Project/Src/Model/AddInDependency.cs | 143 ++++ .../Src/Model/AddInExceptionEventArgs.cs | 24 + .../Src/Model/AddInInstallationEventArgs.cs | 32 + .../Project/Src/Model/AddInManagerEvents.cs | 104 +++ .../Project/Src/Model/AddInSetup.cs | 655 ++++++++++++++++++ .../Model/Interfaces/IAddInManagerEvents.cs | 41 ++ .../Src/Model/Interfaces/IAddInPackage.cs | 130 ++++ .../Src/Model/Interfaces/IAddInSetup.cs | 35 + .../Model/Interfaces/INuGetPackageManager.cs | 24 + .../Model/Interfaces/IPackageRepositories.cs | 33 + .../Project/Src/Model/ManagedAddIn.cs | 60 ++ .../AddInManager2/Project/Src/Model/Model.cs | 43 ++ .../Project/Src/Model/NuGetPackageManager.cs | 155 +++++ .../Model/NuGetPackageOperationEventArgs.cs | 25 + .../Model/PackageMessageLoggedEventArgs.cs | 45 ++ .../Project/Src/Model/PackageRepositories.cs | 136 ++++ .../Project/Src/Model/PackageRepository.cs | 61 ++ .../AddInManager2/Project/Src/Model/Page.cs | 18 + .../AddInManager2/Project/Src/Model/Pages.cs | 155 +++++ .../Project/Src/Model/ReadPackagesResult.cs | 25 + .../Project/Src/View/AddInManagerView.xaml | 103 +++ .../Project/Src/View/AddInManagerView.xaml.cs | 28 + .../Project/Src/View/AddInsView.xaml | 554 +++++++++++++++ .../Project/Src/View/AddInsView.xaml.cs | 16 + .../Src/View/LicenseAcceptanceView.xaml | 124 ++++ .../Src/View/LicenseAcceptanceView.xaml.cs | 21 + .../Src/View/PackageRepositoriesView.xaml | 145 ++++ .../Src/View/PackageRepositoriesView.xaml.cs | 53 ++ .../Project/Src/View/PagedResultsView.xaml | 70 ++ .../Project/Src/View/PagedResultsView.xaml.cs | 16 + .../Src/ViewModel/AddInManagerViewModel.cs | 144 ++++ .../ViewModel/AddInPackageViewModelBase.cs | 415 +++++++++++ .../Src/ViewModel/AddInsViewModelBase.cs | 430 ++++++++++++ .../Src/ViewModel/AvailableAddInsViewModel.cs | 66 ++ .../Src/ViewModel/InstalledAddInsViewModel.cs | 165 +++++ .../ViewModel/LicenseAcceptanceViewModel.cs | 45 ++ .../Src/ViewModel/NuGetAddInsViewModelBase.cs | 213 ++++++ .../Src/ViewModel/NuGetPackageViewModel.cs | 464 +++++++++++++ .../Src/ViewModel/OfflineAddInViewModel.cs | 527 ++++++++++++++ .../Src/ViewModel/OpenHyperlinkCommand.cs | 52 ++ .../ViewModel/PackageRepositoriesViewModel.cs | 315 +++++++++ .../Src/ViewModel/UpdatedAddInsViewModel.cs | 108 +++ .../ICSharpCode.SharpZipLib.dll | Bin 0 -> 143360 bytes 64 files changed, 6883 insertions(+) create mode 100644 src/AddIns/Misc/AddInManager2/Project/AddInManager2.addin create mode 100644 src/AddIns/Misc/AddInManager2/Project/AddInManager2.csproj create mode 100644 src/AddIns/Misc/AddInManager2/Project/Configuration/AssemblyInfo.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Resources/StringResources.resx create mode 100644 src/AddIns/Misc/AddInManager2/Project/Resources/accept.png create mode 100644 src/AddIns/Misc/AddInManager2/Project/Resources/exclamation.png create mode 100644 src/AddIns/Misc/AddInManager2/Project/Resources/license.txt create mode 100644 src/AddIns/Misc/AddInManager2/Project/Resources/magnifier.png create mode 100644 src/AddIns/Misc/AddInManager2/Project/Resources/packageicon.png create mode 100644 src/AddIns/Misc/AddInManager2/Project/Resources/packageicon_small.png create mode 100644 src/AddIns/Misc/AddInManager2/Project/Resources/resultset_next.png create mode 100644 src/AddIns/Misc/AddInManager2/Project/Resources/resultset_previous.png create mode 100644 src/AddIns/Misc/AddInManager2/Project/Resources/sd_packageicon.png create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/AddInManager.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/AddInManagerTask.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/BooleanToFontWeightConverter.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/Commands.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/DelegateCommand.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/EnumerableExtensions.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/Model/AcceptLicensesEventArgs.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/Model/AddInDependency.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/Model/AddInExceptionEventArgs.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/Model/AddInInstallationEventArgs.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/Model/AddInManagerEvents.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/Model/AddInSetup.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/Model/Interfaces/IAddInManagerEvents.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/Model/Interfaces/IAddInPackage.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/Model/Interfaces/IAddInSetup.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/Model/Interfaces/INuGetPackageManager.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/Model/Interfaces/IPackageRepositories.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/Model/ManagedAddIn.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/Model/Model.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/Model/NuGetPackageManager.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/Model/NuGetPackageOperationEventArgs.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/Model/PackageMessageLoggedEventArgs.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/Model/PackageRepositories.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/Model/PackageRepository.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/Model/Page.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/Model/Pages.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/Model/ReadPackagesResult.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/View/AddInManagerView.xaml create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/View/AddInManagerView.xaml.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/View/AddInsView.xaml create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/View/AddInsView.xaml.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/View/LicenseAcceptanceView.xaml create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/View/LicenseAcceptanceView.xaml.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/View/PackageRepositoriesView.xaml create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/View/PackageRepositoriesView.xaml.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/View/PagedResultsView.xaml create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/View/PagedResultsView.xaml.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/AddInManagerViewModel.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/AddInPackageViewModelBase.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/AddInsViewModelBase.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/AvailableAddInsViewModel.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/InstalledAddInsViewModel.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/LicenseAcceptanceViewModel.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/NuGetAddInsViewModelBase.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/NuGetPackageViewModel.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/OfflineAddInViewModel.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/OpenHyperlinkCommand.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/PackageRepositoriesViewModel.cs create mode 100644 src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/UpdatedAddInsViewModel.cs create mode 100644 src/AddIns/Misc/AddInManager2/RequiredLibraries/ICSharpCode.SharpZipLib.dll diff --git a/SharpDevelop.sln b/SharpDevelop.sln index 71430b65a8..976826c80b 100644 --- a/SharpDevelop.sln +++ b/SharpDevelop.sln @@ -105,6 +105,8 @@ Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "SharpDevelop.EnvDTE", "src\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PackageManagement.Cmdlets", "src\AddIns\Misc\PackageManagement\Cmdlets\Project\PackageManagement.Cmdlets.csproj", "{E0A5E80A-003B-4335-A9DC-A76E2E46D38D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AddInManager2", "src\AddIns\Misc\AddInManager2\Project\AddInManager2.csproj", "{60480C2F-F228-4D86-B98F-AF75A7DCEC34}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Display Bindings", "Display Bindings", "{11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AvalonEdit.AddIn", "src\AddIns\DisplayBindings\AvalonEdit.AddIn\AvalonEdit.AddIn.csproj", "{0162E499-42D0-409B-AA25-EED21F75336B}" @@ -701,6 +703,14 @@ Global {0CD86A0B-73BB-4E7E-B476-AB84389164B1}.Release|Any CPU.Build.0 = Release|Any CPU {0CD86A0B-73BB-4E7E-B476-AB84389164B1}.Release|x86.ActiveCfg = Release|x86 {0CD86A0B-73BB-4E7E-B476-AB84389164B1}.Release|x86.Build.0 = Release|x86 + {60480C2F-F228-4D86-B98F-AF75A7DCEC34}.Debug|Any CPU.Build.0 = Debug|Any CPU + {60480C2F-F228-4D86-B98F-AF75A7DCEC34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {60480C2F-F228-4D86-B98F-AF75A7DCEC34}.Debug|x86.Build.0 = Debug|Any CPU + {60480C2F-F228-4D86-B98F-AF75A7DCEC34}.Debug|x86.ActiveCfg = Debug|Any CPU + {60480C2F-F228-4D86-B98F-AF75A7DCEC34}.Release|Any CPU.Build.0 = Release|Any CPU + {60480C2F-F228-4D86-B98F-AF75A7DCEC34}.Release|Any CPU.ActiveCfg = Release|Any CPU + {60480C2F-F228-4D86-B98F-AF75A7DCEC34}.Release|x86.Build.0 = Release|Any CPU + {60480C2F-F228-4D86-B98F-AF75A7DCEC34}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -754,6 +764,9 @@ Global {752BD058-5517-48A1-BB27-5ED186FA052B} = {87D549AF-8FCD-4E84-9C33-3DB6E42FEF6D} {E0A5E80A-003B-4335-A9DC-A76E2E46D38D} = {87D549AF-8FCD-4E84-9C33-3DB6E42FEF6D} {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F} = {39327899-ED91-4F7F-988C-4FE4E17C014D} + {60480C2F-F228-4D86-B98F-AF75A7DCEC34} = {F3662720-9EA2-4591-BBC6-97361DCE50A9} + {6B1CFE35-DA17-4DEB-9C6E-227E5E251DA0} = {59A30AA6-D600-41AB-B7A1-9543469DBE36} + {0008FCE9-9EB4-4E2E-979B-553278E5BBA6} = {59A30AA6-D600-41AB-B7A1-9543469DBE36} {0162E499-42D0-409B-AA25-EED21F75336B} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F} {E618A9CD-A39F-4925-A538-E8A3FEF24E54} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F} {DCA2703D-250A-463E-A68A-07ED105AE6BD} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F} diff --git a/src/AddIns/Misc/AddInManager2/Project/AddInManager2.addin b/src/AddIns/Misc/AddInManager2/Project/AddInManager2.addin new file mode 100644 index 0000000000..8182d49efa --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/AddInManager2.addin @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/AddIns/Misc/AddInManager2/Project/AddInManager2.csproj b/src/AddIns/Misc/AddInManager2/Project/AddInManager2.csproj new file mode 100644 index 0000000000..a4153aa8e5 --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/AddInManager2.csproj @@ -0,0 +1,189 @@ + + + + {60480C2F-F228-4D86-B98F-AF75A7DCEC34} + Debug + AnyCPU + Library + ICSharpCode.AddInManager2 + ICSharpCode.AddInManager2 + v4.5 + False + False + OnBuildSuccess + False + False + 4 + false + False + Auto + 4194304 + AnyCPU + 4096 + False + C:\Users\WheizWork\AppData\Roaming\ICSharpCode/SharpDevelop4\Settings.SourceAnalysis + + + + ..\..\..\..\..\AddIns\Misc\AddInManager2\ + true + Full + False + True + DEBUG;TRACE + Project + + + ..\..\..\..\..\AddIns\Misc\OnlineUpdate\ + false + None + True + False + TRACE + Project + + + + ..\RequiredLibraries\ICSharpCode.SharpZipLib.dll + + + ..\..\PackageManagement\RequiredLibraries\NuGet.Core.dll + True + + + + + + 3.5 + + + + 3.5 + + + + + + + 3.5 + + + + + + Always + + + + + + + + + + + + + + Configuration\GlobalAssemblyInfo.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AddInManagerView.xaml + + + AddInsView.xaml + + + Code + LicenseAcceptanceView.xaml + + + PagedResultsView.xaml + + + PackageRepositoriesView.xaml + Code + + + + + {2748AD25-9C63-4E12-877B-4DCE96FBED54} + ICSharpCode.SharpDevelop + False + + + {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} + ICSharpCode.Core + False + + + {7E4A7172-7FF5-48D0-B719-7CD959DD1AC9} + ICSharpCode.Core.Presentation + False + + + {857CA1A3-FC88-4BE0-AB6A-D1EE772AB288} + ICSharpCode.Core.WinForms + False + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/AddIns/Misc/AddInManager2/Project/Configuration/AssemblyInfo.cs b/src/AddIns/Misc/AddInManager2/Project/Configuration/AssemblyInfo.cs new file mode 100644 index 0000000000..0af1cd427d --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Configuration/AssemblyInfo.cs @@ -0,0 +1,15 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("AddInManager2")] +[assembly: AssemblyDescription("AddInManager 2nd Generation for ICSharpCode.Core")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + diff --git a/src/AddIns/Misc/AddInManager2/Project/Resources/StringResources.resx b/src/AddIns/Misc/AddInManager2/Project/Resources/StringResources.resx new file mode 100644 index 0000000000..cb0baca542 --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Resources/StringResources.resx @@ -0,0 +1,248 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Following installed AddIns are dependent from "{0}": + +{1} + +They will stop working after disabling this AddIn! Are you sure you want to continue? + + + You cannot uninstall the AddInManager because you need it to re-install AddIns! + + + The following packages require a click-to-accept license: + + + Following installed AddIns are dependent from "{0}": + +{1} + +They will stop working after removing this AddIn! Are you sure you want to continue? + + + The following package requires a click-to-accept license: + + + By clicking "I Accept" you agree to the license terms for the packages listed above. +If you do not agree to the license terms click "I Decline". + + + By clicking "I Accept" you agree to the license terms for the package listed above. +If you do not agree to the license terms click "I Decline". + + + I &Accept + + + I &Decline + + + View License Terms + + + Created by: + + + Version: + + + Installed version: + + + New version: + + + Last updated: + + + Downloads: + + + Rating: + + + Report abuse + + + Dependencies: + + + Show preinstalled AddIns + + + Show prereleases + + + Install from archive... + + + Installed + + + Updates + + + Available + + + More information + + + AddIns + + + Repositories + + + Move Up + + + Move Down + + + Remove + + + Add + + + Name: + + + Source: + + + Default Repository + + + License Agreements + + + SharpDevelop AddIns|*.sdaddin|All files|*.* + + + Package "{0}" needs at least one additional package: + +{1} + +The application will try to download and install them, as well. Do you want to continue? + + \ No newline at end of file diff --git a/src/AddIns/Misc/AddInManager2/Project/Resources/accept.png b/src/AddIns/Misc/AddInManager2/Project/Resources/accept.png new file mode 100644 index 0000000000000000000000000000000000000000..a7d7a96be3f2282a62e3c0733bac89c7f6de7b4a GIT binary patch literal 582 zcmV-M0=fN(P)tYd4K$mX5uyr2F@fdffp{&DSHtl4|Bn9=ukpCx`#%PTUr-D(@b7;C zhJXJjrnw{;1KBM=6&|E`fsNrGL!Y6%zUh}QUl`(@V)PmQFtotEKmafTHP0uP}7&%m4pcKQ#X)BiAJ2y+PrtBI>9eEIt2-_c7)?*Lsf z5vX5*Af@m-wBJRV%#Fiz=BdPr0!2^az8K(fJ0K@xl?-_o)`1bna-SH57SeZUButL)d$cI_) zkpl5Q!w#U+YtHUCagF&eebP3jhEB literal 0 HcmV?d00001 diff --git a/src/AddIns/Misc/AddInManager2/Project/Resources/exclamation.png b/src/AddIns/Misc/AddInManager2/Project/Resources/exclamation.png new file mode 100644 index 0000000000000000000000000000000000000000..91f855308f2c9f15969cddb3ae4e319196852ce0 GIT binary patch literal 658 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 D+?H?$ literal 0 HcmV?d00001 diff --git a/src/AddIns/Misc/AddInManager2/Project/Resources/packageicon_small.png b/src/AddIns/Misc/AddInManager2/Project/Resources/packageicon_small.png new file mode 100644 index 0000000000000000000000000000000000000000..517705401d12978e5f0d71be8ed6fc916683b4f3 GIT binary patch literal 938 zcmV;b16BNqP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D02*{fSaefwW^{L9 za%BKeVQFr3E>1;MAa*k@H7+qQF!XYv0009SNklsw2LXsOLN|JigVL#rYqOH zMiJ30b^7nTPP01j<9*?I{=ah`p7*6&ZG+DODr^pU=f?z@Oy>3%%NY@~j9Ib({a^lh zX3$+D4wP7&yuo9E+;;u;ybi;*g09}JN~2{h94d)j1+kCkZkY{KAq#S2H!%OuUF`XlQMW%suB$Y~6GHbGQh8vrdgYaWPfz^SO zp6~F?`2>1m17+J8mgcZP+|{A z&Nmm|i{N5@E=&;i#_S*oZ;tB(-PD0%+d$V_=h&0k3#H*JD*L}9vSJ9L*mlt6Rs`p> zjKkg%iDZ4)E0cq+arlaBk)oTJM))V|F+DSjzbo|}A1zXg;O9IUU!xZb)`pd`AezHb zExgzWil_%&)Ig!J&&L*PVrmAW3pEJJ=7z#|zyj~cQo}borvZi6fKFF}LSKVIRCru# zosa!n?%`^LJkiYx0yjQ}XOh;aS{kb6&|A=nm!O~*pui#o%IoJNi&fsiAJ)lo#FXlc zfn(OEhSeVM?-q!UW54Zdf}vHNNjvg%tS z1i?~fIZHfpI=Z8er8GF*16gpW7<7;Y0r{WNyGX3WKB*e_s#_j$##ayszo{S* zCUMJd*6mDh)W;>&8fAAX^|JViu0JHI^)h)?n=Dh)w4e8SOQllJ-$uG@|5lOY3IG5A M07*qoM6N<$f?}_&%K!iX literal 0 HcmV?d00001 diff --git a/src/AddIns/Misc/AddInManager2/Project/Resources/resultset_next.png b/src/AddIns/Misc/AddInManager2/Project/Resources/resultset_next.png new file mode 100644 index 0000000000000000000000000000000000000000..1799620988456b4fd515e7550988bd337404d5fc GIT binary patch literal 525 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 + /// Container for public services of AddInManager AddIn. + /// + public class AddInManager + { + private static readonly AddInManagerEvents _events; + private static readonly PackageRepositories _repositories; + private static readonly AddInSetup _setup; + private static readonly NuGetPackageManager _nuGet; + + static AddInManager() + { + _events = new AddInManagerEvents(); + _repositories = new PackageRepositories(); + _nuGet = new NuGetPackageManager(_repositories, _events); + _setup = new AddInSetup(_events, _nuGet); + } + + public static IAddInManagerEvents Events + { + get + { + return _events; + } + } + + public static IPackageRepositories Repositories + { + get + { + return _repositories; + } + } + + public static IAddInSetup Setup + { + get + { + return _setup; + } + } + + public static INuGetPackageManager NuGet + { + get + { + return _nuGet; + } + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/AddInManagerTask.cs b/src/AddIns/Misc/AddInManager2/Project/Src/AddInManagerTask.cs new file mode 100644 index 0000000000..2f692e7775 --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/AddInManagerTask.cs @@ -0,0 +1,89 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ICSharpCode.AddInManager2 +{ + public class AddInManagerTask + { + public static AddInManagerTask Create( + Func function, + Action> continueWith) + { + return new AddInManagerTask(function, continueWith); + } + } + + public class AddInManagerTask + { + Task task; + Action> continueWith; + CancellationTokenSource cancellationTokenSource; + + public AddInManagerTask( + Func function, + Action> continueWith) + { + this.continueWith = continueWith; + CreateTask(function); + } + + private void CreateTask(Func function) + { + TaskScheduler scheduler = TaskScheduler.FromCurrentSynchronizationContext(); + cancellationTokenSource = new CancellationTokenSource(); + task = new Task(function, cancellationTokenSource.Token); + task.ContinueWith(result => OnContinueWith(result), scheduler); + } + + private void OnContinueWith(Task task) + { + continueWith(this); + } + + public void Start() + { + task.Start(); + } + + public TResult Result + { + get + { + return task.Result; + } + } + + public void Cancel() + { + cancellationTokenSource.Cancel(); + } + + public bool IsCancelled + { + get + { + return task.IsCanceled; + } + } + + public bool IsFaulted + { + get + { + return task.IsFaulted; + } + } + + public AggregateException Exception + { + get + { + return task.Exception; + } + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/BooleanToFontWeightConverter.cs b/src/AddIns/Misc/AddInManager2/Project/Src/BooleanToFontWeightConverter.cs new file mode 100644 index 0000000000..ca3d89a06e --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/BooleanToFontWeightConverter.cs @@ -0,0 +1,48 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; + +namespace ICSharpCode.AddInManager2 +{ + [ValueConversion(typeof(Boolean), typeof(FontWeight))] + public class BooleanToFontWeightConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is Boolean) + { + Boolean fontIsBold = (Boolean)value; + return ConvertToFontWeight(fontIsBold); + } + return DependencyProperty.UnsetValue; + } + + FontWeight ConvertToFontWeight(Boolean bold) + { + if (bold) + { + return FontWeights.Bold; + } + return FontWeights.Normal; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is FontWeight) + { + FontWeight fontWeight = (FontWeight)value; + return ConvertToBoolean(fontWeight); + } + return DependencyProperty.UnsetValue; + } + + bool ConvertToBoolean(FontWeight fontWeight) + { + return fontWeight == FontWeights.Bold; + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/Commands.cs b/src/AddIns/Misc/AddInManager2/Project/Src/Commands.cs new file mode 100644 index 0000000000..7bf44b4967 --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/Commands.cs @@ -0,0 +1,44 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.Gui; +using ICSharpCode.AddInManager2.View; + +namespace ICSharpCode.AddInManager2 +{ + public class ShowCommand : SimpleCommand + { + public override void Execute(object parameter) + { + // Open AddInManager2 main dialog + using (AddInManagerView view = CreateManagerView()) + { + view.ShowDialog(); + } + } + + private AddInManagerView CreateManagerView() + { + return new AddInManagerView() + { + Owner = SD.Workbench.MainWindow + }; + } + } + + public class AddInManagerInitializationCommand : SimpleCommand + { + public override void Execute(object parameter) + { + // Load string resources needed by AddInManager2 + SD.ResourceService.RegisterStrings("ICSharpCode.AddInManager2.Resources.StringResources", GetType().Assembly); + + // Remove all unreferenced NuGet packages + AddInManager.Setup.RemoveUnreferencedNuGetPackages(); + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/DelegateCommand.cs b/src/AddIns/Misc/AddInManager2/Project/Src/DelegateCommand.cs new file mode 100644 index 0000000000..4aac34ed8e --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/DelegateCommand.cs @@ -0,0 +1,51 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Windows.Input; + +namespace ICSharpCode.AddInManager2 +{ + public class DelegateCommand : ICommand + { + Action execute; + Predicate canExecute; + + public DelegateCommand(Action execute, Predicate canExecute) + { + this.execute = execute; + this.canExecute = canExecute; + } + + public DelegateCommand(Action execute) + : this(execute, null) + { + } + + public event EventHandler CanExecuteChanged + { + add + { + CommandManager.RequerySuggested += value; + } + remove + { + CommandManager.RequerySuggested -= value; + } + } + + public void Execute(object parameter) + { + execute(parameter); + } + + public bool CanExecute(object parameter) + { + if (canExecute != null) + { + return canExecute(parameter); + } + return true; + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/EnumerableExtensions.cs b/src/AddIns/Misc/AddInManager2/Project/Src/EnumerableExtensions.cs new file mode 100644 index 0000000000..ad8188a621 --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/EnumerableExtensions.cs @@ -0,0 +1,34 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using NuGet; + +namespace ICSharpCode.AddInManager2 +{ + public static class EnumerableExtensions + { + public static IEnumerable DistinctLast(this IEnumerable source, IEqualityComparer comparer) + { + T previousItem = default(T); + + foreach (T currentItem in source) + { + if (previousItem != null) + { + if (!comparer.Equals(previousItem, currentItem)) + { + yield return previousItem; + } + } + previousItem = currentItem; + } + + if (previousItem != null) + { + yield return previousItem; + } + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/Model/AcceptLicensesEventArgs.cs b/src/AddIns/Misc/AddInManager2/Project/Src/Model/AcceptLicensesEventArgs.cs new file mode 100644 index 0000000000..8eed36b553 --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/Model/AcceptLicensesEventArgs.cs @@ -0,0 +1,29 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using NuGet; + +namespace ICSharpCode.AddInManager2.Model +{ + public class AcceptLicensesEventArgs : EventArgs + { + public AcceptLicensesEventArgs(IEnumerable packages) + { + this.Packages = packages; + } + + public IEnumerable Packages + { + get; + private set; + } + + public bool IsAccepted + { + get; + set; + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/Model/AddInDependency.cs b/src/AddIns/Misc/AddInManager2/Project/Src/Model/AddInDependency.cs new file mode 100644 index 0000000000..fcc6ed124a --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/Model/AddInDependency.cs @@ -0,0 +1,143 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Text; +using ICSharpCode.Core; +using NuGet; + +namespace ICSharpCode.AddInManager2.Model +{ + /// + /// Object describing a dependency constraint of an AddIn. + /// + public class AddInDependency + { + public AddInDependency() + { + } + + public AddInDependency(PackageDependency packageDependency) + { + if (packageDependency != null) + { + IVersionSpec versionSpec = packageDependency.VersionSpec; + + Id = packageDependency.Id; + if (versionSpec.MinVersion != null) + { + MinimumVersion = versionSpec.MinVersion.Version; + } + if (versionSpec.MaxVersion != null) + { + MaximumVersion = versionSpec.MaxVersion.Version; + } + IncludeMinimumVersion = packageDependency.VersionSpec.IsMinInclusive; + IncludeMaximumVersion = packageDependency.VersionSpec.IsMaxInclusive; + } + } + + public AddInDependency(AddInReference reference) + { + if (reference != null) + { + Id = reference.Name; + + // Hint: An absolutely minimal or maximal version means no version constraint at all! + Version absMinimumVersion = new Version(0, 0, 0, 0); + Version absMaximumVersion = new Version(Int32.MaxValue, Int32.MaxValue); + + if ((reference.MinimumVersion != null) && (reference.MinimumVersion != absMinimumVersion)) + { + MinimumVersion = reference.MinimumVersion; + } + if ((reference.MaximumVersion != null) && (reference.MaximumVersion != absMaximumVersion)) + { + MaximumVersion = reference.MaximumVersion; + } + + IncludeMinimumVersion = true; + IncludeMaximumVersion = true; + } + } + + public string Id + { + get; + set; + } + + public Version MinimumVersion + { + get; + set; + } + + public bool IncludeMinimumVersion + { + get; + set; + } + + public Version MaximumVersion + { + get; + set; + } + + public bool IncludeMaximumVersion + { + get; + set; + } + + public override string ToString() + { + string printedMinimalDependency = FormatVersionConstraint(MinimumVersion, IncludeMinimumVersion, true); + string printedMaximalDependency = FormatVersionConstraint(MaximumVersion, IncludeMaximumVersion, false); + + StringBuilder formattedDependency = new StringBuilder(); + if (Id != null) + { + formattedDependency.Append(Id); + } + if ((printedMinimalDependency != null) || (printedMaximalDependency != null)) + { + formattedDependency.Append(" ("); + } + if (printedMinimalDependency != null) + { + formattedDependency.Append(printedMinimalDependency); + } + if ((printedMinimalDependency != null) && (printedMaximalDependency != null)) + { + formattedDependency.Append(" && "); + } + if (printedMaximalDependency != null) + { + formattedDependency.Append(printedMaximalDependency); + } + if ((printedMinimalDependency != null) || (printedMaximalDependency != null)) + { + formattedDependency.Append(")"); + } + + return formattedDependency.ToString(); + } + + + private string FormatVersionConstraint(Version version, bool includeVersion, bool isMinimum) + { + if (version == null) + { + return null; + } + else + { + string constraintOperator = isMinimum ? ">" : "<"; + string equalityOperator = includeVersion ? "=" : ""; + return String.Format("{0}{1} {2}", constraintOperator, equalityOperator, version.ToString()); + } + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/Model/AddInExceptionEventArgs.cs b/src/AddIns/Misc/AddInManager2/Project/Src/Model/AddInExceptionEventArgs.cs new file mode 100644 index 0000000000..9cc0a54d2b --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/Model/AddInExceptionEventArgs.cs @@ -0,0 +1,24 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.AddInManager2.Model +{ + /// + /// Data for events indicating an exception during package-related operations. + /// + public class AddInExceptionEventArgs : EventArgs + { + public AddInExceptionEventArgs(Exception exception) + { + Exception = exception; + } + + public Exception Exception + { + get; + private set; + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/Model/AddInInstallationEventArgs.cs b/src/AddIns/Misc/AddInManager2/Project/Src/Model/AddInInstallationEventArgs.cs new file mode 100644 index 0000000000..06f61f679f --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/Model/AddInInstallationEventArgs.cs @@ -0,0 +1,32 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using ICSharpCode.Core; + +namespace ICSharpCode.AddInManager2.Model +{ + /// + /// EventArgs for local AddIn installation events. + /// + public class AddInInstallationEventArgs : EventArgs + { + public AddInInstallationEventArgs(AddIn addIn) + { + AddIn = addIn; + PreviousVersionRemains = false; + } + + public AddIn AddIn + { + get; + private set; + } + + public bool PreviousVersionRemains + { + get; + set; + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/Model/AddInManagerEvents.cs b/src/AddIns/Misc/AddInManager2/Project/Src/Model/AddInManagerEvents.cs new file mode 100644 index 0000000000..5500a1cd91 --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/Model/AddInManagerEvents.cs @@ -0,0 +1,104 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using NuGet; + +namespace ICSharpCode.AddInManager2.Model +{ + /// + /// Catches and broadcasts package-related events. + /// + public class AddInManagerEvents : IAddInManagerEvents + { + public event EventHandler OperationStarted; + + public void OnOperationStarted(EventArgs e) + { + if (OperationStarted != null) + { + OperationStarted(this, e); + } + } + + public event EventHandler AddInInstalled; + + public void OnAddInInstalled(AddInInstallationEventArgs e) + { + if (AddInInstalled != null) + { + AddInInstalled(this, e); + } + } + + public event EventHandler AddInUninstalled; + + public void OnAddInUninstalled(AddInInstallationEventArgs e) + { + if (AddInUninstalled != null) + { + AddInUninstalled(this, e); + } + } + + public event EventHandler AddInOperationError; + + public void OnAddInOperationError(AddInExceptionEventArgs e) + { + if (AddInOperationError != null) + { + AddInOperationError(this, e); + } + } + + public event EventHandler AddInPackageDownloaded; + + public void OnAddInPackageDownloaded(PackageOperationEventArgs e) + { + if (AddInPackageDownloaded != null) + { + AddInPackageDownloaded(this, e); + } + } + + public event EventHandler AddInPackageRemoved; + + public void OnAddInPackageRemoved(PackageOperationEventArgs e) + { + if (AddInPackageRemoved != null) + { + AddInPackageRemoved(this, e); + } + } + + public event EventHandler AddInStateChanged; + + public void OnAddInStateChanged(AddInInstallationEventArgs e) + { + if (AddInStateChanged != null) + { + AddInStateChanged(this, e); + } + } + + public event EventHandler PackageMessageLogged; + + public void OnPackageMessageLogged(PackageMessageLoggedEventArgs e) + { + if (PackageMessageLogged != null) + { + PackageMessageLogged(this, e); + } + } + + public event EventHandler AcceptLicenses; + + public void OnAcceptLicenses(AcceptLicensesEventArgs e) + { + if (AcceptLicenses != null) + { + AcceptLicenses(this, e); + } + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/Model/AddInSetup.cs b/src/AddIns/Misc/AddInManager2/Project/Src/Model/AddInSetup.cs new file mode 100644 index 0000000000..be4168d45f --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/Model/AddInSetup.cs @@ -0,0 +1,655 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpZipLib.Zip; +using NuGet; + +namespace ICSharpCode.AddInManager2.Model +{ + /// + /// Helper class for AddIn setup operations. + /// + public class AddInSetup : IAddInSetup + { + private IAddInManagerEvents _events = null; + private INuGetPackageManager _nuGet = null; + + private List _addInsMarkedForInstall; + + public AddInSetup(IAddInManagerEvents events, INuGetPackageManager nuGet) + { + _events = events; + _nuGet = nuGet; + + _addInsMarkedForInstall = new List(); + + // Register event handlers + _events.AddInPackageDownloaded += events_AddInPackageDownloaded; + } + + public IEnumerable AddInsWithMarkedForInstallation + { + get + { + return SD.AddInTree.AddIns.Select(a => new ManagedAddIn(a) { IsTemporary = false, IsUpdate = false }) + .GroupJoin( + _addInsMarkedForInstall, + installedAddIn => installedAddIn.AddIn.Manifest.PrimaryIdentity, + markedAddIn => markedAddIn.AddIn.Manifest.PrimaryIdentity, + (installedAddIn, e) => e.ElementAtOrDefault(0) ?? installedAddIn); + } + } + + private void events_AddInPackageDownloaded(object sender, PackageOperationEventArgs e) + { + try + { + InstallAddIn(e.Package, e.InstallPath); + } + catch (Exception ex) + { + _events.OnAddInOperationError(new AddInExceptionEventArgs(ex)); + } + } + + private AddIn LoadAddInFromZip(ZipFile file) + { + AddIn resultAddIn = null; + ZipEntry addInEntry = null; + foreach (ZipEntry entry in file) + { + if (entry.Name.EndsWith(".addin")) + { + if (addInEntry != null) + { + throw new AddInLoadException("The package may only contain one .addin file."); + } + addInEntry = entry; + } + } + if (addInEntry == null) + { + throw new AddInLoadException("The package must contain one .addin file."); + } + using (Stream s = file.GetInputStream(addInEntry)) + { + using (StreamReader r = new StreamReader(s)) + { + resultAddIn = AddIn.Load(SD.AddInTree, r); + } + } + + return resultAddIn; + } + + public AddIn InstallAddIn(string archiveFileName) + { + if (archiveFileName != null) + { + // Try to load the *.sdaddin file as ZIP archive + AddIn addIn = null; + ZipFile zipFile = new ZipFile(archiveFileName); + try + { + addIn = LoadAddInFromZip(zipFile); + } + finally + { + zipFile.Close(); + } + + if (addIn != null) + { + if (addIn.Manifest.PrimaryIdentity == null) + { + throw new AddInLoadException(ResourceService.GetString("AddInManager.AddInMustHaveIdentity")); + } + + // Try to find this AddIn in current registry + string identity = addIn.Manifest.PrimaryIdentity; + AddIn foundAddIn = null; + foreach (AddIn treeAddIn in SD.AddInTree.AddIns) + { + if (treeAddIn.Manifest.Identities.ContainsKey(identity)) + { + foundAddIn = treeAddIn; + break; + } + } + + // Prevent this AddIn from being uninstalled, if marked for deinstallation + foreach (string installedIdentity in addIn.Manifest.Identities.Keys) + { + ICSharpCode.Core.AddInManager.AbortRemoveUserAddInOnNextStart(installedIdentity); + } + + // Create target directory for AddIn in user profile & copy package contents there + CopyAddInFromZip(addIn, archiveFileName); + + // Install the AddIn using manifest + if (foundAddIn != null) + { + addIn.Action = AddInAction.Update; + } + else + { + addIn.Action = AddInAction.Install; + ((AddInTreeImpl)SD.AddInTree).InsertAddIn(addIn); + } + + // Mark this AddIn + ManagedAddIn markedAddIn = new ManagedAddIn(addIn) + { + IsTemporary = true, + IsUpdate = (foundAddIn != null), + OldVersion = (foundAddIn != null) ? foundAddIn.Version : null + }; + _addInsMarkedForInstall.Add(markedAddIn); + + // Successful installation + AddInInstallationEventArgs eventArgs = new AddInInstallationEventArgs(addIn); + _events.OnAddInInstalled(eventArgs); + _events.OnAddInStateChanged(eventArgs); + + return addIn; + } + else + { + // This is not a valid SharpDevelop AddIn package! + // TODO Throw something. + } + } + + return null; + } + + public AddIn InstallAddIn(IPackage package, string packageDirectory) + { + // Lookup for .addin file in package output + var addInManifestFile = Directory.EnumerateFiles(packageDirectory, "*.addin", SearchOption.TopDirectoryOnly).FirstOrDefault(); + if (addInManifestFile != null) + { + AddIn addIn = AddIn.Load(SD.AddInTree, addInManifestFile); + if (addIn.Manifest.PrimaryIdentity == null) + { + throw new AddInLoadException(ResourceService.GetString("AddInManager.AddInMustHaveIdentity")); + } + + // Try to find this AddIn in current registry + string identity = addIn.Manifest.PrimaryIdentity; + AddIn foundAddIn = null; + foreach (AddIn treeAddIn in SD.AddInTree.AddIns) + { + if (treeAddIn.Manifest.Identities.ContainsKey(identity)) + { + foundAddIn = treeAddIn; + break; + } + } + + // Prevent this AddIn from being uninstalled, if marked for deinstallation + foreach (string installedIdentity in addIn.Manifest.Identities.Keys) + { + ICSharpCode.Core.AddInManager.AbortRemoveUserAddInOnNextStart(installedIdentity); + } + + // Create target directory for AddIn in user profile & copy package contents there + CopyAddInFromPackage(addIn, packageDirectory); + + // Install the AddIn using manifest + if (foundAddIn != null) + { + addIn.Action = AddInAction.Update; + } + else + { + addIn.Action = AddInAction.Install; + ((AddInTreeImpl)SD.AddInTree).InsertAddIn(addIn); + } + + // Mark this AddIn + ManagedAddIn markedAddIn = new ManagedAddIn(addIn) + { + IsTemporary = true, + IsUpdate = (foundAddIn != null), + OldVersion = (foundAddIn != null) ? foundAddIn.Version : null + }; + _addInsMarkedForInstall.Add(markedAddIn); + + // Successful installation + AddInInstallationEventArgs eventArgs = new AddInInstallationEventArgs(addIn); + _events.OnAddInInstalled(eventArgs); + _events.OnAddInStateChanged(eventArgs); + + return addIn; + } + else + { + // This is not a valid SharpDevelop AddIn package! + // TODO Throw something. + } + + return null; + } + + private bool CopyAddInFromZip(AddIn addIn, string zipFile) + { + try + { + string targetDir = Path.Combine(ICSharpCode.Core.AddInManager.AddInInstallTemp, + addIn.Manifest.PrimaryIdentity); + if (Directory.Exists(targetDir)) + { + Directory.Delete(targetDir, true); + } + Directory.CreateDirectory(targetDir); + FastZip fastZip = new FastZip(); + fastZip.CreateEmptyDirectories = true; + fastZip.ExtractZip(zipFile, targetDir, null); + + return true; + } + catch (Exception) + { + return false; + } + } + + private bool CopyAddInFromPackage(AddIn addIn, string packageDirectory) + { + try + { + string targetDir = Path.Combine(ICSharpCode.Core.AddInManager.AddInInstallTemp, + addIn.Manifest.PrimaryIdentity); + if (Directory.Exists(targetDir)) + { + Directory.Delete(targetDir, true); + } + Directory.CreateDirectory(targetDir); + var packageContentsFiles = Directory.EnumerateFiles(packageDirectory, "*.*", SearchOption.TopDirectoryOnly); + if (packageContentsFiles != null) + { + foreach (var file in packageContentsFiles) + { + // Don't copy the .nupkg file + FileInfo fileInfo = new FileInfo(file); + if (fileInfo.Extension != ".nupkg") + { + File.Copy(file, Path.Combine(targetDir, fileInfo.Name)); + } + } + } + + return true; + } + catch (Exception) + { + return false; + } + } + + public void CancelUpdate(AddIn addIn) + { + if (addIn != null) + { + CancelPendingUpdate(addIn); + + // If there is also a NuGet package installed for this, delete it as well + IPackage addInPackage = GetNuGetPackageForAddIn(addIn, true); + if (addInPackage != null) + { + _nuGet.Packages.UninstallPackage(addInPackage, true, false); + } + + AddInInstallationEventArgs eventArgs = new AddInInstallationEventArgs(addIn); + eventArgs.PreviousVersionRemains = true; + _events.OnAddInUninstalled(eventArgs); + _events.OnAddInStateChanged(eventArgs); + } + } + + private void CancelPendingUpdate(AddIn addIn) + { + if (addIn != null) + { + foreach (string identity in addIn.Manifest.Identities.Keys) + { + // Delete from installation temp (if installation or update is pending) + string targetDir = Path.Combine(ICSharpCode.Core.AddInManager.AddInInstallTemp, identity); + if (Directory.Exists(targetDir)) + { + Directory.Delete(targetDir, true); + } + } + + _addInsMarkedForInstall.RemoveAll(markedAddIn => markedAddIn.AddIn == addIn); + } + } + + public void CancelInstallation(AddIn addIn) + { + if (addIn != null) + { + _addInsMarkedForInstall.RemoveAll(markedAddIn => markedAddIn.AddIn == addIn); + UninstallAddIn(addIn); + + // If there is also a NuGet package installed for this, delete it as well + IPackage addInPackage = GetNuGetPackageForAddIn(addIn, true); + if (addInPackage != null) + { + _nuGet.Packages.UninstallPackage(addInPackage, true, false); + } + } + } + + public void CancelUninstallation(AddIn addIn) + { + if (addIn != null) + { + // Abort uninstallation of this AddIn + foreach (string identity in addIn.Manifest.Identities.Keys) + { + ICSharpCode.Core.AddInManager.AbortRemoveUserAddInOnNextStart(identity); + } + ICSharpCode.Core.AddInManager.Enable(new AddIn[] { addIn }); + _events.OnAddInStateChanged(new AddInInstallationEventArgs(addIn)); + } + } + + public void UninstallAddIn(AddIn addIn) + { + if (addIn != null) + { + List addInList = new List(); + addInList.Add(addIn); + ICSharpCode.Core.AddInManager.RemoveExternalAddIns(addInList); + + CancelPendingUpdate(addIn); + foreach (string identity in addIn.Manifest.Identities.Keys) + { + // Remove the user AddIn + string targetDir = Path.Combine(ICSharpCode.Core.AddInManager.UserAddInPath, identity); + if (Directory.Exists(targetDir)) + { + if (!addIn.Enabled) + { + try + { + Directory.Delete(targetDir, true); + continue; + } + catch + { + // TODO Throw something? + } + } + + ICSharpCode.Core.AddInManager.RemoveUserAddInOnNextStart(identity); + } + } + + // Successfully uninstalled + AddInInstallationEventArgs eventArgs = new AddInInstallationEventArgs(addIn); + _events.OnAddInUninstalled(eventArgs); + _events.OnAddInStateChanged(eventArgs); + } + } + + public void SwitchAddInActivation(AddIn addIn) + { + if (addIn != null) + { + // Decide whether to enable or to disable the AddIn + bool disable = addIn.Enabled; + if (addIn.Action == AddInAction.Disable) + { + disable = false; + } + else if (addIn.Action == AddInAction.Enable) + { + disable = true; + } + + if (disable) + { + ICSharpCode.Core.AddInManager.Disable(new AddIn[] { addIn }); + } + else + { + ICSharpCode.Core.AddInManager.Enable(new AddIn[] { addIn }); + } + + _events.OnAddInStateChanged(new AddInInstallationEventArgs(addIn)); + } + } + + public AddIn GetInstalledAddInByIdentity(string identity) + { + if (!String.IsNullOrEmpty(identity)) + { + return SD.AddInTree.AddIns + .Where(a => (a.Manifest != null) && a.Manifest.Identities.ContainsKey(identity)) + .FirstOrDefault(); + } + else + { + return null; + } + } + + public bool IsAddInInstalled(AddIn addIn) + { + if ((addIn == null) || (addIn.Manifest == null)) + { + // Without a valid AddIn instance or a manifest we can't do anything... + return false; + } + + string identity = addIn.Manifest.PrimaryIdentity; + if (!String.IsNullOrEmpty(identity)) + { + return (SD.AddInTree.AddIns + .Where(a => (addIn == a) || ((a.Manifest != null) && a.Manifest.Identities.ContainsKey(identity))) + .FirstOrDefault() != null); + } + else + { + return false; + } + } + + public bool IsAddInPreinstalled(AddIn addIn) + { + if (addIn != null) + { + return + String.Equals(addIn.Properties["addInManagerHidden"], "preinstalled", StringComparison.OrdinalIgnoreCase) + && FileUtility.IsBaseDirectory(FileUtility.ApplicationRootPath, addIn.FileName); + } + else + { + return false; + } + } + + public IPackage GetNuGetPackageForAddIn(AddIn addIn, bool getLatest) + { + if (addIn == null) + { + throw new ArgumentNullException("addIn"); + } + + IPackage package = null; + string nuGetPackageID = null; + if (addIn.Properties.Contains("nuGetPackageID")) + { + nuGetPackageID = addIn.Properties["nuGetPackageID"]; + } + string primaryIdentity = null; + if (addIn.Manifest != null) + { + primaryIdentity = addIn.Manifest.PrimaryIdentity; + } + + if (!String.IsNullOrEmpty(nuGetPackageID)) + { + // Find installed package with mapped NuGet package ID + var matchingPackages = _nuGet.Packages.LocalRepository.GetPackages() + .Where(p => (p.Id == primaryIdentity) || (p.Id == nuGetPackageID)) + .OrderBy(p => p.Version); + if (getLatest) + { + // Return latest package version + package = matchingPackages.LastOrDefault(); + } + else + { + // Return oldest installed package version + package = matchingPackages.FirstOrDefault(); + } + } + + return package; + } + + public AddIn GetAddInForNuGetPackage(IPackage package) + { + return GetAddInForNuGetPackage(package, false); + } + + public AddIn GetAddInForNuGetPackage(IPackage package, bool withAddInsMarkedForInstallation) + { + if (withAddInsMarkedForInstallation) + { + return GetInstalledOrMarkedAddInForNuGetPackage(package); + } + else + { + return GetInstalledAddInForNuGetPackage(package); + } + } + + private AddIn GetInstalledAddInForNuGetPackage(IPackage package) + { + if (package == null) + { + throw new ArgumentNullException("package"); + } + + AddIn foundAddIn = SD.AddInTree.AddIns.Where( + a => ((a.Manifest != null) && (a.Manifest.PrimaryIdentity == package.Id)) + || (a.Properties.Contains("nuGetPackageID") && (a.Properties["nuGetPackageID"] == package.Id))) + .FirstOrDefault(); + + return foundAddIn; + } + + private AddIn GetInstalledOrMarkedAddInForNuGetPackage(IPackage package) + { + if (package == null) + { + throw new ArgumentNullException("package"); + } + + ManagedAddIn foundAddIn = AddInsWithMarkedForInstallation.Where( + a => ((a.AddIn.Manifest != null) && (a.AddIn.Manifest.PrimaryIdentity == package.Id)) + || (a.AddIn.Properties.Contains("nuGetPackageID") && (a.AddIn.Properties["nuGetPackageID"] == package.Id))) + .FirstOrDefault(); + + return (foundAddIn != null) ? foundAddIn.AddIn : null; + } + + public IEnumerable GetDependentAddIns(AddIn addIn) + { + if ((addIn != null) && (addIn.Manifest != null) && (addIn.Manifest.PrimaryIdentity != null)) + { + // Get all AddIns which are dependent from given AddIn + var dependentAddIns = AddInsWithMarkedForInstallation.Where( + a => (a.AddIn.Manifest != null) && a.AddIn.Manifest.Dependencies.Any( + reference => reference.Name == addIn.Manifest.PrimaryIdentity)); + return dependentAddIns; + } + + return null; + } + + public void RemoveUnreferencedNuGetPackages() + { + // Get list of installed NuGet packages + var localRepositoryPackages = _nuGet.Packages.LocalRepository.GetPackages(); + if (localRepositoryPackages != null) + { + List installedNuGetPackages = new List(localRepositoryPackages); + foreach (var installedPackage in installedNuGetPackages) + { + bool removeThisPackage = false; + AddIn addIn = GetAddInForNuGetPackage(installedPackage); + if (addIn == null) + { + // There is no AddIn for this package -> remove it + removeThisPackage = true; + } + else + { + // Try to get the most recent (= with highest version) package for this AddIn + IPackage latestPackage = installedNuGetPackages + .Where(p => p.Id == installedPackage.Id) + .OrderBy(p => p.Version) + .LastOrDefault(); + if (latestPackage.Version != installedPackage.Version) + { + // This is not the most recent installed package for this AddIn -> remove it + removeThisPackage = true; + } + } + + if (removeThisPackage) + { + // We decided to remove this package + LoggingService.InfoFormatted("Removing unreferenced NuGet package {0} {1}.", + installedPackage.Id, installedPackage.Version.ToString()); + _nuGet.Packages.UninstallPackage(installedPackage, true, false); + } + } + } + } + +// private void LoadMappings() +// { +// var savedMappings = PropertyService.Get("AddInManager2.AddInPackageMappings", null); +// if (savedMappings != null) +// { +// foreach (var mapping in savedMappings) +// { +// string[] mappingParts = mapping.Split(new char[] { '|' }, 2); +// if ((mappingParts != null) && (mappingParts.Length == 2)) +// { +// _addInToNuGetMapping[mappingParts[0]] = mappingParts[1]; +// } +// } +// } +// else +// { +// // Save empty mapping +// SaveMappings(); +// } +// } +// +// private void SaveMappings() +// { +// List mappingList = new List(); +// foreach (var mapping in _addInToNuGetMapping) +// { +// mappingList.Add(mapping.Key + "|" + mapping.Value); +// } +// +// PropertyService.Set("AddInManager2.AddInPackageMappings", mappingList.ToArray()); +// } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/Model/Interfaces/IAddInManagerEvents.cs b/src/AddIns/Misc/AddInManager2/Project/Src/Model/Interfaces/IAddInManagerEvents.cs new file mode 100644 index 0000000000..8828207538 --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/Model/Interfaces/IAddInManagerEvents.cs @@ -0,0 +1,41 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using NuGet; + +namespace ICSharpCode.AddInManager2.Model +{ + /// + /// Interface for AddInManager event service. + /// + public interface IAddInManagerEvents + { + event EventHandler OperationStarted; + void OnOperationStarted(EventArgs e); + + event EventHandler AddInInstalled; + void OnAddInInstalled(AddInInstallationEventArgs e); + + event EventHandler AddInUninstalled; + void OnAddInUninstalled(AddInInstallationEventArgs e); + + event EventHandler AddInOperationError; + void OnAddInOperationError(AddInExceptionEventArgs e); + + event EventHandler AddInPackageDownloaded; + void OnAddInPackageDownloaded(PackageOperationEventArgs e); + + event EventHandler AddInPackageRemoved; + void OnAddInPackageRemoved(PackageOperationEventArgs e); + + event EventHandler AddInStateChanged; + void OnAddInStateChanged(AddInInstallationEventArgs e); + + event EventHandler PackageMessageLogged; + void OnPackageMessageLogged(PackageMessageLoggedEventArgs e); + + event EventHandler AcceptLicenses; + void OnAcceptLicenses(AcceptLicensesEventArgs e); + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/Model/Interfaces/IAddInPackage.cs b/src/AddIns/Misc/AddInManager2/Project/Src/Model/Interfaces/IAddInPackage.cs new file mode 100644 index 0000000000..b6f631760e --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/Model/Interfaces/IAddInPackage.cs @@ -0,0 +1,130 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using NuGet; + +namespace ICSharpCode.AddInManager2.Model +{ + /// + /// Describes an AddIn package entry in AddInManager. + /// + public interface IAddInPackage + { + string Name + { + get; + } + + bool HasLicenseUrl + { + get; + } + + Uri LicenseUrl + { + get; + } + + bool HasProjectUrl + { + get; + } + + Uri ProjectUrl + { + get; + } + + bool HasReportAbuseUrl + { + get; + } + + Uri ReportAbuseUrl + { + get; + } + + bool IsAdded + { + get; + } + + IEnumerable Dependencies + { + get; + } + + bool HasDependencies + { + get; + } + + bool HasNoDependencies + { + get; + } + + IEnumerable Authors + { + get; + } + + bool HasDownloadCount + { + get; + } + + string Id + { + get; + } + + Uri IconUrl + { + get; + } + + string Summary + { + get; + } + + Version Version + { + get; + } + + int DownloadCount + { + get; + } + + string Description + { + get; + } + + DateTime? LastUpdated + { + get; + } + + bool HasLastUpdated + { + get; + } + + bool HasVersion + { + get; + } + + bool IsManaged + { + get; + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/Model/Interfaces/IAddInSetup.cs b/src/AddIns/Misc/AddInManager2/Project/Src/Model/Interfaces/IAddInSetup.cs new file mode 100644 index 0000000000..422258275d --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/Model/Interfaces/IAddInSetup.cs @@ -0,0 +1,35 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using ICSharpCode.Core; +using NuGet; + +namespace ICSharpCode.AddInManager2.Model +{ + public interface IAddInSetup + { + IEnumerable AddInsWithMarkedForInstallation + { + get; + } + AddIn InstallAddIn(string archiveFileName); + AddIn InstallAddIn(IPackage package, string packageDirectory); + void UninstallAddIn(AddIn addIn); + void CancelUpdate(AddIn addIn); + void CancelInstallation(AddIn addIn); + void CancelUninstallation(AddIn addIn); + void SwitchAddInActivation(AddIn addIn); + AddIn GetInstalledAddInByIdentity(string identity); + bool IsAddInInstalled(AddIn addIn); + bool IsAddInPreinstalled(AddIn addin); + IPackage GetNuGetPackageForAddIn(AddIn addIn, bool getLatest); + AddIn GetAddInForNuGetPackage(IPackage package); + AddIn GetAddInForNuGetPackage(IPackage package, bool withAddInsMarkedForInstallation); + IEnumerable GetDependentAddIns(AddIn addIn); + void RemoveUnreferencedNuGetPackages(); + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/Model/Interfaces/INuGetPackageManager.cs b/src/AddIns/Misc/AddInManager2/Project/Src/Model/Interfaces/INuGetPackageManager.cs new file mode 100644 index 0000000000..98a5b68623 --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/Model/Interfaces/INuGetPackageManager.cs @@ -0,0 +1,24 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using ICSharpCode.Core; +using NuGet; + +namespace ICSharpCode.AddInManager2.Model +{ + public interface INuGetPackageManager + { + IPackageManager Packages + { + get; + } + string PackageOutputDirectory + { + get; + } + bool PackageContainsAddIn(IPackage package); + IPackageOperationResolver CreateInstallPackageOperationResolver(bool allowPrereleaseVersions); + void ExecuteOperation(PackageOperation operation); + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/Model/Interfaces/IPackageRepositories.cs b/src/AddIns/Misc/AddInManager2/Project/Src/Model/Interfaces/IPackageRepositories.cs new file mode 100644 index 0000000000..3a2d88990f --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/Model/Interfaces/IPackageRepositories.cs @@ -0,0 +1,33 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using NuGet; + +namespace ICSharpCode.AddInManager2.Model +{ + public interface IPackageRepositories + { + IPackageRepository Registered + { + get; + } + + IPackageRepository Active + { + get; + } + + PackageSource ActiveSource + { + get; + set; + } + IEnumerable RegisteredPackageSources + { + get; + set; + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/Model/ManagedAddIn.cs b/src/AddIns/Misc/AddInManager2/Project/Src/Model/ManagedAddIn.cs new file mode 100644 index 0000000000..59047cc264 --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/Model/ManagedAddIn.cs @@ -0,0 +1,60 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using ICSharpCode.Core; + +namespace ICSharpCode.AddInManager2.Model +{ + public enum AddInInstallationSource + { + Offline, + NuGetRepository + } + + /// + /// Extension of AddIn class used in AddInManager internally. + /// + public class ManagedAddIn + { + private AddIn _addIn; + + public ManagedAddIn(AddIn addIn) + { + _addIn = addIn; + InstallationSource = AddInInstallationSource.Offline; + } + + public bool IsTemporary + { + get; + set; + } + + public bool IsUpdate + { + get; + set; + } + + public Version OldVersion + { + get; + set; + } + + public AddInInstallationSource InstallationSource + { + get; + set; + } + + public AddIn AddIn + { + get + { + return _addIn; + } + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/Model/Model.cs b/src/AddIns/Misc/AddInManager2/Project/Src/Model/Model.cs new file mode 100644 index 0000000000..582cdc104e --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/Model/Model.cs @@ -0,0 +1,43 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.ComponentModel; +using System.Linq.Expressions; + +namespace ICSharpCode.AddInManager2.Model +{ + public abstract class Model : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + public string PropertyChangedFor(Expression> expression) + { + MemberExpression memberExpression = expression.Body as MemberExpression; + return PropertyChangedFor(memberExpression); + } + + private string PropertyChangedFor(MemberExpression memberExpression) + { + if (memberExpression != null) + { + return memberExpression.Member.Name; + } + return String.Empty; + } + + protected void OnPropertyChanged(Expression> expression) + { + string propertyName = PropertyChangedFor(expression); + OnPropertyChanged(propertyName); + } + + protected void OnPropertyChanged(string propertyName) + { + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); + } + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/Model/NuGetPackageManager.cs b/src/AddIns/Misc/AddInManager2/Project/Src/Model/NuGetPackageManager.cs new file mode 100644 index 0000000000..5ac153c906 --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/Model/NuGetPackageManager.cs @@ -0,0 +1,155 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.Versioning; +using ICSharpCode.Core; +using NuGet; + +namespace ICSharpCode.AddInManager2.Model +{ + /// + /// Wrapper around native NuGet package manager. + /// + public class NuGetPackageManager : INuGetPackageManager + { + /// + /// Helper for log messages generated by NuGet component. + /// + public class PackageMessageLogger : ILogger + { + IAddInManagerEvents _events = null; + + public PackageMessageLogger(IAddInManagerEvents events) + { + _events = events; + } + + public void Log(MessageLevel level, string message, params object[] args) + { + _events.OnPackageMessageLogged(new PackageMessageLoggedEventArgs(level, message, args)); + } + } + + private NuGetPackageManagerImplementation _packageManager = null; + private IPackageRepositories _repositories = null; + private IAddInManagerEvents _events = null; + private ILogger _logger = null; + private string _packageOutputDirectory; + + public NuGetPackageManager(IPackageRepositories repositories, IAddInManagerEvents events) + { + _repositories = repositories; + _events = events; + _packageOutputDirectory = Path.Combine(PropertyService.ConfigDirectory, "NuGet"); + + _logger = new PackageMessageLogger(_events); + + _events.PackageMessageLogged += Events_PackageMessageLogged; + } + + public IPackageManager Packages + { + get + { + // Create PackageManager instance lazily + return EnsurePackageManagerInstance(); + } + } + + public ILogger Logger + { + get + { + return _logger; + } + } + + public string PackageOutputDirectory + { + get + { + return _packageOutputDirectory; + } + } + + public bool PackageContainsAddIn(IPackage package) + { + return (package != null) && (package.Tags != null) && package.Tags.Contains("SharpDevelopAddIn"); + } + + public IPackageOperationResolver CreateInstallPackageOperationResolver(bool allowPrereleaseVersions) + { + EnsurePackageManagerInstance(); + + return new InstallWalker( + _packageManager.LocalRepository, + _packageManager.SourceRepository, + null, + _logger, + false, + allowPrereleaseVersions); + } + + public void ExecuteOperation(PackageOperation operation) + { + EnsurePackageManagerInstance(); + _packageManager.ExecuteOperation(operation); + } + + private IPackageManager EnsurePackageManagerInstance() + { + if (_packageManager != null) + { + return _packageManager; + } + + // Ensure that package directory exists + if (!Directory.Exists(_packageOutputDirectory)) + { + Directory.CreateDirectory(_packageOutputDirectory); + } + + // Create new package manager instance + _packageManager = new NuGetPackageManagerImplementation(_repositories.Registered, _packageOutputDirectory); + _packageManager.PackageInstalled += _packageEvents_NuGetPackageInstalled; + _packageManager.PackageUninstalled += _packageEvents_NuGetPackageUninstalled; + return _packageManager; + } + + private void _packageEvents_NuGetPackageInstalled(object sender, PackageOperationEventArgs e) + { + // NuGet package has been downloaded and extracted, now install the AddIn from it + // TODO Error management? + + _events.OnAddInPackageDownloaded(e); + } + + private void _packageEvents_NuGetPackageUninstalled(object sender, PackageOperationEventArgs e) + { + _events.OnAddInPackageRemoved(e); + } + + private void Events_PackageMessageLogged(object sender, PackageMessageLoggedEventArgs e) + { + LoggingService.InfoFormatted("[NuGetPackageManager] {0}", e.Message); + } + + private class NuGetPackageManagerImplementation : PackageManager + { + public NuGetPackageManagerImplementation(IPackageRepository sourceRepository, string path) + : base(sourceRepository, path) + { + } + + public void ExecuteOperation(PackageOperation operation) + { + // Allow to call this method from outside of the class + base.Execute(operation); + } + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/Model/NuGetPackageOperationEventArgs.cs b/src/AddIns/Misc/AddInManager2/Project/Src/Model/NuGetPackageOperationEventArgs.cs new file mode 100644 index 0000000000..65bfd12fe7 --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/Model/NuGetPackageOperationEventArgs.cs @@ -0,0 +1,25 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using NuGet; + +namespace ICSharpCode.AddInManager2.Model +{ + /// + /// Event data for operations related to NuGet packages. + /// + public class NuGetPackageOperationEventArgs : EventArgs + { + public NuGetPackageOperationEventArgs(IPackage package) + { + Package = package; + } + + public IPackage Package + { + get; + private set; + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/Model/PackageMessageLoggedEventArgs.cs b/src/AddIns/Misc/AddInManager2/Project/Src/Model/PackageMessageLoggedEventArgs.cs new file mode 100644 index 0000000000..abb6a1ff54 --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/Model/PackageMessageLoggedEventArgs.cs @@ -0,0 +1,45 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using NuGet; + +namespace ICSharpCode.AddInManager2.Model +{ + public class PackageMessageLoggedEventArgs : EventArgs + { + public PackageMessageLoggedEventArgs(MessageLevel level, string message, params object[] args) + { + this.Message = new PackageOperationMessage(level, message, args); + } + + public PackageOperationMessage Message + { + get; + private set; + } + } + + public class PackageOperationMessage + { + string message; + object[] args; + + public PackageOperationMessage(MessageLevel level, string message, params object[] args) + { + this.Level = level; + this.message = message; + this.args = args; + } + + public MessageLevel Level + { + get; private set; + } + + public override string ToString() + { + return String.Format(message, args); + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/Model/PackageRepositories.cs b/src/AddIns/Misc/AddInManager2/Project/Src/Model/PackageRepositories.cs new file mode 100644 index 0000000000..e42477ca84 --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/Model/PackageRepositories.cs @@ -0,0 +1,136 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Linq; +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop; +using NuGet; + +namespace ICSharpCode.AddInManager2.Model +{ + /// + /// Holds reference to currently used NuGet package repositories to install AddIns from. + /// + public class PackageRepositories : IPackageRepositories + { + private IPackageRepository _currentRepository; + private IPackageRepository _activeRepository; + private PackageSource _activeSource; + private List _registeredPackageSources; + + public PackageRepositories() + { + _registeredPackageSources = new List(); + LoadPackageSources(); + UpdateCurrentRepository(); + UpdateActiveRepository(); + } + + public IPackageRepository Registered + { + get + { + return _currentRepository; + } + } + + public IPackageRepository Active + { + get + { + return _activeRepository; + } + } + + public PackageSource ActiveSource + { + get + { + return _activeSource; + } + set + { + _activeSource = value; + UpdateActiveRepository(); + } + } + + public IEnumerable RegisteredPackageSources + { + get + { + return _registeredPackageSources; + } + set + { + _registeredPackageSources.Clear(); + _registeredPackageSources.AddRange(value); + SavePackageSources(); + } + } + + private void LoadPackageSources() + { + _registeredPackageSources.Clear(); + var savedRepositories = SD.PropertyService.Get("AddInManager2.PackageRepositories", null); + if ((savedRepositories != null) && (savedRepositories.Length > 0)) + { + foreach (string repositoryEntry in savedRepositories) + { + string[] splittedEntry = repositoryEntry.Split(new char[] { '=' }, 2); + if ((splittedEntry != null) && (splittedEntry.Length == 2)) + { + // Create PackageSource from this entry + PackageSource savedPackageSource = new PackageSource(splittedEntry[1], splittedEntry[0]); + _registeredPackageSources.Add(savedPackageSource); + } + } + } + else + { + // If we don't have any repositories, so add the default one + PackageSource defaultPackageSource = + new PackageSource("https://nuget.org/api/v2", ResourceService.GetString("AddInManager2.DefaultRepository")); + _registeredPackageSources.Add(defaultPackageSource); + SavePackageSources(); + } + } + + private void SavePackageSources() + { + var savedRepositories = _registeredPackageSources.Select(ps => ps.Name + "=" + ps.Source); + PropertyService.Set("AddInManager2.PackageRepositories", savedRepositories.ToArray()); + UpdateCurrentRepository(); + } + + private void UpdateCurrentRepository() + { + var repositories = + _registeredPackageSources.Select(packageSource => PackageRepositoryFactory.Default.CreateRepository(packageSource.Source)); + if (repositories.Any()) + { + _currentRepository = new AggregateRepository(repositories); + } + } + + private void UpdateActiveRepository() + { + if ((_activeSource == null) && (_registeredPackageSources != null)) + { + _activeSource = _registeredPackageSources[0]; + } + + if (_activeSource != null) + { + _activeRepository = PackageRepositoryFactory.Default.CreateRepository(_activeSource.Source); + } + else + { + // If no active repository is set, get packages from all repositories + _activeRepository = _currentRepository; + } + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/Model/PackageRepository.cs b/src/AddIns/Misc/AddInManager2/Project/Src/Model/PackageRepository.cs new file mode 100644 index 0000000000..0a06938c64 --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/Model/PackageRepository.cs @@ -0,0 +1,61 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using NuGet; + +namespace ICSharpCode.AddInManager2.Model +{ + public class PackageRepository : Model + { +// RegisteredPackageSource packageSource; + + public PackageRepository() + { + } + + public PackageRepository(PackageSource packageSource) + { +// this.packageSource = new RegisteredPackageSource(packageSource); + Name = packageSource.Name; + SourceUrl = packageSource.Source; + } + + public string Name + { +// get +// { +// return packageSource.Name; + // TODO +// return null; +// } +// set +// { +// packageSource.Name = value; +// } + get; + set; + } + + public string SourceUrl + { +// get +// { +// return packageSource.Source; + // TODO +// return null; +// } +// set +// { +// packageSource.Source = value; +// } + get; + set; + } + + public PackageSource ToPackageSource() + { + return new PackageSource(SourceUrl, Name); + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/Model/Page.cs b/src/AddIns/Misc/AddInManager2/Project/Src/Model/Page.cs new file mode 100644 index 0000000000..6a29458d8f --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/Model/Page.cs @@ -0,0 +1,18 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.AddInManager2.Model +{ + public class Page + { + public int Number { get; set; } + public bool IsSelected { get; set; } + + public override string ToString() + { + return String.Format("[Page] Number={0}, IsSelected={1}", Number, IsSelected); + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/Model/Pages.cs b/src/AddIns/Misc/AddInManager2/Project/Src/Model/Pages.cs new file mode 100644 index 0000000000..06e5e42ac6 --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/Model/Pages.cs @@ -0,0 +1,155 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.ObjectModel; + +namespace ICSharpCode.AddInManager2.Model +{ + public class Pages : ObservableCollection + { + public const int DefaultPageSize = 10; + public const int DefaultMaximumSelectablePages = 5; + + int pageSize = DefaultPageSize; + int selectedPageNumber = 1; + int maximumSelectablePages = DefaultMaximumSelectablePages; + int totalItems = 0; + int itemsOnSelectedPage = 0; + + public int TotalItems { + get { return totalItems; } + set { + if (totalItems != value) { + totalItems = value; + UpdatePages(); + } + } + } + + public int SelectedPageNumber { + get { return selectedPageNumber; } + set { + if (selectedPageNumber != value) { + selectedPageNumber = value; + UpdatePages(); + } + } + } + + public int MaximumSelectablePages { + get { return maximumSelectablePages; } + set { + if (maximumSelectablePages != value) { + maximumSelectablePages = value; + UpdatePages(); + } + } + } + + public int ItemsBeforeFirstPage { + get { + return (selectedPageNumber - 1) * pageSize; + } + } + + public bool IsPaged { + get { return totalItems > pageSize; } + } + + public bool HasPreviousPage { + get { return IsPaged && !IsFirstPageSelected; } + } + + bool IsFirstPageSelected { + get { return selectedPageNumber == 1; } + } + + public bool HasNextPage { + get { return IsPaged && !IsLastPageSelected; } + } + + bool IsLastPageSelected { + get { return selectedPageNumber == TotalPages; } + } + + public int TotalPages { + get { return (totalItems + pageSize - 1) / pageSize; } + } + + public int PageSize { + get { return pageSize; } + set { + if (pageSize != value) { + pageSize = value; + UpdatePages(); + } + } + } + + void UpdatePages() + { + Clear(); + + int startPage = GetStartPage(); + for (int pageNumber = startPage; pageNumber <= TotalPages; ++pageNumber) { + if (Count >= maximumSelectablePages) { + break; + } + Page page = CreatePage(pageNumber); + Add(page); + } + } + + int GetStartPage() + { + // Less pages than can be selected? + int totalPages = TotalPages; + if (totalPages <= maximumSelectablePages) { + return 1; + } + + // First choice for start page. + int startPage = selectedPageNumber - (maximumSelectablePages / 2); + if (startPage <= 0) { + return 1; + } + + // Do we have enough pages? + int totalPagesBasedOnStartPage = totalPages - startPage + 1; + if (totalPagesBasedOnStartPage >= maximumSelectablePages) { + return startPage; + } + + // Ensure we have enough pages. + startPage -= maximumSelectablePages - totalPagesBasedOnStartPage; + if (startPage > 0) { + return startPage; + } + return 1; + } + + Page CreatePage(int pageNumber) + { + var page = new Page(); + page.Number = pageNumber; + page.IsSelected = IsSelectedPage(pageNumber); + return page; + } + + bool IsSelectedPage(int pageNumber) + { + return pageNumber == selectedPageNumber; + } + + public int TotalItemsOnSelectedPage { + get { return itemsOnSelectedPage; } + set { + itemsOnSelectedPage = value; + if (itemsOnSelectedPage < pageSize) { + TotalItems = (selectedPageNumber - 1) * pageSize + itemsOnSelectedPage; + } + } + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/Model/ReadPackagesResult.cs b/src/AddIns/Misc/AddInManager2/Project/Src/Model/ReadPackagesResult.cs new file mode 100644 index 0000000000..010715c4aa --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/Model/ReadPackagesResult.cs @@ -0,0 +1,25 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Linq; +using ICSharpCode.AddInManager2.ViewModel; +using NuGet; + +namespace ICSharpCode.AddInManager2.Model +{ + public class ReadPackagesResult + { + public ReadPackagesResult(IEnumerable packages, int totalPackages) + { + this.Packages = packages; + this.TotalPackagesOnPage = packages.Count(); + this.TotalPackages = totalPackages; + } + + public IEnumerable Packages { get; set; } + public int TotalPackagesOnPage { get; set; } + public int TotalPackages { get; set; } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/View/AddInManagerView.xaml b/src/AddIns/Misc/AddInManager2/Project/Src/View/AddInManagerView.xaml new file mode 100644 index 0000000000..db56187271 --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/View/AddInManagerView.xaml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/View/PagedResultsView.xaml.cs b/src/AddIns/Misc/AddInManager2/Project/Src/View/PagedResultsView.xaml.cs new file mode 100644 index 0000000000..1126ba7786 --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/View/PagedResultsView.xaml.cs @@ -0,0 +1,16 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Windows.Controls; + +namespace ICSharpCode.AddInManager2.View +{ + public partial class PagedResultsView : UserControl + { + public PagedResultsView() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/AddInManagerViewModel.cs b/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/AddInManagerViewModel.cs new file mode 100644 index 0000000000..4052a1b904 --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/AddInManagerViewModel.cs @@ -0,0 +1,144 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; +using ICSharpCode.AddInManager2.Model; +using ICSharpCode.AddInManager2.View; +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.Gui; +using NuGet; + +namespace ICSharpCode.AddInManager2.ViewModel +{ + public class AddInManagerViewModel : Model, IDisposable + { + private string _message; + private bool _hasError; + + public AddInManagerViewModel() + { + // Visuals + this.Title = ResourceService.GetString("AddInManager.Title"); + + // Add event handlers + AddInManager.Events.OperationStarted += AddInManager_Events_OperationStarted; + AddInManager.Events.AddInOperationError += AddInManager_Events_NuGetPackageOperationError; + AddInManager.Events.AcceptLicenses += AddInManager_Events_AcceptLicenses; + + AvailableAddInsViewModel = new AvailableAddInsViewModel(); + InstalledAddInsViewModel = new InstalledAddInsViewModel(); + UpdatedAddInsViewModel = new UpdatedAddInsViewModel(); + + // Read the packages + AvailableAddInsViewModel.ReadPackages(); + InstalledAddInsViewModel.ReadPackages(); + UpdatedAddInsViewModel.ReadPackages(); + } + + public AvailableAddInsViewModel AvailableAddInsViewModel + { + get; + private set; + } + + public InstalledAddInsViewModel InstalledAddInsViewModel + { + get; + private set; + } + + public UpdatedAddInsViewModel UpdatedAddInsViewModel + { + get; + private set; + } + + public string Title + { +// get { return viewTitle.Title; } + get; + private set; + } + + public void Dispose() + { + AddInManager.Events.AddInOperationError -= AddInManager_Events_NuGetPackageOperationError; + AddInManager.Events.AcceptLicenses -= AddInManager_Events_AcceptLicenses; + } + + private void ShowErrorMessage(string message) + { + this.Message = message; + this.HasError = true; + } + + public string Message + { + get + { + return _message; + } + set + { + _message = value; + OnPropertyChanged(model => model.Message); + } + } + + public bool HasError + { + get + { + return _hasError; + } + set + { + _hasError = value; + OnPropertyChanged(model => model.HasError); + } + } + + private void AddInManager_Events_OperationStarted(object sender, EventArgs e) + { + ClearMessage(); + } + + private void ClearMessage() + { + this.Message = null; + this.HasError = false; + } + + private void AddInManager_Events_NuGetPackageOperationError(object sender, AddInExceptionEventArgs e) + { + ShowErrorMessage(e.Exception.Message); + } + + private void AddInManager_Events_AcceptLicenses(object sender, AcceptLicensesEventArgs e) + { + // Show a license acceptance prompt to the user + e.IsAccepted = ShowLicenseAcceptancePrompt(e.Packages); + } + + private bool ShowLicenseAcceptancePrompt(IEnumerable packages) + { + if (packages == null) + { + // No package -> nothing to accept + return true; + } + + // Create a license acceptance view + var viewModel = new LicenseAcceptanceViewModel(packages); + var view = new LicenseAcceptanceView(); + view.DataContext = viewModel; + view.Owner = SD.Workbench.MainWindow; + return view.ShowDialog() ?? false; + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/AddInPackageViewModelBase.cs b/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/AddInPackageViewModelBase.cs new file mode 100644 index 0000000000..5f8b5de51f --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/AddInPackageViewModelBase.cs @@ -0,0 +1,415 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Input; +using ICSharpCode.AddInManager2.Model; +using NuGet; + +namespace ICSharpCode.AddInManager2.ViewModel +{ + public abstract class AddInPackageViewModelBase : Model, IAddInPackage + { + private DelegateCommand addPackageCommand; + private DelegateCommand updatePackageCommand; + private DelegateCommand removePackageCommand; + private DelegateCommand disablePackageCommand; + private DelegateCommand cancelInstallationCommand; + private DelegateCommand cancelUpdateCommand; + private DelegateCommand cancelUninstallationCommand; + private DelegateCommand optionsCommand; + + public AddInPackageViewModelBase() + { + CreateCommands(); + } + + private void CreateCommands() + { + addPackageCommand = new DelegateCommand( + param => AddPackage(), + param => !IsAdded && !IsRemoved && !IsInstalled && IsInstallable + ); + updatePackageCommand = new DelegateCommand( + param => UpdatePackage(), + param => !IsAdded && !IsRemoved && IsUpdate && IsInstallable + ); + removePackageCommand = new DelegateCommand( + param => RemovePackage(), + param => !IsAdded && !IsRemoved && IsInstalled && !IsUpdate && IsUninstallable + ); + disablePackageCommand = new DelegateCommand( + param => DisablePackage(), + param => !IsAdded && !IsRemoved && IsInstalled && IsOffline + ); + cancelInstallationCommand = new DelegateCommand( + param => CancelInstallation(), + param => IsAdded && !IsUpdate + ); + cancelUpdateCommand = new DelegateCommand( + param => CancelUpdate(), + param => IsAdded && IsUpdate + ); + cancelUninstallationCommand = new DelegateCommand( + param => CancelUninstallation(), + param => IsRemoved + ); + optionsCommand = new DelegateCommand( + param => ShowOptions(), + param => HasOptions + ); + } + + public ICommand AddPackageCommand + { + get + { + return addPackageCommand; + } + } + + public ICommand UpdatePackageCommand + { + get + { + return updatePackageCommand; + } + } + + public ICommand RemovePackageCommand + { + get + { + return removePackageCommand; + } + } + + public ICommand DisablePackageCommand + { + get + { + return disablePackageCommand; + } + } + + public ICommand CancelInstallationCommand + { + get + { + return cancelInstallationCommand; + } + } + + public ICommand CancelUpdateCommand + { + get + { + return cancelUpdateCommand; + } + } + + public ICommand CancelUninstallationCommand + { + get + { + return cancelUninstallationCommand; + } + } + + public ICommand OptionsCommand + { + get + { + return optionsCommand; + } + } + + public abstract string Name + { + get; + } + + public bool HasLicenseUrl + { + get + { + return LicenseUrl != null; + } + } + + public bool HasProjectUrl + { + get + { + return ProjectUrl != null; + } + } + + public bool HasReportAbuseUrl + { + get + { + return ReportAbuseUrl != null; + } + } + + public bool HasNoDependencies + { + get + { + return !HasDependencies; + } + } + + public bool HasLastUpdated + { + get + { + return LastUpdated.HasValue; + } + } + + public abstract bool HasDependencyConflicts + { + get; + } + + public bool HasVersion + { + get + { + return !ShowSplittedVersions && (Version != null) && (Version.ToString() != "0.0.0.0"); + } + } + + public bool HasOldVersion + { + get + { + return ShowSplittedVersions && (OldVersion != null) && (OldVersion.ToString() != "0.0.0.0"); + } + } + + public bool HasNewVersion + { + get + { + return ShowSplittedVersions && (Version != null) && (Version.ToString() != "0.0.0.0"); + } + } + + public virtual bool ShowSplittedVersions + { + get + { + return false; + } + } + + public abstract bool IsOffline + { + get; + } + + public abstract bool IsPreinstalled + { + get; + } + + public abstract bool IsEnabled + { + get; + } + + public abstract bool IsRemoved + { + get; + } + + public virtual bool HasNuGetConnection + { + get + { + return false; + } + } + + public virtual void AddPackage() + { + } + + public virtual void UpdatePackage() + { + } + + public virtual void RemovePackage() + { + } + + public virtual void DisablePackage() + { + } + + public virtual void CancelInstallation() + { + } + + public virtual void CancelUpdate() + { + } + + public virtual void CancelUninstallation() + { + } + + public virtual void ShowOptions() + { + + } + + public void UpdateInstallationState() + { + OnPropertyChanged(m => m.IsAdded); + OnPropertyChanged(m => m.IsUpdate); + OnPropertyChanged(m => m.IsInstallable); + OnPropertyChanged(m => m.IsInstalled); + OnPropertyChanged(m => m.IsEnabled); + OnPropertyChanged(m => m.IsRemoved); + OnPropertyChanged(m => m.Summary); + } + + public bool IsManaged + { + get + { + // if (selectedProjects.HasMultipleProjects()) { + // return true; + // } + // return !selectedProjects.HasSingleProjectSelected(); + return false; + } + } + + public abstract Uri LicenseUrl + { + get; + } + + public abstract Uri ProjectUrl + { + get; + } + + public abstract Uri ReportAbuseUrl + { + get; + } + + public abstract bool IsAdded + { + get; + } + + public abstract bool IsUpdate + { + get; + } + + public abstract bool IsInstalled + { + get; + } + + public abstract bool IsInstallable + { + get; + } + + public abstract bool IsUninstallable + { + get; + } + + public abstract IEnumerable Dependencies + { + get; + } + + public virtual bool HasDependencies + { + get + { + return (Dependencies != null) && Dependencies.Any(); + } + } + + public abstract IEnumerable Authors + { + get; + } + + public abstract bool HasDownloadCount + { + get; + } + + public abstract string Id + { + get; + } + + public abstract Uri IconUrl + { + get; + } + + public abstract string Summary + { + get; + } + + public abstract Version Version + { + get; + } + + public virtual Version OldVersion + { + get + { + return null; + } + } + + + public virtual bool HasOptions + { + get + { + return false; + } + } + + public abstract int DownloadCount + { + get; + } + + public abstract string Description + { + get; + } + + public abstract DateTime? LastUpdated + { + get; + } + + protected string SurroundWithParantheses(string content) + { + return "(" + content + ")"; + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/AddInsViewModelBase.cs b/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/AddInsViewModelBase.cs new file mode 100644 index 0000000000..0529b5716a --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/AddInsViewModelBase.cs @@ -0,0 +1,430 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Linq; +using System.Text; +using System.Windows.Input; +using ICSharpCode.AddInManager2.Model; +using NuGet; + +namespace ICSharpCode.AddInManager2.ViewModel +{ + public abstract class AddInsViewModelBase : Model, IDisposable + { + private Pages _pages; + private int _highlightCount; + private string _title; + + public AddInsViewModelBase() + { + _pages = new Pages(); + _highlightCount = 0; + AddInPackages = new ObservableCollection(); + ErrorMessage = String.Empty; + + CreateCommands(); + } + + void CreateCommands() + { + ShowNextPageCommand = new DelegateCommand(param => ShowNextPage()); + ShowPreviousPageCommand = new DelegateCommand(param => ShowPreviousPage()); + ShowPageCommand = new DelegateCommand(param => ExecuteShowPageCommand(param)); + SearchCommand = new DelegateCommand(param => Search()); + UpdatePreinstalledFilterCommand = new DelegateCommand(param => UpdatePreinstalledFilter()); + UpdatePrereleaseFilterCommand = new DelegateCommand(param => UpdatePrereleaseFilter()); + InstallFromArchiveCommand = new DelegateCommand(param => InstallFromArchive()); + } + + public ICommand ShowNextPageCommand + { + get; + private set; + } + + public ICommand ShowPreviousPageCommand + { + get; + private set; + } + + public ICommand ShowPageCommand + { + get; + private set; + } + + public ICommand SearchCommand + { + get; + private set; + } + + public ICommand UpdatePreinstalledFilterCommand + { + get; + private set; + } + + public ICommand UpdatePrereleaseFilterCommand + { + get; + private set; + } + + public ICommand InstallFromArchiveCommand + { + get; + private set; + } + + public void Dispose() + { + OnDispose(); + IsDisposed = true; + } + + protected virtual void OnDispose() + { + } + + public bool IsDisposed + { + get; + protected set; + } + + public bool HasError + { + get; + protected set; + } + + public string ErrorMessage + { + get; + protected set; + } + + public int HighlightCount + { + get + { + return _highlightCount; + } + protected set + { + _highlightCount = value; + OnPropertyChanged(vm => vm.HighlightCount); + OnPropertyChanged(vm => vm.HasHighlightCount); + OnPropertyChanged(vm => vm.TitleWithHighlight); + } + } + + public bool HasHighlightCount + { + get + { + return (_highlightCount > 0); + } + } + + public string Title + { + get + { + return _title; + } + set + { + _title = value; + OnPropertyChanged(vm => vm.HighlightCount); + OnPropertyChanged(vm => vm.HasHighlightCount); + OnPropertyChanged(vm => vm.TitleWithHighlight); + } + } + + public string TitleWithHighlight + { + get + { + if (_highlightCount > 0) + { + return String.Format("{0} ({1})", Title, _highlightCount); + } + else + { + return Title; + } + } + } + + public ObservableCollection AddInPackages + { + get; + private set; + } + + public ObservableCollection Pages + { + get + { + return _pages; + } + } + + protected Pages PagesCollection + { + get + { + return _pages; + } + } + + public bool IsReadingPackages + { + get; + protected set; + } + + public virtual void ReadPackages() + { + _pages.SelectedPageNumber = 1; + } + + protected void SaveError(AggregateException ex) + { + HasError = true; + ErrorMessage = GetErrorMessage(ex); + ICSharpCode.Core.LoggingService.Debug(ex); + } + + protected string GetErrorMessage(AggregateException ex) + { + StringBuilder errorMessage = new StringBuilder(); + BuildErrorMessage(ex.InnerExceptions, errorMessage); + return errorMessage.ToString().TrimEnd(); + } + + private void BuildErrorMessage(IEnumerable exceptions, StringBuilder errorMessage) + { + foreach (Exception ex in exceptions) + { + var aggregateEx = ex as AggregateException; + if (aggregateEx != null) + { + BuildErrorMessage(aggregateEx.InnerExceptions, errorMessage); + } + else + { + errorMessage.AppendLine(ex.Message); + } + } + } + + protected void UpdatePackageViewModels(IEnumerable newPackageViewModels) + { + ClearPackages(); + AddInPackages.AddRange(newPackageViewModels); + UpdateInstallationState(); + } + + protected void ClearPackages() + { + AddInPackages.Clear(); + } + + public virtual int SelectedPageNumber + { + get + { + return _pages.SelectedPageNumber; + } + set + { + if (_pages.SelectedPageNumber != value) + { + _pages.SelectedPageNumber = value; + } + } + } + + public int PageSize + { + get + { + return _pages.PageSize; + } + set + { + _pages.PageSize = value; + } + } + + public bool IsPaged + { + get + { + return _pages.IsPaged; + } + } + + public bool HasPreviousPage + { + get + { + return _pages.HasPreviousPage; + } + } + + public bool HasNextPage + { + get + { + return _pages.HasNextPage; + } + } + + public int MaximumSelectablePages + { + get + { + return _pages.MaximumSelectablePages; + } + set + { + _pages.MaximumSelectablePages = value; + } + } + + public int TotalItems + { + get; + protected set; + } + + public void ShowNextPage() + { + SelectedPageNumber += 1; + } + + public void ShowPreviousPage() + { + SelectedPageNumber -= 1; + } + + private void ExecuteShowPageCommand(object param) + { + int pageNumber = (int)param; + ShowPage(pageNumber); + } + + public void ShowPage(int pageNumber) + { + SelectedPageNumber = pageNumber; + } + + public bool IsSearchable + { + get; + set; + } + + public bool HasFilterForPreinstalled + { + get; + set; + } + + public bool HasFilterForPrereleases + { + get; + set; + } + + public bool AllowInstallFromArchive + { + get; + set; + } + + public string SearchTerms + { + get; + set; + } + + public void Search() + { + ReadPackages(); + OnPropertyChanged(null); + } + + protected virtual void UpdatePreinstalledFilter() + { + } + + protected virtual void UpdatePrereleaseFilter() + { + } + + protected virtual void InstallFromArchive() + { + } + + public bool ShowPackageSources + { + get; + set; + } + + public IEnumerable PackageSources + { + get + { + foreach (PackageSource packageSource in AddInManager.Repositories.RegisteredPackageSources) + { + yield return packageSource; + } + } + } + + public PackageSource SelectedPackageSource + { + get + { + return AddInManager.Repositories.ActiveSource; + } + set + { + AddInManager.Repositories.ActiveSource = value; + ReadPackages(); + } + } + + public bool ShowPreinstalledAddIns + { + get; + set; + } + + public bool ShowPrereleases + { + get; + set; + } + + public void UpdateInstallationState() + { + // Update installation-state-related properties of all AddIn items in here + foreach (var packageViewModel in AddInPackages) + { + packageViewModel.UpdateInstallationState(); + } + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/AvailableAddInsViewModel.cs b/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/AvailableAddInsViewModel.cs new file mode 100644 index 0000000000..ba0e950ea4 --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/AvailableAddInsViewModel.cs @@ -0,0 +1,66 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Linq; +using System.Text; +using System.Windows.Input; +using ICSharpCode.AddInManager2.Model; +using ICSharpCode.Core; +using NuGet; + +namespace ICSharpCode.AddInManager2.ViewModel +{ + public class AvailableAddInsViewModel : NuGetAddInsViewModelBase + { + public AvailableAddInsViewModel() + : base() + { + IsSearchable = true; + ShowPackageSources = true; + Title = ResourceService.GetString("AddInManager2.Views.Available"); + + AddInManager.Events.AddInInstalled += AddInInstallationStateChanged; + AddInManager.Events.AddInUninstalled += AddInInstallationStateChanged; + AddInManager.Events.AddInStateChanged += AddInInstallationStateChanged; + } + + protected override void OnDispose() + { + AddInManager.Events.AddInInstalled -= AddInInstallationStateChanged; + AddInManager.Events.AddInUninstalled -= AddInInstallationStateChanged; + AddInManager.Events.AddInStateChanged += AddInInstallationStateChanged; + } + + protected override IQueryable GetAllPackages() + { + return AddInManager.Repositories.Active.GetPackages(); + } + + protected override IEnumerable GetFilteredPackagesBeforePagingResults(IQueryable allPackages) + { + return base.GetFilteredPackagesBeforePagingResults(allPackages) + .Where(package => AddInManager.NuGet.PackageContainsAddIn(package)) + .Where(package => package.IsReleaseVersion()) + .DistinctLast(PackageEqualityComparer.Id); + } + + protected override IQueryable OrderPackages(IQueryable packages) + { + return packages.OrderByDescending(package => package.DownloadCount); + } + + protected override void UpdatePrereleaseFilter() + { + ReadPackages(); + } + + private void AddInInstallationStateChanged(object sender, AddInInstallationEventArgs e) + { + UpdateInstallationState(); + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/InstalledAddInsViewModel.cs b/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/InstalledAddInsViewModel.cs new file mode 100644 index 0000000000..5359cde867 --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/InstalledAddInsViewModel.cs @@ -0,0 +1,165 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Linq; +using System.Text; +using System.Windows.Input; +using ICSharpCode.AddInManager2.Model; +using ICSharpCode.Core; +using Microsoft.Win32; +using NuGet; + +namespace ICSharpCode.AddInManager2.ViewModel +{ + public class InstalledAddInsViewModel : NuGetAddInsViewModelBase + { + public InstalledAddInsViewModel() + : base() + { + AllowInstallFromArchive = true; + HasFilterForPreinstalled = true; + Title = ResourceService.GetString("AddInManager2.Views.Installed"); + + // Load preinstalled AddIn filter + LoadPreinstalledAddInFilter(); + + AddInManager.Events.AddInInstalled += InstalledAddInsChanged; + AddInManager.Events.AddInUninstalled += InstalledAddInsChanged; + AddInManager.Events.AddInStateChanged += InstalledAddInStateChanged; + } + + protected override void OnDispose() + { + AddInManager.Events.AddInInstalled -= InstalledAddInsChanged; + AddInManager.Events.AddInUninstalled -= InstalledAddInsChanged; + AddInManager.Events.AddInStateChanged -= InstalledAddInStateChanged; + } + + protected override IQueryable GetAllPackages() + { + return AddInManager.NuGet.Packages.LocalRepository.GetPackages(); + } + + protected override IEnumerable GetFilteredPackagesBeforePagingResults(IQueryable allPackages) + { + return base.GetFilteredPackagesBeforePagingResults(allPackages) + .Where(package => package.IsReleaseVersion()) + .DistinctLast(PackageEqualityComparer.Id); + } + + protected override void UpdatePackageViewModels(IEnumerable packages) + { + IEnumerable offlineAddInViewModels = GetInstalledAddIns(packages); +// IEnumerable nuGetViewModels = ConvertToAddInViewModels(packages); + + // Merge lists of offline entries (internal AddIn objects) and online entries (installed NuGet packages) +// IEnumerable viewModels = CombineOnlineAndOfflineAddIns(nuGetViewModels, offlineAddInViewModels); +// UpdatePackageViewModels(viewModels.OrderBy(vm => vm.Name)); + + UpdatePackageViewModels(offlineAddInViewModels.OrderBy(vm => vm.Name)); + } + + private IEnumerable CombineOnlineAndOfflineAddIns( + IEnumerable onlineAddIns, IEnumerable offlineAddIns) + { + return offlineAddIns.GroupJoin( + onlineAddIns, + offlinevm => offlinevm.Id, + onlinevm => onlinevm.Id, + (offlinevm, e) => e.ElementAtOrDefault(0) ?? offlinevm); + } + + private IEnumerable GetInstalledAddIns(IEnumerable installedPackages) + { + AddInPackageViewModelBase addInPackage; + + // Fill set of ID of installed NuGet packages, so we can later quickly check, whether NuGet package is installed for an AddIn + HashSet nuGetPackageIDs = new HashSet(); + foreach (IPackage package in installedPackages) + { + if (!nuGetPackageIDs.Contains(package.Id)) + { + nuGetPackageIDs.Add(package.Id); + } + } + + List addInList = new List(AddInManager.Setup.AddInsWithMarkedForInstallation); + addInList.Sort(delegate(ManagedAddIn a, ManagedAddIn b) + { + return a.AddIn.Name.CompareTo(b.AddIn.Name); + }); + foreach (ManagedAddIn addIn in addInList) + { + if (string.Equals(addIn.AddIn.Properties["addInManagerHidden"], "true", StringComparison.OrdinalIgnoreCase)) + { + // This excludes the SharpDevelop application appearing as AddIn in the tree + continue; + } + if (!ShowPreinstalledAddIns && AddInManager.Setup.IsAddInPreinstalled(addIn.AddIn)) + { + continue; + } + + string nuGetPackageID = addIn.AddIn.Properties["nuGetPackageID"]; + if (!string.IsNullOrEmpty(nuGetPackageID)) + { + if (nuGetPackageIDs.Contains(nuGetPackageID)) + { + addIn.InstallationSource = AddInInstallationSource.NuGetRepository; + } + } + + addInPackage = new OfflineAddInsViewModelBase(addIn); + yield return addInPackage; + } + } + + protected override void UpdatePreinstalledFilter() + { + // Save the preinstalled AddIn filter + SavePreinstalledAddInFilter(); + + // Update the list + Search(); + } + + private void InstalledAddInsChanged(object sender, AddInInstallationEventArgs e) + { + ReadPackages(); + } + + private void InstalledAddInStateChanged(object sender, AddInInstallationEventArgs e) + { + UpdateInstallationState(); + } + + private void LoadPreinstalledAddInFilter() + { + ShowPreinstalledAddIns = PropertyService.Get("AddInManager2.ShowPreinstalledAddIns", false); + } + + private void SavePreinstalledAddInFilter() + { + PropertyService.Set("AddInManager2.ShowPreinstalledAddIns", ShowPreinstalledAddIns); + } + + protected override void InstallFromArchive() + { + OpenFileDialog dlg = new OpenFileDialog(); + dlg.Filter = ResourceService.GetString("AddInManager2.SDAddInFileFilter"); + dlg.Multiselect = true; + var showDialogResult = dlg.ShowDialog(); + if (showDialogResult.HasValue && showDialogResult.Value) + { + foreach (var file in dlg.FileNames) + { + AddInManager.Setup.InstallAddIn(file); + } + } + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/LicenseAcceptanceViewModel.cs b/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/LicenseAcceptanceViewModel.cs new file mode 100644 index 0000000000..cc9e9bb29d --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/LicenseAcceptanceViewModel.cs @@ -0,0 +1,45 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Linq; +using ICSharpCode.AddInManager2.Model; +using NuGet; + +namespace ICSharpCode.AddInManager2.ViewModel +{ + public class LicenseAcceptanceViewModel : Model + { + IList packages; + + public LicenseAcceptanceViewModel(IEnumerable packages) + { + this.packages = packages.ToList(); + } + + public IEnumerable Packages + { + get + { + return packages; + } + } + + public bool HasOnePackage + { + get + { + return packages.Count == 1; + } + } + + public bool HasMultiplePackages + { + get + { + return packages.Count > 1; + } + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/NuGetAddInsViewModelBase.cs b/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/NuGetAddInsViewModelBase.cs new file mode 100644 index 0000000000..15ce36d0c9 --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/NuGetAddInsViewModelBase.cs @@ -0,0 +1,213 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Linq; +using System.Text; +using System.Windows.Input; +using ICSharpCode.AddInManager2.Model; +using NuGet; + +namespace ICSharpCode.AddInManager2.ViewModel +{ + public class NuGetAddInsViewModelBase : AddInsViewModelBase + { + private AddInManagerTask _task; + private IEnumerable _allPackages; + + public NuGetAddInsViewModelBase() + : base() + { + } + + /// + /// Returns all the packages. + /// + protected virtual IQueryable GetAllPackages() + { + return null; + } + + public override void ReadPackages() + { + base.ReadPackages(); + _allPackages = null; + UpdateRepositoryBeforeReadPackagesTaskStarts(); + StartReadPackagesTask(); + } + + private void StartReadPackagesTask() + { + IsReadingPackages = true; + HasError = false; + ClearPackages(); + CancelReadPackagesTask(); + CreateReadPackagesTask(); + _task.Start(); + } + + protected virtual void UpdateRepositoryBeforeReadPackagesTaskStarts() + { + } + + private void CancelReadPackagesTask() + { + if (_task != null) + { + _task.Cancel(); + } + } + + private void CreateReadPackagesTask() + { + _task = AddInManagerTask.Create( + () => GetPackagesForSelectedPageResult(), + (result) => OnPackagesReadForSelectedPage(result)); + } + + private ReadPackagesResult GetPackagesForSelectedPageResult() + { + IEnumerable packages = GetPackagesForSelectedPage(); + return new ReadPackagesResult(packages, TotalItems); + } + + private void OnPackagesReadForSelectedPage(AddInManagerTask task) + { + IsReadingPackages = false; + if (task.IsFaulted) + { + SaveError(task.Exception); + } + else if (task.IsCancelled) + { + // Ignore + } + else + { + UpdatePackagesForSelectedPage(task.Result); + } + base.OnPropertyChanged(null); + } + + private void UpdatePackagesForSelectedPage(ReadPackagesResult result) + { + PagesCollection.TotalItems = result.TotalPackages; + PagesCollection.TotalItemsOnSelectedPage = result.TotalPackagesOnPage; + UpdatePackageViewModels(result.Packages); + } + + private void PagesChanged(object sender, NotifyCollectionChangedEventArgs e) + { + StartReadPackagesTask(); + base.OnPropertyChanged(null); + } + + private IEnumerable GetPackagesForSelectedPage() + { + IEnumerable filteredPackages = GetFilteredPackagesBeforePagingResults(); + return GetPackagesForSelectedPage(filteredPackages); + } + + private IEnumerable GetFilteredPackagesBeforePagingResults() + { + if (_allPackages == null) + { + IQueryable packages = GetAllPackages(); + packages = OrderPackages(packages); + packages = FilterPackagesBySearchCriteria(packages); + TotalItems = packages.Count(); + _allPackages = GetFilteredPackagesBeforePagingResults(packages); + } + return _allPackages; + } + + protected virtual IQueryable OrderPackages(IQueryable packages) + { + return packages + .OrderBy(package => package.Id); + } + + private IQueryable FilterPackagesBySearchCriteria(IQueryable packages) + { + string searchCriteria = GetSearchCriteria(); + return FilterPackagesBySearchCriteria(packages, searchCriteria); + } + + private string GetSearchCriteria() + { + if (String.IsNullOrWhiteSpace(SearchTerms)) + { + return null; + } + return SearchTerms; + } + + protected IQueryable FilterPackagesBySearchCriteria(IQueryable packages, string searchCriteria) + { + return packages.Find(searchCriteria); + } + + private IEnumerable GetPackagesForSelectedPage(IEnumerable allPackages) + { + int packagesToSkip = PagesCollection.ItemsBeforeFirstPage; + return allPackages + .Skip(packagesToSkip) + .Take(PagesCollection.PageSize); + } + + /// + /// Allows filtering of the packages before paging the results. Call base class method + /// to run default filtering. + /// + protected virtual IEnumerable GetFilteredPackagesBeforePagingResults(IQueryable allPackages) + { + return GetBufferedPackages(allPackages) + .Where(package => package.IsReleaseVersion()) + .DistinctLast(PackageEqualityComparer.Id); + } + + private IEnumerable GetBufferedPackages(IQueryable allPackages) + { + return allPackages.AsBufferedEnumerable(30); + } + + protected virtual void UpdatePackageViewModels(IEnumerable packages) + { + IEnumerable currentViewModels = ConvertToAddInViewModels(packages); + UpdatePackageViewModels(currentViewModels); + } + + protected IEnumerable ConvertToAddInViewModels(IEnumerable packages) + { + foreach (IPackage package in packages) + { + yield return CreateAddInViewModel(package); + } + } + + protected virtual AddInPackageViewModelBase CreateAddInViewModel(IPackage package) + { + return new NuGetPackageViewModel(package); + } + + public override int SelectedPageNumber + { + get + { + return base.SelectedPageNumber; + } + set + { + if (base.SelectedPageNumber != value) + { + base.SelectedPageNumber = value; + StartReadPackagesTask(); + base.OnPropertyChanged(null); + } + } + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/NuGetPackageViewModel.cs b/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/NuGetPackageViewModel.cs new file mode 100644 index 0000000000..e421fdf8ce --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/NuGetPackageViewModel.cs @@ -0,0 +1,464 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Windows.Input; +using ICSharpCode.AddInManager2.Model; +using ICSharpCode.Core; +using NuGet; + +namespace ICSharpCode.AddInManager2.ViewModel +{ + public class NuGetPackageViewModel : AddInPackageViewModelBase + { + private IPackage _package; + private IEnumerable _packageOperations = new PackageOperation[0]; + + public NuGetPackageViewModel(IPackage package) + : base() + { + this._package = package; + } + + public IPackage Package + { + get + { + return _package; + } + } + + public override string Name + { + get + { + return _package.Id; + } + } + + public override Uri LicenseUrl + { + get + { + return _package.LicenseUrl; + } + } + + public override Uri ProjectUrl + { + get + { + return _package.ProjectUrl; + } + } + + public override Uri ReportAbuseUrl + { + get + { + return _package.ReportAbuseUrl; + } + } + + public override bool IsOffline + { + get + { + return false; + } + } + + public override bool IsPreinstalled + { + get + { + return false; + } + } + + public override bool IsAdded + { + get + { + AddIn installedAddIn = AddInManager.Setup.GetAddInForNuGetPackage(_package, true); + +// if (installedAddIn != null) +// LoggingService.DebugFormatted("isAdded: installedAddIn.Action = {0}", installedAddIn.Action); +// else +// LoggingService.DebugFormatted("isAdded: installedAddIn for {0} is null", _package.Id); + + return (installedAddIn != null) && ((installedAddIn.Action == AddInAction.Install) || (installedAddIn.Action == AddInAction.Update)); + } + } + + public override bool IsUpdate + { + get + { + AddIn installedAddIn = AddInManager.Setup.GetAddInForNuGetPackage(_package); + +// if (installedAddIn != null) +// LoggingService.DebugFormatted("isUpdate: installed {0}, package {1}", installedAddIn.Version.ToString(), _package.Version.Version.ToString()); +// else +// LoggingService.DebugFormatted("isUpdate: installedAddIn for {0} is null", _package.Id); + + return (installedAddIn != null) + && AddInManager.Setup.IsAddInInstalled(installedAddIn) + && (installedAddIn.Version < _package.Version.Version); + } + } + + public override bool IsInstalled + { + get + { + AddIn installedAddIn = AddInManager.Setup.GetAddInForNuGetPackage(_package); + return (installedAddIn != null) && AddInManager.Setup.IsAddInInstalled(installedAddIn); + } + } + + public override bool IsInstallable + { + get + { + return true; + } + } + + public override bool IsUninstallable + { + get + { + return true; + } + } + + public override bool IsEnabled + { + get + { + return true; + } + } + + public override bool IsRemoved + { + get + { + AddIn installedAddIn = AddInManager.Setup.GetAddInForNuGetPackage(_package); + return (installedAddIn != null) && (installedAddIn.Action == AddInAction.Uninstall); + } + } + + private bool IsPackageInstalled() + { + return AddInManager.NuGet.Packages.LocalRepository.Exists(_package); + } + + public override IEnumerable Dependencies + { + get + { + if ((_package.DependencySets != null) && _package.DependencySets.Any()) + { + PackageDependencySet firstSet = _package.DependencySets.First(); + if ((firstSet != null) && (firstSet.Dependencies != null) && (firstSet.Dependencies.Count > 0)) + { + return firstSet.Dependencies.Select(d => new AddInDependency(d)); + } + } + return null; + } + } + + public override IEnumerable Authors + { + get + { + return _package.Authors; + } + } + + public override bool HasDownloadCount + { + get + { + return _package.DownloadCount >= 0; + } + } + + public override string Id + { + get + { + return _package.Id; + } + } + + public override Uri IconUrl + { + get + { + return _package.IconUrl; + } + } + + public override string Summary + { + get + { + if (IsAdded) + { + if (IsUpdate) + { + return ResourceService.GetString("AddInManager.AddInUpdated"); + } + else + { + return SurroundWithParantheses(ResourceService.GetString("AddInManager.AddInInstalled")); + } + } + else if (IsRemoved) + { + return SurroundWithParantheses(ResourceService.GetString("AddInManager.AddInRemoved")); + } + else if (!IsEnabled) + { + return SurroundWithParantheses(ResourceService.GetString("AddInManager.AddInDisabled")); + } + else + { + return _package.Summary; + } + } + } + + public override Version Version + { + get + { + return _package.Version.Version; + } + } + + public override int DownloadCount + { + get + { + return _package.DownloadCount; + } + } + + public override string Description + { + get + { + return _package.Description; + } + } + + public override DateTime? LastUpdated + { + get + { + // TODO +// return package.LastUpdated; + return null; + } + } + + public override bool HasDependencyConflicts + { + get + { + return false; + } + } + + public override void AddPackage() + { + ClearReportedMessages(); + TryInstallingPackage(); + } + + public override void UpdatePackage() + { + ClearReportedMessages(); + TryInstallingPackage(); + } + + private void ClearReportedMessages() + { + // packageManagementEvents.OnPackageOperationsStarting(); + } + + private void GetPackageOperations() + { + var packageOperationResolver = AddInManager.NuGet.CreateInstallPackageOperationResolver(false); + _packageOperations = packageOperationResolver.ResolveOperations(_package); + } + + private bool CanInstallPackage() + { + // Ask for downloading dependent packages + if ((_packageOperations != null) && _packageOperations.Any()) + { + var operationsForDependencies = _packageOperations.Where(p => p.Package.Id != _package.Id); + if ((operationsForDependencies != null) && operationsForDependencies.Any()) + { + string addInNames = ""; + foreach (var packageOperation in operationsForDependencies) + { + addInNames += "\t " + + packageOperation.Package.Id + " " + packageOperation.Package.Version.ToString() + Environment.NewLine; + } + if (!MessageService.AskQuestionFormatted( + "${res:AddInManager.Title}", "${res:AddInManager2.InstallDependentMessage}", _package.Id, addInNames)) + { + return false; + } + } + } + + // Ask for license acceptance + IEnumerable packages = GetPackagesRequiringLicenseAcceptance(); + if (packages.Any()) + { + AcceptLicensesEventArgs acceptLicenses = new AcceptLicensesEventArgs(packages); + acceptLicenses.IsAccepted = true; + AddInManager.Events.OnAcceptLicenses(acceptLicenses); + return acceptLicenses.IsAccepted; + } + return true; + } + + private IEnumerable GetPackagesRequiringLicenseAcceptance() + { + IList packagesToBeInstalled = GetPackagesToBeInstalled(); + return GetPackagesRequiringLicenseAcceptance(packagesToBeInstalled); + } + + private IEnumerable GetPackagesRequiringLicenseAcceptance(IList packagesToBeInstalled) + { + return packagesToBeInstalled.Where(package => PackageRequiresLicenseAcceptance(package)); + } + + private IList GetPackagesToBeInstalled() + { + List packages = new List(); + foreach (PackageOperation operation in _packageOperations) + { + if (operation.Action == PackageAction.Install) + { + packages.Add(operation.Package); + } + } + return packages; + } + + private bool PackageRequiresLicenseAcceptance(IPackage package) + { + return package.RequireLicenseAcceptance && !IsPackageInstalled(); + } + + private void TryInstallingPackage() + { + try + { + if (IsPackageInstalled()) + { + // Package is already installed, but seems to be not registered as SD AddIn + string assumedPackageOutputDir = + Path.Combine(AddInManager.NuGet.PackageOutputDirectory, _package.Id + "." + _package.Version.Version.ToString()); + AddInManager.Setup.InstallAddIn(_package, assumedPackageOutputDir); + } + else + { + // Perform a normal download and AddIn installation + GetPackageOperations(); + if (CanInstallPackage()) + { + InstallPackage(_packageOperations); + } + } + } + catch (Exception ex) + { + ReportError(ex); + LoggingService.Error("Error when trying to install package.", ex); + } + } + + private void InstallPackage(IEnumerable packageOperations) + { + foreach (PackageOperation operation in packageOperations) + { + AddInManager.NuGet.ExecuteOperation(operation); + } + } + + private void ReportError(Exception ex) + { + AddInManager.Events.OnAddInOperationError(new AddInExceptionEventArgs(ex)); + } + + public override void RemovePackage() + { + TryUninstallingPackage(); + } + + private void TryUninstallingPackage() + { + try + { + UninstallPackage(); + } + catch (Exception ex) + { + ReportError(ex); + LoggingService.Error("Error when trying to uninstall package.", ex); + } + } + + public void UninstallPackage() + { + ClearReportedMessages(); + + AddIn installedAddIn = AddInManager.Setup.GetAddInForNuGetPackage(_package); + if (installedAddIn != null) + { + AddInManager.Setup.UninstallAddIn(installedAddIn); + } + } + + public override void CancelInstallation() + { + AddIn addIn = AddInManager.Setup.GetAddInForNuGetPackage(_package, true); + if (addIn != null) + { + AddInManager.Setup.CancelInstallation(addIn); + } + } + + public override void CancelUpdate() + { + AddIn addIn = AddInManager.Setup.GetAddInForNuGetPackage(_package, true); + if (addIn != null) + { + AddInManager.Setup.CancelUpdate(addIn); + } + } + + public override void CancelUninstallation() + { + AddIn addIn = AddInManager.Setup.GetAddInForNuGetPackage(_package); + if (addIn != null) + { + AddInManager.Setup.CancelUninstallation(addIn); + } + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/OfflineAddInViewModel.cs b/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/OfflineAddInViewModel.cs new file mode 100644 index 0000000000..838cc201ea --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/OfflineAddInViewModel.cs @@ -0,0 +1,527 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Input; +using ICSharpCode.AddInManager2.Model; +using ICSharpCode.Core; +using NuGet; + +namespace ICSharpCode.AddInManager2.ViewModel +{ + public class OfflineAddInsViewModelBase : AddInPackageViewModelBase + { + private AddIn _addIn; + private ManagedAddIn _markedAddIn; + + private string _name; + private Uri _licenseUrl; + private Uri _projectUrl; + private Uri _reportAbuseUrl; + private IEnumerable _dependencies; + private IEnumerable _authors; + private bool _hasDownloadCount; + private string _id; + private Uri _iconUrl; + private string _summary; + private Version _version; + private Version _oldVersion; + private int _downloadCount; + private string _description; + private DateTime? _lastUpdated; + + public OfflineAddInsViewModelBase(ManagedAddIn addIn) + : base() + { + _markedAddIn = addIn; + if (_markedAddIn != null) + { + _addIn = addIn.AddIn; + } + if (_addIn != null) + { + UpdateMembers(); + } + } + + public void UpdateMembers() + { + if ((_addIn == null) || (_markedAddIn == null)) + { + return; + } + + _id = _addIn.Manifest.PrimaryIdentity; + _name = _addIn.Name; + if (_addIn.Version != null) + { + _version = _addIn.Version; + } + _description = _addIn.Properties["description"]; + _summary = _addIn.Properties["description"]; + if (!String.IsNullOrEmpty(_addIn.Properties["url"])) + { + _projectUrl = new Uri(_addIn.Properties["url"]); + } + if (!String.IsNullOrEmpty(_addIn.Properties["license"])) + { + _licenseUrl = new Uri(_addIn.Properties["license"]); + } + if (!String.IsNullOrEmpty(_addIn.Properties["author"])) + { + _authors = new string[] { _addIn.Properties["author"] }; + } + + if ((_addIn.Manifest != null) && (_addIn.Manifest.Dependencies != null)) + { + _dependencies = _addIn.Manifest.Dependencies.Select(d => new AddInDependency(d)); + } + + if (_markedAddIn.IsUpdate) + { + _oldVersion = _markedAddIn.OldVersion; + } + + _iconUrl = null; + _hasDownloadCount = false; + _downloadCount = 0; + _lastUpdated = null; + _reportAbuseUrl = null; + } + + public AddIn AddIn + { + get + { + return _addIn; + } + } + + public override string Name + { + get + { + return _name; + } + } + + public override Uri LicenseUrl + { + get + { + return _licenseUrl; + } + } + + public override Uri ProjectUrl + { + get + { + return _projectUrl; + } + } + + public override Uri ReportAbuseUrl + { + get + { + return _reportAbuseUrl; + } + } + + public override bool IsOffline + { + get + { + return true; + } + } + + public override bool IsPreinstalled + { + get + { + if (_addIn != null) + { + return AddInManager.Setup.IsAddInPreinstalled(_addIn); + } + else + { + return false; + } + } + } + + public override bool IsAdded + { + get + { + if (_addIn != null) + { +// return (_addIn.Action == AddInAction.Install) || (_addIn.Action == AddInAction.Update); + return _markedAddIn.IsTemporary; + } + else + { + return false; + } + } + } + + public override bool IsUpdate + { + get + { + return _markedAddIn.IsUpdate; + } + } + + public override bool IsInstalled + { + get + { + if (_addIn != null) + { + return AddInManager.Setup.IsAddInInstalled(_addIn); + } + else + { + return false; + } + } + } + + public override bool IsInstallable + { + get + { + return false; + } + } + + public override bool IsUninstallable + { + get + { + return !IsPreinstalled; + } + } + + public override bool IsEnabled + { + get + { + if (_addIn != null) + { + return (_addIn.Action != AddInAction.Disable); + } + else + { + return false; + } + } + } + + public override bool IsRemoved + { + get + { + if (_addIn != null) + { + return !_markedAddIn.IsTemporary && (_addIn.Action == AddInAction.Uninstall); + } + else + { + return false; + } + } + } + + public override IEnumerable Dependencies + { + get + { + return _dependencies; + } + } + + public override IEnumerable Authors + { + get + { + return _authors; + } + } + + public override bool HasDownloadCount + { + get + { + return _hasDownloadCount; + } + } + + public override string Id + { + get + { + return _id; + } + } + + public override Uri IconUrl + { + get + { + return _iconUrl; + } + } + + public override string Summary + { + get + { + if (_addIn != null) + { + if (_addIn.Action == AddInAction.Install) + { + return SurroundWithParantheses(ResourceService.GetString("AddInManager.AddInInstalled")); + } + else if (_addIn.Action == AddInAction.Update) + { + return ResourceService.GetString("AddInManager.AddInUpdated"); + } + else if (HasDependencyConflicts) + { + return SurroundWithParantheses(ResourceService.GetString("AddInManager.AddInDependencyFailed")); + } + else if (IsRemoved) + { + return SurroundWithParantheses(ResourceService.GetString("AddInManager.AddInRemoved")); + } + else if (IsEnabled && !_addIn.Enabled) + { + return SurroundWithParantheses(ResourceService.GetString("AddInManager.AddInEnabled")); + } + else if (!IsEnabled) + { + if (_addIn.Enabled) + { + return SurroundWithParantheses(ResourceService.GetString("AddInManager.AddInWillBeDisabled")); + } + else + { + return SurroundWithParantheses(ResourceService.GetString("AddInManager.AddInDisabled")); + } + } + else if (_addIn.Action == AddInAction.InstalledTwice) + { + return SurroundWithParantheses(ResourceService.GetString("AddInManager.AddInInstalledTwice")); + } + else + { + return _summary; + } + } + else + { + return null; + } + } + } + + public override Version Version + { + get + { + return _version; + } + } + + public override Version OldVersion + { + get + { + return _oldVersion; + } + } + + public override bool ShowSplittedVersions + { + get + { + return IsUpdate; + } + } + + public override int DownloadCount + { + get + { + return _downloadCount; + } + } + + public override string Description + { + get + { + return _description; + } + } + + public override DateTime? LastUpdated + { + get + { + return _lastUpdated; + } + } + + public override bool HasDependencyConflicts + { + get + { + if (_addIn != null) + { + return (_addIn.Action == AddInAction.DependencyError); + } + else + { + return false; + } + } + } + + public override bool HasNuGetConnection + { + get + { + if (_markedAddIn != null) + { + return (_markedAddIn.InstallationSource == AddInInstallationSource.NuGetRepository); + } + else + { + return false; + } + } + } + + public override void AddPackage() + { + } + + public override void RemovePackage() + { + if (_addIn.Manifest.PrimaryIdentity == "ICSharpCode.AddInManager2") + { + MessageService.ShowMessage("${res:AddInManager2.CannotRemoveAddInManager}", "${res:AddInManager.Title}"); + return; + } + + if (!this.IsRemoved) + { + var dependentAddIns = AddInManager.Setup.GetDependentAddIns(_addIn); + if ((dependentAddIns != null) && dependentAddIns.Any()) + { + string addInNames = ""; + foreach (var dependentAddIn in dependentAddIns) + { + addInNames += "\t " + dependentAddIn.AddIn.Name + Environment.NewLine; + } + if (!MessageService.AskQuestionFormatted( + "${res:AddInManager.Title}", "${res:AddInManager2.DisableDependentWarning}", _addIn.Name, addInNames)) + { + return; + } + } + } + + AddInManager.Setup.UninstallAddIn(_addIn); + } + + public override void CancelInstallation() + { + AddInManager.Setup.CancelInstallation(_addIn); + } + + public override void CancelUpdate() + { + AddInManager.Setup.CancelUpdate(_addIn); + } + + public override void CancelUninstallation() + { + AddInManager.Setup.CancelUninstallation(_addIn); + } + + public override void DisablePackage() + { + if (_addIn == null) + { + return; + } + if (_addIn.Manifest.PrimaryIdentity == "ICSharpCode.AddInManager2") + { + MessageService.ShowMessage("${res:AddInManager.CannotDisableAddInManager}", "${res:AddInManager.Title}"); + return; + } + + if (this.IsEnabled) + { + var dependentAddIns = AddInManager.Setup.GetDependentAddIns(_addIn); + if ((dependentAddIns != null) && dependentAddIns.Any()) + { + string addInNames = ""; + foreach (var dependentAddIn in dependentAddIns) + { + addInNames += "\t " + dependentAddIn.AddIn.Name + Environment.NewLine; + } + if (!MessageService.AskQuestionFormatted( + "${res:AddInManager.Title}", "${res:AddInManager2.DisableDependentWarning}", _addIn.Name, addInNames)) + { + return; + } + } + } + + AddInManager.Setup.SwitchAddInActivation(_addIn); + } + + public override bool HasOptions + { + get + { + if (_addIn.Enabled) + { + foreach (KeyValuePair pair in _addIn.Paths) + { + if (pair.Key.StartsWith("/SharpDevelop/Dialogs/OptionsDialog")) + { + return true; + } + } + } + return false; + } + } + + public override void ShowOptions() + { + AddInTreeNode dummyNode = new AddInTreeNode(); + foreach (KeyValuePair pair in _addIn.Paths) + { + if (pair.Key.StartsWith("/SharpDevelop/Dialogs/OptionsDialog")) + { + dummyNode.AddCodons(pair.Value.Codons); + } + } + ICSharpCode.SharpDevelop.Commands.OptionsCommand.ShowTabbedOptions( + _addIn.Name + " " + ResourceService.GetString("AddInManager.Options"), + dummyNode); + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/OpenHyperlinkCommand.cs b/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/OpenHyperlinkCommand.cs new file mode 100644 index 0000000000..b35e7f7300 --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/OpenHyperlinkCommand.cs @@ -0,0 +1,52 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Diagnostics; +using System.Windows.Input; + +namespace ICSharpCode.AddInManager2.ViewModel +{ + public class OpenHyperlinkCommand : ICommand + { + public event EventHandler CanExecuteChanged; + + protected virtual void OnCanExecuteChanged() + { + if (CanExecuteChanged != null) + { + CanExecuteChanged(this, new EventArgs()); + } + } + + public bool CanExecute(object parameter) + { + return true; + } + + public void Execute(object parameter) + { + Uri uri = parameter as Uri; + if (uri != null) + { + StartProcess(uri.AbsoluteUri); + } + else + { + StartProcess(parameter as string); + } + } + + protected virtual void StartProcess(string fileName) + { + try + { + System.Diagnostics.Process.Start(fileName); + } + catch (Exception) + { + // TODO Show error message or ignore silently? + } + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/PackageRepositoriesViewModel.cs b/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/PackageRepositoriesViewModel.cs new file mode 100644 index 0000000000..1b0f1d4c6f --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/PackageRepositoriesViewModel.cs @@ -0,0 +1,315 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; +using System.Windows.Forms; +using System.Windows.Input; +using System.Windows.Interop; +using ICSharpCode.AddInManager2.Model; +using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.Gui; +using NuGet; + +namespace ICSharpCode.AddInManager2.ViewModel +{ + public class PackageRepositoriesViewModel : Model + { + ObservableCollection packageRepositories = + new ObservableCollection(); + ObservableCollection packageSources; + + DelegateCommand addPackageSourceCommmand; + DelegateCommand removePackageSourceCommand; + DelegateCommand movePackageSourceUpCommand; + DelegateCommand movePackageSourceDownCommand; + DelegateCommand browsePackageFolderCommand; + + PackageRepository newPackageSource = new PackageRepository(); + PackageRepository selectedPackageRepository; + + public PackageRepositoriesViewModel() + { + this.packageSources = new ObservableCollection(); + CreateCommands(); + } + + private void CreateCommands() + { + addPackageSourceCommmand = + new DelegateCommand(param => AddPackageSource(), + param => CanAddPackageSource); + + removePackageSourceCommand = + new DelegateCommand(param => RemovePackageSource(), + param => CanRemovePackageSource); + + movePackageSourceUpCommand = + new DelegateCommand(param => MovePackageSourceUp(), + param => CanMovePackageSourceUp); + + movePackageSourceDownCommand = + new DelegateCommand(param => MovePackageSourceDown(), + param => CanMovePackageSourceDown); + + browsePackageFolderCommand = + new DelegateCommand(param => BrowsePackageFolder()); + } + + public ICommand AddPackageSourceCommand + { + get + { + return addPackageSourceCommmand; + } + } + + public ICommand RemovePackageSourceCommand + { + get + { + return removePackageSourceCommand; + } + } + + public ICommand MovePackageSourceUpCommand + { + get + { + return movePackageSourceUpCommand; + } + } + + public ICommand MovePackageSourceDownCommand + { + get + { + return movePackageSourceDownCommand; + } + } + + public ICommand BrowsePackageFolderCommand + { + get + { + return browsePackageFolderCommand; + } + } + + public ObservableCollection PackageRepositories + { + get + { + return packageRepositories; + } + } + + public void Load() + { + packageSources.Clear(); +// packageSources.AddRange(AddInManager.Repositories.RegisteredPackageSources); + NuGet.CollectionExtensions.AddRange(packageSources, AddInManager.Repositories.RegisteredPackageSources); + foreach (PackageSource packageSource in packageSources) + { + AddPackageSourceToViewModel(packageSource); + } + } + + private void AddPackageSourceToViewModel(PackageSource packageSource) + { + var packageRepository = new PackageRepository(packageSource); + packageRepositories.Add(packageRepository); + } + + public void Save() + { + packageSources.Clear(); + foreach (PackageRepository packageRepository in packageRepositories) + { + PackageSource source = packageRepository.ToPackageSource(); + packageSources.Add(source); + } + AddInManager.Repositories.RegisteredPackageSources = packageSources; + } + + public string NewPackageSourceName + { + get + { + return newPackageSource.Name; + } + set + { + newPackageSource.Name = value; + OnPropertyChanged(viewModel => viewModel.NewPackageSourceName); + } + } + + public string NewPackageSourceUrl + { + get + { + return newPackageSource.SourceUrl; + } + set + { + newPackageSource.SourceUrl = value; + OnPropertyChanged(viewModel => viewModel.NewPackageSourceUrl); + } + } + + public PackageRepository SelectedPackageRepository + { + get + { + return selectedPackageRepository; + } + set + { + selectedPackageRepository = value; + OnPropertyChanged(viewModel => viewModel.SelectedPackageRepository); + OnPropertyChanged(viewModel => viewModel.CanAddPackageSource); + } + } + + public void AddPackageSource() + { + AddNewPackageSourceToViewModel(); + SelectLastPackageSourceViewModel(); + } + + private void AddNewPackageSourceToViewModel() + { + var packageSource = newPackageSource.ToPackageSource(); + AddPackageSourceToViewModel(packageSource); + } + + private void SelectLastPackageSourceViewModel() + { + SelectedPackageRepository = GetLastPackageSourceViewModel(); + } + + public bool CanAddPackageSource + { + get + { + return NewPackageSourceHasUrl && NewPackageSourceHasName; + } + } + + private bool NewPackageSourceHasUrl + { + get + { + return !String.IsNullOrEmpty(NewPackageSourceUrl); + } + } + + private bool NewPackageSourceHasName + { + get + { + return !String.IsNullOrEmpty(NewPackageSourceName); + } + } + + public void RemovePackageSource() + { + RemoveSelectedPackageSourceViewModel(); + } + + public bool CanRemovePackageSource + { + get + { + return selectedPackageRepository != null; + } + } + + private void RemoveSelectedPackageSourceViewModel() + { + packageRepositories.Remove(selectedPackageRepository); + } + + public void MovePackageSourceUp() + { + int selectedPackageSourceIndex = GetSelectedPackageSourceViewModelIndex(); + int destinationPackageSourceIndex = selectedPackageSourceIndex--; + packageRepositories.Move(selectedPackageSourceIndex, destinationPackageSourceIndex); + } + + private int GetSelectedPackageSourceViewModelIndex() + { + return packageRepositories.IndexOf(selectedPackageRepository); + } + + public bool CanMovePackageSourceUp + { + get + { + return HasAtLeastTwoPackageSources() && !IsFirstPackageSourceSelected(); + } + } + + private bool IsFirstPackageSourceSelected() + { + return selectedPackageRepository == packageRepositories[0]; + } + + public void MovePackageSourceDown() + { + int selectedPackageSourceIndex = GetSelectedPackageSourceViewModelIndex(); + int destinationPackageSourceIndex = selectedPackageSourceIndex++; + packageRepositories.Move(selectedPackageSourceIndex, destinationPackageSourceIndex); + } + + public bool CanMovePackageSourceDown + { + get + { + return HasAtLeastTwoPackageSources() && !IsLastPackageSourceSelected(); + } + } + + private bool HasAtLeastTwoPackageSources() + { + return packageRepositories.Count >= 2; + } + + private bool IsLastPackageSourceSelected() + { + PackageRepository lastViewModel = GetLastPackageSourceViewModel(); + return lastViewModel == selectedPackageRepository; + } + + private PackageRepository GetLastPackageSourceViewModel() + { + return packageRepositories.Last(); + } + + public void BrowsePackageFolder() + { + using (var dialog = new FolderBrowserDialog()) + { + var owner = SD.WinForms.MainWin32Window; + if (dialog.ShowDialog(owner) == DialogResult.OK) + { + UpdateNewPackageSourceUsingSelectedFolder(dialog.SelectedPath); + } + } + } + + private void UpdateNewPackageSourceUsingSelectedFolder(string folder) + { + NewPackageSourceUrl = folder; + NewPackageSourceName = GetPackageSourceNameFromFolder(folder); + } + + private string GetPackageSourceNameFromFolder(string folder) + { + return Path.GetFileName(folder); + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/UpdatedAddInsViewModel.cs b/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/UpdatedAddInsViewModel.cs new file mode 100644 index 0000000000..9ead50645c --- /dev/null +++ b/src/AddIns/Misc/AddInManager2/Project/Src/ViewModel/UpdatedAddInsViewModel.cs @@ -0,0 +1,108 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Linq; +using System.Text; +using System.Windows.Input; +using ICSharpCode.AddInManager2.Model; +using ICSharpCode.Core; +using NuGet; + +namespace ICSharpCode.AddInManager2.ViewModel +{ + public class UpdatedAddInsViewModel : NuGetAddInsViewModelBase + { + private IQueryable installedPackages; + private string errorMessage = String.Empty; + private bool hasSavedException = false; + + public UpdatedAddInsViewModel() + : base() + { + IsSearchable = true; + HasFilterForPrereleases = true; + Title = ResourceService.GetString("AddInManager2.Views.Updates");; + + AddInManager.Events.AddInInstalled += NuGetPackagesChanged; + AddInManager.Events.AddInUninstalled += NuGetPackagesChanged; + AddInManager.Events.AddInStateChanged += InstalledAddInStateChanged; + } + + protected override void OnDispose() + { + AddInManager.Events.AddInInstalled -= NuGetPackagesChanged; + AddInManager.Events.AddInUninstalled -= NuGetPackagesChanged; + AddInManager.Events.AddInStateChanged -= InstalledAddInStateChanged; + } + + protected override IQueryable GetAllPackages() + { + if (hasSavedException) + { + ThrowSavedException(); + } + return GetUpdatedPackages(); + } + + protected override void UpdateRepositoryBeforeReadPackagesTaskStarts() + { + try + { + installedPackages = GetInstalledPackages(); + } + catch (Exception ex) + { + hasSavedException = true; + errorMessage = ex.Message; + } + } + + private IQueryable GetInstalledPackages() + { + return AddInManager.NuGet.Packages.LocalRepository.GetPackages(); + } + + private IQueryable GetUpdatedPackages() + { + IQueryable localPackages = installedPackages; + localPackages = FilterPackages(localPackages); + var updatedPackages = GetUpdatedPackages(AddInManager.Repositories.Registered, localPackages); + HighlightCount = updatedPackages.Count(); + return updatedPackages.AsQueryable(); + } + + private IEnumerable GetUpdatedPackages(IPackageRepository sourceRepository, IQueryable localPackages) + { + return sourceRepository.GetUpdates(localPackages, ShowPrereleases, false); + } + + private IQueryable FilterPackages(IQueryable localPackages) + { + return localPackages.Find(SearchTerms); + } + + private void ThrowSavedException() + { + throw new ApplicationException(errorMessage); + } + + protected override void UpdatePrereleaseFilter() + { + ReadPackages(); + } + + private void NuGetPackagesChanged(object sender, AddInInstallationEventArgs e) + { + ReadPackages(); + } + + private void InstalledAddInStateChanged(object sender, AddInInstallationEventArgs e) + { + UpdateInstallationState(); + } + } +} diff --git a/src/AddIns/Misc/AddInManager2/RequiredLibraries/ICSharpCode.SharpZipLib.dll b/src/AddIns/Misc/AddInManager2/RequiredLibraries/ICSharpCode.SharpZipLib.dll new file mode 100644 index 0000000000000000000000000000000000000000..77bafe8ba867a1618b8735200289f6fad68b825e GIT binary patch literal 143360 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