zoukankan      html  css  js  c++  java
  • nopCommerce 3.9 大波浪系列 之 网页加载Widgets插件原理

    一.插件简介

           插件用于扩展nopCommerce的功能。nopCommerce有几种类型的插件如:支付、税率、配送方式、小部件等(接口如下图),更多插件可以访问nopCommerce官网

    image

      我们看下后台如何配置管理插件的。

    【后台管理】【商城配置】【挂件管理】用于配置小部件,【插件管理】【本地插件】管理本地所有插件

    image

    nop自带小部件:Nop.Plugin.Widgets.NivoSlider插件用于主页显示幻灯片,后续我们以此插件为例介绍nop是如何加载小部件的

    image

    本文主要介绍的是用于在网站中显示的小部件widgets(实现IWidgetPlugin接口)。

    只介绍网站中是如何调用的,后续文章中再介绍如何创建IWidgetPlugin插件。

    二.小部件介绍及使用

    小部件也可以叫小挂件,继承IWidgetPlugin接口。

    是用于在网站中显示的小插件。

    自带有:Nop.Plugin.Widgets.NivoSlider插件显示幻灯片。如下图红色区域

    )SDY2NP6`%C7LZM_N1LXFRA

    我们先看下NivoSlider插件文档结构,每一个插件都有一个Description.txt用于插件的描述。

    image 

    NivoSlider插件Description.txt内容如下图。

    SystemName:系统名称唯一。

    SupportedVersions: 该插件支持的nop版本号,nop版本号不对可是在插件列表里不显示的。

    FileName:程序集dll文件名,一定要和插件生成的dll文件名一样,否则报错

    image

    对比后台理解更直观些

    image

    安装、卸载插件在后台管理中心进行管理,这里就不多说了。

    安装成功的插件会将系统名称保存在项目文件"~/App_Data/InstalledPlugins.txt"中。

    三.小部件调用原理分析

          我们看网站是如显示小部件的,还是以NivoSlider插件为例.NivoSlider是在首页显示的,打开首页试图“HomeIndex.chtml”

    image 

    我们发现有很多像@Html.Widget("home_page_top")的节点。

    @Html.Widget会调用WidgetController控制器下WidgetsByZone方法从而获取显示内容并输出到页面中

      1  public static MvcHtmlString Widget(this HtmlHelper helper, string widgetZone, object additionalData = null, string area = null)
      2         {
      3             return helper.Action("WidgetsByZone", "Widget", new { widgetZone = widgetZone, additionalData = additionalData, area = area });
      4         }

    再来看下小部件中NivoSliderPlugin类,它继承了IWidgetPlugin接口

    image

    并实现了IWidgetPlugin接口GetWidgetZones()方法返回显示位置名称集合。

    我们发现NivoSlider插件包含了“home_page_top”的位置。

    Index.chtml试图中也出现了@Html.Widget("home_page_top"),因此该小部件会在首页中显示。

    所以试图中想要使用小部件,使用@Html.Widget("位置名称")就可以了。

    image

    ok,知道怎么使用了,我们再看看源码中涉及到哪些相关接口,之间调用关系是怎样的,先上图。

    Widget加载时序图

    首先WidgetController控制器WidgetsByZone会返回部分视图

      1  [ChildActionOnly]
      2         public virtual ActionResult WidgetsByZone(string widgetZone, object additionalData = null)
      3         {
      4 	    //查找到符合要求的List<RenderWidgetModel>
      5             var model = _widgetModelFactory.GetRenderWidgetModels(widgetZone, additionalData);
      6 
      7             //no data?
      8             if (!model.Any())
      9                 return Content("");
     10 
     11             return PartialView(model);
     12         }

    WidgetsByZone.cshtml中代码如下,我们发现这里重新调用了插件中某个action

      1 @model List<RenderWidgetModel>
      2 @using Nop.Web.Models.Cms;
      3 @foreach (var widget in Model)
      4 {
      5     @Html.Action(widget.ActionName, widget.ControllerName, widget.RouteValues)
      6 }

    那上边的Action信息又是哪里得到的呢?IWidgetPlugin接口GetDisplayWidgetRoute就是用来返回显示时调用的处理Action信息。

    NivoSliderPlugin类实现了GetDisplayWidgetRoute代码如下。

      1 /// <summary>
      2         /// 获取显示插件的路由
      3         /// </summary>
      4         /// <param name="widgetZone">Widget zone where it's displayed</param>
      5         /// <param name="actionName">Action name</param>
      6         /// <param name="controllerName">Controller name</param>
      7         /// <param name="routeValues">Route values</param>
      8         public void GetDisplayWidgetRoute(string widgetZone, out string actionName, out string controllerName, out RouteValueDictionary routeValues)
      9         {
     10             actionName = "PublicInfo";
     11             controllerName = "WidgetsNivoSlider";
     12             routeValues = new RouteValueDictionary
     13             {
     14                 {"Namespaces", "Nop.Plugin.Widgets.NivoSlider.Controllers"},
     15                 {"area", null},
     16                 {"widgetZone", widgetZone}
     17             };
     18         }

    总结下:WidgetController->WidgetsByZone负责调用显示插件。

               而我们开发的小部件需要实现IWidgetPlugin接口GetDisplayWidgetRoute方法告诉上层,我的显示入口是哪个controller  下的action。

      下面我们分析下nop是如何找到我们开发的小部件呢?继续看图。
    Widget加载时序图

    IWidgetModelFactory

       GetRenderWidgetModels(widgetZone, additionalData)方法,

       传入小部件位置widgetZone(本例中为"home_page_top")获取List<RenderWidgetModel>

    IWidgetService

       LoadActiveWidgetsByWidgetZone(widgetZone, _workContext.CurrentCustomer, _storeContext.CurrentStore.Id)

       负责返回符合要求的IList<IWidgetPlugin>集合,过滤条件为部件位置,用户,商城。

      1  /// <summary>
      2         /// Load active widgets
      3         /// </summary>
      4         /// <param name="widgetZone">Widget zone</param>
      5         /// <param name="customer">Load records allowed only to a specified customer; pass null to ignore ACL permissions</param>
      6         /// <param name="storeId">Load records allowed only in a specified store; pass 0 to load all records</param>
      7         /// <returns>Widgets</returns>
      8         public virtual IList<IWidgetPlugin> LoadActiveWidgetsByWidgetZone(string  widgetZone, Customer customer = null, int storeId = 0)
      9         {
     10             if (String.IsNullOrWhiteSpace(widgetZone))
     11                 return new List<IWidgetPlugin>();
     12 
     13             return LoadActiveWidgets(customer, storeId)
     14                 .Where(x => x.GetWidgetZones().Contains(widgetZone, StringComparer.InvariantCultureIgnoreCase)).ToList();
     15         }
     16 
     17 /// <summary>
     18         /// Load active widgets
     19         /// </summary>
     20         /// <param name="customer">Load records allowed only to a specified customer; pass null to ignore ACL permissions</param>
     21         /// <param name="storeId">Load records allowed only in a specified store; pass 0 to load all records</param>
     22         /// <returns>Widgets</returns>
     23         public virtual IList<IWidgetPlugin> LoadActiveWidgets(Customer customer = null, int storeId = 0)
     24         {
     25             return LoadAllWidgets(customer, storeId)
     26                 .Where(x => _widgetSettings.ActiveWidgetSystemNames.Contains(x.PluginDescriptor.SystemName, StringComparer.InvariantCultureIgnoreCase)).ToList();
     27         }
     28 
     29 /// <summary>
     30         /// Load all widgets
     31         /// </summary>
     32         /// <param name="customer">Load records allowed only to a specified customer; pass null to ignore ACL permissions</param>
     33         /// <param name="storeId">Load records allowed only in a specified store; pass 0 to load all records</param>
     34         /// <returns>Widgets</returns>
     35         public virtual IList<IWidgetPlugin> LoadAllWidgets(Customer customer = null, int storeId = 0)
     36         {
     37             return _pluginFinder.GetPlugins<IWidgetPlugin>(customer: customer, storeId: storeId).ToList();
     38         }
    按条件获取到可用小部件

    IPluginFinder

       _pluginFinder.GetPlugins<IWidgetPlugin>(customer: customer, storeId: storeId).ToList();

       查询继承IWidgetPlugin接口的插件也就是小部件了,这里只返回IWidgetPlugin实现类。

    PluginManager

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Configuration;
      4 using System.Diagnostics;
      5 using System.IO;
      6 using System.Linq;
      7 using System.Reflection;
      8 using System.Threading;
      9 using System.Web;
     10 using System.Web.Compilation;
     11 using Nop.Core.ComponentModel;
     12 using Nop.Core.Plugins;
     13 
     14 //Contributor: Umbraco (http://www.umbraco.com). Thanks a lot! 
     15 //SEE THIS POST for full details of what this does - http://shazwazza.com/post/Developing-a-plugin-framework-in-ASPNET-with-medium-trust.aspx
     16 
     17 [assembly: PreApplicationStartMethod(typeof(PluginManager), "Initialize")]
     18 namespace Nop.Core.Plugins
     19 {
     20     /// <summary>
     21     /// Sets the application up for the plugin referencing
     22     /// </summary>
     23     public class PluginManager
     24     {
     25         #region Const
     26 
     27         private const string InstalledPluginsFilePath = "~/App_Data/InstalledPlugins.txt";
     28         private const string PluginsPath = "~/Plugins";
     29         private const string ShadowCopyPath = "~/Plugins/bin";
     30 
     31         #endregion
     32 
     33         #region Fields
     34 
     35         private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim();
     36         private static DirectoryInfo _shadowCopyFolder;
     37         private static bool _clearShadowDirectoryOnStartup;
     38 
     39         #endregion
     40 
     41         #region Methods
     42 
     43         /// <summary>
     44         /// Returns a collection of all referenced plugin assemblies that have been shadow copied
     45         /// </summary>
     46         public static IEnumerable<PluginDescriptor> ReferencedPlugins { get; set; }
     47 
     48         /// <summary>
     49         /// Returns a collection of all plugin which are not compatible with the current version
     50         /// </summary>
     51         public static IEnumerable<string> IncompatiblePlugins { get; set; }
     52 
     53         /// <summary>
     54         /// Initialize
     55         /// </summary>
     56         public static void Initialize()
     57         {
     58             using (new WriteLockDisposable(Locker))
     59             {
     60                 // TODO: Add verbose exception handling / raising here since this is happening on app startup and could
     61                 // prevent app from starting altogether
     62                 var pluginFolder = new DirectoryInfo(CommonHelper.MapPath(PluginsPath));
     63                 _shadowCopyFolder = new DirectoryInfo(CommonHelper.MapPath(ShadowCopyPath));
     64 
     65                 var referencedPlugins = new List<PluginDescriptor>();
     66                 var incompatiblePlugins = new List<string>();
     67 
     68                 _clearShadowDirectoryOnStartup = !String.IsNullOrEmpty(ConfigurationManager.AppSettings["ClearPluginsShadowDirectoryOnStartup"]) &&
     69                    Convert.ToBoolean(ConfigurationManager.AppSettings["ClearPluginsShadowDirectoryOnStartup"]);
     70 
     71                 try
     72                 {
     73                     var installedPluginSystemNames = PluginFileParser.ParseInstalledPluginsFile(GetInstalledPluginsFilePath());
     74 
     75                     Debug.WriteLine("Creating shadow copy folder and querying for dlls");
     76                     //ensure folders are created
     77                     Directory.CreateDirectory(pluginFolder.FullName);
     78                     Directory.CreateDirectory(_shadowCopyFolder.FullName);
     79 
     80                     //get list of all files in bin
     81                     var binFiles = _shadowCopyFolder.GetFiles("*", SearchOption.AllDirectories);
     82                     if (_clearShadowDirectoryOnStartup)
     83                     {
     84                         //clear out shadow copied plugins
     85                         foreach (var f in binFiles)
     86                         {
     87                             Debug.WriteLine("Deleting " + f.Name);
     88                             try
     89                             {
     90                                 File.Delete(f.FullName);
     91                             }
     92                             catch (Exception exc)
     93                             {
     94                                 Debug.WriteLine("Error deleting file " + f.Name + ". Exception: " + exc);
     95                             }
     96                         }
     97                     }
     98 
     99                     //load description files
    100                     foreach (var dfd in GetDescriptionFilesAndDescriptors(pluginFolder))
    101                     {
    102                         var descriptionFile = dfd.Key;
    103                         var pluginDescriptor = dfd.Value;
    104 
    105                         //ensure that version of plugin is valid
    106                         if (!pluginDescriptor.SupportedVersions.Contains(NopVersion.CurrentVersion, StringComparer.InvariantCultureIgnoreCase))
    107                         {
    108                             incompatiblePlugins.Add(pluginDescriptor.SystemName);
    109                             continue;
    110                         }
    111 
    112                         //some validation
    113                         if (String.IsNullOrWhiteSpace(pluginDescriptor.SystemName))
    114                             throw new Exception(string.Format("A plugin '{0}' has no system name. Try assigning the plugin a unique name and recompiling.", descriptionFile.FullName));
    115                         if (referencedPlugins.Contains(pluginDescriptor))
    116                             throw new Exception(string.Format("A plugin with '{0}' system name is already defined", pluginDescriptor.SystemName));
    117 
    118                         //set 'Installed' property
    119                         pluginDescriptor.Installed = installedPluginSystemNames
    120                             .FirstOrDefault(x => x.Equals(pluginDescriptor.SystemName, StringComparison.InvariantCultureIgnoreCase)) != null;
    121 
    122                         try
    123                         {
    124                             if (descriptionFile.Directory == null)
    125                                 throw new Exception(string.Format("Directory cannot be resolved for '{0}' description file", descriptionFile.Name));
    126                             //get list of all DLLs in plugins (not in bin!)
    127                             var pluginFiles = descriptionFile.Directory.GetFiles("*.dll", SearchOption.AllDirectories)
    128                                 //just make sure we're not registering shadow copied plugins
    129                                 .Where(x => !binFiles.Select(q => q.FullName).Contains(x.FullName))
    130                                 .Where(x => IsPackagePluginFolder(x.Directory))
    131                                 .ToList();
    132 
    133                             //other plugin description info
    134                             var mainPluginFile = pluginFiles
    135                                 .FirstOrDefault(x => x.Name.Equals(pluginDescriptor.PluginFileName, StringComparison.InvariantCultureIgnoreCase));
    136                             pluginDescriptor.OriginalAssemblyFile = mainPluginFile;
    137 
    138                             //shadow copy main plugin file
    139                             pluginDescriptor.ReferencedAssembly = PerformFileDeploy(mainPluginFile);
    140 
    141                             //load all other referenced assemblies now
    142                             foreach (var plugin in pluginFiles
    143                                 .Where(x => !x.Name.Equals(mainPluginFile.Name, StringComparison.InvariantCultureIgnoreCase))
    144                                 .Where(x => !IsAlreadyLoaded(x)))
    145                                     PerformFileDeploy(plugin);
    146 
    147                             //init plugin type (only one plugin per assembly is allowed)
    148                             foreach (var t in pluginDescriptor.ReferencedAssembly.GetTypes())
    149                                 if (typeof(IPlugin).IsAssignableFrom(t))
    150                                     if (!t.IsInterface)
    151                                         if (t.IsClass && !t.IsAbstract)
    152                                         {
    153                                             pluginDescriptor.PluginType = t;
    154                                             break;
    155                                         }
    156 
    157                             referencedPlugins.Add(pluginDescriptor);
    158                         }
    159                         catch (ReflectionTypeLoadException ex)
    160                         {
    161                             //add a plugin name. this way we can easily identify a problematic plugin
    162                             var msg = string.Format("Plugin '{0}'. ", pluginDescriptor.FriendlyName);
    163                             foreach (var e in ex.LoaderExceptions)
    164                                 msg += e.Message + Environment.NewLine;
    165 
    166                             var fail = new Exception(msg, ex);
    167                             throw fail;
    168                         }
    169                         catch (Exception ex)
    170                         {
    171                             //add a plugin name. this way we can easily identify a problematic plugin
    172                             var msg = string.Format("Plugin '{0}'. {1}", pluginDescriptor.FriendlyName, ex.Message);
    173 
    174                             var fail = new Exception(msg, ex);
    175                             throw fail;
    176                         }
    177                     }
    178                 }
    179                 catch (Exception ex)
    180                 {
    181                     var msg = string.Empty;
    182                     for (var e = ex; e != null; e = e.InnerException)
    183                         msg += e.Message + Environment.NewLine;
    184 
    185                     var fail = new Exception(msg, ex);
    186                     throw fail;
    187                 }
    188 
    189 
    190                 ReferencedPlugins = referencedPlugins;
    191                 IncompatiblePlugins = incompatiblePlugins;
    192 
    193             }
    194         }
    195 
    196         /// <summary>
    197         /// Mark plugin as installed
    198         /// </summary>
    199         /// <param name="systemName">Plugin system name</param>
    200         public static void MarkPluginAsInstalled(string systemName)
    201         {
    202             if (String.IsNullOrEmpty(systemName))
    203                 throw new ArgumentNullException("systemName");
    204 
    205             var filePath = CommonHelper.MapPath(InstalledPluginsFilePath);
    206             if (!File.Exists(filePath))
    207                 using (File.Create(filePath))
    208                 {
    209                     //we use 'using' to close the file after it's created
    210                 }
    211 
    212 
    213             var installedPluginSystemNames = PluginFileParser.ParseInstalledPluginsFile(GetInstalledPluginsFilePath());
    214             bool alreadyMarkedAsInstalled = installedPluginSystemNames
    215                                 .FirstOrDefault(x => x.Equals(systemName, StringComparison.InvariantCultureIgnoreCase)) != null;
    216             if (!alreadyMarkedAsInstalled)
    217                 installedPluginSystemNames.Add(systemName);
    218             PluginFileParser.SaveInstalledPluginsFile(installedPluginSystemNames,filePath);
    219         }
    220 
    221         /// <summary>
    222         /// Mark plugin as uninstalled
    223         /// </summary>
    224         /// <param name="systemName">Plugin system name</param>
    225         public static void MarkPluginAsUninstalled(string systemName)
    226         {
    227             if (String.IsNullOrEmpty(systemName))
    228                 throw new ArgumentNullException("systemName");
    229 
    230             var filePath = CommonHelper.MapPath(InstalledPluginsFilePath);
    231             if (!File.Exists(filePath))
    232                 using (File.Create(filePath))
    233                 {
    234                     //we use 'using' to close the file after it's created
    235                 }
    236 
    237 
    238             var installedPluginSystemNames = PluginFileParser.ParseInstalledPluginsFile(GetInstalledPluginsFilePath());
    239             bool alreadyMarkedAsInstalled = installedPluginSystemNames
    240                                 .FirstOrDefault(x => x.Equals(systemName, StringComparison.InvariantCultureIgnoreCase)) != null;
    241             if (alreadyMarkedAsInstalled)
    242                 installedPluginSystemNames.Remove(systemName);
    243             PluginFileParser.SaveInstalledPluginsFile(installedPluginSystemNames,filePath);
    244         }
    245 
    246         /// <summary>
    247         /// Mark plugin as uninstalled
    248         /// </summary>
    249         public static void MarkAllPluginsAsUninstalled()
    250         {
    251             var filePath = CommonHelper.MapPath(InstalledPluginsFilePath);
    252             if (File.Exists(filePath))
    253                 File.Delete(filePath);
    254         }
    255 
    256         #endregion
    257 
    258         #region Utilities
    259 
    260         /// <summary>
    261         /// Get description files
    262         /// </summary>
    263         /// <param name="pluginFolder">Plugin directory info</param>
    264         /// <returns>Original and parsed description files</returns>
    265         private static IEnumerable<KeyValuePair<FileInfo, PluginDescriptor>> GetDescriptionFilesAndDescriptors(DirectoryInfo pluginFolder)
    266         {
    267             if (pluginFolder == null)
    268                 throw new ArgumentNullException("pluginFolder");
    269 
    270             //create list (<file info, parsed plugin descritor>)
    271             var result = new List<KeyValuePair<FileInfo, PluginDescriptor>>();
    272             //add display order and path to list
    273             foreach (var descriptionFile in pluginFolder.GetFiles("Description.txt", SearchOption.AllDirectories))
    274             {
    275                 if (!IsPackagePluginFolder(descriptionFile.Directory))
    276                     continue;
    277 
    278                 //parse file
    279                 var pluginDescriptor = PluginFileParser.ParsePluginDescriptionFile(descriptionFile.FullName);
    280 
    281                 //populate list
    282                 result.Add(new KeyValuePair<FileInfo, PluginDescriptor>(descriptionFile, pluginDescriptor));
    283             }
    284 
    285             //sort list by display order. NOTE: Lowest DisplayOrder will be first i.e 0 , 1, 1, 1, 5, 10
    286             //it's required: http://www.nopcommerce.com/boards/t/17455/load-plugins-based-on-their-displayorder-on-startup.aspx
    287             result.Sort((firstPair, nextPair) => firstPair.Value.DisplayOrder.CompareTo(nextPair.Value.DisplayOrder));
    288             return result;
    289         }
    290 
    291         /// <summary>
    292         /// Indicates whether assembly file is already loaded
    293         /// </summary>
    294         /// <param name="fileInfo">File info</param>
    295         /// <returns>Result</returns>
    296         private static bool IsAlreadyLoaded(FileInfo fileInfo)
    297         {
    298             //compare full assembly name
    299             //var fileAssemblyName = AssemblyName.GetAssemblyName(fileInfo.FullName);
    300             //foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
    301             //{
    302             //    if (a.FullName.Equals(fileAssemblyName.FullName, StringComparison.InvariantCultureIgnoreCase))
    303             //        return true;
    304             //}
    305             //return false;
    306 
    307             //do not compare the full assembly name, just filename
    308             try
    309             {
    310                 string fileNameWithoutExt = Path.GetFileNameWithoutExtension(fileInfo.FullName);
    311                 if (fileNameWithoutExt == null)
    312                     throw new Exception(string.Format("Cannot get file extension for {0}", fileInfo.Name));
    313                 foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
    314                 {
    315                     string assemblyName = a.FullName.Split(new[] { ',' }).FirstOrDefault();
    316                     if (fileNameWithoutExt.Equals(assemblyName, StringComparison.InvariantCultureIgnoreCase))
    317                         return true;
    318                 }
    319             }
    320             catch (Exception exc)
    321             {
    322                 Debug.WriteLine("Cannot validate whether an assembly is already loaded. " + exc);
    323             }
    324             return false;
    325         }
    326 
    327         /// <summary>
    328         /// Perform file deply
    329         /// </summary>
    330         /// <param name="plug">Plugin file info</param>
    331         /// <returns>Assembly</returns>
    332         private static Assembly PerformFileDeploy(FileInfo plug)
    333         {
    334             if (plug.Directory == null || plug.Directory.Parent == null)
    335                 throw new InvalidOperationException("The plugin directory for the " + plug.Name + " file exists in a folder outside of the allowed nopCommerce folder hierarchy");
    336 
    337             FileInfo shadowCopiedPlug;
    338 
    339             if (CommonHelper.GetTrustLevel() != AspNetHostingPermissionLevel.Unrestricted)
    340             {
    341                 //all plugins will need to be copied to ~/Plugins/bin/
    342                 //this is absolutely required because all of this relies on probingPaths being set statically in the web.config
    343 
    344                 //were running in med trust, so copy to custom bin folder
    345                 var shadowCopyPlugFolder = Directory.CreateDirectory(_shadowCopyFolder.FullName);
    346                 shadowCopiedPlug = InitializeMediumTrust(plug, shadowCopyPlugFolder);
    347             }
    348             else
    349             {
    350                 var directory = AppDomain.CurrentDomain.DynamicDirectory;
    351                 Debug.WriteLine(plug.FullName + " to " + directory);
    352                 //were running in full trust so copy to standard dynamic folder
    353                 shadowCopiedPlug = InitializeFullTrust(plug, new DirectoryInfo(directory));
    354             }
    355 
    356             //we can now register the plugin definition
    357             var shadowCopiedAssembly = Assembly.Load(AssemblyName.GetAssemblyName(shadowCopiedPlug.FullName));
    358 
    359             //add the reference to the build manager
    360             Debug.WriteLine("Adding to BuildManager: '{0}'", shadowCopiedAssembly.FullName);
    361             BuildManager.AddReferencedAssembly(shadowCopiedAssembly);
    362 
    363             return shadowCopiedAssembly;
    364         }
    365 
    366         /// <summary>
    367         /// Used to initialize plugins when running in Full Trust
    368         /// </summary>
    369         /// <param name="plug"></param>
    370         /// <param name="shadowCopyPlugFolder"></param>
    371         /// <returns></returns>
    372         private static FileInfo InitializeFullTrust(FileInfo plug, DirectoryInfo shadowCopyPlugFolder)
    373         {
    374             var shadowCopiedPlug = new FileInfo(Path.Combine(shadowCopyPlugFolder.FullName, plug.Name));
    375             try
    376             {
    377                 File.Copy(plug.FullName, shadowCopiedPlug.FullName, true);
    378             }
    379             catch (IOException)
    380             {
    381                 Debug.WriteLine(shadowCopiedPlug.FullName + " is locked, attempting to rename");
    382                 //this occurs when the files are locked,
    383                 //for some reason devenv locks plugin files some times and for another crazy reason you are allowed to rename them
    384                 //which releases the lock, so that it what we are doing here, once it's renamed, we can re-shadow copy
    385                 try
    386                 {
    387                     var oldFile = shadowCopiedPlug.FullName + Guid.NewGuid().ToString("N") + ".old";
    388                     File.Move(shadowCopiedPlug.FullName, oldFile);
    389                 }
    390                 catch (IOException exc)
    391                 {
    392                     throw new IOException(shadowCopiedPlug.FullName + " rename failed, cannot initialize plugin", exc);
    393                 }
    394                 //ok, we've made it this far, now retry the shadow copy
    395                 File.Copy(plug.FullName, shadowCopiedPlug.FullName, true);
    396             }
    397             return shadowCopiedPlug;
    398         }
    399 
    400         /// <summary>
    401         /// Used to initialize plugins when running in Medium Trust
    402         /// </summary>
    403         /// <param name="plug"></param>
    404         /// <param name="shadowCopyPlugFolder"></param>
    405         /// <returns></returns>
    406         private static FileInfo InitializeMediumTrust(FileInfo plug, DirectoryInfo shadowCopyPlugFolder)
    407         {
    408             var shouldCopy = true;
    409             var shadowCopiedPlug = new FileInfo(Path.Combine(shadowCopyPlugFolder.FullName, plug.Name));
    410 
    411             //check if a shadow copied file already exists and if it does, check if it's updated, if not don't copy
    412             if (shadowCopiedPlug.Exists)
    413             {
    414                 //it's better to use LastWriteTimeUTC, but not all file systems have this property
    415                 //maybe it is better to compare file hash?
    416                 var areFilesIdentical = shadowCopiedPlug.CreationTimeUtc.Ticks >= plug.CreationTimeUtc.Ticks;
    417                 if (areFilesIdentical)
    418                 {
    419                     Debug.WriteLine("Not copying; files appear identical: '{0}'", shadowCopiedPlug.Name);
    420                     shouldCopy = false;
    421                 }
    422                 else
    423                 {
    424                     //delete an existing file
    425 
    426                     //More info: http://www.nopcommerce.com/boards/t/11511/access-error-nopplugindiscountrulesbillingcountrydll.aspx?p=4#60838
    427                     Debug.WriteLine("New plugin found; Deleting the old file: '{0}'", shadowCopiedPlug.Name);
    428                     File.Delete(shadowCopiedPlug.FullName);
    429                 }
    430             }
    431 
    432             if (shouldCopy)
    433             {
    434                 try
    435                 {
    436                     File.Copy(plug.FullName, shadowCopiedPlug.FullName, true);
    437                 }
    438                 catch (IOException)
    439                 {
    440                     Debug.WriteLine(shadowCopiedPlug.FullName + " is locked, attempting to rename");
    441                     //this occurs when the files are locked,
    442                     //for some reason devenv locks plugin files some times and for another crazy reason you are allowed to rename them
    443                     //which releases the lock, so that it what we are doing here, once it's renamed, we can re-shadow copy
    444                     try
    445                     {
    446                         var oldFile = shadowCopiedPlug.FullName + Guid.NewGuid().ToString("N") + ".old";
    447                         File.Move(shadowCopiedPlug.FullName, oldFile);
    448                     }
    449                     catch (IOException exc)
    450                     {
    451                         throw new IOException(shadowCopiedPlug.FullName + " rename failed, cannot initialize plugin", exc);
    452                     }
    453                     //ok, we've made it this far, now retry the shadow copy
    454                     File.Copy(plug.FullName, shadowCopiedPlug.FullName, true);
    455                 }
    456             }
    457 
    458             return shadowCopiedPlug;
    459         }
    460 
    461         /// <summary>
    462         /// Determines if the folder is a bin plugin folder for a package
    463         /// </summary>
    464         /// <param name="folder"></param>
    465         /// <returns></returns>
    466         private static bool IsPackagePluginFolder(DirectoryInfo folder)
    467         {
    468             if (folder == null) return false;
    469             if (folder.Parent == null) return false;
    470             if (!folder.Parent.Name.Equals("Plugins", StringComparison.InvariantCultureIgnoreCase)) return false;
    471             return true;
    472         }
    473 
    474         /// <summary>
    475         /// Gets the full path of InstalledPlugins.txt file
    476         /// </summary>
    477         /// <returns></returns>
    478         private static string GetInstalledPluginsFilePath()
    479         {
    480             return CommonHelper.MapPath(InstalledPluginsFilePath);
    481         }
    482 
    483         #endregion
    484     }
    485 }
    486 
    PluginManager

      PluginManager.ReferencedPlugins获取到所有的插件。

      PluginManager类用于管理插件,我们发现[assembly: PreApplicationStartMethod(typeof(PluginManager), "Initialize")]表示应用程序启动就调用PluginManager类下边的Initialize方法进行初始化。初始化过程中会把所有插件保存到ReferencedPlugins变量中。

    经过上边一层层的过滤查找,终于找到了符合要求的插件了并保存在IList<IWidgetPlugin>集合中。最后经过IWidgetModelFactory接口GetRenderWidgetModels(widgetZone, additionalData)方法处理保存为List<RenderWidgetModel>最后为WidgetsByZone试图使用。

    ps: 调用关系描述的不是很清晰,请见谅,大家还是看图,结合代码理解吧。

    四.总结

    1.nop支持各种插件,不同插件继承接口不一样

    2.小部件继承IWidgetPlugin,用于在网页中显示。

    3.IWidgetPlugin接口 IList<string> GetWidgetZones()返回显示部件的位置名称集合。GetDisplayWidgetRoute方法返回显示插件时的路由。

    4.网站视图中用@Html.Widget("位置名称")来显示插件。”位置名称”包含在插件GetWidgetZones()返回的集合中。

    文中有错误的理解和不正确的观点请指正、留言、一起交流共同进步。

    本文地址:http://www.cnblogs.com/yaoshangjin/p/7239183.html

    本文为大波浪原创、转载请注明出处。

    如果您认为这篇文章还不错或者有所收获,可以点击下方的【关注】按钮,因为你的支持是我继续写作,分享的最大动力!
    作者:大波浪
    声明: 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。如果您发现博客中出现了错误,或者有更好的建议、想法,请及时与我联系!!如果想找我私下交流,可以私信或者加我QQ。
  • 相关阅读:
    堆和栈的区别
    熟悉熟悉常用的几个算法用JS的实现
    JS设置CSS样式的几种方式
    javascript的基本语法、数据结构
    DOM的概念及子节点类型
    三列自适应布局
    Javascript中括号“[]”的多义性
    Javascript中大括号“{}”的多义性
    Javascript小括号“()”的多义性
    JavaScript 开发者经常忽略或误用的七个基础知识点
  • 原文地址:https://www.cnblogs.com/yaoshangjin/p/7239183.html
Copyright © 2011-2022 走看看