Guest User

Remove plugin shadow copy code from PluginManager.cs

a guest
Jan 23rd, 2018
1,716
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Reflection;
  7. using System.Threading;
  8. using Microsoft.AspNetCore.Mvc.ApplicationParts;
  9. using Newtonsoft.Json;
  10. using Nop.Core.ComponentModel;
  11. using Nop.Core.Configuration;
  12.  
  13. //Contributor: Umbraco (http://www.umbraco.com). Thanks a lot!
  14. //SEE THIS POST for full details of what this does - http://shazwazza.com/post/Developing-a-plugin-framework-in-ASPNET-with-medium-trust.aspx
  15.  
  16. namespace Nop.Core.Plugins
  17. {
  18.     /// <summary>
  19.     /// Sets the application up for the plugin referencing
  20.     /// </summary>
  21.     public class PluginManager
  22.     {
  23.         #region Fields
  24.  
  25.         private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim();
  26.         private static readonly List<string> BaseAppLibraries;
  27.  
  28.         #endregion
  29.  
  30.         #region Ctor
  31.  
  32.         static PluginManager()
  33.         {
  34.             //get all libraries from /bin/{version}/ directory
  35.             BaseAppLibraries = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory)
  36.                 .GetFiles("*.dll", SearchOption.TopDirectoryOnly).Select(fi => fi.Name).ToList();
  37.  
  38.             //get all libraries from base site directory
  39.             if (!AppDomain.CurrentDomain.BaseDirectory.Equals(Environment.CurrentDirectory, StringComparison.InvariantCultureIgnoreCase))
  40.                 BaseAppLibraries.AddRange(new DirectoryInfo(Environment.CurrentDirectory).GetFiles("*.dll", SearchOption.TopDirectoryOnly).Select(fi => fi.Name));
  41.  
  42.             //get all libraries from refs directory
  43.             var refsPathName = new DirectoryInfo(Path.Combine(Environment.CurrentDirectory, RefsPathName));
  44.             if (refsPathName.Exists)
  45.                 BaseAppLibraries.AddRange(refsPathName.GetFiles("*.dll", SearchOption.TopDirectoryOnly).Select(fi => fi.Name));
  46.         }
  47.  
  48.         #endregion
  49.  
  50.         #region Utilities
  51.  
  52.         /// <summary>
  53.         /// Get description files
  54.         /// </summary>
  55.         /// <param name="pluginFolder">Plugin directory info</param>
  56.         /// <returns>Original and parsed description files</returns>
  57.         private static IEnumerable<KeyValuePair<FileInfo, PluginDescriptor>> GetDescriptionFilesAndDescriptors(DirectoryInfo pluginFolder)
  58.         {
  59.             if (pluginFolder == null)
  60.                 throw new ArgumentNullException(nameof(pluginFolder));
  61.  
  62.             //create list (<file info, parsed plugin descritor>)
  63.             var result = new List<KeyValuePair<FileInfo, PluginDescriptor>>();
  64.  
  65.             //add display order and path to list
  66.             foreach (var descriptionFile in pluginFolder.GetFiles(PluginDescriptionFileName, SearchOption.AllDirectories))
  67.             {
  68.                 if (!IsPackagePluginFolder(descriptionFile.Directory))
  69.                     continue;
  70.  
  71.                 //parse file
  72.                 var pluginDescriptor = GetPluginDescriptorFromFile(descriptionFile.FullName);
  73.  
  74.                 //populate list
  75.                 result.Add(new KeyValuePair<FileInfo, PluginDescriptor>(descriptionFile, pluginDescriptor));
  76.             }
  77.  
  78.             //sort list by display order. NOTE: Lowest DisplayOrder will be first i.e 0 , 1, 1, 1, 5, 10
  79.             //it's required: https://www.nopcommerce.com/boards/t/17455/load-plugins-based-on-their-displayorder-on-startup.aspx
  80.             result.Sort((firstPair, nextPair) => firstPair.Value.DisplayOrder.CompareTo(nextPair.Value.DisplayOrder));
  81.             return result;
  82.         }
  83.  
  84.         /// <summary>
  85.         /// Get system names of installed plugins
  86.         /// </summary>
  87.         /// <param name="filePath">Path to the file</param>
  88.         /// <returns>List of plugin system names</returns>
  89.         private static IList<string> GetInstalledPluginNames(string filePath)
  90.         {
  91.             //check whether file exists
  92.             if (!File.Exists(filePath))
  93.             {
  94.                 //if not, try to parse the file that was used in previous nopCommerce versions
  95.                 filePath = CommonHelper.MapPath(ObsoleteInstalledPluginsFilePath);
  96.                 if (!File.Exists(filePath))
  97.                     return new List<string>();
  98.  
  99.                 //get plugin system names from the old txt file
  100.                 var pluginSystemNames = new List<string>();
  101.                 using (var reader = new StringReader(File.ReadAllText(filePath)))
  102.                 {
  103.                     var pluginName = string.Empty;
  104.                     while ((pluginName = reader.ReadLine()) != null)
  105.                     {
  106.                         if (!string.IsNullOrWhiteSpace(pluginName))
  107.                             pluginSystemNames.Add(pluginName.Trim());
  108.                     }
  109.                 }
  110.  
  111.                 //save system names of installed plugins to the new file
  112.                 SaveInstalledPluginNames(pluginSystemNames, CommonHelper.MapPath(InstalledPluginsFilePath));
  113.  
  114.                 //and delete the old one
  115.                 File.Delete(filePath);
  116.  
  117.                 return pluginSystemNames;
  118.             }
  119.  
  120.             var text = File.ReadAllText(filePath);
  121.             if (string.IsNullOrEmpty(text))
  122.                 return new List<string>();
  123.  
  124.             //get plugin system names from the JSON file
  125.             return JsonConvert.DeserializeObject<IList<string>>(text);
  126.         }
  127.  
  128.         /// <summary>
  129.         /// Save system names of installed plugins to the file
  130.         /// </summary>
  131.         /// <param name="pluginSystemNames">List of plugin system names</param>
  132.         /// <param name="filePath">Path to the file</param>
  133.         private static void SaveInstalledPluginNames(IList<string> pluginSystemNames, string filePath)
  134.         {
  135.             //save the file
  136.             var text = JsonConvert.SerializeObject(pluginSystemNames, Formatting.Indented);
  137.             File.WriteAllText(filePath, text);
  138.         }
  139.  
  140.         /// <summary>
  141.         /// Indicates whether assembly file is already loaded
  142.         /// </summary>
  143.         /// <param name="fileInfo">File info</param>
  144.         /// <returns>Result</returns>
  145.         private static bool IsAlreadyLoaded(FileInfo fileInfo)
  146.         {
  147.             //search library file name in base directory to ignore already existing (loaded) libraries
  148.             //(we do it because not all libraries are loaded immediately after application start)
  149.             if (BaseAppLibraries.Any(sli => sli.Equals(fileInfo.Name, StringComparison.InvariantCultureIgnoreCase)))
  150.                 return true;
  151.  
  152.             //compare full assembly name
  153.             //var fileAssemblyName = AssemblyName.GetAssemblyName(fileInfo.FullName);
  154.             //foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
  155.             //{
  156.             //    if (a.FullName.Equals(fileAssemblyName.FullName, StringComparison.InvariantCultureIgnoreCase))
  157.             //        return true;
  158.             //}
  159.             //return false;
  160.  
  161.             //do not compare the full assembly name, just filename
  162.             try
  163.             {
  164.                 var fileNameWithoutExt = Path.GetFileNameWithoutExtension(fileInfo.FullName);
  165.                 if (string.IsNullOrEmpty(fileNameWithoutExt))
  166.                     throw new Exception($"Cannot get file extension for {fileInfo.Name}");
  167.  
  168.                 foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
  169.                 {
  170.                     var assemblyName = a.FullName.Split(',').FirstOrDefault();
  171.                     if (fileNameWithoutExt.Equals(assemblyName, StringComparison.InvariantCultureIgnoreCase))
  172.                         return true;
  173.                 }
  174.             }
  175.             catch (Exception exc)
  176.             {
  177.                 Debug.WriteLine("Cannot validate whether an assembly is already loaded. " + exc);
  178.             }
  179.             return false;
  180.         }
  181.  
  182.         /// <summary>
  183.         /// Perform file deploy
  184.         /// </summary>
  185.         /// <param name="plug">Plugin file info</param>
  186.         /// <param name="applicationPartManager">Application part manager</param>
  187.         /// <param name="config">Config</param>
  188.         /// <returns>Assembly</returns>
  189.         private static Assembly PerformFileDeploy(FileInfo plug, ApplicationPartManager applicationPartManager, NopConfig config)
  190.         {
  191.             if (plug.Directory == null || plug.Directory.Parent == null)
  192.                 throw new InvalidOperationException("The plugin directory for the " + plug.Name + " file exists in a folder outside of the allowed nopCommerce folder hierarchy");
  193.  
  194.             //we can now register the plugin definition
  195.             var assemblyName = AssemblyName.GetAssemblyName(plug.FullName);
  196.             Assembly assembly;
  197.             try
  198.             {
  199.                 assembly = Assembly.Load(assemblyName);
  200.             }
  201.             catch (FileLoadException)
  202.             {
  203.                 if (config.UseUnsafeLoadAssembly)
  204.                 {
  205.                     //if an application has been copied from the web, it is flagged by Windows as being a web application,
  206.                     //even if it resides on the local computer.You can change that designation by changing the file properties,
  207.                     //or you can use the<loadFromRemoteSources> element to grant the assembly full trust.As an alternative,
  208.                     //you can use the UnsafeLoadFrom method to load a local assembly that the operating system has flagged as
  209.                     //having been loaded from the web.
  210.                     //see http://go.microsoft.com/fwlink/?LinkId=155569 for more information.
  211.                     assembly = Assembly.UnsafeLoadFrom(plug.FullName);
  212.                 }
  213.                 else
  214.                 {
  215.                     throw;
  216.                 }
  217.             }
  218.  
  219.             Debug.WriteLine("Adding to ApplicationParts: '{0}'", assembly.FullName);
  220.             applicationPartManager.ApplicationParts.Add(new AssemblyPart(assembly));
  221.  
  222.             return assembly;
  223.         }
  224.  
  225.  
  226.         /// <summary>
  227.         /// Determines if the folder is a bin plugin folder for a package
  228.         /// </summary>
  229.         /// <param name="folder"></param>
  230.         /// <returns></returns>
  231.         private static bool IsPackagePluginFolder(DirectoryInfo folder)
  232.         {
  233.             if (folder?.Parent == null) return false;
  234.             if (!folder.Parent.Name.Equals(PluginsPathName, StringComparison.InvariantCultureIgnoreCase)) return false;
  235.  
  236.             return true;
  237.         }
  238.  
  239.         #endregion
  240.  
  241.         #region Methods
  242.  
  243.         /// <summary>
  244.         /// Initialize
  245.         /// </summary>
  246.         /// <param name="applicationPartManager">Application part manager</param>
  247.         /// <param name="config">Config</param>
  248.         public static void Initialize(ApplicationPartManager applicationPartManager, NopConfig config)
  249.         {
  250.             if (applicationPartManager == null)
  251.                 throw new ArgumentNullException(nameof(applicationPartManager));
  252.  
  253.             if (config == null)
  254.                 throw new ArgumentNullException(nameof(config));
  255.  
  256.             using (new WriteLockDisposable(Locker))
  257.             {
  258.                 // TODO: Add verbose exception handling / raising here since this is happening on app startup and could
  259.                 // prevent app from starting altogether
  260.                 var pluginFolder = new DirectoryInfo(CommonHelper.MapPath(PluginsPath));
  261.  
  262.                 var referencedPlugins = new List<PluginDescriptor>();
  263.                 var incompatiblePlugins = new List<string>();
  264.  
  265.                 try
  266.                 {
  267.                     var installedPluginSystemNames = GetInstalledPluginNames(CommonHelper.MapPath(InstalledPluginsFilePath));
  268.  
  269.                     //ensure folders are created
  270.                     Directory.CreateDirectory(pluginFolder.FullName);
  271.  
  272.                     //load description files
  273.                     foreach (var dfd in GetDescriptionFilesAndDescriptors(pluginFolder))
  274.                     {
  275.                         var descriptionFile = dfd.Key;
  276.                         var pluginDescriptor = dfd.Value;
  277.  
  278.                         //ensure that version of plugin is valid
  279.                         if (!pluginDescriptor.SupportedVersions.Contains(NopVersion.CurrentVersion, StringComparer.InvariantCultureIgnoreCase))
  280.                         {
  281.                             incompatiblePlugins.Add(pluginDescriptor.SystemName);
  282.                             continue;
  283.                         }
  284.  
  285.                         //some validation
  286.                         if (string.IsNullOrWhiteSpace(pluginDescriptor.SystemName))
  287.                             throw new Exception($"A plugin '{descriptionFile.FullName}' has no system name. Try assigning the plugin a unique name and recompiling.");
  288.                         if (referencedPlugins.Contains(pluginDescriptor))
  289.                             throw new Exception($"A plugin with '{pluginDescriptor.SystemName}' system name is already defined");
  290.  
  291.                         //set 'Installed' property
  292.                         pluginDescriptor.Installed = installedPluginSystemNames
  293.                             .FirstOrDefault(x => x.Equals(pluginDescriptor.SystemName, StringComparison.InvariantCultureIgnoreCase)) != null;
  294.  
  295.                         try
  296.                         {
  297.                             if (descriptionFile.Directory == null)
  298.                                 throw new Exception($"Directory cannot be resolved for '{descriptionFile.Name}' description file");
  299.  
  300.                             //get list of all DLLs in plugins (not in bin!)
  301.                             var pluginFiles = descriptionFile.Directory.GetFiles("*.dll", SearchOption.AllDirectories)
  302.                                 .Where(x => IsPackagePluginFolder(x.Directory))
  303.                                 .ToList();
  304.  
  305.                             //other plugin description info
  306.                             var mainPluginFile = pluginFiles
  307.                                 .FirstOrDefault(x => x.Name.Equals(pluginDescriptor.AssemblyFileName, StringComparison.InvariantCultureIgnoreCase));
  308.  
  309.                             //plugin have wrong directory
  310.                             if (mainPluginFile == null)
  311.                             {
  312.                                 incompatiblePlugins.Add(pluginDescriptor.SystemName);
  313.                                 continue;
  314.                             }
  315.  
  316.                             pluginDescriptor.OriginalAssemblyFile = mainPluginFile;
  317.  
  318.                             //shadow copy main plugin file
  319.                             pluginDescriptor.ReferencedAssembly = PerformFileDeploy(mainPluginFile, applicationPartManager, config);
  320.  
  321.                             //load all other referenced assemblies now
  322.                             foreach (var plugin in pluginFiles
  323.                                 .Where(x => !x.Name.Equals(mainPluginFile.Name, StringComparison.InvariantCultureIgnoreCase))
  324.                                 .Where(x => !IsAlreadyLoaded(x)))
  325.                                 PerformFileDeploy(plugin, applicationPartManager, config);
  326.  
  327.                             //init plugin type (only one plugin per assembly is allowed)
  328.                             foreach (var t in pluginDescriptor.ReferencedAssembly.GetTypes())
  329.                                 if (typeof(IPlugin).IsAssignableFrom(t))
  330.                                     if (!t.IsInterface)
  331.                                         if (t.IsClass && !t.IsAbstract)
  332.                                         {
  333.                                             pluginDescriptor.PluginType = t;
  334.                                             break;
  335.                                         }
  336.  
  337.                             referencedPlugins.Add(pluginDescriptor);
  338.                         }
  339.                         catch (ReflectionTypeLoadException ex)
  340.                         {
  341.                             //add a plugin name. this way we can easily identify a problematic plugin
  342.                             var msg = $"Plugin '{pluginDescriptor.FriendlyName}'. ";
  343.                             foreach (var e in ex.LoaderExceptions)
  344.                                 msg += e.Message + Environment.NewLine;
  345.  
  346.                             var fail = new Exception(msg, ex);
  347.                             throw fail;
  348.                         }
  349.                         catch (Exception ex)
  350.                         {
  351.                             //add a plugin name. this way we can easily identify a problematic plugin
  352.                             var msg = $"Plugin '{pluginDescriptor.FriendlyName}'. {ex.Message}";
  353.  
  354.                             var fail = new Exception(msg, ex);
  355.                             throw fail;
  356.                         }
  357.                     }
  358.                 }
  359.                 catch (Exception ex)
  360.                 {
  361.                     var msg = string.Empty;
  362.                     for (var e = ex; e != null; e = e.InnerException)
  363.                         msg += e.Message + Environment.NewLine;
  364.  
  365.                     var fail = new Exception(msg, ex);
  366.                     throw fail;
  367.                 }
  368.  
  369.                 ReferencedPlugins = referencedPlugins;
  370.                 IncompatiblePlugins = incompatiblePlugins;
  371.             }
  372.         }
  373.  
  374.         /// <summary>
  375.         /// Mark plugin as installed
  376.         /// </summary>
  377.         /// <param name="systemName">Plugin system name</param>
  378.         public static void MarkPluginAsInstalled(string systemName)
  379.         {
  380.             if (string.IsNullOrEmpty(systemName))
  381.                 throw new ArgumentNullException(nameof(systemName));
  382.  
  383.             var filePath = CommonHelper.MapPath(InstalledPluginsFilePath);
  384.  
  385.             //create file if not exists
  386.             if (!File.Exists(filePath))
  387.             {
  388.                 //we use 'using' to close the file after it's created
  389.                 using (File.Create(filePath)) { }
  390.             }
  391.  
  392.             //get installed plugin names
  393.             var installedPluginSystemNames = GetInstalledPluginNames(filePath);
  394.  
  395.             //add plugin system name to the list if doesn't already exist
  396.             var alreadyMarkedAsInstalled = installedPluginSystemNames.Any(pluginName => pluginName.Equals(systemName, StringComparison.InvariantCultureIgnoreCase));
  397.             if (!alreadyMarkedAsInstalled)
  398.                 installedPluginSystemNames.Add(systemName);
  399.  
  400.             //save installed plugin names to the file
  401.             SaveInstalledPluginNames(installedPluginSystemNames, filePath);
  402.         }
  403.  
  404.         /// <summary>
  405.         /// Mark plugin as uninstalled
  406.         /// </summary>
  407.         /// <param name="systemName">Plugin system name</param>
  408.         public static void MarkPluginAsUninstalled(string systemName)
  409.         {
  410.             if (string.IsNullOrEmpty(systemName))
  411.                 throw new ArgumentNullException(nameof(systemName));
  412.  
  413.             var filePath = CommonHelper.MapPath(InstalledPluginsFilePath);
  414.  
  415.             //create file if not exists
  416.             if (!File.Exists(filePath))
  417.             {
  418.                 //we use 'using' to close the file after it's created
  419.                 using (File.Create(filePath)) { }
  420.             }
  421.  
  422.             //get installed plugin names
  423.             var installedPluginSystemNames = GetInstalledPluginNames(filePath);
  424.  
  425.             //remove plugin system name from the list if exists
  426.             var alreadyMarkedAsInstalled = installedPluginSystemNames.Any(pluginName => pluginName.Equals(systemName, StringComparison.InvariantCultureIgnoreCase));
  427.             if (alreadyMarkedAsInstalled)
  428.                 installedPluginSystemNames.Remove(systemName);
  429.  
  430.             //save installed plugin names to the file
  431.             SaveInstalledPluginNames(installedPluginSystemNames, filePath);
  432.         }
  433.  
  434.         /// <summary>
  435.         /// Mark plugin as uninstalled
  436.         /// </summary>
  437.         public static void MarkAllPluginsAsUninstalled()
  438.         {
  439.             var filePath = CommonHelper.MapPath(InstalledPluginsFilePath);
  440.             if (File.Exists(filePath))
  441.                 File.Delete(filePath);
  442.         }
  443.  
  444.         /// <summary>
  445.         /// Find a plugin descriptor by some type which is located into the same assembly as plugin
  446.         /// </summary>
  447.         /// <param name="typeInAssembly">Type</param>
  448.         /// <returns>Plugin descriptor if exists; otherwise null</returns>
  449.         public static PluginDescriptor FindPlugin(Type typeInAssembly)
  450.         {
  451.             if (typeInAssembly == null)
  452.                 throw new ArgumentNullException(nameof(typeInAssembly));
  453.  
  454.             if (ReferencedPlugins == null)
  455.                 return null;
  456.  
  457.             return ReferencedPlugins.FirstOrDefault(plugin => plugin.ReferencedAssembly != null
  458.                 && plugin.ReferencedAssembly.FullName.Equals(typeInAssembly.Assembly.FullName, StringComparison.InvariantCultureIgnoreCase));
  459.         }
  460.  
  461.         /// <summary>
  462.         /// Get plugin descriptor from the plugin description file
  463.         /// </summary>
  464.         /// <param name="filePath">Path to the description file</param>
  465.         /// <returns>Plugin descriptor</returns>
  466.         public static PluginDescriptor GetPluginDescriptorFromFile(string filePath)
  467.         {
  468.             var text = File.ReadAllText(filePath);
  469.  
  470.             return GetPluginDescriptorFromText(text);
  471.         }
  472.  
  473.         /// <summary>
  474.         /// Get plugin descriptor from the description text
  475.         /// </summary>
  476.         /// <param name="text">Description text</param>
  477.         /// <returns>Plugin descriptor</returns>
  478.         public static PluginDescriptor GetPluginDescriptorFromText(string text)
  479.         {
  480.             if (string.IsNullOrEmpty(text))
  481.                 return new PluginDescriptor();
  482.  
  483.             //get plugin descriptor from the JSON file
  484.             var descriptor = JsonConvert.DeserializeObject<PluginDescriptor>(text);
  485.  
  486.             //nopCommerce 2.00 didn't have 'SupportedVersions' parameter, so let's set it to "2.00"
  487.             if (!descriptor.SupportedVersions.Any())
  488.                 descriptor.SupportedVersions.Add("2.00");
  489.  
  490.             return descriptor;
  491.         }
  492.  
  493.         /// <summary>
  494.         /// Save plugin descriptor to the plugin description file
  495.         /// </summary>
  496.         /// <param name="pluginDescriptor">Plugin descriptor</param>
  497.         public static void SavePluginDescriptor(PluginDescriptor pluginDescriptor)
  498.         {
  499.             if (pluginDescriptor == null)
  500.                 throw new ArgumentException(nameof(pluginDescriptor));
  501.  
  502.             //get the description file path
  503.             if (pluginDescriptor.OriginalAssemblyFile == null)
  504.                 throw new Exception($"Cannot load original assembly path for {pluginDescriptor.SystemName} plugin.");
  505.  
  506.             var filePath = Path.Combine(pluginDescriptor.OriginalAssemblyFile.Directory.FullName, PluginDescriptionFileName);
  507.             if (!File.Exists(filePath))
  508.                 throw new Exception($"Description file for {pluginDescriptor.SystemName} plugin does not exist. {filePath}");
  509.  
  510.             //save the file
  511.             var text = JsonConvert.SerializeObject(pluginDescriptor, Formatting.Indented);
  512.             File.WriteAllText(filePath, text);
  513.         }
  514.  
  515.         /// <summary>
  516.         /// Delete plugin directory from disk storage
  517.         /// </summary>
  518.         /// <param name="pluginDescriptor">Plugin descriptor</param>
  519.         /// <returns>True if plugin directory is deleted, false if not</returns>
  520.         public static bool DeletePlugin(PluginDescriptor pluginDescriptor)
  521.         {
  522.             //no plugin descriptor set
  523.             if (pluginDescriptor == null)
  524.                 return false;
  525.  
  526.             //check whether plugin is installed
  527.             if (pluginDescriptor.Installed)
  528.                 return false;
  529.  
  530.             if (pluginDescriptor.OriginalAssemblyFile.Directory.Exists)
  531.                 CommonHelper.DeleteDirectory(pluginDescriptor.OriginalAssemblyFile.DirectoryName);
  532.  
  533.             return true;
  534.         }
  535.  
  536.         #endregion
  537.  
  538.         #region Properties
  539.  
  540.         /// <summary>
  541.         /// Gets the path to file that contained (in previous versions) installed plugin system names
  542.         /// </summary>
  543.         public static string ObsoleteInstalledPluginsFilePath => "~/App_Data/InstalledPlugins.txt";
  544.  
  545.         /// <summary>
  546.         /// Gets the path to file that contains installed plugin system names
  547.         /// </summary>
  548.         public static string InstalledPluginsFilePath => "~/App_Data/installedPlugins.json";
  549.  
  550.         /// <summary>
  551.         /// Gets the path to plugins folder
  552.         /// </summary>
  553.         public static string PluginsPath => "~/Plugins";
  554.  
  555.         /// <summary>
  556.         /// Gets the plugins folder name
  557.         /// </summary>
  558.         public static string PluginsPathName => "Plugins";
  559.  
  560.         /// <summary>
  561.         /// Gets the path to plugins shadow copies folder
  562.         /// </summary>
  563.         public static string ShadowCopyPath => "~/Plugins/bin";
  564.  
  565.         /// <summary>
  566.         /// Gets the path to plugins refs folder
  567.         /// </summary>
  568.         public static string RefsPathName => "refs";
  569.  
  570.         /// <summary>
  571.         /// Gets the name of the plugin description file
  572.         /// </summary>
  573.         public static string PluginDescriptionFileName => "plugin.json";
  574.  
  575.         /// <summary>
  576.         /// Returns a collection of all referenced plugin assemblies that have been shadow copied
  577.         /// </summary>
  578.         public static IEnumerable<PluginDescriptor> ReferencedPlugins { get; set; }
  579.  
  580.         /// <summary>
  581.         /// Returns a collection of all plugin which are not compatible with the current version
  582.         /// </summary>
  583.         public static IEnumerable<string> IncompatiblePlugins { get; set; }
  584.  
  585.         #endregion
  586.     }
  587. }
RAW Paste Data