PageRenderTime 61ms CodeModel.GetById 16ms app.highlight 38ms RepoModel.GetById 1ms app.codeStats 0ms

/BlogEngine/DotNetSlave.BusinessLogic/Web/Extensions/ExtensionManager.cs

#
C# | 534 lines | 266 code | 52 blank | 216 comment | 48 complexity | 23b6ce64e86335bbaa1d6877f8388075 MD5 | raw file
  1namespace BlogEngine.Core.Web.Extensions
  2{
  3    using System;
  4    using System.Collections.Generic;
  5    using System.Collections.Specialized;
  6    using System.IO;
  7    using System.Linq;
  8    using System.Reflection;
  9    using System.Web;
 10    using System.Web.Hosting;
 11    using System.Xml.Serialization;
 12
 13    using BlogEngine.Core.Web.Controls;
 14
 15    /// <summary>
 16    /// Extension Manager - top level object in the hierarchy
 17    ///     Holds collection of extensions and methods to manipulate
 18    ///     extensions
 19    /// </summary>
 20    [XmlRoot]
 21    public class ExtensionManager
 22    {
 23        #region Constants and Fields
 24
 25        /// <summary>
 26        ///     The extensions.
 27        /// </summary>
 28		private static Dictionary<string, ManagedExtension> extensions = new Dictionary<string, ManagedExtension>();
 29
 30        /// <summary>
 31        ///     The new extensions.
 32        /// </summary>
 33        private static StringCollection newExtensions = new StringCollection();
 34
 35        #endregion
 36
 37        #region Properties
 38
 39        /// <summary>
 40        ///     Gets a collection of extensions
 41        /// </summary>
 42        [XmlElement]
 43		public static Dictionary<string, ManagedExtension> Extensions
 44        {
 45            get { return extensions; }
 46        }
 47
 48        /// <summary>
 49        ///     Gets or sets an exeption thrown when extension can not be serialized because of
 50        ///     file access permission. Not serializable, used by UI to show error message.
 51        /// </summary>
 52        [XmlIgnore]
 53        public static Exception FileAccessException { get; set; }
 54
 55        #endregion
 56
 57        #region Public Methods
 58
 59        /// <summary>
 60        /// Method to change extension status
 61        /// </summary>
 62        /// <param name="extension">
 63        /// Extensio Name
 64        /// </param>
 65        /// <param name="enabled">
 66        /// If true, enables extension
 67        /// </param>
 68        public static void ChangeStatus(string extension, bool enabled)
 69        {
 70            foreach (var x in extensions.Where(x => x.Key == extension))
 71            {
 72                x.Value.Enabled = enabled;
 73                var xs = new DataStore.ExtensionSettings(x.Key);
 74                xs.SaveSettings(x.Value);
 75                SaveToCache();
 76
 77                var configPath = string.Format("{0}Web.Config", HostingEnvironment.ApplicationPhysicalPath);
 78                File.SetLastWriteTimeUtc(configPath, DateTime.UtcNow);
 79                break;
 80            }
 81        }
 82
 83        /// <summary>
 84        /// Tell if manager already has this extension
 85        /// </summary>
 86        /// <param name="type">
 87        /// Extension Type
 88        /// </param>
 89        /// <returns>
 90        /// True if already has
 91        /// </returns>
 92        public static bool Contains(Type type)
 93        {
 94            return extensions.Any(extension => extension.Key == type.Name);
 95        }
 96
 97        /// <summary>
 98        /// Enabled / Disabled
 99        /// </summary>
100        /// <param name="extensionName">
101        /// Name of the extension.
102        /// </param>
103        /// <returns>
104        /// True if enabled
105        /// </returns>
106        public static bool ExtensionEnabled(string extensionName)
107        {
108            LoadExtensions();
109            ManagedExtension extension = GetExtension(extensionName);
110
111            if (Blog.CurrentInstance.IsPrimary)
112            {
113                return extension == null ? false : extension.Enabled;
114            }
115            else
116            {
117                if (extension != null)
118                {
119                    // if the extension is disabled at the primary blog, then
120                    // it is automatically considered disabled for all child blogs.
121                    if (!extension.Enabled) { return false; }
122
123                    if (extension.Blogs != null && extension.Blogs.Contains(Blog.CurrentInstance.Id))
124                    {
125                        // when non-primary blog disables extension,
126                        // this blog added to Blogs list for this extension
127                        return false;
128                    }
129                }
130                return true;
131            }
132        }
133
134        /// <summary>
135        /// Returns extension to the client based on name
136        /// </summary>
137        /// <param name="extensionName">
138        /// Extension name
139        /// </param>
140        /// <returns>
141        /// Extension object or null
142        /// </returns>
143        public static ManagedExtension GetExtension(string extensionName)
144        {
145            ManagedExtension extension;
146            extensions.TryGetValue(extensionName, out extension);
147            return extension;
148        }
149
150        /// <summary>
151        /// Method to get settings collection
152        /// </summary>
153        /// <param name="extensionName">
154        /// Extension Name
155        /// </param>
156        /// <returns>
157        /// Collection of settings
158        /// </returns>
159        public static ExtensionSettings GetSettings(string extensionName)
160        {
161			return extensions.SelectMany(
162				x => x.Value.Settings.Where(setting => setting != null && setting.Name == extensionName)).FirstOrDefault();
163        }
164
165        /// <summary>
166        /// Returns settings for specified extension
167        /// </summary>
168        /// <param name="extensionName">
169        /// Extension Name
170        /// </param>
171        /// <param name="settingName">
172        /// Settings Name
173        /// </param>
174        /// <returns>
175        /// Settings object
176        /// </returns>
177        public static ExtensionSettings GetSettings(string extensionName, string settingName)
178        {
179            ManagedExtension extension = GetExtension(extensionName);
180            if (extension == null)
181                return null;
182
183        	return extension.Settings.Where(setting => setting != null && setting.Name == settingName).
184        			FirstOrDefault();
185        }
186
187        /// <summary>
188        /// Do initial import here.
189        ///     If already imported, let extension manager take care of settings
190        ///     To reset, blogger has to delete all settings in the manager
191        /// </summary>
192        /// <param name="settings">
193        /// The settings.
194        /// </param>
195        /// <returns>
196        /// The import settings.
197        /// </returns>
198        public static bool ImportSettings(ExtensionSettings settings)
199        {
200            return ImportSettings(settings.Name, settings);
201        }
202
203        /// <summary>
204        /// Imports the settings.
205        /// </summary>
206        /// <param name="extensionName">
207        /// Name of the extension.
208        /// </param>
209        /// <param name="settings">
210        /// The settings.
211        /// </param>
212        /// <returns>
213        /// If import settings works.
214        /// </returns>
215        public static bool ImportSettings(string extensionName, ExtensionSettings settings)
216        {
217            var ext = extensions.FirstOrDefault(x => x.Key == extensionName && !x.Value.Initialized(settings));
218            if (ext.Value != null)
219            {
220                ext.Value.InitializeSettings(settings);
221            }
222
223            SaveToCache();
224
225            // return SaveToStorage();
226            return true;
227        }
228
229        /// <summary>
230        /// Initializes settings by importing default parameters
231        /// </summary>
232        /// <param name="extensionName">
233        /// Extension Name
234        /// </param>
235        /// <param name="settings">
236        /// Settings object
237        /// </param>
238        /// <returns>
239        /// The Extension Settings
240        /// </returns>
241        public static ExtensionSettings InitSettings(string extensionName, ExtensionSettings settings)
242        {
243            ImportSettings(extensionName, settings);
244            return GetSettings(extensionName, settings.Name);
245        }
246
247        /// <summary>
248        /// Will serialize and cache ext. mgr. object
249        /// </summary>
250        public static void Save()
251        {
252            SaveToStorage();
253            SaveToCache();
254        }
255
256        /// <summary>
257        /// Will save settings (add to extension object, then
258        ///     cache and serialize all object hierarhy to XML)
259        /// </summary>
260        /// <param name="settings">
261        /// Settings object
262        /// </param>
263        public static void SaveSettings(ExtensionSettings settings)
264        {
265            SaveSettings(settings.Name, settings);
266        }
267
268        /// <summary>
269        /// The save settings.
270        /// </summary>
271        /// <param name="extensionName">
272        /// The extension name.
273        /// </param>
274        /// <param name="settings">
275        /// The settings.
276        /// </param>
277        public static void SaveSettings(string extensionName, ExtensionSettings settings)
278        {
279            var ext = extensions.FirstOrDefault(x => x.Key == extensionName);
280            if (ext.Value != null)
281            {
282                ext.Value.SaveSettings(settings);
283            }
284
285            Save();
286        }
287
288        /// <summary>
289        /// Saves ext. manager object to XML file
290        ///     or database table using provider model
291        /// </summary>
292        /// <returns>
293        /// True if successful
294        /// </returns>
295        public static bool SaveToStorage()
296        {
297            foreach (var ext in extensions)
298            {
299                var xs = new DataStore.ExtensionSettings(ext.Key);
300                xs.SaveSettings(ext.Value);
301            }
302
303            return true;
304        }
305
306        /// <summary>
307        /// Save individual extension to storage
308        /// </summary>
309        /// <param name="ext">
310        /// The Extension
311        /// </param>
312        /// <returns>
313        /// True if saved
314        /// </returns>
315        public static bool SaveToStorage(ManagedExtension ext)
316        {
317            var xs = new DataStore.ExtensionSettings(ext.Name);
318            xs.SaveSettings(ext);
319            return true;
320        }
321
322        /// <summary>
323        /// A way to let extension author to use custom
324        ///     admin page. Will show up as link on extensions page
325        /// </summary>
326        /// <param name="extension">
327        /// Extension Name
328        /// </param>
329        /// <param name="url">
330        /// Path to custom admin page
331        /// </param>
332        public static void SetAdminPage(string extension, string url)
333        {
334            var x = extensions.FirstOrDefault(ext => ext.Key == extension);
335            if (x.Value == null)
336            {
337                return;
338            }
339
340            x.Value.AdminPage = url;
341            SaveToStorage();
342            SaveToCache();
343        }
344
345        /// <summary>
346        /// Only change status on first load;
347        ///     This allows to enable/disable extension on
348        ///     initial load and then be able to override it with
349        ///     change status from admin interface
350        /// </summary>
351        /// <param name="extension">
352        /// Extension Name
353        /// </param>
354        /// <param name="enabled">
355        /// Enable/disable extension on initial load
356        /// </param>
357        public static void SetStatus(string extension, bool enabled)
358        {
359            if (IsNewExtension(extension))
360            {
361                ChangeStatus(extension, enabled);
362            }
363        }
364
365        /// <summary>
366        /// Show of hide settings in the admin/extensions list
367        /// </summary>
368        /// <param name="extensionName">
369        /// Extension name
370        /// </param>
371        /// <param name="flag">
372        /// True of false
373        /// </param>
374        public static void ShowSettings(string extensionName, bool flag)
375        {
376            var extension = extensions.FirstOrDefault(ext => ext.Key == extensionName);
377            if (extension.Value == null)
378            {
379                return;
380            }
381
382            extension.Value.ShowSettings = flag;
383            Save();
384        }
385
386        #endregion
387
388        #region Methods
389
390        /// <summary>
391        /// Returns extension object
392        /// </summary>
393        /// <param name="name">
394        /// Extension name
395        /// </param>
396        /// <returns>
397        /// The Extension
398        /// </returns>
399        private static ManagedExtension DataStoreExtension(string name)
400        {
401            ManagedExtension ex = null;
402            var xs = new DataStore.ExtensionSettings(name);
403            var serializer = new XmlSerializer(typeof(ManagedExtension));
404            var o = xs.GetSettings();
405
406            if (o != null)
407            {
408                if (o.GetType().Name == "FileStream")
409                {
410                    Stream stm = (FileStream)o;
411                    ex = (ManagedExtension)serializer.Deserialize(stm);
412                    stm.Close();
413                }
414                else
415                {
416                    if (!string.IsNullOrEmpty((string)o))
417                    {
418                        using (var reader = new StringReader(o.ToString()))
419                        {
420                            ex = (ManagedExtension)serializer.Deserialize(reader);
421                        }
422                    }
423                }
424            }
425
426            return ex;
427        }
428
429        /// <summary>
430        /// Extension is "new" if it is loaded from assembly
431        ///     but not yet saved to the disk. This state is needed
432        ///     so that we can initialize extension and its settings
433        ///     on the first load and then override it from admin
434        /// </summary>
435        /// <param name="name">
436        /// Extension name
437        /// </param>
438        /// <returns>
439        /// True if new
440        /// </returns>
441        private static bool IsNewExtension(string name)
442        {
443            return newExtensions.Contains(name);
444        }
445
446        /// <summary>
447        /// If extensions not in the cache will load
448        ///     from the XML file. If file not exists
449        ///     will load from assembly using reflection
450        /// </summary>
451        private static void LoadExtensions()
452        {
453            if (HttpRuntime.Cache["Extensions"] != null &&
454                ((Dictionary<string, ManagedExtension>)HttpRuntime.Cache["Extensions"]).Count != 0)
455            {
456                return;
457            }
458
459            var codeAssemblies = Utils.CodeAssemblies();
460
461            var meta = DataStoreExtension("MetaExtension");
462            if (meta == null)
463            {
464                extensions.Add("MetaExtension", new ManagedExtension("MetaExtension", "1.0", "Meta extension", "BlogEngine.net"));
465            }
466            else
467            {
468				if (!extensions.ContainsKey("MetaExtension"))
469                {
470                    extensions.Add("MetaExtension", meta);
471                }
472            }
473
474            foreach (Assembly a in codeAssemblies)
475            {
476                var types = a.GetTypes();
477                foreach (var type in types)
478                {
479                    var attributes = type.GetCustomAttributes(typeof(ExtensionAttribute), false);
480                    foreach (var xa in attributes.Cast<ExtensionAttribute>())
481                    {
482                        // try to load from storage
483                        try
484                        {
485                            var x = DataStoreExtension(type.Name);
486
487                            // if nothing, crete new extension
488                            if (x == null)
489                            {
490                                x = new ManagedExtension(type.Name, xa.Version, xa.Description, xa.Author);
491                                newExtensions.Add(type.Name);
492                                SaveToStorage(x);
493                            }
494                            else
495                            {
496                                // update attributes from assembly
497                                x.Version = xa.Version;
498                                x.Description = xa.Description;
499                                x.Author = xa.Author;
500
501                                if (x.Priority == 0)
502                                {
503                                    x.Priority = xa.Priority;
504                                }
505                            }
506							if (!extensions.ContainsKey(x.Name))
507								extensions.Add(x.Name, x);
508                        }
509                        catch (Exception e)
510                        {
511                            Utils.Log(string.Format("Can not load {0}: {1}", type.Name, e.Message));
512                        }
513                    }
514                }
515            }
516
517            // SaveToStorage();
518            SaveToCache();
519        }
520
521        /// <summary>
522        /// Caches for performance. If manager cached
523        ///     and not updates done, chached copy always 
524        ///     returned
525        /// </summary>
526        private static void SaveToCache()
527        {
528            HttpRuntime.Cache.Remove("Extensions");
529            HttpRuntime.Cache["Extensions"] = extensions;
530        }
531
532        #endregion
533    }
534}