internal PluginFeatureInfo(Plugin plugin, Type featureType, PluginFeatureEntity pluginFeatureEntity, PluginFeatureAttribute?attribute) { Plugin = plugin ?? throw new ArgumentNullException(nameof(plugin)); FeatureType = featureType ?? throw new ArgumentNullException(nameof(featureType)); Entity = pluginFeatureEntity; Name = attribute?.Name ?? featureType.Name.Humanize(LetterCasing.Title); Description = attribute?.Description; Icon = attribute?.Icon; AlwaysEnabled = attribute?.AlwaysEnabled ?? false; if (Icon != null) { return; } if (typeof(BaseDataModelExpansion).IsAssignableFrom(featureType)) { Icon = "TableAdd"; } else if (typeof(DeviceProvider).IsAssignableFrom(featureType)) { Icon = "Devices"; } else if (typeof(ProfileModule).IsAssignableFrom(featureType)) { Icon = "VectorRectangle"; } else if (typeof(Module).IsAssignableFrom(featureType)) { Icon = "GearBox"; } else if (typeof(LayerBrushProvider).IsAssignableFrom(featureType)) { Icon = "Brush"; } else if (typeof(LayerEffectProvider).IsAssignableFrom(featureType)) { Icon = "AutoAwesome"; } else { Icon = "Plugin"; } }
public Plugin LoadPlugin(DirectoryInfo directory) { _logger.Verbose("Loading plugin from {directory}", directory.FullName); // Load the metadata string metadataFile = Path.Combine(directory.FullName, "plugin.json"); if (!File.Exists(metadataFile)) { _logger.Warning(new ArtemisPluginException("Couldn't find the plugins metadata file at " + metadataFile), "Plugin exception"); } // PluginInfo contains the ID which we need to move on PluginInfo pluginInfo = CoreJson.DeserializeObject <PluginInfo>(File.ReadAllText(metadataFile)) !; if (pluginInfo.Guid == Constants.CorePluginInfo.Guid) { throw new ArtemisPluginException($"Plugin {pluginInfo} cannot use reserved GUID {pluginInfo.Guid}"); } lock (_plugins) { // Ensure the plugin is not already loaded if (_plugins.Any(p => p.Guid == pluginInfo.Guid)) { throw new ArtemisCoreException($"Cannot load plugin {pluginInfo} because it is using a GUID already used by another plugin"); } } // Load the entity and fall back on creating a new one Plugin plugin = new(pluginInfo, directory, _pluginRepository.GetPluginByGuid(pluginInfo.Guid)); OnPluginLoading(new PluginEventArgs(plugin)); // Locate the main assembly entry string?mainFile = plugin.ResolveRelativePath(plugin.Info.Main); if (!File.Exists(mainFile)) { throw new ArtemisPluginException(plugin, "Couldn't find the plugins main entry at " + mainFile); } FileInfo[] fileInfos = directory.GetFiles(); if (!fileInfos.Any(f => string.Equals(f.Name, plugin.Info.Main, StringComparison.InvariantCulture))) { throw new ArtemisPluginException(plugin, "Plugin main entry casing mismatch at " + plugin.Info.Main); } // Load the plugin, all types implementing Plugin and register them with DI plugin.PluginLoader = PluginLoader.CreateFromAssemblyFile(mainFile !, configure => { configure.IsUnloadable = true; configure.LoadInMemory = true; configure.PreferSharedTypes = true; }); try { plugin.Assembly = plugin.PluginLoader.LoadDefaultAssembly(); } catch (Exception e) { throw new ArtemisPluginException(plugin, "Failed to load the plugins assembly", e); } // Get the Plugin feature from the main assembly and if there is only one, instantiate it List <Type> featureTypes; try { featureTypes = plugin.Assembly.GetTypes().Where(t => typeof(PluginFeature).IsAssignableFrom(t)).ToList(); } catch (ReflectionTypeLoadException e) { throw new ArtemisPluginException( plugin, "Failed to initialize the plugin assembly", // ReSharper disable once RedundantEnumerableCastCall - Casting from nullable to non-nullable here new AggregateException(e.LoaderExceptions.Where(le => le != null).Cast <Exception>().ToArray()) ); } foreach (Type featureType in featureTypes) { // Load the enabled state and if not found, default to true PluginFeatureEntity featureEntity = plugin.Entity.Features.FirstOrDefault(i => i.Type == featureType.FullName) ?? new PluginFeatureEntity { IsEnabled = plugin.Info.AutoEnableFeatures, Type = featureType.FullName ! }; plugin.AddFeature(new PluginFeatureInfo(plugin, featureType, featureEntity, (PluginFeatureAttribute?)Attribute.GetCustomAttribute(featureType, typeof(PluginFeatureAttribute)))); } if (!featureTypes.Any()) { _logger.Warning("Plugin {plugin} contains no features", plugin); } List <Type> bootstrappers = plugin.Assembly.GetTypes().Where(t => typeof(PluginBootstrapper).IsAssignableFrom(t)).ToList(); if (bootstrappers.Count > 1) { _logger.Warning($"{plugin} has more than one bootstrapper, only initializing {bootstrappers.First().FullName}"); } if (bootstrappers.Any()) { plugin.Bootstrapper = (PluginBootstrapper?)Activator.CreateInstance(bootstrappers.First()); plugin.Bootstrapper?.InternalOnPluginLoaded(plugin); } lock (_plugins) { _plugins.Add(plugin); } OnPluginLoaded(new PluginEventArgs(plugin)); return(plugin); }