博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
nopCommerce 3.9 大波浪系列 之 网页加载Widgets插件原理
阅读量:5035 次
发布时间:2019-06-12

本文共 27177 字,大约阅读时间需要 90 分钟。

一.插件简介

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

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

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

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

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

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

二.小部件介绍及使用

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

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

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

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

 

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

SystemName:系统名称唯一。

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

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

对比后台理解更直观些

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

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

三.小部件调用原理分析

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

 

我们发现有很多像@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接口

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

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

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

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

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

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

1  [ChildActionOnly]  2         public virtual ActionResult WidgetsByZone(string widgetZone, object additionalData = null)  3         {  4 	    //查找到符合要求的List
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
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 ///   2         /// 获取显示插件的路由  3         ///   4         /// Widget zone where it's displayed  5         /// Action name  6         /// Controller name  7         /// Route values  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是如何找到我们开发的小部件呢?继续看图。

IWidgetModelFactory

   GetRenderWidgetModels(widgetZone, additionalData)方法,

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

IWidgetService

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

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

1  ///   2         /// Load active widgets  3         ///   4         /// Widget zone  5         /// Load records allowed only to a specified customer; pass null to ignore ACL permissions  6         /// Load records allowed only in a specified store; pass 0 to load all records  7         /// 
Widgets
8 public virtual IList
LoadActiveWidgetsByWidgetZone(string widgetZone, Customer customer = null, int storeId = 0) 9 { 10 if (String.IsNullOrWhiteSpace(widgetZone)) 11 return new List
(); 12 13 return LoadActiveWidgets(customer, storeId) 14 .Where(x => x.GetWidgetZones().Contains(widgetZone, StringComparer.InvariantCultureIgnoreCase)).ToList(); 15 } 16 17 ///
18 /// Load active widgets 19 /// 20 ///
Load records allowed only to a specified customer; pass null to ignore ACL permissions 21 ///
Load records allowed only in a specified store; pass 0 to load all records 22 ///
Widgets
23 public virtual IList
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 ///
30 /// Load all widgets 31 /// 32 ///
Load records allowed only to a specified customer; pass null to ignore ACL permissions 33 ///
Load records allowed only in a specified store; pass 0 to load all records 34 ///
Widgets
35 public virtual IList
LoadAllWidgets(Customer customer = null, int storeId = 0) 36 { 37 return _pluginFinder.GetPlugins
(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     ///  21     /// Sets the application up for the plugin referencing 22     ///  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         ///  44         /// Returns a collection of all referenced plugin assemblies that have been shadow copied 45         ///  46         public static IEnumerable
ReferencedPlugins { get; set; } 47 48 ///
49 /// Returns a collection of all plugin which are not compatible with the current version 50 /// 51 public static IEnumerable
IncompatiblePlugins { get; set; } 52 53 ///
54 /// Initialize 55 /// 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
(); 66 var incompatiblePlugins = new List
(); 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 files100 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 valid106 if (!pluginDescriptor.SupportedVersions.Contains(NopVersion.CurrentVersion, StringComparer.InvariantCultureIgnoreCase))107 {108 incompatiblePlugins.Add(pluginDescriptor.SystemName);109 continue;110 }111 112 //some validation113 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' property119 pluginDescriptor.Installed = installedPluginSystemNames120 .FirstOrDefault(x => x.Equals(pluginDescriptor.SystemName, StringComparison.InvariantCultureIgnoreCase)) != null;121 122 try123 {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 plugins129 .Where(x => !binFiles.Select(q => q.FullName).Contains(x.FullName))130 .Where(x => IsPackagePluginFolder(x.Directory))131 .ToList();132 133 //other plugin description info134 var mainPluginFile = pluginFiles135 .FirstOrDefault(x => x.Name.Equals(pluginDescriptor.PluginFileName, StringComparison.InvariantCultureIgnoreCase));136 pluginDescriptor.OriginalAssemblyFile = mainPluginFile;137 138 //shadow copy main plugin file139 pluginDescriptor.ReferencedAssembly = PerformFileDeploy(mainPluginFile);140 141 //load all other referenced assemblies now142 foreach (var plugin in pluginFiles143 .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 plugin162 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 plugin172 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 ///
197 /// Mark plugin as installed198 /// 199 ///
Plugin system name200 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 created210 }211 212 213 var installedPluginSystemNames = PluginFileParser.ParseInstalledPluginsFile(GetInstalledPluginsFilePath());214 bool alreadyMarkedAsInstalled = installedPluginSystemNames215 .FirstOrDefault(x => x.Equals(systemName, StringComparison.InvariantCultureIgnoreCase)) != null;216 if (!alreadyMarkedAsInstalled)217 installedPluginSystemNames.Add(systemName);218 PluginFileParser.SaveInstalledPluginsFile(installedPluginSystemNames,filePath);219 }220 221 ///
222 /// Mark plugin as uninstalled223 /// 224 ///
Plugin system name225 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 created235 }236 237 238 var installedPluginSystemNames = PluginFileParser.ParseInstalledPluginsFile(GetInstalledPluginsFilePath());239 bool alreadyMarkedAsInstalled = installedPluginSystemNames240 .FirstOrDefault(x => x.Equals(systemName, StringComparison.InvariantCultureIgnoreCase)) != null;241 if (alreadyMarkedAsInstalled)242 installedPluginSystemNames.Remove(systemName);243 PluginFileParser.SaveInstalledPluginsFile(installedPluginSystemNames,filePath);244 }245 246 ///
247 /// Mark plugin as uninstalled248 /// 249 public static void MarkAllPluginsAsUninstalled()250 {251 var filePath = CommonHelper.MapPath(InstalledPluginsFilePath);252 if (File.Exists(filePath))253 File.Delete(filePath);254 }255 256 #endregion257 258 #region Utilities259 260 ///
261 /// Get description files262 /// 263 ///
Plugin directory info264 ///
Original and parsed description files
265 private static IEnumerable
> GetDescriptionFilesAndDescriptors(DirectoryInfo pluginFolder)266 {267 if (pluginFolder == null)268 throw new ArgumentNullException("pluginFolder");269 270 //create list (
)271 var result = new List
>();272 //add display order and path to list273 foreach (var descriptionFile in pluginFolder.GetFiles("Description.txt", SearchOption.AllDirectories))274 {275 if (!IsPackagePluginFolder(descriptionFile.Directory))276 continue;277 278 //parse file279 var pluginDescriptor = PluginFileParser.ParsePluginDescriptionFile(descriptionFile.FullName);280 281 //populate list282 result.Add(new KeyValuePair
(descriptionFile, pluginDescriptor));283 }284 285 //sort list by display order. NOTE: Lowest DisplayOrder will be first i.e 0 , 1, 1, 1, 5, 10286 //it's required: http://www.nopcommerce.com/boards/t/17455/load-plugins-based-on-their-displayorder-on-startup.aspx287 result.Sort((firstPair, nextPair) => firstPair.Value.DisplayOrder.CompareTo(nextPair.Value.DisplayOrder));288 return result;289 }290 291 ///
292 /// Indicates whether assembly file is already loaded293 /// 294 ///
File info295 ///
Result
296 private static bool IsAlreadyLoaded(FileInfo fileInfo)297 {298 //compare full assembly name299 //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 filename308 try309 {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 ///
328 /// Perform file deply329 /// 330 ///
Plugin file info331 ///
Assembly
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.config343 344 //were running in med trust, so copy to custom bin folder345 var shadowCopyPlugFolder = Directory.CreateDirectory(_shadowCopyFolder.FullName);346 shadowCopiedPlug = InitializeMediumTrust(plug, shadowCopyPlugFolder);347 }348 else349 {350 var directory = AppDomain.CurrentDomain.DynamicDirectory;351 Debug.WriteLine(plug.FullName + " to " + directory);352 //were running in full trust so copy to standard dynamic folder353 shadowCopiedPlug = InitializeFullTrust(plug, new DirectoryInfo(directory));354 }355 356 //we can now register the plugin definition357 var shadowCopiedAssembly = Assembly.Load(AssemblyName.GetAssemblyName(shadowCopiedPlug.FullName));358 359 //add the reference to the build manager360 Debug.WriteLine("Adding to BuildManager: '{0}'", shadowCopiedAssembly.FullName);361 BuildManager.AddReferencedAssembly(shadowCopiedAssembly);362 363 return shadowCopiedAssembly;364 }365 366 ///
367 /// Used to initialize plugins when running in Full Trust368 /// 369 ///
370 ///
371 ///
372 private static FileInfo InitializeFullTrust(FileInfo plug, DirectoryInfo shadowCopyPlugFolder)373 {374 var shadowCopiedPlug = new FileInfo(Path.Combine(shadowCopyPlugFolder.FullName, plug.Name));375 try376 {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 them384 //which releases the lock, so that it what we are doing here, once it's renamed, we can re-shadow copy385 try386 {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 copy395 File.Copy(plug.FullName, shadowCopiedPlug.FullName, true);396 }397 return shadowCopiedPlug;398 }399 400 ///
401 /// Used to initialize plugins when running in Medium Trust402 /// 403 ///
404 ///
405 ///
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 copy412 if (shadowCopiedPlug.Exists)413 {414 //it's better to use LastWriteTimeUTC, but not all file systems have this property415 //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 else423 {424 //delete an existing file425 426 //More info: http://www.nopcommerce.com/boards/t/11511/access-error-nopplugindiscountrulesbillingcountrydll.aspx?p=4#60838427 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 try435 {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 them443 //which releases the lock, so that it what we are doing here, once it's renamed, we can re-shadow copy444 try445 {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 copy454 File.Copy(plug.FullName, shadowCopiedPlug.FullName, true);455 }456 }457 458 return shadowCopiedPlug;459 }460 461 ///
462 /// Determines if the folder is a bin plugin folder for a package463 /// 464 ///
465 ///
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 ///
475 /// Gets the full path of InstalledPlugins.txt file476 /// 477 ///
478 private static string GetInstalledPluginsFilePath()479 {480 return CommonHelper.MapPath(InstalledPluginsFilePath);481 }482 483 #endregion484 }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()返回的集合中。

 

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

本文地址:

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

转载于:https://www.cnblogs.com/yaoshangjin/p/7239183.html

你可能感兴趣的文章
Feign使用Hystrix无效原因及解决方法
查看>>
Sam做题记录
查看>>
软件工程APP进度更新
查看>>
hexo 搭建博客
查看>>
建造者模式(屌丝专用)
查看>>
Nginx + Tomcat 反向代理 如何在高效的在一台服务器部署多个站点
查看>>
C++的引用
查看>>
T-SQL查询进阶--深入浅出视图
查看>>
Android读取url图片保存及文件读取
查看>>
完整ASP.Net Excel导入
查看>>
循环队列的运用---求K阶斐波那契序列
查看>>
python itertools
查看>>
http://lorempixel.com/ 可以快速产生假图
查看>>
编写一个函数isMerge,判断一个字符串str是否可以由其他两个字符串part1和part2“组合”而成...
查看>>
文件操作
查看>>
NYOJ-613//HDU-1176-免费馅饼,数字三角形的兄弟~~
查看>>
linux下设置固定IP的方法
查看>>
ubuntu 16.04 (软件应用)-输入法
查看>>
graphite custom functions
查看>>
js获取请求地址后面带的参数
查看>>