diff --git a/Plugins/Discord/source/Plugin.Common/Delegates/FetchProfilesDelegate.cs b/Plugins/Discord/source/Plugin.Common/Delegates/FetchProfilesDelegate.cs deleted file mode 100644 index 32ef73f5..00000000 --- a/Plugins/Discord/source/Plugin.Common/Delegates/FetchProfilesDelegate.cs +++ /dev/null @@ -1,7 +0,0 @@ -using ServerManagerTool.Plugin.Common.Lib; -using System.Collections.Generic; - -namespace ServerManagerTool.Plugin.Common.Delegates -{ - public delegate IList FetchProfilesDelegate(); -} diff --git a/Plugins/Discord/source/Plugin.Common/Enums/AlertTypeEnum.cs b/Plugins/Discord/source/Plugin.Common/Enums/AlertTypeEnum.cs deleted file mode 100644 index ff88e7af..00000000 --- a/Plugins/Discord/source/Plugin.Common/Enums/AlertTypeEnum.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace ServerManagerTool.Plugin.Common -{ - public enum AlertType - { - Error, - Shutdown, - ShutdownMessage, - ShutdownReason, - Startup, - Backup, - UpdateResults, - ServerStatusChange, - ModUpdateDetected, - } -} diff --git a/Plugins/Discord/source/Plugin.Common/Extensions/IconExtension.cs b/Plugins/Discord/source/Plugin.Common/Extensions/IconExtension.cs deleted file mode 100644 index b838d14a..00000000 --- a/Plugins/Discord/source/Plugin.Common/Extensions/IconExtension.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Linq; -using System.Windows.Markup; -using System.Windows.Media.Imaging; - -namespace ServerManagerTool.Plugin.Common -{ - /// - /// Simple extension for icon, to let you choose icon with specific size. - /// Usage sample: - /// Image Stretch="None" Source="{common:Icon /Controls;component/icons/custom.ico, 16}" - /// Or: - /// Image Source="{common:Icon Source={Binding IconResource}, Size=16}" - /// - public class IconExtension : MarkupExtension - { - private string _path; - - public string Path - { - get - { - return _path; - } - set - { - // Have to make full pack URI from short form, so System.Uri recognizes it. - _path = $"pack://application:,,,{value}"; - } - } - - public int Size { get; set; } - - public override object ProvideValue(IServiceProvider serviceProvider) - { - var decoder = BitmapDecoder.Create(new Uri(Path), BitmapCreateOptions.DelayCreation, BitmapCacheOption.OnDemand); - - var result = decoder.Frames.SingleOrDefault(f => f.Width == Size); - if (result == default(BitmapFrame)) - { - result = decoder.Frames.OrderBy(f => f.Width).First(); - } - - return result; - } - } -} diff --git a/Plugins/Discord/source/Plugin.Common/Interfaces/IAlertPlugin.cs b/Plugins/Discord/source/Plugin.Common/Interfaces/IAlertPlugin.cs deleted file mode 100644 index 1fde0a56..00000000 --- a/Plugins/Discord/source/Plugin.Common/Interfaces/IAlertPlugin.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace ServerManagerTool.Plugin.Common -{ - public interface IAlertPlugin : IPlugin - { - /// - /// Handles the alert message passed for the profile. - /// - /// The type of alert message. - /// The name of the profile the alert message is associated with. - /// The message of the alert. - void HandleAlert(AlertType alertType, string profileName, string alertMessage); - } -} diff --git a/Plugins/Discord/source/Plugin.Common/Interfaces/IBeta.cs b/Plugins/Discord/source/Plugin.Common/Interfaces/IBeta.cs deleted file mode 100644 index 964c32f8..00000000 --- a/Plugins/Discord/source/Plugin.Common/Interfaces/IBeta.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace ServerManagerTool.Plugin.Common -{ - public interface IBeta - { - bool BetaEnabled - { - get; - set; - } - } -} diff --git a/Plugins/Discord/source/Plugin.Common/Interfaces/IPlugin.cs b/Plugins/Discord/source/Plugin.Common/Interfaces/IPlugin.cs deleted file mode 100644 index 9585bc65..00000000 --- a/Plugins/Discord/source/Plugin.Common/Interfaces/IPlugin.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Windows; - -namespace ServerManagerTool.Plugin.Common -{ - public interface IPlugin - { - /// - /// Gets a values indicating if the plugin can be used - /// - bool Enabled - { - get; - } - /// - /// Gets a value indicating the code of the plugin - /// - string PluginCode - { - get; - } - /// - /// Gets a value indicating the name of the plugin - /// - string PluginName - { - get; - } - /// - /// Gets a value indicating the version of the plugin - /// - Version PluginVersion - { - get; - } - - /// - /// Gets a value that indicates if the plugin has a configuration form. - /// - bool HasConfigForm - { - get; - } - - /// - /// Performs any initialization for the plugin. - /// - void Initialize(); - - /// - /// Opens the configuration form. - /// - /// The owner window. - void OpenConfigForm(Window owner); - } -} diff --git a/Plugins/Discord/source/Plugin.Common/Lib/Profile.cs b/Plugins/Discord/source/Plugin.Common/Lib/Profile.cs deleted file mode 100644 index f99c0a68..00000000 --- a/Plugins/Discord/source/Plugin.Common/Lib/Profile.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace ServerManagerTool.Plugin.Common.Lib -{ - public class Profile - { - public string ProfileName { get; set; } - public string InstallationFolder { get; set; } - } -} diff --git a/Plugins/Discord/source/Plugin.Common/Plugin.Common.csproj b/Plugins/Discord/source/Plugin.Common/Plugin.Common.csproj deleted file mode 100644 index 70274438..00000000 --- a/Plugins/Discord/source/Plugin.Common/Plugin.Common.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - %24/Development/ServerManagers/Main/Plugin.Common - {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C} - https://dev.azure.com/bretthewitson - . - - - net462 - false - ServerManagerTool.Plugin.Common - ServerManager.Plugin.Common - - - none - false - - - - - - - - \ No newline at end of file diff --git a/Plugins/Discord/source/Plugin.Common/PluginException.cs b/Plugins/Discord/source/Plugin.Common/PluginException.cs deleted file mode 100644 index 15c6fa78..00000000 --- a/Plugins/Discord/source/Plugin.Common/PluginException.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Runtime.Serialization; -using System.Security; - -namespace ServerManagerTool.Plugin.Common -{ - public class PluginException : Exception - { - public PluginException() - : base() - { - } - - public PluginException(string message) - : base(message) - { - } - - public PluginException(string message, Exception innerException) - : base(message, innerException) - { - } - - [SecuritySafeCritical] - protected PluginException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - } -} diff --git a/Plugins/Discord/source/Plugin.Common/PluginHelper.cs b/Plugins/Discord/source/Plugin.Common/PluginHelper.cs deleted file mode 100644 index 20c1ea03..00000000 --- a/Plugins/Discord/source/Plugin.Common/PluginHelper.cs +++ /dev/null @@ -1,305 +0,0 @@ -using ServerManagerTool.Plugin.Common.Delegates; -using ServerManagerTool.Plugin.Common.Lib; -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Windows; - -namespace ServerManagerTool.Plugin.Common -{ - public sealed class PluginHelper : IDisposable - { - private const string PLUGINFILE_FOLDER = "Plugins"; - private const string PLUGINFILE_EXTENSION = "dll"; - - private static volatile PluginHelper _instance; - private static readonly object _syncLock = new object(); - - private readonly Object _syncLockProcessAlert = new Object(); - private readonly Object _syncLockFetchProfiles = new Object(); - private FetchProfilesDelegate _fetchProfilesCallback; - private bool _disposed; - - private PluginHelper() - { - BetaEnabled = false; - Plugins = new ObservableCollection(); - } - - public static PluginHelper Instance - { - get - { - if (_instance != null) - return _instance; - - lock(_syncLock) - { - if (_instance == null) - _instance = new PluginHelper(); - } - return _instance; - } - } - - public static string PluginFolder - { - get - { - var folder = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location ?? Environment.CurrentDirectory); - return Path.Combine(folder, PLUGINFILE_FOLDER); - } - } - - internal bool BetaEnabled - { - get; - set; - } - - public ObservableCollection Plugins - { - get; - private set; - } - - internal void AddPlugin(string folder, string pluginFile) - { - if (!CheckPluginFile(pluginFile)) - throw new PluginException("The selected file does not contain server manager plugins or is for a previous version of server manager."); - - var pluginFolder = Path.Combine(folder, PLUGINFILE_FOLDER); - if (!Directory.Exists(pluginFolder)) - Directory.CreateDirectory(pluginFolder); - - var newPluginFile = Path.Combine(pluginFolder, $"{Path.GetFileName(pluginFile)}"); - if (File.Exists(newPluginFile)) - throw new PluginException("A file with the same name already exists, delete the existing file and try again."); - - File.Copy(pluginFile, newPluginFile, true); - - LoadPlugin(newPluginFile); - } - - internal bool CheckPluginFile(string pluginFile) - { - if (string.IsNullOrWhiteSpace(pluginFile)) - return false; - if (!File.Exists(pluginFile)) - return false; - - Assembly assembly = Assembly.Load(File.ReadAllBytes(pluginFile)); - if (assembly == null) - return false; - - Type[] types; - - try - { - types = assembly.GetTypes(); - } - catch - { - return false; - } - - if (types.Length == 0) - return false; - - // check if the file contains a plugin - foreach (Type type in types) - { - if (type.GetInterface(typeof(IPlugin).Name) != null) - return true; - } - - return false; - } - - internal void DeleteAllPlugins() - { - for (int index = Plugins.Count - 1; index >= 0; index--) - { - var pluginFile = Plugins[index].PluginFile; - - Plugins.RemoveAt(index); - - if (File.Exists(pluginFile)) - File.Delete(pluginFile); - } - } - - internal void DeletePlugin(string pluginFile) - { - if (string.IsNullOrWhiteSpace(pluginFile)) - return; - - for (int index = Plugins.Count - 1; index >= 0; index--) - { - if (Plugins[index].PluginFile.Equals(pluginFile, StringComparison.OrdinalIgnoreCase)) - Plugins.RemoveAt(index); - } - - if (File.Exists(pluginFile)) - File.Delete(pluginFile); - } - - public IList FetchProfileList() - { - lock (_syncLockFetchProfiles) - { - return _fetchProfilesCallback?.Invoke() ?? new List(); - } - } - - internal void LoadPlugin(string pluginFile) - { - if (string.IsNullOrWhiteSpace(pluginFile)) - return; - if (!File.Exists(pluginFile)) - return; - - Assembly assembly = Assembly.Load(File.ReadAllBytes(pluginFile)); - if (assembly == null) - return; - - Type[] types; - - try - { - types = assembly.GetTypes(); - } - catch - { - return; - } - - if (types.Length == 0) - return; - - // check if the file contains one or more plugins - foreach (Type type in types) - { - try - { - if (type.GetInterface(typeof(IAlertPlugin).Name) != null) - { - var plugin = assembly.CreateInstance(type.FullName) as IAlertPlugin; - if (plugin != null && plugin.Enabled) - { - if (type.GetInterface(typeof(IBeta).Name) != null) - ((IBeta)plugin).BetaEnabled = BetaEnabled; - plugin.Initialize(); - - Plugins.Add(new PluginItem { Plugin = plugin, PluginFile = pluginFile, PluginType = nameof(IAlertPlugin) }); - } - } - else if (type.GetInterface(typeof(IPlugin).Name) != null) - { - var plugin = assembly.CreateInstance(type.FullName) as IPlugin; - if (plugin != null && plugin.Enabled) - { - if (type.GetInterface(typeof(IBeta).Name) != null) - ((IBeta)plugin).BetaEnabled = BetaEnabled; - plugin.Initialize(); - - Plugins.Add(new PluginItem { Plugin = plugin, PluginFile = pluginFile, PluginType = nameof(IPlugin) }); - } - } - } - catch (Exception ex) - { - Debug.WriteLine($"ERROR: {nameof(LoadPlugin)} - {type.FullName}\r\n{ex.Message}"); - } - } - } - - internal void LoadPlugins(string folder, bool ClearExisting) - { - if (ClearExisting) - Plugins.Clear(); - - var pluginFolder = Path.Combine(folder, PLUGINFILE_FOLDER); - if (string.IsNullOrWhiteSpace(pluginFolder)) - return; - if (!Directory.Exists(pluginFolder)) - return; - - var pluginFiles = Directory.GetFiles(pluginFolder, $"*.{PLUGINFILE_EXTENSION}"); - foreach (var pluginFile in pluginFiles) - { - LoadPlugin(pluginFile); - } - } - - internal void OpenConfigForm(string pluginCode, Window owner) - { - if (Plugins == null) - return; - - var pluginItem = Plugins.FirstOrDefault(p => p.Plugin.PluginCode.Equals(pluginCode, StringComparison.OrdinalIgnoreCase)); - OpenConfigForm(pluginItem.Plugin, owner); - } - - internal void OpenConfigForm(IPlugin plugin, Window owner) - { - if (plugin == null || !plugin.Enabled || !plugin.HasConfigForm) - return; - - plugin.OpenConfigForm(owner); - } - - internal bool ProcessAlert(AlertType alertType, string profileName, string alertMessage) - { - if (Plugins == null || Plugins.Count == 0 || string.IsNullOrWhiteSpace(alertMessage)) - return false; - - var plugins = Plugins.Where(p => (p.PluginType is nameof(IAlertPlugin)) && (p.Plugin?.Enabled ?? false)); - if (plugins.Count() == 0) - return false; - - lock (_syncLockProcessAlert) - { - var message = alertMessage.Replace("\\r\\n", "\\n"); - message = message.Replace("\\n", "\n"); - - foreach (var pluginItem in plugins) - { - ((IAlertPlugin)pluginItem.Plugin).HandleAlert(alertType, profileName, message.ToString()); - } - } - - return true; - } - - internal void SetFetchProfileCallback(FetchProfilesDelegate callback) - { - _fetchProfilesCallback = callback; - } - - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - private void Dispose(bool disposing) - { - if (_disposed) - return; - - if (disposing) - { - _fetchProfilesCallback = null; - _instance = null; - } - - _disposed = true; - } - } -} diff --git a/Plugins/Discord/source/Plugin.Common/PluginItem.cs b/Plugins/Discord/source/Plugin.Common/PluginItem.cs deleted file mode 100644 index 5c2bdcec..00000000 --- a/Plugins/Discord/source/Plugin.Common/PluginItem.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace ServerManagerTool.Plugin.Common -{ - public sealed class PluginItem - { - internal PluginItem() - { - } - - public IPlugin Plugin - { - get; - set; - } - - public string PluginFile - { - get; - set; - } - - public string PluginType - { - get; - set; - } - } -} diff --git a/Plugins/Discord/source/Plugin.Common/Properties/AssemblyInfo.cs b/Plugins/Discord/source/Plugin.Common/Properties/AssemblyInfo.cs deleted file mode 100644 index f5474544..00000000 --- a/Plugins/Discord/source/Plugin.Common/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,34 +0,0 @@ -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("ServerManager Common Plugin Library")] -[assembly: AssemblyDescription("The library is used to provide common plugin functionality to the server managers.")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Bletch1971")] -[assembly: AssemblyProduct("Server Managers")] -[assembly: AssemblyCopyright("Copyright © 2015-2020")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("679fe859-9a82-4ffb-a758-c1e8df915f58")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.1.0")] -[assembly: AssemblyFileVersion("1.0.1.0")] diff --git a/Plugins/Discord/source/Plugin.Common/Utils/JsonUtils.cs b/Plugins/Discord/source/Plugin.Common/Utils/JsonUtils.cs deleted file mode 100644 index f969488b..00000000 --- a/Plugins/Discord/source/Plugin.Common/Utils/JsonUtils.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System.IO; -using System.Runtime.Serialization.Json; - -namespace ServerManagerTool.Plugin.Common -{ - public static class JsonUtils - { - public static T DeserializeFromFile(string file) - { - if (string.IsNullOrEmpty(file) || !File.Exists(file)) - return default(T); - - StreamReader streamReader = null; - - try - { - streamReader = File.OpenText(file); - - Data​Contract​Json​Serializer serializer = new DataContractJsonSerializer(typeof(T)); - return (T)serializer.ReadObject(streamReader.BaseStream); - } - catch - { - return default(T); - } - finally - { - if (streamReader != null) - streamReader.Close(); - } - } - - public static bool SerializeToFile(T value, string file) - { - if (value == null) - return false; - - StreamWriter streamWriter = null; - - try - { - var folder = Path.GetDirectoryName(file); - if (!Directory.Exists(folder)) - Directory.CreateDirectory(folder); - - streamWriter = File.CreateText(file); - - Data​Contract​Json​Serializer serializer = new DataContractJsonSerializer(typeof(T)); - serializer.WriteObject(streamWriter.BaseStream, value); - - return true; - } - catch - { - return false; - } - finally - { - if (streamWriter != null) - streamWriter.Close(); - } - } - } -} diff --git a/Plugins/Discord/source/Plugin.Common/Utils/ResourceUtils.cs b/Plugins/Discord/source/Plugin.Common/Utils/ResourceUtils.cs deleted file mode 100644 index 18b9ae62..00000000 --- a/Plugins/Discord/source/Plugin.Common/Utils/ResourceUtils.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Windows; - -namespace ServerManagerTool.Plugin.Common -{ - public static class ResourceUtils - { - public static string GetResourceString(ResourceDictionary resources, string inKey) - { - if (resources == null) - throw new ArgumentNullException(nameof(resources), "parameter cannot be null."); - if (string.IsNullOrWhiteSpace(inKey)) - throw new ArgumentNullException(nameof(inKey), "parameter cannot be null."); - - if (resources.Contains(inKey) && resources[inKey] is string) - { - var resourceString = resources[inKey].ToString(); - resourceString = resourceString.Replace("\\r", "\r"); - resourceString = resourceString.Replace("\\n", "\n"); - return resourceString; - } - return null; - } - } -} diff --git a/Plugins/Discord/source/Plugin.Discord/Art/Add.ico b/Plugins/Discord/source/Plugin.Discord/Art/Add.ico deleted file mode 100644 index f5b8bc3e..00000000 Binary files a/Plugins/Discord/source/Plugin.Discord/Art/Add.ico and /dev/null differ diff --git a/Plugins/Discord/source/Plugin.Discord/Art/ChangeNotes.ico b/Plugins/Discord/source/Plugin.Discord/Art/ChangeNotes.ico deleted file mode 100644 index 81247582..00000000 Binary files a/Plugins/Discord/source/Plugin.Discord/Art/ChangeNotes.ico and /dev/null differ diff --git a/Plugins/Discord/source/Plugin.Discord/Art/Delete.ico b/Plugins/Discord/source/Plugin.Discord/Art/Delete.ico deleted file mode 100644 index 43936478..00000000 Binary files a/Plugins/Discord/source/Plugin.Discord/Art/Delete.ico and /dev/null differ diff --git a/Plugins/Discord/source/Plugin.Discord/Art/Download.ico b/Plugins/Discord/source/Plugin.Discord/Art/Download.ico deleted file mode 100644 index e8713e50..00000000 Binary files a/Plugins/Discord/source/Plugin.Discord/Art/Download.ico and /dev/null differ diff --git a/Plugins/Discord/source/Plugin.Discord/Art/Drag.ico b/Plugins/Discord/source/Plugin.Discord/Art/Drag.ico deleted file mode 100644 index 34f7816a..00000000 Binary files a/Plugins/Discord/source/Plugin.Discord/Art/Drag.ico and /dev/null differ diff --git a/Plugins/Discord/source/Plugin.Discord/Art/Edit.ico b/Plugins/Discord/source/Plugin.Discord/Art/Edit.ico deleted file mode 100644 index fdafc83f..00000000 Binary files a/Plugins/Discord/source/Plugin.Discord/Art/Edit.ico and /dev/null differ diff --git a/Plugins/Discord/source/Plugin.Discord/Art/favicon.ico b/Plugins/Discord/source/Plugin.Discord/Art/favicon.ico deleted file mode 100644 index 1ce0205a..00000000 Binary files a/Plugins/Discord/source/Plugin.Discord/Art/favicon.ico and /dev/null differ diff --git a/Plugins/Discord/source/Plugin.Discord/Config.Designer.cs b/Plugins/Discord/source/Plugin.Discord/Config.Designer.cs deleted file mode 100644 index fc944820..00000000 --- a/Plugins/Discord/source/Plugin.Discord/Config.Designer.cs +++ /dev/null @@ -1,158 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace ServerManagerTool.Plugin.Discord { - - - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.8.0.0")] - internal sealed partial class Config : global::System.Configuration.ApplicationSettingsBase { - - private static Config defaultInstance = ((Config)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Config()))); - - public static Config Default { - get { - return defaultInstance; - } - } - - [global::System.Configuration.ApplicationScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("http://servermanager.azurewebsites.net/api/plugin/call/{0}/{1}/")] - public string PluginCallUrlFormat { - get { - return ((string)(this["PluginCallUrlFormat"])); - } - } - - [global::System.Configuration.ApplicationScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("http://whatismyip.akamai.com/")] - public string PublicIPCheckUrl { - get { - return ((string)(this["PublicIPCheckUrl"])); - } - } - - [global::System.Configuration.ApplicationScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("12")] - public int CallHomeDelay { - get { - return ((int)(this["CallHomeDelay"])); - } - } - - [global::System.Configuration.ApplicationScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("5000")] - public int RequestTimeout { - get { - return ((int)(this["RequestTimeout"])); - } - } - - [global::System.Configuration.ApplicationScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("1B745000-6389-4770-9509-C6A05E209323")] - public string PluginCode { - get { - return ((string)(this["PluginCode"])); - } - } - - [global::System.Configuration.ApplicationScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("Discord Plugin")] - public string PluginName { - get { - return ((string)(this["PluginName"])); - } - } - - [global::System.Configuration.ApplicationScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discor" + - "d/latest.zip")] - public string LatestDownloadUrl { - get { - return ((string)(this["LatestDownloadUrl"])); - } - } - - [global::System.Configuration.ApplicationScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discor" + - "d/latest.txt")] - public string LatestVersionUrl { - get { - return ((string)(this["LatestVersionUrl"])); - } - } - - [global::System.Configuration.ApplicationScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discor" + - "d/beta/latest.zip")] - public string LatestBetaDownloadUrl { - get { - return ((string)(this["LatestBetaDownloadUrl"])); - } - } - - [global::System.Configuration.ApplicationScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discor" + - "d/beta/latest.txt")] - public string LatestBetaVersionUrl { - get { - return ((string)(this["LatestBetaVersionUrl"])); - } - } - - [global::System.Configuration.ApplicationScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("ServerManager.Plugin.Discord.zip")] - public string PluginZipFilename { - get { - return ((string)(this["PluginZipFilename"])); - } - } - - [global::System.Configuration.ApplicationScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("_discordplugin.cfg")] - public string ConfigFile { - get { - return ((string)(this["ConfigFile"])); - } - } - - [global::System.Configuration.ApplicationScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discor" + - "d/VersionFeed.xml")] - public string VersionFeedUrl { - get { - return ((string)(this["VersionFeedUrl"])); - } - } - - [global::System.Configuration.ApplicationScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discor" + - "d/beta/VersionFeed.xml")] - public string VersionBetaFeedUrl { - get { - return ((string)(this["VersionBetaFeedUrl"])); - } - } - } -} diff --git a/Plugins/Discord/source/Plugin.Discord/Config.settings b/Plugins/Discord/source/Plugin.Discord/Config.settings deleted file mode 100644 index e95f2570..00000000 --- a/Plugins/Discord/source/Plugin.Discord/Config.settings +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - http://servermanager.azurewebsites.net/api/plugin/call/{0}/{1}/ - - - http://whatismyip.akamai.com/ - - - 12 - - - 5000 - - - 1B745000-6389-4770-9509-C6A05E209323 - - - Discord Plugin - - - https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discord/latest.zip - - - https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discord/latest.txt - - - https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discord/beta/latest.zip - - - https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discord/beta/latest.txt - - - ServerManager.Plugin.Discord.zip - - - _discordplugin.cfg - - - https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discord/VersionFeed.xml - - - https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/Plugins/Discord/beta/VersionFeed.xml - - - \ No newline at end of file diff --git a/Plugins/Discord/source/Plugin.Discord/DiscordPlugin.cs b/Plugins/Discord/source/Plugin.Discord/DiscordPlugin.cs deleted file mode 100644 index f3d3cb91..00000000 --- a/Plugins/Discord/source/Plugin.Discord/DiscordPlugin.cs +++ /dev/null @@ -1,260 +0,0 @@ -using ServerManagerTool.Plugin.Common; -using ServerManagerTool.Plugin.Discord.Windows; -using System; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Net; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; -using System.Web; -using System.Windows; - -namespace ServerManagerTool.Plugin.Discord -{ - public sealed class DiscordPlugin : IAlertPlugin, IBeta - { - private const int MAX_MESSAGE_LENGTH = 1980; // 2000 minus some formatting characters - - private Object lockObject = new Object(); - - public DiscordPlugin() - { - BetaEnabled = false; - } - - private DiscordPluginConfig PluginConfig - { - get; - set; - } - - public bool BetaEnabled - { - get; - set; - } - - public bool Enabled => true; - - public string PluginCode => Config.Default.PluginCode; - - public string PluginName => Config.Default.PluginName; - - public Version PluginVersion - { - get - { - try - { - return Assembly.GetExecutingAssembly().GetName().Version; - } - catch - { - return new Version(); - } - } - } - - public bool HasConfigForm => true; - - private async Task CallHomeAsync() - { - try - { - var publicIP = await NetworkUtils.DiscoverPublicIPAsync(); - await NetworkUtils.PerformCallToAPIAsync(PluginCode, publicIP); -#if DEBUG - var logFile = Path.Combine(PluginHelper.PluginFolder, "DiscordApiCalls.log"); - File.AppendAllLines(logFile, new[] { "CallHomeAsync successful" }, Encoding.Unicode); -#endif - } - catch (Exception ex) - { - Debug.WriteLine($"Failed calling home {ex.Message}"); -#if DEBUG - var logFile = Path.Combine(PluginHelper.PluginFolder, "DiscordErrors.log"); - File.AppendAllLines(logFile, new[] { $"Failed calling home {ex.Message}" }, Encoding.Unicode); -#endif - } - } - - public void HandleAlert(AlertType alertType, string profileName, string alertMessage) - { - if (string.IsNullOrWhiteSpace(alertMessage)) - return; - - lock (lockObject) - { - var configProfiles = PluginConfig.ConfigProfiles.Where(cp => cp.IsEnabled - && cp.AlertTypes.Any(pn => pn.Value.Equals(alertType)) - && cp.ProfileNames.Any(pn => pn.Value.Equals(profileName, StringComparison.OrdinalIgnoreCase)) - && !string.IsNullOrWhiteSpace(cp.DiscordWebhookUrl)); - if (configProfiles == null || configProfiles.Count() == 0) - { -#if DEBUG - var logFile = Path.Combine(PluginHelper.PluginFolder, "DiscordErrors.log"); - File.AppendAllLines(logFile, new[] { $"{alertType}; {profileName} - {alertMessage.Replace(Environment.NewLine, " ")} (No config profiles found)" }, Encoding.Unicode); -#endif - return; - } - - foreach (var configProfile in configProfiles) - { - HandleAlert(configProfile, alertType, profileName, alertMessage); - } - } - } - - internal void HandleAlert(ConfigProfile configProfile, AlertType alertType, string profileName, string alertMessage) - { - if (configProfile == null || string.IsNullOrWhiteSpace(configProfile.DiscordWebhookUrl) || string.IsNullOrWhiteSpace(alertMessage)) - return; - - // remove any bad characters - var formattedProfileName = profileName?.Replace("&", "_") ?? string.Empty; - var formattedAlertMessage = alertMessage?.Replace("&", "_") ?? string.Empty; - - // check if we need to add the profile name to the message - if (configProfile.PrefixMessageWithProfileName && !string.IsNullOrWhiteSpace(formattedProfileName)) - formattedAlertMessage = $"({formattedProfileName}) {formattedAlertMessage}"; - - // check if the message is too long - if (formattedAlertMessage.Length > MAX_MESSAGE_LENGTH) - formattedAlertMessage = $"{formattedAlertMessage.Substring(0, MAX_MESSAGE_LENGTH - 3)}..."; - - // check if we need to apply any styles to the message - if (configProfile.MessageCodeBlock) - formattedAlertMessage = $"```{formattedAlertMessage}```"; - if (configProfile.MessageBold) - formattedAlertMessage = $"**{formattedAlertMessage}**"; - if (configProfile.MessageItalic) - formattedAlertMessage = $"*{formattedAlertMessage}*"; - if (configProfile.MessageUnderlined) - formattedAlertMessage = $"__{formattedAlertMessage}__"; - formattedAlertMessage = HttpUtility.UrlEncode(formattedAlertMessage); - - var postData = string.Empty; - - if (configProfile.DiscordUseTTS) - postData += $"&tts={configProfile.DiscordUseTTS}"; - if (!string.IsNullOrWhiteSpace(configProfile.DiscordBotName)) - postData += $"&username={configProfile.DiscordBotName.Replace("&", "_")}"; - postData += $"&content={formattedAlertMessage}"; - - try - { - var data = Encoding.UTF8.GetBytes(postData); - - var url = configProfile.DiscordWebhookUrl; - url = url.Trim(); - if (url.EndsWith("/")) - url = url.Substring(0, url.Length - 1); - - var httpRequest = WebRequest.Create($"{url}?wait=true"); - httpRequest.Timeout = Config.Default.RequestTimeout; - httpRequest.Method = "POST"; - httpRequest.ContentType = "application/x-www-form-urlencoded"; - httpRequest.ContentLength = data.Length; - - using (var stream = httpRequest.GetRequestStream()) - { - stream.Write(data, 0, data.Length); - } - - var httpResponse = (HttpWebResponse)httpRequest.GetResponse(); - var responseString = new StreamReader(httpResponse.GetResponseStream()).ReadToEnd(); - if (httpResponse.StatusCode == HttpStatusCode.OK) - { - Debug.WriteLine($"{nameof(HandleAlert)}\r\nResponse: {responseString}"); -#if DEBUG - var logFile = Path.Combine(PluginHelper.PluginFolder, "DiscordSuccess.log"); - File.AppendAllLines(logFile, new[] { $"{alertType}; {profileName} - {alertMessage.Replace(Environment.NewLine, " ")} ({responseString})" }, Encoding.Unicode); -#endif - } - else - { - Debug.WriteLine($"{nameof(HandleAlert)}\r\n{httpResponse.StatusCode}: {responseString}"); -#if DEBUG - var logFile = Path.Combine(PluginHelper.PluginFolder, "DiscordErrors.log"); - File.AppendAllLines(logFile, new[] { $"{alertType}; {profileName} - {alertMessage.Replace(Environment.NewLine, " ")} ({responseString})" }, Encoding.Unicode); -#endif - } - } - catch (Exception ex) - { - Debug.WriteLine($"ERROR: {nameof(HandleAlert)}\r\n{ex.Message}"); -#if DEBUG - var logFile = Path.Combine(PluginHelper.PluginFolder, "DiscordExceptions.log"); - File.AppendAllLines(logFile, new[] { $"{alertType}; {profileName} - {alertMessage.Replace(Environment.NewLine, " ")} ({ex.Message})" }, Encoding.Unicode); -#endif - } - } - - public void Initialize() - { - LoadConfig(); - - if (PluginConfig.LastCallHome.AddHours(Config.Default.CallHomeDelay) < DateTime.Now) - { - //CallHomeAsync().DoNotWait(); - - PluginConfig.LastCallHome = DateTime.Now; - SaveConfig(); - } - } - - private void LoadConfig() - { - try - { - PluginConfig = null; - - var configFile = Path.Combine(PluginHelper.PluginFolder, Config.Default.ConfigFile); - PluginConfig = JsonUtils.DeserializeFromFile(configFile); - - if ((PluginConfig?.ConfigProfiles?.Count ?? 0) == 0) - { - PluginConfig = new DiscordPluginConfig(); - - SaveConfig(); - } - - PluginConfig?.CommitChanges(); - } - catch (Exception ex) - { - PluginConfig = new DiscordPluginConfig(); - Debug.WriteLine($"ERROR: {nameof(LoadConfig)}\r\n{ex.Message}"); - } - } - - public void OpenConfigForm(Window owner) - { - var window = new ConfigWindow(this, this.PluginConfig); - window.Owner = owner; - - var dialogResult = window.ShowDialog(); - if (dialogResult.HasValue && dialogResult.Value) - { - SaveConfig(); - LoadConfig(); - } - } - - private void SaveConfig() - { - try - { - var configFile = Path.Combine(PluginHelper.PluginFolder, Config.Default.ConfigFile); - JsonUtils.SerializeToFile(PluginConfig, configFile); - PluginConfig?.CommitChanges(); - } - catch (Exception ex) - { - Debug.WriteLine($"ERROR: {nameof(SaveConfig)}\r\n{ex.Message}"); - } - } - } -} diff --git a/Plugins/Discord/source/Plugin.Discord/Globalization/en-US/en-US.xaml b/Plugins/Discord/source/Plugin.Discord/Globalization/en-US/en-US.xaml deleted file mode 100644 index 7e86cb25..00000000 --- a/Plugins/Discord/source/Plugin.Discord/Globalization/en-US/en-US.xaml +++ /dev/null @@ -1,119 +0,0 @@ - - - - Cancel - Close - OK - Save - - (Beta Mode) - A new version of the plugin is available. - Download the latest version. - - - - Discord Plugin Configuration - - View the Patch Notes. - Add Profile - Delete All Profiles - Delete Profile - Edit Profile - - Name - - Add Action Error - An error occurred while trying to add a profile. - Confirm Delete All Action - Click 'Yes' to confirm you want to delete all. - Delete All Action Error - An error occurred while trying to delete all profiles. - Confirm Close Action - Click 'Yes' to confirm you want to close the form. If you have any unsaved changes they will be lost. - Confirm Delete Action - Click 'Yes' to confirm you want to perform the delete. - Delete Action Error - An error occurred while trying to delete the profile. - Download Action Error - An error occurred while trying to perform the download. - Download Successful - The latest version has been saved to your desktop. - Edit Action Error - An error occurred while trying to edit a profile. - Save Action Error - An error occurred while trying to perform the save. - - - - Profile Configuration - - Name: - The name of your config profile. - Enabled - Is the config profile enabled. - Webhook Url: - This is your Webhook Url provided by discord. - Bot Name: - This will override the name you setup in your webhook. Leave blank to use default. - Use Text to Speech (TTS) - If enabled, will make the bot announce with Text to Speech, otherwise will turn off Text to Speech. - Prefix Message with Profile Name - If enabled, the alert message will be sent prefixed with the server manager profile name. - Message Options - Bold - If enabled, will show the message in bold. - Underline - If enabled, will show the message with an underline. - Italic - If enabled, will show the message in italic. - Embedded Code Block - CIf enabled, will show the message in an embedded code block. - Profile Names - Alert Types - - Add Server Manager Profile Name - Delete All Server Manager Profile Names - Delete Server Manager Profile Name - - Add Alert Type - Delete All Alert Types - Delete Alert Type - - Server Manager Profile Name - Alert Type - - Test - - Add Action Error - An error occurred while trying to add an alert type. - An error occurred while trying to add a profile name. - Confirm Delete All Action - Click 'Yes' to confirm you want to delete all. - Delete All Action Error - An error occurred while trying to delete all alert types. - An error occurred while trying to delete all profile names. - Confirm Close Action - Click 'Yes' to confirm you want to close the form. If you have any unsaved changes they will be lost. - Confirm Delete Action - Click 'Yes' to confirm you want to perform the delete. - Delete Action Error - An error occurred while trying to delete the alert type. - An error occurred while trying to delete the profile name. - Test Action Error - An error occurred while trying to test the config profile. - The profile is not enabled and cannot be tested. - - - - Discord Plugin Version Details - Load Feed Error - - Version: - Select the version to view details. - - - \ No newline at end of file diff --git a/Plugins/Discord/source/Plugin.Discord/Interfaces/IBindable.cs b/Plugins/Discord/source/Plugin.Discord/Interfaces/IBindable.cs deleted file mode 100644 index 2978db18..00000000 --- a/Plugins/Discord/source/Plugin.Discord/Interfaces/IBindable.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace ServerManagerTool.Plugin.Discord -{ - internal interface IBindable - { - bool HasChanges { get; set; } - - bool HasAnyChanges { get; } - - void CommitChanges(); - - void BeginUpdate(); - - void EndUpdate(); - } -} diff --git a/Plugins/Discord/source/Plugin.Discord/Lib/BrowserBehavior.cs b/Plugins/Discord/source/Plugin.Discord/Lib/BrowserBehavior.cs deleted file mode 100644 index 6b8e2099..00000000 --- a/Plugins/Discord/source/Plugin.Discord/Lib/BrowserBehavior.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Windows; -using System.Windows.Controls; - -namespace ServerManagerTool.Plugin.Discord -{ - public static class BrowserBehavior - { - public static readonly DependencyProperty HtmlProperty = DependencyProperty.RegisterAttached("Html", typeof(string), typeof(BrowserBehavior), new FrameworkPropertyMetadata(OnHtmlChanged)); - - [AttachedPropertyBrowsableForType(typeof(WebBrowser))] - public static string GetHtml(WebBrowser d) - { - return (string)d.GetValue(HtmlProperty); - } - - public static void SetHtml(WebBrowser d, string value) - { - d.SetValue(HtmlProperty, value); - } - - static void OnHtmlChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - if (e.NewValue == null) - return; - - if (d is WebBrowser wb) - { - wb.NavigateToString(e.NewValue as string); - } - } - } -} diff --git a/Plugins/Discord/source/Plugin.Discord/Models/AlertTypeValue.cs b/Plugins/Discord/source/Plugin.Discord/Models/AlertTypeValue.cs deleted file mode 100644 index 5d408bd0..00000000 --- a/Plugins/Discord/source/Plugin.Discord/Models/AlertTypeValue.cs +++ /dev/null @@ -1,55 +0,0 @@ -using ServerManagerTool.Plugin.Common; -using System.Runtime.Serialization; - -namespace ServerManagerTool.Plugin.Discord -{ - [DataContract] - internal class AlertTypeValue : Bindable - { - public AlertTypeValue() - : base() - { - Value = AlertType.Error; - OriginalValue = Value; - HasChanges = false; - } - - public AlertTypeValue(AlertType value) - : base() - { - Value = value; - OriginalValue = Value; - HasChanges = !Value.Equals(OriginalValue); - } - - public AlertTypeValue(AlertType value, AlertType originalValue) - : base() - { - Value = value; - OriginalValue = originalValue; - HasChanges = !Value.Equals(OriginalValue); - } - - [DataMember] - public AlertType Value - { - get { return Get(); } - set { Set(value); } - } - - public AlertType OriginalValue - { - get; - set; - } - - public override bool HasAnyChanges => base.HasChanges && !Value.Equals(OriginalValue); - - public override void CommitChanges() - { - base.CommitChanges(); - - OriginalValue = Value; - } - } -} diff --git a/Plugins/Discord/source/Plugin.Discord/Models/AlertTypeValueList.cs b/Plugins/Discord/source/Plugin.Discord/Models/AlertTypeValueList.cs deleted file mode 100644 index bbe47fdf..00000000 --- a/Plugins/Discord/source/Plugin.Discord/Models/AlertTypeValueList.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System.Collections.Generic; -using System.Collections.Specialized; -using System.ComponentModel; -using System.Linq; -using System.Runtime.CompilerServices; - -namespace ServerManagerTool.Plugin.Discord -{ - internal class AlertTypeValueList : List, IBindable, INotifyCollectionChanged - { - private bool _hasChanges = false; - - public bool HasChanges - { - get => _hasChanges; - set => _hasChanges = value; - } - - public bool HasAnyChanges => _hasChanges || this.Any(a => a?.HasAnyChanges ?? false); - - public void BeginUpdate() - { - } - - public void CommitChanges() - { - HasChanges = false; - - foreach (var alertType in this) - { - alertType.CommitChanges(); - } - } - - public void EndUpdate() - { - } - - #region INotifyCollectionChanged - public event NotifyCollectionChangedEventHandler CollectionChanged; - #endregion - - protected void OnCollectionChanged(NotifyCollectionChangedEventArgs e) - { - CollectionChanged?.Invoke(this, e); - } - - public void NotifyAdd(AlertTypeValue item, bool setChanged = true) - { - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item)); - if (setChanged) - HasChanges = true; - } - - public void NotifyClear(bool setChanged = true) - { - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); - if (setChanged) - HasChanges = true; - } - - public void NotifyRemove(AlertTypeValue item, int index, bool setChanged = true) - { - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index)); - if (setChanged) - HasChanges = true; - } - } -} diff --git a/Plugins/Discord/source/Plugin.Discord/Models/Bindable.cs b/Plugins/Discord/source/Plugin.Discord/Models/Bindable.cs deleted file mode 100644 index 8b12e9f4..00000000 --- a/Plugins/Discord/source/Plugin.Discord/Models/Bindable.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Runtime.Serialization; - -namespace ServerManagerTool.Plugin.Discord -{ - [DataContract] - internal class Bindable : INotifyPropertyChanged, IBindable - { - private Dictionary _properties = new Dictionary(); - - protected bool _isUpdating = false; - - public Bindable() - { - _properties = new Dictionary(); - } - - protected T Get([CallerMemberName] string name = null) - { - object value = null; - if (_properties?.TryGetValue(name, out value) ?? false) - return value == null ? default(T) : (T)value; - return default(T); - } - - protected void Set(T value, bool setChanged = true, [CallerMemberName] string name = null) - { - if (Equals(value, Get(name))) - return; - if (_properties == null) - _properties = new Dictionary(); - _properties[name] = value; - OnPropertyChanged(name); - if (!_isUpdating && setChanged) - HasChanges = true; - } - - protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - - #region INotifyPropertyChanged - public event PropertyChangedEventHandler PropertyChanged; - #endregion - - #region IBindable - public bool HasChanges - { - get { return Get(); } - set { Set(value, false); } - } - - public virtual bool HasAnyChanges => HasChanges || (_properties?.Any(p => (p.Value as IBindable)?.HasAnyChanges ?? false) ?? false); - - public virtual void CommitChanges() - { - HasChanges = false; - - if (_properties == null) - return; - - foreach (var property in _properties) - { - var bindable = property.Value as IBindable; - if (bindable != null) - bindable.CommitChanges(); - } - } - - public void BeginUpdate() - { - _isUpdating = true; - } - - public void EndUpdate() - { - _isUpdating = false; - } - #endregion - } -} diff --git a/Plugins/Discord/source/Plugin.Discord/Models/ComboBoxItem.cs b/Plugins/Discord/source/Plugin.Discord/Models/ComboBoxItem.cs deleted file mode 100644 index 8cc71d5d..00000000 --- a/Plugins/Discord/source/Plugin.Discord/Models/ComboBoxItem.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Windows; - -namespace ServerManagerTool.Plugin.Discord -{ - public class ComboBoxItem : DependencyObject - { - public static readonly DependencyProperty ValueMemberProperty = DependencyProperty.Register(nameof(ValueMember), typeof(string), typeof(ComboBoxItem), new PropertyMetadata(string.Empty)); - public static readonly DependencyProperty DisplayMemberProperty = DependencyProperty.Register(nameof(DisplayMember), typeof(string), typeof(ComboBoxItem), new PropertyMetadata(string.Empty)); - public static readonly DependencyProperty GroupMemberProperty = DependencyProperty.Register(nameof(GroupMember), typeof(string), typeof(ComboBoxItem), new PropertyMetadata(string.Empty)); - - public ComboBoxItem() - { - } - - public ComboBoxItem(string valueMember, string displayMember) - { - ValueMember = valueMember; - DisplayMember = displayMember; - } - - public ComboBoxItem(string valueMember, string displayMember, string groupMember) - { - ValueMember = valueMember; - DisplayMember = displayMember; - GroupMember = groupMember; - } - - public string ValueMember - { - get { return (string)GetValue(ValueMemberProperty); } - set { SetValue(ValueMemberProperty, value); } - } - - public string DisplayMember - { - get { return (string)GetValue(DisplayMemberProperty); } - set { SetValue(DisplayMemberProperty, value); } - } - - public string GroupMember - { - get { return (string)GetValue(GroupMemberProperty); } - set { SetValue(GroupMemberProperty, value); } - } - - public ComboBoxItem Duplicate() - { - return new ComboBoxItem - { - DisplayMember = this.DisplayMember, - ValueMember = this.ValueMember, - GroupMember = this.GroupMember, - }; - } - } -} diff --git a/Plugins/Discord/source/Plugin.Discord/Models/ComboBoxItemList.cs b/Plugins/Discord/source/Plugin.Discord/Models/ComboBoxItemList.cs deleted file mode 100644 index eb0c490c..00000000 --- a/Plugins/Discord/source/Plugin.Discord/Models/ComboBoxItemList.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.ObjectModel; - -namespace ServerManagerTool.Plugin.Discord -{ - public class ComboBoxItemList : ObservableCollection - { - public ComboBoxItemList() - { - } - } -} diff --git a/Plugins/Discord/source/Plugin.Discord/Models/ConfigProfile.cs b/Plugins/Discord/source/Plugin.Discord/Models/ConfigProfile.cs deleted file mode 100644 index 2dfd109d..00000000 --- a/Plugins/Discord/source/Plugin.Discord/Models/ConfigProfile.cs +++ /dev/null @@ -1,188 +0,0 @@ -using System.Runtime.Serialization; - -namespace ServerManagerTool.Plugin.Discord -{ - [DataContract] - internal sealed class ConfigProfile : Bindable - { - public ConfigProfile() - : base() - { - Name = "New Discord Profile"; - ProfileNames = new ProfileNameValueList(); - AlertTypes = new AlertTypeValueList(); - DiscordWebhookUrl = string.Empty; - DiscordBotName = string.Empty; - DiscordUseTTS = false; - PrefixMessageWithProfileName = false; - MessageBold = false; - MessageUnderlined = false; - MessageItalic = false; - MessageCodeBlock = false; - IsEnabled = true; - } - - [DataMember] - public string Name - { - get { return Get(); } - set { Set(value); } - } - - [DataMember] - public ProfileNameValueList ProfileNames - { - get { return Get(); } - set { Set(value); } - } - - [DataMember] - public AlertTypeValueList AlertTypes - { - get { return Get(); } - set { Set(value); } - } - - [DataMember] - public string DiscordWebhookUrl - { - get { return Get(); } - set { Set(value); } - } - - [DataMember] - public string DiscordBotName - { - get { return Get(); } - set { Set(value); } - } - - [DataMember] - public bool DiscordUseTTS - { - get { return Get(); } - set { Set(value); } - } - - [DataMember] - public bool PrefixMessageWithProfileName - { - get { return Get(); } - set { Set(value); } - } - - [DataMember] - public bool IsEnabled - { - get { return Get(); } - set { Set(value); } - } - - [DataMember] - public bool MessageBold - { - get { return Get(); } - set { Set(value); } - } - - [DataMember] - public bool MessageUnderlined - { - get { return Get(); } - set { Set(value); } - } - - [DataMember] - public bool MessageItalic - { - get { return Get(); } - set { Set(value); } - } - - [DataMember] - public bool MessageCodeBlock - { - get { return Get(); } - set { Set(value); } - } - - public ConfigProfile Clone() - { - var clone = new ConfigProfile(); - clone.Name = this.Name; - - foreach (var profileName in this.ProfileNames) - { - clone.ProfileNames.Add(new ProfileNameValue(profileName.Value, profileName.OriginalValue) { HasChanges = profileName.HasChanges }); - } - clone.ProfileNames.HasChanges = this.ProfileNames.HasChanges; - foreach (var alertType in this.AlertTypes) - { - clone.AlertTypes.Add(new AlertTypeValue(alertType.Value, alertType.OriginalValue) { HasChanges = alertType.HasChanges }); - } - clone.AlertTypes.HasChanges = this.AlertTypes.HasChanges; - - clone.DiscordWebhookUrl = this.DiscordWebhookUrl; - clone.DiscordBotName = this.DiscordBotName; - clone.DiscordUseTTS = this.DiscordUseTTS; - clone.PrefixMessageWithProfileName = this.PrefixMessageWithProfileName; - clone.MessageBold = this.MessageBold; - clone.MessageUnderlined = this.MessageUnderlined; - clone.MessageItalic = this.MessageItalic; - clone.MessageCodeBlock = this.MessageCodeBlock; - clone.IsEnabled = this.IsEnabled; - clone.HasChanges = this.HasChanges; - return clone; - } - - public void CopyFrom(ConfigProfile source) - { - if (source == null) - return; - - try - { - this.BeginUpdate(); - - this.Name = source.Name; - - this.ProfileNames.BeginUpdate(); - this.ProfileNames.Clear(); - foreach (var profileName in source.ProfileNames) - { - this.ProfileNames.Add(new ProfileNameValue(profileName.Value, profileName.OriginalValue)); - } - if (source.ProfileNames.HasChanges) - this.ProfileNames.HasChanges = true; - this.ProfileNames.EndUpdate(); - - this.AlertTypes.BeginUpdate(); - this.AlertTypes.Clear(); - foreach (var alertType in source.AlertTypes) - { - this.AlertTypes.Add(new AlertTypeValue(alertType.Value, alertType.OriginalValue)); - } - if (source.AlertTypes.HasChanges) - this.AlertTypes.HasChanges = true; - this.AlertTypes.EndUpdate(); - - this.DiscordWebhookUrl = source.DiscordWebhookUrl; - this.DiscordBotName = source.DiscordBotName; - this.DiscordUseTTS = source.DiscordUseTTS; - this.PrefixMessageWithProfileName = source.PrefixMessageWithProfileName; - this.MessageBold = source.MessageBold; - this.MessageUnderlined = source.MessageUnderlined; - this.MessageItalic = source.MessageItalic; - this.MessageCodeBlock = source.MessageCodeBlock; - this.IsEnabled = source.IsEnabled; - - if (source.HasChanges) - this.HasChanges = true; - } - finally - { - this.EndUpdate(); - } - } - } -} diff --git a/Plugins/Discord/source/Plugin.Discord/Models/DiscordPluginConfig.cs b/Plugins/Discord/source/Plugin.Discord/Models/DiscordPluginConfig.cs deleted file mode 100644 index b0705f91..00000000 --- a/Plugins/Discord/source/Plugin.Discord/Models/DiscordPluginConfig.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace ServerManagerTool.Plugin.Discord -{ - [DataContract] - internal sealed class DiscordPluginConfig : Bindable - { - public DiscordPluginConfig() - : base() - { - LastCallHome = DateTime.MinValue; - ConfigProfiles = new ObservableList(); - } - - [DataMember] - public DateTime LastCallHome - { - get; - set; - } - - [DataMember] - public ObservableList ConfigProfiles - { - get { return Get>(); } - set { Set(value); } - } - - public override bool HasAnyChanges - { - get => base.HasChanges || (ConfigProfiles?.HasAnyChanges ?? false); - } - } -} diff --git a/Plugins/Discord/source/Plugin.Discord/Models/ObservableList.cs b/Plugins/Discord/source/Plugin.Discord/Models/ObservableList.cs deleted file mode 100644 index 6f7e922b..00000000 --- a/Plugins/Discord/source/Plugin.Discord/Models/ObservableList.cs +++ /dev/null @@ -1,142 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Linq; -using System.Runtime.Serialization; - -namespace ServerManagerTool.Plugin.Discord -{ - [DataContract] - internal class ObservableList : Bindable, IList, INotifyCollectionChanged - { - private List _listObject = null; - - public ObservableList() - : base() - { - _listObject = new List(); - CommitChanges(); - } - - public override bool HasAnyChanges => base.HasChanges || (_listObject?.Any(i => (i as IBindable)?.HasAnyChanges ?? false) ?? false); - - public override void CommitChanges() - { - base.CommitChanges(); - - if (_listObject == null) - return; - - foreach (T item in _listObject) - { - var bindable = item as IBindable; - if (bindable != null) - bindable.CommitChanges(); - } - } - - [DataMember] - internal List List - { - get => _listObject; - set => _listObject = value; - } - - #region IList - public T this[int index] - { - get => _listObject[index]; - set - { - T oldValue = _listObject[index]; - _listObject[index] = value; - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, oldValue)); - } - } - - public int Count => _listObject.Count; - - public bool IsReadOnly => false; - - public void Add(T item) - { - _listObject.Add(item); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item)); - } - - public void Clear() - { - _listObject.Clear(); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); - } - - public bool Contains(T item) - { - return _listObject.Contains(item); - } - - public void CopyTo(T[] array, int arrayIndex) - { - _listObject.CopyTo(array, arrayIndex); - } - - public IEnumerator GetEnumerator() - { - return _listObject.GetEnumerator(); - } - - public int IndexOf(T item) - { - return _listObject.IndexOf(item); - } - - public void Insert(int index, T item) - { - _listObject.Insert(index, item); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item)); - } - - public void Move(int oldIndex, int newIndex) - { - var item = _listObject.ElementAt(oldIndex); - if (item != null) - { - _listObject.Remove(item); - _listObject.Insert(newIndex, item); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Move, item, newIndex, oldIndex)); - } - } - - public bool Remove(T item) - { - int index = _listObject.IndexOf(item); - var result = _listObject.Remove(item); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index)); - return result; - } - - public void RemoveAt(int index) - { - T item = _listObject[index]; - _listObject.RemoveAt(index); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index)); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return _listObject.GetEnumerator(); - } - #endregion - - #region INotifyCollectionChanged - public event NotifyCollectionChangedEventHandler CollectionChanged; - - protected void OnCollectionChanged(NotifyCollectionChangedEventArgs e) - { - CollectionChanged?.Invoke(this, e); - if (!_isUpdating) - HasChanges = true; - } - #endregion - } -} diff --git a/Plugins/Discord/source/Plugin.Discord/Models/ProfileNameValue.cs b/Plugins/Discord/source/Plugin.Discord/Models/ProfileNameValue.cs deleted file mode 100644 index e505f555..00000000 --- a/Plugins/Discord/source/Plugin.Discord/Models/ProfileNameValue.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Runtime.Serialization; - -namespace ServerManagerTool.Plugin.Discord -{ - [DataContract] - internal class ProfileNameValue : Bindable - { - public ProfileNameValue() - : base() - { - Value = string.Empty; - OriginalValue = Value; - HasChanges = false; - } - - public ProfileNameValue(string value) - : base() - { - Value = value; - OriginalValue = Value; - HasChanges = !Value.Equals(OriginalValue); - } - - public ProfileNameValue(string value, string originalValue) - : base() - { - Value = value; - OriginalValue = originalValue; - HasChanges = !Value.Equals(OriginalValue); - } - - [DataMember] - public string Value - { - get { return Get(); } - set { Set(value); } - } - - public string OriginalValue - { - get; - set; - } - - public override bool HasAnyChanges => base.HasChanges && !Value.Equals(OriginalValue); - - public override void CommitChanges() - { - base.CommitChanges(); - - OriginalValue = Value; - } - } -} diff --git a/Plugins/Discord/source/Plugin.Discord/Models/ProfileNameValueList.cs b/Plugins/Discord/source/Plugin.Discord/Models/ProfileNameValueList.cs deleted file mode 100644 index 9101cabb..00000000 --- a/Plugins/Discord/source/Plugin.Discord/Models/ProfileNameValueList.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Linq; - -namespace ServerManagerTool.Plugin.Discord -{ - internal class ProfileNameValueList : List, IBindable, INotifyCollectionChanged - { - private bool _hasChanges = false; - - public bool HasChanges - { - get => _hasChanges; - set => _hasChanges = value; - } - - public bool HasAnyChanges => _hasChanges || this.Any(p => p?.HasAnyChanges ?? false); - - public void BeginUpdate() - { - } - - public void CommitChanges() - { - HasChanges = false; - - foreach (var profileName in this) - { - profileName.CommitChanges(); - } - } - - public void EndUpdate() - { - } - - #region INotifyCollectionChanged - public event NotifyCollectionChangedEventHandler CollectionChanged; - #endregion - - protected void OnCollectionChanged(NotifyCollectionChangedEventArgs e) - { - CollectionChanged?.Invoke(this, e); - } - - public void NotifyAdd(ProfileNameValue item, bool setChanged = true) - { - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item)); - if (setChanged) - HasChanges = true; - } - - public void NotifyClear(bool setChanged = true) - { - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); - if (setChanged) - HasChanges = true; - } - - public void NotifyRemove(ProfileNameValue item, int index, bool setChanged = true) - { - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index)); - if (setChanged) - HasChanges = true; - } - } -} diff --git a/Plugins/Discord/source/Plugin.Discord/Models/VersionFeed.cs b/Plugins/Discord/source/Plugin.Discord/Models/VersionFeed.cs deleted file mode 100644 index db41f723..00000000 --- a/Plugins/Discord/source/Plugin.Discord/Models/VersionFeed.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace ServerManagerTool.Plugin.Discord -{ - public class VersionFeed - { - public string Id { get; set; } = string.Empty; - public string Title { get; set; } = string.Empty; - public string SubTitle { get; set; } = string.Empty; - public Uri Link { get; set; } = null; - public DateTimeOffset Updated { get; set; } = DateTimeOffset.Now; - - public List Entries { get; set; } = new List(); - } -} diff --git a/Plugins/Discord/source/Plugin.Discord/Models/VersionFeedEntry.cs b/Plugins/Discord/source/Plugin.Discord/Models/VersionFeedEntry.cs deleted file mode 100644 index 6883a4fd..00000000 --- a/Plugins/Discord/source/Plugin.Discord/Models/VersionFeedEntry.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; - -namespace ServerManagerTool.Plugin.Discord -{ - public class VersionFeedEntry - { - public string Id { get; set; } = string.Empty; - public string Title { get; set; } = string.Empty; - public string Summary { get; set; } = string.Empty; - public Uri Link { get; set; } = null; - public DateTimeOffset Updated { get; set; } = DateTimeOffset.Now; - public string Content { get; set; } = string.Empty; - public string Author { get; set; } = string.Empty; - - public bool IsCurrent { get; set; } = false; - } -} diff --git a/Plugins/Discord/source/Plugin.Discord/Plugin.Discord.csproj b/Plugins/Discord/source/Plugin.Discord/Plugin.Discord.csproj deleted file mode 100644 index 72a4460e..00000000 --- a/Plugins/Discord/source/Plugin.Discord/Plugin.Discord.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - %24/Development/ServerManagers/Main/Plugin.Discord - {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C} - https://dev.azure.com/bretthewitson - . - - - net462 - false - Art\favicon.ico - ServerManager.Plugin.Discord - ServerManagerTool.Plugin.Discord - - - none - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - True - True - Config.settings - - - - - SettingsSingleFileGenerator - Config.Designer.cs - - - \ No newline at end of file diff --git a/Plugins/Discord/source/Plugin.Discord/Properties/AssemblyInfo.cs b/Plugins/Discord/source/Plugin.Discord/Properties/AssemblyInfo.cs deleted file mode 100644 index d7975adb..00000000 --- a/Plugins/Discord/source/Plugin.Discord/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,34 +0,0 @@ -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("ServerManager Discord Plugin")] -[assembly: AssemblyDescription("A Discord plugin that can be used with the server managers.")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Bletch1971")] -[assembly: AssemblyProduct("Server Managers")] -[assembly: AssemblyCopyright("Copyright © 2015-2020")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("936ef260-fecf-4e9e-a21e-092d65931c7d")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.17.2")] -[assembly: AssemblyFileVersion("1.0.17.2")] diff --git a/Plugins/Discord/source/Plugin.Discord/Utils/NetworkUtils.cs b/Plugins/Discord/source/Plugin.Discord/Utils/NetworkUtils.cs deleted file mode 100644 index 0590e7d4..00000000 --- a/Plugins/Discord/source/Plugin.Discord/Utils/NetworkUtils.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.Diagnostics; -using System.Net; -using System.Threading.Tasks; - -namespace ServerManagerTool.Plugin.Discord -{ - internal static class NetworkUtils - { - public static async Task DiscoverPublicIPAsync() - { - using (var webClient = new WebClient()) - { - try - { - var publicIP = await webClient.DownloadStringTaskAsync(Config.Default.PublicIPCheckUrl); - - if (IPAddress.TryParse(publicIP, out IPAddress address)) - return address; - - return IPAddress.None; - } - catch (Exception ex) - { - Debug.WriteLine($"ERROR: {nameof(DiscoverPublicIPAsync)}\r\n{ex.Message}"); - return IPAddress.None; - } - } - } - - public static async Task CheckLatestVersionAsync(bool betaEnabled) - { - try - { - using (var webClient = new WebClient()) - { - string latestVersion = null; - - if (betaEnabled) - latestVersion = await webClient.DownloadStringTaskAsync(Config.Default.LatestBetaVersionUrl); - else - latestVersion = await webClient.DownloadStringTaskAsync(Config.Default.LatestVersionUrl); - - if (Version.TryParse(latestVersion, out Version version)) - return version; - - return new Version(); - } - } - catch (Exception ex) - { - Debug.WriteLine($"ERROR: {nameof(CheckLatestVersionAsync)}\r\n{ex.Message}"); - return new Version(); - } - } - - public static bool DownloadLatestVersion(string sourceUrl, string destinationFile) - { - try - { - using (var client = new WebClient()) - { - client.DownloadFile(sourceUrl, destinationFile); - return true; - } - } - catch (Exception ex) - { - Debug.WriteLine($"ERROR: {nameof(DownloadLatestVersion)}\r\n{ex.Message}"); - return false; - } - } - - public static async Task PerformCallToAPIAsync(string pluginCode, IPAddress ipAddress) - { - try - { - using (var client = new WebClient()) - { - var url = string.Format(Config.Default.PluginCallUrlFormat, pluginCode, ipAddress); - await client.DownloadStringTaskAsync(url); - } - } - catch (Exception ex) - { - Debug.WriteLine($"ERROR: {nameof(PerformCallToAPIAsync)} - {pluginCode}; {ipAddress}\r\n{ex.Message}"); - } - } - } -} diff --git a/Plugins/Discord/source/Plugin.Discord/Utils/TaskUtils.cs b/Plugins/Discord/source/Plugin.Discord/Utils/TaskUtils.cs deleted file mode 100644 index 52439873..00000000 --- a/Plugins/Discord/source/Plugin.Discord/Utils/TaskUtils.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Threading.Tasks; - -namespace ServerManagerTool.Plugin.Discord -{ - internal static class TaskUtils - { - public static readonly Task FinishedTask = Task.FromResult(true); - - public static void DoNotWait(this Task task) - { - // Do nothing, let the task continue. Eliminates compiler warning about non-awaited tasks in an async method. - } - } -} diff --git a/Plugins/Discord/source/Plugin.Discord/Utils/VersionFeedUtils.cs b/Plugins/Discord/source/Plugin.Discord/Utils/VersionFeedUtils.cs deleted file mode 100644 index 3059e059..00000000 --- a/Plugins/Discord/source/Plugin.Discord/Utils/VersionFeedUtils.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.ServiceModel.Syndication; -using System.Xml; - -namespace ServerManagerTool.Plugin.Discord -{ - public static class VersionFeedUtils - { - public static VersionFeed LoadVersionFeed(string inputUri, string currentVersion) - { - try - { - var reader = XmlReader.Create(inputUri); - var feed = SyndicationFeed.Load(reader); - - var versionFeed = new VersionFeed - { - Id = feed.Id, - Title = feed.Title?.Text, - SubTitle = feed.Description?.Text, - Link = feed.Links?[0].Uri, - Updated = feed.LastUpdatedTime.ToLocalTime(), - }; - - //Loop through all items in the SyndicationFeed - foreach (var item in feed.Items) - { - var textContent = item.Content as TextSyndicationContent; - - var versionFeedEntry = new VersionFeedEntry - { - Id = item.Id, - Title = item.Title?.Text, - Summary = item.Summary?.Text, - Link = item.Links?[0].Uri, - Updated = item.LastUpdatedTime.ToLocalTime(), - Content = textContent?.Text, - Author = item.Authors?[0].Name, - - IsCurrent = (item.Summary?.Text ?? string.Empty).Equals(currentVersion), - }; - versionFeed.Entries.Add(versionFeedEntry); - } - - return versionFeed; - } - catch (Exception) - { - return new VersionFeed(); - } - } - } -} diff --git a/Plugins/Discord/source/Plugin.Discord/Utils/WindowUtils.cs b/Plugins/Discord/source/Plugin.Discord/Utils/WindowUtils.cs deleted file mode 100644 index 6c765945..00000000 --- a/Plugins/Discord/source/Plugin.Discord/Utils/WindowUtils.cs +++ /dev/null @@ -1,122 +0,0 @@ -using System.Linq; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Media; - -namespace ServerManagerTool.Plugin.Discord -{ - public static class WindowUtils - { - public static void RemoveDefaultResourceDictionary(Window window, string defaultDictionary) - { - if (window == null) - return; - - var dictToRemove = window.Resources.MergedDictionaries.FirstOrDefault(d => d.Source.OriginalString.Contains(defaultDictionary)); - if (dictToRemove != null) - { - window.Resources.MergedDictionaries.Remove(dictToRemove); - } - } - - public static void RemoveDefaultResourceDictionary(UserControl control, string defaultDictionary) - { - if (control == null) - return; - - var dictToRemove = control.Resources.MergedDictionaries.FirstOrDefault(d => d.Source.OriginalString.Contains(defaultDictionary)); - if (dictToRemove != null) - { - control.Resources.MergedDictionaries.Remove(dictToRemove); - } - } - - /// - /// Finds a parent of a given item on the visual tree. - /// - /// The type of the queried item. - /// A direct or indirect child of the queried item. - /// The first parent item that matches the submitted type parameter. If not matching item can be found, a null reference is being returned. - public static T TryFindParent(DependencyObject child) - where T : DependencyObject - { - //get parent item - DependencyObject parentObject = GetParentObject(child); - - //we've reached the end of the tree - if (parentObject == null) return null; - - //check if the parent matches the type we're looking for - T parent = parentObject as T; - if (parent != null) - return parent; - - //use recursion to proceed with next level - return TryFindParent(parentObject); - } - - /// - /// This method is an alternative to WPF's method, which also supports content elements. - /// Do note, that for content element, this method falls back to the logical tree of the element. - /// - /// The item to be processed. - /// The submitted item's parent, if available. Otherwise null. - public static DependencyObject GetParentObject(DependencyObject child) - { - if (child == null) return null; - var contentElement = child as ContentElement; - - if (contentElement != null) - { - var parent = ContentOperations.GetParent(contentElement); - if (parent != null) return parent; - - var fce = contentElement as FrameworkContentElement; - return fce?.Parent; - } - - //if it's not a ContentElement, rely on VisualTreeHelper - return VisualTreeHelper.GetParent(child); - } - - /// - /// Recursively processes a given dependency object and all its children, and updates sources of all objects that use a binding expression on a given property. - /// - /// The dependency object that marks a starting point. This could be a dialog window or a panel control that hosts bound controls. - /// The properties to be updated if - /// or one of its childs provide it along with a binding expression. - public static void UpdateBindingSources(DependencyObject obj, params DependencyProperty[] properties) - { - foreach (var depProperty in properties) - { - //check whether the submitted object provides a bound property that matches the property parameters - var be = BindingOperations.GetBindingExpression(obj, depProperty); - be?.UpdateSource(); - } - - int count = VisualTreeHelper.GetChildrenCount(obj); - for (int i = 0; i < count; i++) - { - //process child items recursively - var childObject = VisualTreeHelper.GetChild(obj, i); - UpdateBindingSources(childObject, properties); - } - } - - /// - /// Tries to locate a given item within the visual tree, starting with the dependency object at a given position. - /// - /// The type of the element to be found on the visual tree of the element at the given location. - /// The main element which is used to perform hit testing. - /// The position to be evaluated on the origin. - public static T TryFindFromPoint(UIElement reference, Point point) - where T : DependencyObject - { - var element = reference.InputHitTest(point) as DependencyObject; - if (element == null) return null; - if (element is T) return (T)element; - return TryFindParent(element); - } - } -} diff --git a/Plugins/Discord/source/Plugin.Discord/VersionFeed.xml b/Plugins/Discord/source/Plugin.Discord/VersionFeed.xml deleted file mode 100644 index 82e77d98..00000000 --- a/Plugins/Discord/source/Plugin.Discord/VersionFeed.xml +++ /dev/null @@ -1,316 +0,0 @@ - - - - urn:uuid:C93B24FC-C8DB-44F3-8B6F-A27A52E7AF9F - Discord Plugin Version Feed - This is the Discord Plugin release version feed. - - 2020-07-13T00:00:00Z - - - urn:uuid:D8974ABF-8444-4D40-A594-D4443921B3B8 - 1.0.17 (1.0.17.2) - 1.0.17.2 - - 2020-07-13T00:00:00Z - -
-

- CHANGE -
-

    -
  • Profile name has been converted to a droplist and is now populated with the Server Manager Profile Names for selection.
  • -
  • Added drag/drop feature to the config window.
  • -
-

-
-
- - bletch - bletch1971@hotmail.com - -
- - - urn:uuid:A7158CB1-C5B2-4505-B171-CDD54918B227 - 1.0.16 (1.0.16.1) - 1.0.16.1 - - 2020-06-12T00:00:01Z - -
-

- BUGFIX -
-

    -
  • Added url encoding so that special characters are sent to the webhook correctly.
  • -
  • Changed the text encoding from ASCII to UTF8, so that unicode languages should work correctly.
  • -
-

-
-
- - bletch - bletch1971@hotmail.com - -
- - - urn:uuid:6ADCC571-3933-44E1-91FB-4DC8F8B836F2 - 1.0.15 (1.0.15.1) - 1.0.15.1 - - 2020-03-03T00:00:01Z - -
-

- CHANGE -
-

    -
  • Config Save - nows creates a backup file (.bak) of the config file before the new changes are saved.
  • -
-

-
-
- - bletch - bletch1971@hotmail.com - -
- - - urn:uuid:6ADCC571-3933-44E1-91FB-4DC8F8B836F2 - 1.0.14 (1.0.14.2) - 1.0.14.2 - - 2018-08-16T00:00:01Z - -
-

- CHANGE -
-

    -
  • Download Location Changed - Have moved the server manager files to a new location for hosting.
  • -
-

-
-
- - bletch - bletch1971@hotmail.com - -
- - - urn:uuid:6ADCC571-3933-44E1-91FB-4DC8F8B836F2 - 1.0.13 (1.0.13.1) - 1.0.13.1 - - 2018-06-13T04:40:00Z - -
-

- NEW -
-

    -
  • Message Options - Added new message options to display the messages in Bold, Italic, Underlined and Embedded Code Block.
  • -
-

-
-
- - bletch - bletch1971@hotmail.com - -
- - - urn:uuid:21C68E55-E915-4337-8CFB-7E96FE6967CB - 1.0.12 (1.0.12.1) - 1.0.12.1 - - 2018-05-24T00:20:00Z - -
-

- NEW -
-

    -
  • Added version feed window.
  • -
- CHANGE -
-
    -
  • DotNet Framework updated.
  • -
-

-
-
- - bletch - bletch1971@hotmail.com - -
- - - urn:uuid:C1ED0129-58CF-4AA4-8203-DDFA199E17DE - 1.0.11 (1.0.11.0) - 1.0.11.0 - - 2018-05-24T00:20:00Z - -
-

- CHANGE -
-

    -
  • Icons - all icons have be cleaned up and updated.
  • -
-

-
-
- - bletch - bletch1971@hotmail.com - -
- - - urn:uuid:58894ADA-FDC4-4F5A-A904-B81D9130FD00 - 1.0.10 (1.0.10.0) - 1.0.10.0 - - 2018-05-24T00:20:00Z - -
-

- BUGFIX -
-

    -
  • Attempt to fix the save config bug, where the config file is blank.
  • -
-

-
-
- - bletch - bletch1971@hotmail.com - -
- - - urn:uuid:20DB658C-44FA-4642-923B-A94926B0FDC3 - 1.0.9 (1.0.9.0) - 1.0.9.0 - - 2018-05-24T00:20:00Z - -
-

- BUGFIX -
-

    -
  • Some minor bugfixes and code cleanup.
  • -
-

-
-
- - bletch - bletch1971@hotmail.com - -
- - - urn:uuid:3B0D107D-77DB-4E70-BE8A-336A20A55F8A - 1.0.8 (1.0.8.0) - 1.0.8.0 - - 2018-05-24T00:20:00Z - -
-

- CHANGE -
-

    -
  • Changed the download location of the plugin.
  • -
-

-
-
- - bletch - bletch1971@hotmail.com - -
- - - urn:uuid:CDA4857D-FC4A-44AB-A11E-9CC1CDA11BCD - 1.0.7 (1.0.7.0) - 1.0.7.0 - - 2018-05-24T00:20:00Z - -
-

- CHANGE -
-

    -
  • Minor code clean-up and tweaks.
  • -
-

-
-
- - bletch - bletch1971@hotmail.com - -
- - - urn:uuid:C6CEC6B2-E405-4DF6-B991-FC1D4EEDF3E8 - 1.0.6 (1.0.6.0) - 1.0.6.0 - - 2018-05-24T00:20:00Z - -
-

- CHANGE -
-

    -
  • Have changed the storage host for the discord plugin.
  • -
-

-
-
- - bletch - bletch1971@hotmail.com - -
- - - urn:uuid:9C4A9557-3A74-428F-A22C-727CBBFB6E91 - 1.0.5 (1.0.5.0) - 1.0.5.0 - - 2018-05-24T00:20:00Z - -
-

- NEW -
-

    -
  • New discord plugin to send Server Manager alerts to one or more channels on your discord server.
  • -
-

-
-
- - bletch - bletch1971@hotmail.com - -
- -
\ No newline at end of file diff --git a/Plugins/Discord/source/Plugin.Discord/VersionFeedBeta.xml b/Plugins/Discord/source/Plugin.Discord/VersionFeedBeta.xml deleted file mode 100644 index de95f6f5..00000000 --- a/Plugins/Discord/source/Plugin.Discord/VersionFeedBeta.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - urn:uuid:6BB39661-8638-425E-B5F7-0C75AACC1D26 - Discord Plugin Version Feed - This is the Discord Plugin beta version feed. - - 2020-07-12T02:00:00Z - - - urn:uuid:4750D17C-2C8F-4D8C-AA17-B3512F002170 - 1.0.17 (1.0.17.2) - 1.0.17.2 - - 2020-07-12T02:00:00Z - -
-

- CHANGE -
-

    -
  • Added drag/drop feature to the config window.
  • -
-

-
-
- - bletch - bletch1971@hotmail.com - -
- - - urn:uuid:D8974ABF-8444-4D40-A594-D4443921B3B8 - 1.0.17 (1.0.17.1) - 1.0.17.1 - - 2020-07-12T00:00:00Z - -
-

- CHANGE -
-

    -
  • Profile name has been converted to a droplist and is now populated with the Server Manager Profile Names for selection.
  • -
-

-
-
- - bletch - bletch1971@hotmail.com - -
- -
\ No newline at end of file diff --git a/Plugins/Discord/source/Plugin.Discord/Windows/ConfigProfileWindow.xaml b/Plugins/Discord/source/Plugin.Discord/Windows/ConfigProfileWindow.xaml deleted file mode 100644 index d1ce9835..00000000 --- a/Plugins/Discord/source/Plugin.Discord/Windows/ConfigProfileWindow.xaml +++ /dev/null @@ -1,235 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Plugins/Discord/source/Plugin.Discord/Windows/ConfigProfileWindow.xaml.cs b/Plugins/Discord/source/Plugin.Discord/Windows/ConfigProfileWindow.xaml.cs deleted file mode 100644 index 1a9d84da..00000000 --- a/Plugins/Discord/source/Plugin.Discord/Windows/ConfigProfileWindow.xaml.cs +++ /dev/null @@ -1,320 +0,0 @@ -using ServerManagerTool.Plugin.Common; -using System; -using System.ComponentModel; -using System.Diagnostics; -using System.Linq; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Controls.Primitives; -using System.Windows.Input; - -namespace ServerManagerTool.Plugin.Discord.Windows -{ - /// - /// Interaction logic for ConfigProfileWindow.xaml - /// - public partial class ConfigProfileWindow : Window - { - private static readonly DependencyProperty ProfileProperty = DependencyProperty.Register(nameof(Profile), typeof(ConfigProfile), typeof(ConfigProfileWindow)); - private static readonly DependencyProperty ProfileListProperty = DependencyProperty.Register(nameof(ProfileList), typeof(ComboBoxItemList), typeof(ConfigProfileWindow)); - - internal ConfigProfileWindow(DiscordPlugin plugin, ConfigProfile profile) - { - this.Plugin = plugin ?? new DiscordPlugin(); - this.OriginalProfile = profile; - this.Profile = profile.Clone(); - this.Profile.CommitChanges(); - - RefreshProfileList(); - - InitializeComponent(); - - if (plugin.BetaEnabled) - Title = $"{Title} {ResourceUtils.GetResourceString(this.Resources, "Global_BetaModeLabel")}"; - - this.DataContext = this; - } - - private ConfigProfile OriginalProfile - { - get; - set; - } - - private ConfigProfile Profile - { - get { return GetValue(ProfileProperty) as ConfigProfile; } - set { SetValue(ProfileProperty, value); } - } - - private DiscordPlugin Plugin - { - get; - set; - } - - private ComboBoxItemList ProfileList - { - get { return GetValue(ProfileListProperty) as ComboBoxItemList; } - set { SetValue(ProfileListProperty, value); } - } - - private void ConfigProfileWindow_Closing(object sender, CancelEventArgs e) - { - if (DialogResult.HasValue && DialogResult.Value) - return; - - if (this.Profile.HasAnyChanges) - { - if (MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_CloseLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_CloseTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes) - e.Cancel = true; - } - } - - private void ComboBox_PreviewMouseWheel(object sender, MouseWheelEventArgs e) - { - var comboBox = sender as ComboBox; - if (comboBox == null) - return; - - if (comboBox.IsDropDownOpen) - return; - - e.Handled = true; - } - - private void Ok_Click(object sender, RoutedEventArgs e) - { - if (this.Profile.HasAnyChanges) - { - this.OriginalProfile.CopyFrom(this.Profile); - } - - DialogResult = true; - Close(); - } - - private void Test_Click(object sender, RoutedEventArgs e) - { - if (!Profile.IsEnabled) - { - MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_TestEnabledErrorLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_TestErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error); - return; - } - - try - { - foreach (var profileName in Profile.ProfileNames) - { - foreach (var alertType in Profile.AlertTypes) - { - Plugin.HandleAlert(Profile, alertType.Value, profileName.Value, $"Test '{alertType.Value}' message for profile name '{profileName.Value}'."); - Task.Delay(1000).Wait(); - } - } - } - catch (Exception ex) - { - Debug.WriteLine($"ERROR: {nameof(Test_Click)}\r\n{ex.Message}"); - MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_TestErrorLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_TestErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error); - } - } - - private void AddAlertType_Click(object sender, RoutedEventArgs e) - { - try - { - var alertType = new AlertTypeValue(AlertType.Error); - - Profile.AlertTypes.Add(alertType); - Profile.AlertTypes.NotifyAdd(alertType); - } - catch (Exception ex) - { - Debug.WriteLine($"ERROR: {nameof(AddAlertType_Click)}\r\n{ex.Message}"); - MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_AddAlertTypeErrorLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_AddErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error); - } - } - - private void ClearAlertTypes_Click(object sender, RoutedEventArgs e) - { - if (MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_ClearLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_ClearTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes) - return; - - try - { - if (Profile.AlertTypes.Count == 0) - return; - - Profile.AlertTypes.Clear(); - Profile.AlertTypes.NotifyClear(); - } - catch (Exception ex) - { - Debug.WriteLine($"ERROR: {nameof(ClearAlertTypes_Click)}\r\n{ex.Message}"); - MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_ClearAlertTypesErrorLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_ClearErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error); - } - } - - private void DeleteAlertType_Click(object sender, RoutedEventArgs e) - { - if (MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_DeleteLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_DeleteTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes) - return; - - try - { - var alertType = ((AlertTypeValue)((Button)e.Source).DataContext); - var index = Profile.AlertTypes.IndexOf(alertType); - Profile.AlertTypes.Remove(alertType); - Profile.AlertTypes.NotifyRemove(alertType, index); - } - catch (Exception ex) - { - Debug.WriteLine($"ERROR: {nameof(DeleteAlertType_Click)}\r\n{ex.Message}"); - MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_DeleteAlertTypeErrorLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_DeleteErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error); - } - } - - private void AddProfileName_Click(object sender, RoutedEventArgs e) - { - try - { - var profileName = new ProfileNameValue(); - - Profile.ProfileNames.Add(profileName); - Profile.ProfileNames.NotifyAdd(profileName); - } - catch (Exception ex) - { - Debug.WriteLine($"ERROR: {nameof(AddProfileName_Click)}\r\n{ex.Message}"); - MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_AddProfileNameErrorLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_AddErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error); - } - } - - private void ClearProfileNames_Click(object sender, RoutedEventArgs e) - { - if (MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_ClearLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_ClearTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes) - return; - - try - { - if (Profile.ProfileNames.Count == 0) - return; - - Profile.ProfileNames.Clear(); - Profile.ProfileNames.NotifyClear(); - } - catch (Exception ex) - { - Debug.WriteLine($"ERROR: {nameof(ClearProfileNames_Click)}\r\n{ex.Message}"); - MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_ClearProfileNamesErrorLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_ClearErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error); - } - } - - private void DeleteProfileName_Click(object sender, RoutedEventArgs e) - { - if (MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_DeleteLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_DeleteTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes) - return; - - try - { - var profileName = ((ProfileNameValue)((Button)e.Source).DataContext); - var index = Profile.ProfileNames.IndexOf(profileName); - Profile.ProfileNames.Remove(profileName); - Profile.ProfileNames.NotifyRemove(profileName, index); - } - catch (Exception ex) - { - Debug.WriteLine($"ERROR: {nameof(DeleteProfileName_Click)}\r\n{ex.Message}"); - MessageBox.Show(ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_DeleteProfileNameErrorLabel"), ResourceUtils.GetResourceString(this.Resources, "ConfigProfileWindow_DeleteErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error); - } - } - - private void ComboBox_GotFocus(object sender, RoutedEventArgs e) - { - var comboBox = sender as ComboBox; - if (comboBox == null) - return; - - try - { - comboBox.BeginInit(); - } - finally - { - comboBox.EndInit(); - } - } - - private void ComboBox_LostFocus(object sender, RoutedEventArgs e) - { - var comboBox = sender as ComboBox; - if (comboBox == null) - return; - - if (comboBox.SelectedItem == null) - { - var text = comboBox.Text; - - if (!string.IsNullOrWhiteSpace(text)) - { - var source = comboBox.ItemsSource as ComboBoxItemList; - source?.Add(new ComboBoxItem - { - ValueMember = text, - DisplayMember = text, - }); - } - - comboBox.SelectedValue = text; - } - - var expression = comboBox.GetBindingExpression(Selector.SelectedValueProperty); - expression?.UpdateSource(); - - expression = comboBox.GetBindingExpression(ComboBox.TextProperty); - expression?.UpdateSource(); - } - - private void RefreshProfileList() - { - var newList = new ComboBoxItemList(); - newList.Add(new ComboBoxItem - { - ValueMember = string.Empty, - DisplayMember = string.Empty, - }); - - try - { - foreach (var profile in PluginHelper.Instance.FetchProfileList()) - { - newList.Add(new ComboBoxItem - { - ValueMember = profile.ProfileName, - DisplayMember = $"* {profile.ProfileName}", - }); - } - } - catch - { - // do nothing, most likely they are using an older version of a server manager - } - - foreach (var profile in Profile.ProfileNames) - { - if (!newList.Any(p => p.ValueMember.Equals(profile.Value, StringComparison.OrdinalIgnoreCase))) - { - newList.Add(new ComboBoxItem - { - ValueMember = profile.Value, - DisplayMember = profile.Value, - }); - } - } - - this.ProfileList = newList; - } - } -} diff --git a/Plugins/Discord/source/Plugin.Discord/Windows/ConfigWindow.xaml b/Plugins/Discord/source/Plugin.Discord/Windows/ConfigWindow.xaml deleted file mode 100644 index a2eb74c0..00000000 --- a/Plugins/Discord/source/Plugin.Discord/Windows/ConfigWindow.xaml +++ /dev/null @@ -1,147 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -