mirror of
				https://github.com/jellyfin/jellyfin.git
				synced 2025-11-03 19:17:24 -05:00 
			
		
		
		
	Tweaked plugin downloading a bit
This commit is contained in:
		
							parent
							
								
									7f8a477278
								
							
						
					
					
						commit
						fc735e9187
					
				@ -11,9 +11,9 @@ namespace MediaBrowser.Api.HttpHandlers
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        protected override Task<BasePluginConfiguration> GetObjectToSerialize()
 | 
					        protected override Task<BasePluginConfiguration> GetObjectToSerialize()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            string pluginName = QueryString["name"];
 | 
					            string name = QueryString["assemblyfilename"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            BasePluginConfiguration config = Kernel.Instance.Plugins.First(p => p.Name.Equals(pluginName, StringComparison.OrdinalIgnoreCase)).Configuration;
 | 
					            BasePluginConfiguration config = Kernel.Instance.Plugins.First(p => p.AssemblyFileName.Equals(name, StringComparison.OrdinalIgnoreCase)).Configuration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return Task.FromResult<BasePluginConfiguration>(config);
 | 
					            return Task.FromResult<BasePluginConfiguration>(config);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -22,7 +22,8 @@ namespace MediaBrowser.Api.HttpHandlers
 | 
				
			|||||||
                    Enabled = p.Enabled,
 | 
					                    Enabled = p.Enabled,
 | 
				
			||||||
                    DownloadToUI = p.DownloadToUI,
 | 
					                    DownloadToUI = p.DownloadToUI,
 | 
				
			||||||
                    Version = p.Version.ToString(),
 | 
					                    Version = p.Version.ToString(),
 | 
				
			||||||
                    AssemblyFileName = p.AssemblyFileName
 | 
					                    AssemblyFileName = p.AssemblyFileName,
 | 
				
			||||||
 | 
					                    ConfigurationDateLastModified = p.ConfigurationDateLastModified
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -590,15 +590,34 @@ namespace MediaBrowser.ApiInteraction
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Gets weather information for the default location as set in configuration
 | 
					        /// Gets the current server configuration
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        public async Task<ServerConfiguration> GetServerConfigurationAsync()
 | 
					        public async Task<ServerConfiguration> GetServerConfigurationAsync()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            string url = ApiUrl + "/ServerConfiguration";
 | 
					            string url = ApiUrl + "/ServerConfiguration";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            using (Stream stream = await GetSerializedStreamAsync(url, ApiInteraction.SerializationFormat.Json).ConfigureAwait(false))
 | 
					            // At the moment this can't be retrieved in protobuf format
 | 
				
			||||||
 | 
					            SerializationFormat format = DataSerializer.CanDeserializeJsv ? SerializationFormat.Jsv : ApiInteraction.SerializationFormat.Json;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            using (Stream stream = await GetSerializedStreamAsync(url, format).ConfigureAwait(false))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                return DeserializeFromStream<ServerConfiguration>(stream, ApiInteraction.SerializationFormat.Json);
 | 
					                return DeserializeFromStream<ServerConfiguration>(stream, format);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Gets weather information for the default location as set in configuration
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public async Task<object> GetPluginConfigurationAsync(PluginInfo plugin, Type configurationType)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            string url = ApiUrl + "/PluginConfiguration?assemblyfilename=" + plugin.AssemblyFileName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // At the moment this can't be retrieved in protobuf format
 | 
				
			||||||
 | 
					            SerializationFormat format = DataSerializer.CanDeserializeJsv ? SerializationFormat.Jsv : ApiInteraction.SerializationFormat.Json;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            using (Stream stream = await GetSerializedStreamAsync(url, format).ConfigureAwait(false))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return DeserializeFromStream(stream, format, configurationType);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -685,6 +704,20 @@ namespace MediaBrowser.ApiInteraction
 | 
				
			|||||||
            return DataSerializer.DeserializeJsonFromStream<T>(stream);
 | 
					            return DataSerializer.DeserializeJsonFromStream<T>(stream);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private object DeserializeFromStream(Stream stream, SerializationFormat format, Type type)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (format == ApiInteraction.SerializationFormat.Protobuf)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return DataSerializer.DeserializeProtobufFromStream(stream, type);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (format == ApiInteraction.SerializationFormat.Jsv)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return DataSerializer.DeserializeJsvFromStream(stream, type);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return DataSerializer.DeserializeJsonFromStream(stream, type);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// This is just a helper around HttpClient
 | 
					        /// This is just a helper around HttpClient
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,5 @@
 | 
				
			|||||||
using System.IO;
 | 
					using System;
 | 
				
			||||||
 | 
					using System.IO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace MediaBrowser.ApiInteraction
 | 
					namespace MediaBrowser.ApiInteraction
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -11,6 +12,10 @@ namespace MediaBrowser.ApiInteraction
 | 
				
			|||||||
        T DeserializeJsvFromStream<T>(Stream stream);
 | 
					        T DeserializeJsvFromStream<T>(Stream stream);
 | 
				
			||||||
        T DeserializeProtobufFromStream<T>(Stream stream);
 | 
					        T DeserializeProtobufFromStream<T>(Stream stream);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        object DeserializeJsonFromStream(Stream stream, Type type);
 | 
				
			||||||
 | 
					        object DeserializeJsvFromStream(Stream stream, Type type);
 | 
				
			||||||
 | 
					        object DeserializeProtobufFromStream(Stream stream, Type type);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        bool CanDeserializeJsv { get; }
 | 
					        bool CanDeserializeJsv { get; }
 | 
				
			||||||
        bool CanDeserializeProtobuf { get; }
 | 
					        bool CanDeserializeProtobuf { get; }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -168,6 +168,7 @@ namespace MediaBrowser.Common.Kernel
 | 
				
			|||||||
            if (!File.Exists(ApplicationPaths.SystemConfigurationFilePath))
 | 
					            if (!File.Exists(ApplicationPaths.SystemConfigurationFilePath))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Configuration = new TConfigurationType();
 | 
					                Configuration = new TConfigurationType();
 | 
				
			||||||
 | 
					                XmlSerializer.SerializeToFile(Configuration, ApplicationPaths.SystemConfigurationFilePath);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 | 
				
			|||||||
@ -24,7 +24,7 @@ namespace MediaBrowser.Common.Plugins
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected override Type ConfigurationType
 | 
					        public override Type ConfigurationType
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            get { return typeof(TConfigurationType); }
 | 
					            get { return typeof(TConfigurationType); }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -35,12 +35,12 @@ namespace MediaBrowser.Common.Plugins
 | 
				
			|||||||
    /// </summary>
 | 
					    /// </summary>
 | 
				
			||||||
    public abstract class BasePlugin : IDisposable
 | 
					    public abstract class BasePlugin : IDisposable
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private IKernel Kernel { get; set; }
 | 
					        public IKernel IKernel { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Gets or sets the plugin's current context
 | 
					        /// Gets or sets the plugin's current context
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        protected KernelContext Context { get { return Kernel.KernelContext; } }
 | 
					        protected KernelContext Context { get { return IKernel.KernelContext; } }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Gets the name of the plugin
 | 
					        /// Gets the name of the plugin
 | 
				
			||||||
@ -50,7 +50,7 @@ namespace MediaBrowser.Common.Plugins
 | 
				
			|||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Gets the type of configuration this plugin uses
 | 
					        /// Gets the type of configuration this plugin uses
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        protected abstract Type ConfigurationType { get; }
 | 
					        public abstract Type ConfigurationType { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Gets the plugin version
 | 
					        /// Gets the plugin version
 | 
				
			||||||
@ -74,6 +74,8 @@ namespace MediaBrowser.Common.Plugins
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public DateTime ConfigurationDateLastModified { get; private set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Gets the path to the assembly file
 | 
					        /// Gets the path to the assembly file
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
@ -81,7 +83,7 @@ namespace MediaBrowser.Common.Plugins
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            get
 | 
					            get
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                return Path.Combine(Kernel.ApplicationPaths.PluginsPath, AssemblyFileName);
 | 
					                return Path.Combine(IKernel.ApplicationPaths.PluginsPath, AssemblyFileName);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -102,7 +104,7 @@ namespace MediaBrowser.Common.Plugins
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            get
 | 
					            get
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                return Path.Combine(Kernel.ApplicationPaths.PluginConfigurationsPath, ConfigurationFileName);
 | 
					                return Path.Combine(IKernel.ApplicationPaths.PluginConfigurationsPath, ConfigurationFileName);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -118,7 +120,7 @@ namespace MediaBrowser.Common.Plugins
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    // Give the folder name the same name as the config file name
 | 
					                    // Give the folder name the same name as the config file name
 | 
				
			||||||
                    // We can always make this configurable if/when needed
 | 
					                    // We can always make this configurable if/when needed
 | 
				
			||||||
                    _DataFolderPath = Path.Combine(Kernel.ApplicationPaths.PluginsPath, Path.GetFileNameWithoutExtension(ConfigurationFileName));
 | 
					                    _DataFolderPath = Path.Combine(IKernel.ApplicationPaths.PluginsPath, Path.GetFileNameWithoutExtension(ConfigurationFileName));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if (!Directory.Exists(_DataFolderPath))
 | 
					                    if (!Directory.Exists(_DataFolderPath))
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
@ -154,7 +156,7 @@ namespace MediaBrowser.Common.Plugins
 | 
				
			|||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        public void Initialize(IKernel kernel)
 | 
					        public void Initialize(IKernel kernel)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Kernel = kernel;
 | 
					            IKernel = kernel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            ReloadConfiguration();
 | 
					            ReloadConfiguration();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -183,12 +185,14 @@ namespace MediaBrowser.Common.Plugins
 | 
				
			|||||||
            if (!File.Exists(ConfigurationFilePath))
 | 
					            if (!File.Exists(ConfigurationFilePath))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Configuration = Activator.CreateInstance(ConfigurationType) as BasePluginConfiguration;
 | 
					                Configuration = Activator.CreateInstance(ConfigurationType) as BasePluginConfiguration;
 | 
				
			||||||
 | 
					                XmlSerializer.SerializeToFile(Configuration, ConfigurationFilePath);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Configuration = XmlSerializer.DeserializeFromFile(ConfigurationType, ConfigurationFilePath) as BasePluginConfiguration;
 | 
					                Configuration = XmlSerializer.DeserializeFromFile(ConfigurationType, ConfigurationFilePath) as BasePluginConfiguration;
 | 
				
			||||||
                Configuration.DateLastModified = File.GetLastWriteTime(ConfigurationFilePath);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            ConfigurationDateLastModified = File.GetLastWriteTime(ConfigurationFilePath);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -52,6 +52,13 @@ namespace MediaBrowser.Common.Serialization
 | 
				
			|||||||
            return ServiceStack.Text.JsonSerializer.DeserializeFromStream<T>(stream);
 | 
					            return ServiceStack.Text.JsonSerializer.DeserializeFromStream<T>(stream);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static object DeserializeFromStream(Stream stream, Type type)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Configure();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return ServiceStack.Text.JsonSerializer.DeserializeFromStream(type, stream);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static bool IsConfigured = false;
 | 
					        private static bool IsConfigured = false;
 | 
				
			||||||
        private static void Configure()
 | 
					        private static void Configure()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,5 @@
 | 
				
			|||||||
using System.IO;
 | 
					using System;
 | 
				
			||||||
 | 
					using System.IO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace MediaBrowser.Common.Serialization
 | 
					namespace MediaBrowser.Common.Serialization
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -19,11 +20,16 @@ namespace MediaBrowser.Common.Serialization
 | 
				
			|||||||
            return ServiceStack.Text.TypeSerializer.DeserializeFromStream<T>(stream);
 | 
					            return ServiceStack.Text.TypeSerializer.DeserializeFromStream<T>(stream);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static object DeserializeFromStream(Stream stream, Type type)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return ServiceStack.Text.TypeSerializer.DeserializeFromStream(type, stream);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static void SerializeToFile<T>(T obj, string file)
 | 
					        public static void SerializeToFile<T>(T obj, string file)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            using (Stream stream = File.Open(file, FileMode.Create))
 | 
					            using (Stream stream = File.Open(file, FileMode.Create))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                ServiceStack.Text.TypeSerializer.SerializeToStream<T>(obj, stream);
 | 
					                SerializeToStream<T>(obj, stream);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -31,7 +37,7 @@ namespace MediaBrowser.Common.Serialization
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            using (Stream stream = File.OpenRead(file))
 | 
					            using (Stream stream = File.OpenRead(file))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                return ServiceStack.Text.TypeSerializer.DeserializeFromStream<T>(stream);
 | 
					                return DeserializeFromStream<T>(stream);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,5 @@
 | 
				
			|||||||
using System.IO;
 | 
					using System;
 | 
				
			||||||
 | 
					using System.IO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace MediaBrowser.Common.Serialization
 | 
					namespace MediaBrowser.Common.Serialization
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -18,6 +19,11 @@ namespace MediaBrowser.Common.Serialization
 | 
				
			|||||||
            return ProtoBuf.Serializer.Deserialize<T>(stream);
 | 
					            return ProtoBuf.Serializer.Deserialize<T>(stream);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static object DeserializeFromStream(Stream stream, Type type)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            throw new NotImplementedException();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        public static void SerializeToFile<T>(T obj, string file)
 | 
					        public static void SerializeToFile<T>(T obj, string file)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            using (Stream stream = File.Open(file, FileMode.Create))
 | 
					            using (Stream stream = File.Open(file, FileMode.Create))
 | 
				
			||||||
 | 
				
			|||||||
@ -39,6 +39,14 @@ namespace MediaBrowser.Common.Serialization
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static void SerializeToFile(object obj, string file)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            using (FileStream stream = new FileStream(file, FileMode.Create))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ServiceStack.Text.XmlSerializer.SerializeToStream(obj, stream);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        public static object DeserializeFromFile(Type type, string file)
 | 
					        public static object DeserializeFromFile(Type type, string file)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            using (Stream stream = File.OpenRead(file))
 | 
					            using (Stream stream = File.OpenRead(file))
 | 
				
			||||||
 | 
				
			|||||||
@ -7,9 +7,6 @@ namespace MediaBrowser.Model.Plugins
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        public bool Enabled { get; set; }
 | 
					        public bool Enabled { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [IgnoreDataMember]
 | 
					 | 
				
			||||||
        public DateTime DateLastModified { get; set; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public BasePluginConfiguration()
 | 
					        public BasePluginConfiguration()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Enabled = true;
 | 
					            Enabled = true;
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user