From a9dc5abd50803a8e69db3f4b743feba78dccf7f0 Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Fri, 9 Jul 2010 17:56:58 -0700 Subject: [PATCH] Reports module - Stores service management reports - Use IReportsCoordinator to write message to the same report through multiple components - Views to display reports --HG-- branch : dev --- src/Orchard.Web/Core/Orchard.Core.csproj | 9 +++ src/Orchard.Web/Core/Reports/AdminMenu.cs | 15 ++++ .../Reports/Controllers/AdminController.cs | 28 +++++++ src/Orchard.Web/Core/Reports/Module.txt | 11 +++ src/Orchard.Web/Core/Reports/Routes.cs | 33 +++++++++ .../ViewModels/DisplayReportViewModel.cs | 9 +++ .../ViewModels/ReportsAdminIndexViewModel.cs | 9 +++ .../Core/Reports/Views/Admin/Display.aspx | 40 ++++++++++ .../Core/Reports/Views/Admin/Index.aspx | 40 ++++++++++ src/Orchard.Web/Core/Reports/Views/Web.config | 34 +++++++++ .../Orchard.Setup/Services/SetupService.cs | 5 ++ .../Data/Migration/DataMigrationManager.cs | 12 ++- .../DefaultDataMigrationInterpreter.cs | 10 ++- .../Services/DefaultCultureManager.cs | 2 +- src/Orchard/Orchard.Framework.csproj | 9 +++ src/Orchard/Reports/Report.cs | 17 +++++ src/Orchard/Reports/ReportEntry.cs | 16 ++++ src/Orchard/Reports/ReportExtentions.cs | 16 ++++ .../Reports/Services/IReportsCoordinator.cs | 8 ++ .../Reports/Services/IReportsManager.cs | 11 +++ .../Reports/Services/IReportsPersister.cs | 8 ++ .../Reports/Services/ReportsCoordinator.cs | 35 +++++++++ .../Reports/Services/ReportsManager.cs | 74 +++++++++++++++++++ .../Reports/Services/ReportsPersister.cs | 69 +++++++++++++++++ 24 files changed, 515 insertions(+), 5 deletions(-) create mode 100644 src/Orchard.Web/Core/Reports/AdminMenu.cs create mode 100644 src/Orchard.Web/Core/Reports/Controllers/AdminController.cs create mode 100644 src/Orchard.Web/Core/Reports/Module.txt create mode 100644 src/Orchard.Web/Core/Reports/Routes.cs create mode 100644 src/Orchard.Web/Core/Reports/ViewModels/DisplayReportViewModel.cs create mode 100644 src/Orchard.Web/Core/Reports/ViewModels/ReportsAdminIndexViewModel.cs create mode 100644 src/Orchard.Web/Core/Reports/Views/Admin/Display.aspx create mode 100644 src/Orchard.Web/Core/Reports/Views/Admin/Index.aspx create mode 100644 src/Orchard.Web/Core/Reports/Views/Web.config create mode 100644 src/Orchard/Reports/Report.cs create mode 100644 src/Orchard/Reports/ReportEntry.cs create mode 100644 src/Orchard/Reports/ReportExtentions.cs create mode 100644 src/Orchard/Reports/Services/IReportsCoordinator.cs create mode 100644 src/Orchard/Reports/Services/IReportsManager.cs create mode 100644 src/Orchard/Reports/Services/IReportsPersister.cs create mode 100644 src/Orchard/Reports/Services/ReportsCoordinator.cs create mode 100644 src/Orchard/Reports/Services/ReportsManager.cs create mode 100644 src/Orchard/Reports/Services/ReportsPersister.cs diff --git a/src/Orchard.Web/Core/Orchard.Core.csproj b/src/Orchard.Web/Core/Orchard.Core.csproj index 573e12cac..c18f5cd98 100644 --- a/src/Orchard.Web/Core/Orchard.Core.csproj +++ b/src/Orchard.Web/Core/Orchard.Core.csproj @@ -82,11 +82,16 @@ + + + + + @@ -234,6 +239,9 @@ + + + @@ -300,6 +308,7 @@ + diff --git a/src/Orchard.Web/Core/Reports/AdminMenu.cs b/src/Orchard.Web/Core/Reports/AdminMenu.cs new file mode 100644 index 000000000..3c3596032 --- /dev/null +++ b/src/Orchard.Web/Core/Reports/AdminMenu.cs @@ -0,0 +1,15 @@ +using Orchard.Localization; +using Orchard.Security; +using Orchard.UI.Navigation; + +namespace Orchard.Core.Reports { + public class AdminMenu : INavigationProvider { + public Localizer T { get; set; } + public string MenuName { get { return "admin"; } } + + public void GetNavigation(NavigationBuilder builder) { + builder.Add(T("Site Configuration"), "0", + menu => menu.Add(T("Reports"), "0", item => item.Action("Index", "Admin", new { area = "Reports" }).Permission(StandardPermissions.AccessAdminPanel))); + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Core/Reports/Controllers/AdminController.cs b/src/Orchard.Web/Core/Reports/Controllers/AdminController.cs new file mode 100644 index 000000000..d8f8dd6d9 --- /dev/null +++ b/src/Orchard.Web/Core/Reports/Controllers/AdminController.cs @@ -0,0 +1,28 @@ +using System.Linq; +using System.Web.Mvc; +using Orchard.Core.Reports.ViewModels; +using Orchard.Mvc.ViewModels; +using Orchard.Reports.Services; + +namespace Orchard.Core.Reports.Controllers { + public class AdminController : Controller { + private readonly IReportsManager _reportsManager; + + public AdminController(IReportsManager reportsManager) { + _reportsManager = reportsManager; + } + + public ActionResult Index() { + var model = new ReportsAdminIndexViewModel { Reports = _reportsManager.GetReports().ToList() }; + + return View(model); + } + + public ActionResult Display(int id) { + var model = new DisplayReportViewModel { Report = _reportsManager.Get(id) }; + + return View(model); + } + + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Core/Reports/Module.txt b/src/Orchard.Web/Core/Reports/Module.txt new file mode 100644 index 000000000..9461a81b1 --- /dev/null +++ b/src/Orchard.Web/Core/Reports/Module.txt @@ -0,0 +1,11 @@ +name: Reports +antiforgery: enabled +author: The Orchard Team +website: http://orchardproject.net +version: 0.1 +orchardversion: 0.1.2010.0312 +description: The dashboard module is providing the reports screen of the application. +features: + Dashboard: + Description: Reports management. + Category: Core \ No newline at end of file diff --git a/src/Orchard.Web/Core/Reports/Routes.cs b/src/Orchard.Web/Core/Reports/Routes.cs new file mode 100644 index 000000000..c3e286fa8 --- /dev/null +++ b/src/Orchard.Web/Core/Reports/Routes.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.Web.Mvc; +using System.Web.Routing; +using Orchard.Mvc.Routes; + +namespace Orchard.Core.Reports { + public class Routes : IRouteProvider { + public void GetRoutes(ICollection routes) { + foreach (var routeDescriptor in GetRoutes()) + routes.Add(routeDescriptor); + } + + public IEnumerable GetRoutes() { + return new[] { + new RouteDescriptor { + Priority = -5, + Route = new Route( + "Admin/Reports", + new RouteValueDictionary { + {"area", "Reports"}, + {"controller", "Admin"}, + {"action", "Index"} + }, + new RouteValueDictionary(), + new RouteValueDictionary { + {"area", "Reports"} + }, + new MvcRouteHandler()) + } + }; + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Core/Reports/ViewModels/DisplayReportViewModel.cs b/src/Orchard.Web/Core/Reports/ViewModels/DisplayReportViewModel.cs new file mode 100644 index 000000000..82db7c009 --- /dev/null +++ b/src/Orchard.Web/Core/Reports/ViewModels/DisplayReportViewModel.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; +using Orchard.Mvc.ViewModels; +using Orchard.Reports; + +namespace Orchard.Core.Reports.ViewModels { + public class DisplayReportViewModel : BaseViewModel { + public Report Report { get; set; } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Core/Reports/ViewModels/ReportsAdminIndexViewModel.cs b/src/Orchard.Web/Core/Reports/ViewModels/ReportsAdminIndexViewModel.cs new file mode 100644 index 000000000..fc1f62697 --- /dev/null +++ b/src/Orchard.Web/Core/Reports/ViewModels/ReportsAdminIndexViewModel.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; +using Orchard.Mvc.ViewModels; +using Orchard.Reports; + +namespace Orchard.Core.Reports.ViewModels { + public class ReportsAdminIndexViewModel : BaseViewModel { + public IList Reports { get; set; } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Core/Reports/Views/Admin/Display.aspx b/src/Orchard.Web/Core/Reports/Views/Admin/Display.aspx new file mode 100644 index 000000000..97809f51a --- /dev/null +++ b/src/Orchard.Web/Core/Reports/Views/Admin/Display.aspx @@ -0,0 +1,40 @@ +<%@ Page Language="C#" Inherits="Orchard.Mvc.ViewPage" %> +<%@ Import Namespace="Orchard.Core.Reports.ViewModels"%> +

<%: Html.TitleForPage(T("Display Report").ToString())%>

+<% using(Html.BeginFormAntiForgeryPost()) { %> + <%: Html.ValidationSummary() %> +
+ "> + + + + + + + + + + + + + + + <% + foreach (var reportEntry in Model.Report.Entries) { + %> + + + + + + <% + }%> +
<%: T("Type")%><%: T("Message")%><%: T("Date")%>
+ <%:reportEntry.Type %> + + <%:reportEntry.Message %> + + <%:reportEntry.Utc.ToLocalTime().ToShortDateString()%> <%:reportEntry.Utc.ToLocalTime().ToShortTimeString()%> +
+
+<% } %> \ No newline at end of file diff --git a/src/Orchard.Web/Core/Reports/Views/Admin/Index.aspx b/src/Orchard.Web/Core/Reports/Views/Admin/Index.aspx new file mode 100644 index 000000000..b24c5b474 --- /dev/null +++ b/src/Orchard.Web/Core/Reports/Views/Admin/Index.aspx @@ -0,0 +1,40 @@ +<%@ Page Language="C#" Inherits="Orchard.Mvc.ViewPage" %> +<%@ Import Namespace="Orchard.Core.Reports.ViewModels"%> +

<%: Html.TitleForPage(T("Manage Reports").ToString())%>

+<% using(Html.BeginFormAntiForgeryPost()) { %> + <%: Html.ValidationSummary() %> +
+ "> + + + + + + + + + + + + + + + <% + foreach (var report in Model.Reports) { + %> + + + + + + <% + }%> +
<%: T("Name")%><%: T("Title")%><%: T("Date")%>
+ <%: Html.ActionLink(Html.Encode(report.ActivityName), "Display", new {id = report.ReportId}) %> + + <%:report.Title%> + + <%:report.Utc.ToLocalTime().ToShortDateString()%> <%:report.Utc.ToLocalTime().ToShortTimeString()%> +
+
+<% } %> \ No newline at end of file diff --git a/src/Orchard.Web/Core/Reports/Views/Web.config b/src/Orchard.Web/Core/Reports/Views/Web.config new file mode 100644 index 000000000..7022197d4 --- /dev/null +++ b/src/Orchard.Web/Core/Reports/Views/Web.config @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs b/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs index 0f1176d6b..a73f48fad 100644 --- a/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs +++ b/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs @@ -21,6 +21,7 @@ using Orchard.Environment.Descriptor.Models; using Orchard.Indexing; using Orchard.Localization; using Orchard.Localization.Services; +using Orchard.Reports.Services; using Orchard.Security; using Orchard.Settings; using Orchard.Themes; @@ -68,6 +69,7 @@ namespace Orchard.Setup.Services { "Common", "Contents", "Dashboard", + "Reports", "Feeds", "HomePage", "Navigation", @@ -111,6 +113,9 @@ namespace Orchard.Setup.Services { using (var environment = new StandaloneEnvironment(bootstrapLifetimeScope)) { var schemaBuilder = new SchemaBuilder(environment.Resolve() ); + var reportsCoordinator = environment.Resolve(); + + reportsCoordinator.Register("Data Migration", "Setup", "Orchard installation"); schemaBuilder.CreateTable("Orchard_Framework_DataMigrationRecord", table => table .Column("Id", column => column.PrimaryKey().Identity()) diff --git a/src/Orchard/Data/Migration/DataMigrationManager.cs b/src/Orchard/Data/Migration/DataMigrationManager.cs index f04487def..9d4127b27 100644 --- a/src/Orchard/Data/Migration/DataMigrationManager.cs +++ b/src/Orchard/Data/Migration/DataMigrationManager.cs @@ -8,7 +8,9 @@ using Orchard.Data.Migration.Records; using Orchard.Data.Migration.Schema; using Orchard.Environment.Extensions; using Orchard.Environment.State; +using Orchard.Localization; using Orchard.Logging; +using Orchard.Reports.Services; namespace Orchard.Data.Migration { /// @@ -19,21 +21,24 @@ namespace Orchard.Data.Migration { private readonly IRepository _dataMigrationRepository; private readonly IExtensionManager _extensionManager; private readonly IDataMigrationInterpreter _interpreter; + private readonly IReportsCoordinator _reportsCoordinator; public DataMigrationManager( IEnumerable dataMigrations, IRepository dataMigrationRepository, IExtensionManager extensionManager, - IDataMigrationInterpreter interpreter + IDataMigrationInterpreter interpreter, + IReportsCoordinator reportsCoordinator ) { _dataMigrations = dataMigrations; _dataMigrationRepository = dataMigrationRepository; _extensionManager = extensionManager; _interpreter = interpreter; + _reportsCoordinator = reportsCoordinator; Logger = NullLogger.Instance; } - + public Localizer T { get; set; } public ILogger Logger { get; set; } public IEnumerable GetFeaturesThatNeedUpdate() { @@ -78,7 +83,8 @@ namespace Orchard.Data.Migration { } public void Update(string feature){ - Logger.Information("Updating {0}", feature); + + Logger.Information("Updating feature: {0}", feature); // proceed with dependent features first, whatever the module it's in var dependencies = ShellStateCoordinator.OrderByDependencies(_extensionManager.AvailableExtensions() diff --git a/src/Orchard/Data/Migration/Interpreters/DefaultDataMigrationInterpreter.cs b/src/Orchard/Data/Migration/Interpreters/DefaultDataMigrationInterpreter.cs index fd1533518..c40b40032 100644 --- a/src/Orchard/Data/Migration/Interpreters/DefaultDataMigrationInterpreter.cs +++ b/src/Orchard/Data/Migration/Interpreters/DefaultDataMigrationInterpreter.cs @@ -9,7 +9,9 @@ using NHibernate.SqlTypes; using Orchard.Data.Migration.Schema; using Orchard.Data.Providers; using Orchard.Environment.Configuration; +using Orchard.Localization; using Orchard.Logging; +using Orchard.Reports.Services; namespace Orchard.Data.Migration.Interpreters { public class DefaultDataMigrationInterpreter : AbstractDataMigrationInterpreter, IDataMigrationInterpreter { @@ -20,6 +22,7 @@ namespace Orchard.Data.Migration.Interpreters { private readonly List _sqlStatements; private readonly IDataServicesProviderFactory _dataServicesProviderFactory; private readonly ISessionFactoryHolder _sessionFactoryHolder; + private readonly IReportsCoordinator _reportsCoordinator; private const char Space = ' ' ; @@ -28,13 +31,15 @@ namespace Orchard.Data.Migration.Interpreters { ISessionLocator sessionLocator, IEnumerable commandInterpreters, IDataServicesProviderFactory dataServicesProviderFactory, - ISessionFactoryHolder sessionFactoryHolder) { + ISessionFactoryHolder sessionFactoryHolder, + IReportsCoordinator reportsCoordinator) { _shellSettings = shellSettings; _commandInterpreters = commandInterpreters; _session = sessionLocator.For(typeof(DefaultDataMigrationInterpreter)); _sqlStatements = new List(); _dataServicesProviderFactory = dataServicesProviderFactory; _sessionFactoryHolder = sessionFactoryHolder; + _reportsCoordinator = reportsCoordinator; Logger = NullLogger.Instance; @@ -44,6 +49,7 @@ namespace Orchard.Data.Migration.Interpreters { } public ILogger Logger { get; set; } + public Localizer T { get; set; } public IEnumerable SqlStatements { get { return _sqlStatements; } @@ -318,6 +324,8 @@ namespace Orchard.Data.Migration.Interpreters { command.CommandText = sqlStatement; command.ExecuteNonQuery(); } + + _reportsCoordinator.Information("Data Migration", String.Format("Executing SQL Query: {0}", sqlStatement)); } _sqlStatements.Clear(); diff --git a/src/Orchard/Localization/Services/DefaultCultureManager.cs b/src/Orchard/Localization/Services/DefaultCultureManager.cs index 86f4cfcbf..40bd7b17e 100644 --- a/src/Orchard/Localization/Services/DefaultCultureManager.cs +++ b/src/Orchard/Localization/Services/DefaultCultureManager.cs @@ -54,7 +54,7 @@ namespace Orchard.Localization.Services { .Where(x => x != null) .OrderByDescending(x => x.Priority); - if (requestCulture.Count() < 1) + if ( requestCulture.Count() < 1 ) return String.Empty; foreach (var culture in requestCulture) { diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index 3df3f3119..84f31c7aa 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -408,6 +408,15 @@ + + + + + + + + + diff --git a/src/Orchard/Reports/Report.cs b/src/Orchard/Reports/Report.cs new file mode 100644 index 000000000..07e0e7089 --- /dev/null +++ b/src/Orchard/Reports/Report.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using Orchard.Localization; + +namespace Orchard.Reports { + public class Report { + public Report() { + Entries = new List(); + } + + public IList Entries { get; set;} + public int ReportId { get; set; } + public string Title { get; set; } + public string ActivityName { get; set; } + public DateTime Utc { get; set; } + } +} diff --git a/src/Orchard/Reports/ReportEntry.cs b/src/Orchard/Reports/ReportEntry.cs new file mode 100644 index 000000000..fd168e4f6 --- /dev/null +++ b/src/Orchard/Reports/ReportEntry.cs @@ -0,0 +1,16 @@ +using System; +using Orchard.Localization; + +namespace Orchard.Reports { + public enum ReportEntryType { + Information, + Warning, + Error + } + + public class ReportEntry { + public ReportEntryType Type { get; set; } + public string Message { get; set; } + public DateTime Utc { get; set; } + } +} diff --git a/src/Orchard/Reports/ReportExtentions.cs b/src/Orchard/Reports/ReportExtentions.cs new file mode 100644 index 000000000..44fdd0abe --- /dev/null +++ b/src/Orchard/Reports/ReportExtentions.cs @@ -0,0 +1,16 @@ +using Orchard.Localization; +using Orchard.Reports; +using Orchard.Reports.Services; + +public static class ReportExtentions { + public static void Information(this IReportsCoordinator reportCoordinator, string reportKey, string message) { + reportCoordinator.Add(reportKey, ReportEntryType.Information, message); + } + public static void Warning(this IReportsCoordinator reportCoordinator, string reportKey, string message) { + reportCoordinator.Add(reportKey, ReportEntryType.Warning, message); + } + public static void Error(this IReportsCoordinator reportCoordinator, string reportKey, string message) { + reportCoordinator.Add(reportKey, ReportEntryType.Error, message); + } +} + diff --git a/src/Orchard/Reports/Services/IReportsCoordinator.cs b/src/Orchard/Reports/Services/IReportsCoordinator.cs new file mode 100644 index 000000000..9f094b280 --- /dev/null +++ b/src/Orchard/Reports/Services/IReportsCoordinator.cs @@ -0,0 +1,8 @@ +using Orchard.Localization; + +namespace Orchard.Reports.Services { + public interface IReportsCoordinator : IDependency { + void Add(string reportKey, ReportEntryType type, string message); + void Register(string reportKey, string activityName, string title); + } +} diff --git a/src/Orchard/Reports/Services/IReportsManager.cs b/src/Orchard/Reports/Services/IReportsManager.cs new file mode 100644 index 000000000..36ff65038 --- /dev/null +++ b/src/Orchard/Reports/Services/IReportsManager.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace Orchard.Reports.Services { + public interface IReportsManager : ISingletonDependency { + void Add(int reportId, ReportEntryType type, string message); + int CreateReport(string title, string activityName); + Report Get(int reportId); + IEnumerable GetReports(); + void Flush(); + } +} diff --git a/src/Orchard/Reports/Services/IReportsPersister.cs b/src/Orchard/Reports/Services/IReportsPersister.cs new file mode 100644 index 000000000..d1dd3238f --- /dev/null +++ b/src/Orchard/Reports/Services/IReportsPersister.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace Orchard.Reports.Services { + public interface IReportsPersister : IDependency { + IEnumerable Fetch(); + void Save(IEnumerable reports); + } +} diff --git a/src/Orchard/Reports/Services/ReportsCoordinator.cs b/src/Orchard/Reports/Services/ReportsCoordinator.cs new file mode 100644 index 000000000..8af376af1 --- /dev/null +++ b/src/Orchard/Reports/Services/ReportsCoordinator.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using Orchard.Localization; +using Orchard.Logging; + +namespace Orchard.Reports.Services { + public class ReportsCoordinator : IReportsCoordinator, IDisposable { + private readonly IReportsManager _reportsManager; + private readonly IDictionary _reports; + + public ReportsCoordinator(IReportsManager reportsManager) { + _reportsManager = reportsManager; + Logger = NullLogger.Instance; + _reports = new Dictionary(); + } + + public ILogger Logger { get; set; } + public void Dispose() { + _reportsManager.Flush(); + } + + public void Add(string reportKey, ReportEntryType type, string message) { + if(!_reports.ContainsKey(reportKey)) { + // ignore message if no corresponding report + return; + } + + _reportsManager.Add(_reports[reportKey], type, message); + } + + public void Register(string reportKey, string activityName, string title) { + _reports.Add(reportKey, _reportsManager.CreateReport(title, activityName)); + } + } +} diff --git a/src/Orchard/Reports/Services/ReportsManager.cs b/src/Orchard/Reports/Services/ReportsManager.cs new file mode 100644 index 000000000..7928e316a --- /dev/null +++ b/src/Orchard/Reports/Services/ReportsManager.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Orchard.Logging; + +namespace Orchard.Reports.Services { + public class ReportsManager : IReportsManager { + private readonly IReportsPersister _reportsPersister; + private List _reports; + private static readonly object _synLock = new object(); + private bool _isDirty; + + public ReportsManager(IReportsPersister reportsPersister) { + _reportsPersister = reportsPersister; + Logger = NullLogger.Instance; + } + + public ILogger Logger { get; set; } + + public void Add(int reportId, ReportEntryType type, string message) { + lock ( _synLock ) { + LoadReports(); + _isDirty = true; + var report = Get(reportId); + if(report == null) { + return; + } + report.Entries.Add(new ReportEntry {Message = message, Type = type, Utc = DateTime.UtcNow}); + } + } + + public int CreateReport(string title, string activityName) { + lock ( _synLock ) { + LoadReports(); + _isDirty = true; + var reportId = _reports.Count == 0 ? 1 : _reports.Max(r => r.ReportId) + 1; + var report = new Report {ActivityName = activityName, ReportId = reportId, Title = title, Utc = DateTime.UtcNow}; + _reports.Add(report); + return reportId; + } + } + + public Report Get(int reportId) { + lock(_synLock) { + LoadReports(); + return _reports.Where(r => r.ReportId == reportId).FirstOrDefault(); + } + } + + public IEnumerable GetReports() { + lock ( _synLock ) { + LoadReports(); + return _reports.ToList(); + } + } + + public void Flush() { + if ( _reports == null || !_isDirty) { + return; + } + + lock ( _synLock ) { + _reportsPersister.Save(_reports); + _isDirty = false; + } + } + + private void LoadReports() { + if(_reports == null) { + _reports = _reportsPersister.Fetch().ToList(); + } + } + } +} diff --git a/src/Orchard/Reports/Services/ReportsPersister.cs b/src/Orchard/Reports/Services/ReportsPersister.cs new file mode 100644 index 000000000..7788b0872 --- /dev/null +++ b/src/Orchard/Reports/Services/ReportsPersister.cs @@ -0,0 +1,69 @@ +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Xml; +using System.Xml.Linq; +using Orchard.Environment.Configuration; +using Orchard.Environment.Descriptor.Models; +using Orchard.FileSystems.AppData; +using System.IO; + +namespace Orchard.Reports.Services { + public class ReportsPersister : IReportsPersister { + private readonly IAppDataFolder _appDataFolder; + private readonly ShellSettings _shellSettings; + private readonly string _reportsFileName; + private readonly DataContractSerializer _dataContractSerializer; + private readonly object _synLock = new object(); + + public ReportsPersister(IAppDataFolder appDataFolder, ShellSettings shellSettings) { + _appDataFolder = appDataFolder; + _shellSettings = shellSettings; + _dataContractSerializer = new DataContractSerializer(typeof(Report), new [] { typeof(ReportEntry) }); + _reportsFileName = Path.Combine(Path.Combine("Sites", _shellSettings.Name), "reports.dat"); + } + + public IEnumerable Fetch() { + lock ( _synLock ) { + if ( !_appDataFolder.FileExists(_reportsFileName) ) { + yield break; + } + + var text = _appDataFolder.ReadFile(_reportsFileName); + var xmlDocument = XDocument.Parse(text); + var rootNode = xmlDocument.Root; + if (rootNode == null) { + yield break; + } + + foreach (var reportNode in rootNode.Elements()) { + var reader = new StringReader(reportNode.Value); + using (var xmlReader = XmlReader.Create(reader)) { + yield return (Report) _dataContractSerializer.ReadObject(xmlReader, true); + } + } + } + } + + public void Save(IEnumerable reports) { + lock ( _synLock ) { + var xmlDocument = new XDocument(); + xmlDocument.Add(new XElement("Reports")); + foreach (var report in reports) { + var reportNode = new XElement("Report"); + var writer = new StringWriter(); + using (var xmlWriter = XmlWriter.Create(writer)) { + _dataContractSerializer.WriteObject(xmlWriter, report); + } + reportNode.Value = writer.ToString(); + xmlDocument.Root.Add(reportNode); + } + + var saveWriter = new StringWriter(); + xmlDocument.Save(saveWriter); + _appDataFolder.CreateFile(_reportsFileName, saveWriter.ToString()); + } + } + } +} +