Quellcode durchsuchen

Logging abgeschlossen!

Arne Diekmann vor 8 Jahren
Ursprung
Commit
31f9564c0a
32 geänderte Dateien mit 1735 neuen und 614 gelöschten Zeilen
  1. 4 9
      GreenTree.Nachtragsmanagement.Services/Logging/DefaultLogger.cs
  2. 1 8
      GreenTree.Nachtragsmanagement.Services/Logging/ILogger.cs
  3. 11 2
      GreenTree.Nachtragsmanagement.Services/Scheduling/NotificationScheduler.cs
  4. 24 0
      GreenTree.Nachtragsmanagement.Web/App_Start/FunctionConfig.cs
  5. 14 0
      GreenTree.Nachtragsmanagement.Web/App_Start/RouteConfig.cs
  6. BIN
      GreenTree.Nachtragsmanagement.Web/Content/Images/function-Misc-Logs-32-contrast.png
  7. BIN
      GreenTree.Nachtragsmanagement.Web/Content/Images/function-Misc-Logs-32.png
  8. 130 78
      GreenTree.Nachtragsmanagement.Web/Controllers/AdminController.cs
  9. 231 139
      GreenTree.Nachtragsmanagement.Web/Controllers/AppendixController.cs
  10. 0 33
      GreenTree.Nachtragsmanagement.Web/Controllers/AuthController.cs
  11. 243 139
      GreenTree.Nachtragsmanagement.Web/Controllers/DeviationController.cs
  12. 18 7
      GreenTree.Nachtragsmanagement.Web/Controllers/GlobalController.cs
  13. 33 17
      GreenTree.Nachtragsmanagement.Web/Controllers/LoginController.cs
  14. 214 44
      GreenTree.Nachtragsmanagement.Web/Controllers/MiscController.cs
  15. 138 73
      GreenTree.Nachtragsmanagement.Web/Controllers/SiteController.cs
  16. 207 0
      GreenTree.Nachtragsmanagement.Web/Extensions/GridViewSettingsHelper.cs
  17. 46 19
      GreenTree.Nachtragsmanagement.Web/Global.asax.cs
  18. 7 1
      GreenTree.Nachtragsmanagement.Web/GreenTree.Nachtragsmanagement.Web.csproj
  19. 63 0
      GreenTree.Nachtragsmanagement.Web/Models/Misc/LogDataModel.cs
  20. 15 4
      GreenTree.Nachtragsmanagement.Web/Views/Admin/Roles/View.cshtml
  21. 2 1
      GreenTree.Nachtragsmanagement.Web/Views/Admin/Roles/_RoleGridPartial.cshtml
  22. 15 4
      GreenTree.Nachtragsmanagement.Web/Views/Admin/Users/View.cshtml
  23. 2 1
      GreenTree.Nachtragsmanagement.Web/Views/Admin/Users/_UserGridPartial.cshtml
  24. 15 4
      GreenTree.Nachtragsmanagement.Web/Views/Deviations/View.cshtml
  25. 94 0
      GreenTree.Nachtragsmanagement.Web/Views/Misc/Logs.cshtml
  26. 15 4
      GreenTree.Nachtragsmanagement.Web/Views/Misc/MailNotifications.cshtml
  27. 9 0
      GreenTree.Nachtragsmanagement.Web/Views/Misc/_LogGridPartial.cshtml
  28. 80 0
      GreenTree.Nachtragsmanagement.Web/Views/Misc/_LogViewPartial.cshtml
  29. 7 0
      GreenTree.Nachtragsmanagement.Web/Views/Shared/_FunctionLayout.cshtml
  30. 59 0
      GreenTree.Nachtragsmanagement.Web/Views/Shared/_PopupError.cshtml
  31. 15 4
      GreenTree.Nachtragsmanagement.Web/Views/Sites/View.cshtml
  32. 23 23
      GreenTree.Nachtragsmanagement.Web/Views/Sites/_SiteEditPartial.cshtml

+ 4 - 9
GreenTree.Nachtragsmanagement.Services/Logging/DefaultLogger.cs

@@ -80,15 +80,8 @@ namespace GreenTree.Nachtragsmanagement.Services.Logging
         /// <summary>
         /// Gets all log items
         /// </summary>
-        /// <param name="fromUtc">Log item creation from; null to load all records</param>
-        /// <param name="toUtc">Log item creation to; null to load all records</param>
-        /// <param name="message">Message</param>
-        /// <param name="logLevel">Log level; null to load all records</param>
-        /// <param name="pageIndex">Page index</param>
-        /// <param name="pageSize">Page size</param>
         /// <returns>Log item collection</returns>
-        public IList<Log> GetAllLogs(DateTime? fromUtc, DateTime? toUtc,
-            string message, LogLevel? logLevel, int pageIndex, int pageSize)
+        public IList<Log> GetAllLogs()
         {
             return _logRepository.Table.ToList();
         }
@@ -149,7 +142,9 @@ namespace GreenTree.Nachtragsmanagement.Services.Logging
                 ShortMessage = shortMessage,
                 FullMessage = fullMessage,
                 IpAddress = _webHelper.GetCurrentIpAddress(),
-                User = user,
+                UserId = user == null
+                    ? null
+                    : (int?)user.Id,
                 EntityId = entity == null 
                     ? null
                     : (int?)entity.Id,

+ 1 - 8
GreenTree.Nachtragsmanagement.Services/Logging/ILogger.cs

@@ -30,15 +30,8 @@ namespace GreenTree.Nachtragsmanagement.Services.Logging
         /// <summary>
         /// Gets all log items
         /// </summary>
-        /// <param name="fromUtc">Log item creation from; null to load all records</param>
-        /// <param name="toUtc">Log item creation to; null to load all records</param>
-        /// <param name="message">Message</param>
-        /// <param name="logLevel">Log level; null to load all records</param>
-        /// <param name="pageIndex">Page index</param>
-        /// <param name="pageSize">Page size</param>
         /// <returns>Log item collection</returns>
-        IList<Log> GetAllLogs(DateTime? fromUtc, DateTime? toUtc,
-            string message, LogLevel? logLevel, int pageIndex, int pageSize);
+        IList<Log> GetAllLogs();
 
         /// <summary>
         /// Gets a log item

+ 11 - 2
GreenTree.Nachtragsmanagement.Services/Scheduling/NotificationScheduler.cs

@@ -1,4 +1,5 @@
 using GreenTree.Nachtragsmanagement.Core.Domain.Misc;
+using GreenTree.Nachtragsmanagement.Services.Logging;
 using GreenTree.Nachtragsmanagement.Services.Misc;
 using Quartz;
 using Quartz.Impl;
@@ -15,6 +16,7 @@ namespace GreenTree.Nachtragsmanagement.Services.Scheduling
 
         private readonly INotificationService _notificationService;
         private readonly IMiscService _miscService;
+        private readonly ILogger _logger;
 
         #endregion
 
@@ -25,10 +27,12 @@ namespace GreenTree.Nachtragsmanagement.Services.Scheduling
         /// </summary>
         public NotificationScheduler(
             INotificationService notificationService,
-            IMiscService miscService)
+            IMiscService miscService,
+            ILogger logger)
         {
             _notificationService = notificationService;
             _miscService = miscService;
+            _logger = logger;
         }
 
         #endregion
@@ -67,10 +71,15 @@ namespace GreenTree.Nachtragsmanagement.Services.Scheduling
                         .Build();
 
                     scheduler.ScheduleJob(job, trigger);
+
+                    _logger.Information(
+                        String.Format("Mailbenachrichtigung \"({0}) {1} - {2}\" erfolgreich eingeplant.", 
+                            mailNotification.Id, mailNotification.NotificationPluginSystemName, 
+                            mailNotification.NotificationJobSystemName));
                 }
                 catch (Exception ex)
                 {
-                    continue;
+                    _logger.Error("Fehler beim Einplanen des Jobs für eine Mail-Benachrichtigung.", ex, null);
                 }
             }
         }

+ 24 - 0
GreenTree.Nachtragsmanagement.Web/App_Start/FunctionConfig.cs

@@ -316,6 +316,30 @@ namespace GreenTree.Nachtragsmanagement.Web.App_Start
                     IsMenuMember = false,
                     Plugin = "System"
                 },
+                new Function
+                {
+                    Name = "Misc-Logs",
+                    Description = "Logs",
+                    ImageUrl = "~/Content/Images/function-Misc-Logs-32.png",
+                    GroupName = "Misc",
+                    RouteName = "GreenTree.Nachtragsmanagement.Web.Misc.Logs",
+                    IsMenuMember = true,
+                    Plugin = "System",
+                    BaseWidth = 900,
+                    MinWidth = 700,
+                    BaseHeight = 550,
+                    MinHeight = 450,
+                    AllowMaximize = true,
+                    MaximizedOnStart = false
+                },
+                new Function
+                {
+                    Name = "Misc-Logs-Delete",
+                    Description = "Logs löschen",
+                    GroupName = "Misc-Logs",
+                    IsMenuMember = false,
+                    Plugin = "System"
+                },
             };
         }
     }

+ 14 - 0
GreenTree.Nachtragsmanagement.Web/App_Start/RouteConfig.cs

@@ -145,6 +145,20 @@ namespace GreenTree.Nachtragsmanagement.Web
                     "GreenTree.Nachtragsmanagement.Web.Controllers"
                }
             );
+
+            routes.MapRoute(
+                "GreenTree.Nachtragsmanagement.Web.Misc.Logs",
+                "misc/viewlogs",
+               new
+               {
+                   controller = "Misc",
+                   action = "ViewLogs"
+               },
+               new[]
+               {
+                   "GreenTree.Nachtragsmanagement.Web.Controllers"
+               }
+            );
         }
     }
 }

BIN
GreenTree.Nachtragsmanagement.Web/Content/Images/function-Misc-Logs-32-contrast.png


BIN
GreenTree.Nachtragsmanagement.Web/Content/Images/function-Misc-Logs-32.png


+ 130 - 78
GreenTree.Nachtragsmanagement.Web/Controllers/AdminController.cs

@@ -11,6 +11,7 @@ using GreenTree.Nachtragsmanagement.Core.Domain.User;
 using GreenTree.Nachtragsmanagement.Core;
 using GreenTree.Nachtragsmanagement.Core.Plugins;
 using GreenTree.Nachtragsmanagement.Web.Framework.Authorization;
+using GreenTree.Nachtragsmanagement.Services.Logging;
 
 namespace GreenTree.Nachtragsmanagement.Web.Controllers
 {
@@ -19,15 +20,18 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         private readonly IUserService _userService;
         private readonly IUserHelper _userHelper;
         private readonly IPluginFinder _pluginFinder;
+        private readonly ILogger _logger;
 
         public AdminController(
             IUserService userService,
             IUserHelper userHelper,
-            IPluginFinder pluginFinder)
+            IPluginFinder pluginFinder,
+            ILogger logger)
         {
             _userService = userService;
             _userHelper = userHelper;
             _pluginFinder = pluginFinder;
+            _logger = logger;
 
             ViewData["AllRoles"] = _userService.GetAllRoles();
             ViewData["AllFunctions"] = _userService.GetAllFunctions();
@@ -107,48 +111,61 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         [HttpPost, ValidateInput(false)]
         public ActionResult EditUser(UserDataModel userModel)
         {
-            if (!ModelState.IsValid)
+            try
             {
-                foreach (var role in userModel.RoleValues)
-                    userModel.RoleDescriptions.Add(
-                        ((IList<Role>)ViewData["AllRoles"])
-                            .First(r => r.Id == role).Description);
+                if (!ModelState.IsValid)
+                {
+                    foreach (var role in userModel.RoleValues)
+                        userModel.RoleDescriptions.Add(
+                            ((IList<Role>)ViewData["AllRoles"])
+                                .First(r => r.Id == role).Description);
 
-                return PartialView("~/Views/Admin/Users/_UserEditPartial.cshtml", userModel);
-            }
+                    return PartialView("~/Views/Admin/Users/_UserEditPartial.cshtml", userModel);
+                }
 
-            var selectedRoles = _userService.GetRolesByIds(userModel.RoleValues.ToArray());
+                var selectedRoles = _userService.GetRolesByIds(userModel.RoleValues.ToArray());
 
-            if (userModel.Id == -1)
-            {
-                var user = userModel.ToUser();
+                if (userModel.Id == -1)
+                {
+                    var user = userModel.ToUser();
 
-                user.SetRoles(selectedRoles);
-                user.Password = StaticHelper.GetMD5Hash(userModel.Password);
+                    user.SetRoles(selectedRoles);
+                    user.Password = StaticHelper.GetMD5Hash(userModel.Password);
 
-                _userService.InsertUser(user);
-            }
-            else
-            {
-                var user = _userService.GetUserById(userModel.Id);
+                    _userService.InsertUser(user);
 
-                user.CustomNumber = userModel.CustomNumber;
-                user.Forename = userModel.Forename;
-                user.Lastname = userModel.Lastname;
-                user.MailAddress = userModel.MailAddress;
+                    _logger.Entity(user, Core.Domain.Logging.LogEntityActivity.Insert, _userHelper.FromCookies());
+                }
+                else
+                {
+                    var user = _userService.GetUserById(userModel.Id);
 
-                if (!String.IsNullOrEmpty(userModel.Password))
-                    user.Password = StaticHelper.GetMD5Hash(userModel.Password);
+                    user.CustomNumber = userModel.CustomNumber;
+                    user.Forename = userModel.Forename;
+                    user.Lastname = userModel.Lastname;
+                    user.MailAddress = userModel.MailAddress;
 
-                user.SetRoles(selectedRoles);
+                    if (!String.IsNullOrEmpty(userModel.Password))
+                        user.Password = StaticHelper.GetMD5Hash(userModel.Password);
 
-                _userService.UpdateUser(user);
-            }
+                    user.SetRoles(selectedRoles);
 
-            return new JsonResult
+                    _userService.UpdateUser(user);
+
+                    _logger.Entity(user, Core.Domain.Logging.LogEntityActivity.Update, _userHelper.FromCookies());
+                }
+
+                return new JsonResult
+                {
+                    Data = "success"
+                };
+            }
+            catch (Exception ex)
             {
-                Data = "success"
-            };
+                _logger.Error("Fehler bei Speicherung eines Benutzers.", ex, _userHelper.FromCookies());
+
+                return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+            }
         }
 
         /// <summary>
@@ -158,15 +175,26 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         [HttpPost]
         public ActionResult DeleteUser(int id)
         {
-            var user = _userService.GetUserById(id);
+            try
+            {
+                var user = _userService.GetUserById(id);
 
-            if (user != null)
-                _userService.DeleteUser(user);
+                if (user != null)
+                    _userService.DeleteUser(user);
 
-            return new JsonResult
+                _logger.Entity(user, Core.Domain.Logging.LogEntityActivity.Delete, _userHelper.FromCookies());
+
+                return new JsonResult
+                {
+                    Data = "success"
+                };
+            }
+            catch (Exception ex)
             {
-                Data = "success"
-            };
+                _logger.Error("Fehler bei Löschung eines Benutzers.", ex, _userHelper.FromCookies());
+
+                return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+            }
         }
 
         #endregion
@@ -245,42 +273,55 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         [HttpPost, ValidateInput(false)]
         public ActionResult EditRole(RoleDataModel roleModel)
         {
-            if (!ModelState.IsValid)
+            try
             {
-                foreach (var role in roleModel.FunctionValues)
-                    roleModel.FunctionDescriptions.Add(
-                        ((IList<Function>)ViewData["AllFunctions"])
-                            .First(r => r.Id == role).Description);
+                if (!ModelState.IsValid)
+                {
+                    foreach (var role in roleModel.FunctionValues)
+                        roleModel.FunctionDescriptions.Add(
+                            ((IList<Function>)ViewData["AllFunctions"])
+                                .First(r => r.Id == role).Description);
 
-                return PartialView("~/Views/Admin/Roles/_RoleEditPartial.cshtml", roleModel);
-            }
+                    return PartialView("~/Views/Admin/Roles/_RoleEditPartial.cshtml", roleModel);
+                }
 
-            var selectedFunctions = _userService.GetFunctionsByIds(roleModel.FunctionValues.ToArray());
+                var selectedFunctions = _userService.GetFunctionsByIds(roleModel.FunctionValues.ToArray());
 
-            if (roleModel.Id == -1)
-            {
-                var role = roleModel.ToRole();
+                if (roleModel.Id == -1)
+                {
+                    var role = roleModel.ToRole();
 
-                role.SetFunctions(selectedFunctions);
+                    role.SetFunctions(selectedFunctions);
 
-                _userService.InsertRole(role);
-            }
-            else
-            {
-                var role = _userService.GetRoleById(roleModel.Id);
+                    _userService.InsertRole(role);
 
-                role.Description = roleModel.Description;
-                role.Level = roleModel.Level;
+                    _logger.Entity(role, Core.Domain.Logging.LogEntityActivity.Insert, _userHelper.FromCookies());
+                }
+                else
+                {
+                    var role = _userService.GetRoleById(roleModel.Id);
 
-                role.SetFunctions(selectedFunctions);
+                    role.Description = roleModel.Description;
+                    role.Level = roleModel.Level;
 
-                _userService.UpdateRole(role);
-            }
+                    role.SetFunctions(selectedFunctions);
 
-            return new JsonResult
+                    _userService.UpdateRole(role);
+
+                    _logger.Entity(role, Core.Domain.Logging.LogEntityActivity.Update, _userHelper.FromCookies());
+                }
+
+                return new JsonResult
+                {
+                    Data = "success"
+                };
+            }
+            catch (Exception ex)
             {
-                Data = "success"
-            };
+                _logger.Error("Fehler bei Speicherung einer Rolle.", ex, _userHelper.FromCookies());
+
+                return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+            }
         }
 
         /// <summary>
@@ -291,28 +332,39 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         [HttpPost]
         public ActionResult DeleteRole(int id, int replaceId)
         {
-            var role = _userService.GetRoleById(id);
-            var replaceRole = _userService.GetRoleById(replaceId);
+            try
+            {
+                var role = _userService.GetRoleById(id);
+                var replaceRole = _userService.GetRoleById(replaceId);
 
-            var roleUsers = _userService.GetUsersByRole(id);
+                var roleUsers = _userService.GetUsersByRole(id);
 
-            foreach (var user in roleUsers)
-            {
-                if (replaceId == -1)
-                    user.Roles.Remove(role);
-                else
-                    user.Roles.Add(replaceRole);
+                foreach (var user in roleUsers)
+                {
+                    if (replaceId == -1)
+                        user.Roles.Remove(role);
+                    else
+                        user.Roles.Add(replaceRole);
 
-                _userService.UpdateUser(user);
-            }
+                    _userService.UpdateUser(user);
+                }
 
-            if (role != null)
-                _userService.DeleteRole(role);
+                if (role != null)
+                    _userService.DeleteRole(role);
 
-            return new JsonResult
+                _logger.Entity(role, Core.Domain.Logging.LogEntityActivity.Delete, _userHelper.FromCookies());
+
+                return new JsonResult
+                {
+                    Data = "success"
+                };
+            }
+            catch (Exception ex)
             {
-                Data = "success"
-            };
+                _logger.Error("Fehler bei Löschung einer Rolle.", ex, _userHelper.FromCookies());
+
+                return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+            }
         }
 
         #endregion

+ 231 - 139
GreenTree.Nachtragsmanagement.Web/Controllers/AppendixController.cs

@@ -184,63 +184,72 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         [HttpPost, ValidateInput(false)]
         public ActionResult EditAppendix(AppendixDataModel appendixModel)
         {
-            appendixModel.CategoryValueEntities =
-                appendixModel.CategoryEntities
-                    .Select(r => JsonConvert.DeserializeObject<CategoryValueDataModel>(r))
-                    .ToList();
+            try
+            {
+                appendixModel.CategoryValueEntities =
+            appendixModel.CategoryEntities
+                .Select(r => JsonConvert.DeserializeObject<CategoryValueDataModel>(r))
+                .ToList();
 
-            for (int i = 0; i < appendixModel.CategoryValueEntities.Count; i++)
-                appendixModel.CategoryValueEntities.ElementAt(i).Json = appendixModel.CategoryEntities.ElementAt(i);
+                for (int i = 0; i < appendixModel.CategoryValueEntities.Count; i++)
+                    appendixModel.CategoryValueEntities.ElementAt(i).Json = appendixModel.CategoryEntities.ElementAt(i);
 
-            appendixModel.PercentageValue = appendixModel.OfferingValue * appendixModel.Percentage;
+                appendixModel.PercentageValue = appendixModel.OfferingValue * appendixModel.Percentage;
 
-            if (!ModelState.IsValid)
-                return PartialView("~/Views/Appendices/_AppendixEditPartial.cshtml", appendixModel);
+                if (!ModelState.IsValid)
+                    return PartialView("~/Views/Appendices/_AppendixEditPartial.cshtml", appendixModel);
 
-            var categoryValues = appendixModel.CategoryValueEntities
-                .Select(r => r.ToCategoryValue())
-                .ToList();
+                var categoryValues = appendixModel.CategoryValueEntities
+                    .Select(r => r.ToCategoryValue())
+                    .ToList();
 
-            if (appendixModel.Id == -1)
-            {
-                var appendix = appendixModel.ToAppendix();
+                if (appendixModel.Id == -1)
+                {
+                    var appendix = appendixModel.ToAppendix();
+
+                    appendix.SetCategoryValues(categoryValues);
 
-                appendix.SetCategoryValues(categoryValues);
+                    _appendixService.InsertAppendix(appendix);
 
-                _appendixService.InsertAppendix(appendix);
+                    _logger.Entity(appendix, Core.Domain.Logging.LogEntityActivity.Insert, _userHelper.FromCookies());
+                }
+                else
+                {
+                    var appendix = _appendixService.GetAppendixById(appendixModel.Id);
+
+                    appendix.CustomNumber = appendixModel.CustomNumber;
+                    appendix.Description = appendixModel.Description;
+                    appendix.Percentage = appendixModel.Percentage;
+                    appendix.OfferingNumber = appendixModel.OfferingNumber;
+                    appendix.OfferingDate = appendixModel.OfferingDate;
+                    appendix.NegotiationDate = appendixModel.NegotiationDate;
+                    appendix.NegotiationValue = appendixModel.NegotiationValue;
+                    appendix.ProtocolExists = appendixModel.ProtocolExists;
+                    appendix.StateId = appendixModel.StateId;
+                    appendix.OrderNumber = appendixModel.OrderNumber;
+                    appendix.OrderDate = appendixModel.OrderDate;
+                    appendix.OrderInvoiceCreated = appendixModel.OrderInvoiceCreated;
+                    appendix.Comment = appendixModel.Comment;
+                    appendix.SiteId = appendixModel.SiteId;
+
+                    appendix.SetCategoryValues(categoryValues);
+
+                    _appendixService.UpdateAppendix(appendix);
+
+                    _logger.Entity(appendix, Core.Domain.Logging.LogEntityActivity.Update, _userHelper.FromCookies());
+                }
 
-                _logger.Entity(appendix, Core.Domain.Logging.LogEntityActivity.Insert, _userHelper.FromCookies());
+                return new JsonResult
+                {
+                    Data = "success"
+                };
             }
-            else
+            catch (Exception ex)
             {
-                var appendix = _appendixService.GetAppendixById(appendixModel.Id);
-
-                appendix.CustomNumber = appendixModel.CustomNumber;
-                appendix.Description = appendixModel.Description;
-                appendix.Percentage = appendixModel.Percentage;
-                appendix.OfferingNumber = appendixModel.OfferingNumber;
-                appendix.OfferingDate = appendixModel.OfferingDate;
-                appendix.NegotiationDate = appendixModel.NegotiationDate;
-                appendix.NegotiationValue = appendixModel.NegotiationValue;
-                appendix.ProtocolExists = appendixModel.ProtocolExists;
-                appendix.StateId = appendixModel.StateId;
-                appendix.OrderNumber = appendixModel.OrderNumber;
-                appendix.OrderDate = appendixModel.OrderDate;
-                appendix.OrderInvoiceCreated = appendixModel.OrderInvoiceCreated;
-                appendix.Comment = appendixModel.Comment;
-                appendix.SiteId = appendixModel.SiteId;
-
-                appendix.SetCategoryValues(categoryValues);
-
-                _appendixService.UpdateAppendix(appendix);
-
-                _logger.Entity(appendix, Core.Domain.Logging.LogEntityActivity.Update, _userHelper.FromCookies());
-            }
+                _logger.Error("Fehler bei Speicherung eines Nachtrags.", ex, _userHelper.FromCookies());
 
-            return new JsonResult
-            {
-                Data = "success"
-            };
+                return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+            }
         }
 
         /// <summary>
@@ -250,15 +259,26 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         [HttpPost]
         public ActionResult DeleteAppendix(int id)
         {
-            var appendix = _appendixService.GetAppendixById(id);
+            try
+            {
+                var appendix = _appendixService.GetAppendixById(id);
 
-            if (appendix != null)
-                _appendixService.DeleteAppendix(appendix);
+                if (appendix != null)
+                    _appendixService.DeleteAppendix(appendix);
 
-            return new JsonResult
+                _logger.Entity(appendix, Core.Domain.Logging.LogEntityActivity.Delete, _userHelper.FromCookies());
+
+                return new JsonResult
+                {
+                    Data = "success"
+                };
+            }
+            catch (Exception ex)
             {
-                Data = "success"
-            };
+                _logger.Error("Fehler bei Löschung eines Nachtrags.", ex, _userHelper.FromCookies());
+
+                return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+            }
         }
 
         #endregion
@@ -329,30 +349,43 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         [HttpPost, ValidateInput(false)]
         public ActionResult EditInvoice(InvoiceDataModel invoiceModel)
         {
-            if (!ModelState.IsValid)
-                return PartialView("~/Views/Appendices/_InvoiceEditPartial.cshtml", invoiceModel);
-
-            if (invoiceModel.Id == -1)
+            try
             {
-                var invoice = invoiceModel.ToInvoice();
+                if (!ModelState.IsValid)
+                    return PartialView("~/Views/Appendices/_InvoiceEditPartial.cshtml", invoiceModel);
 
-                _appendixService.InsertInvoice(invoice);
-            }
-            else
-            {
-                var invoice = _appendixService.GetInvoiceById(invoiceModel.Id);
+                if (invoiceModel.Id == -1)
+                {
+                    var invoice = invoiceModel.ToInvoice();
 
-                invoice.CustomNumber = invoiceModel.CustomNumber.Value;
-                invoice.Date = invoiceModel.DateTime.Value;
-                invoice.Value = invoiceModel.Value.Value;
+                    _appendixService.InsertInvoice(invoice);
 
-                _appendixService.UpdateInvoice(invoice);
-            }
+                    _logger.Entity(invoice, Core.Domain.Logging.LogEntityActivity.Insert, _userHelper.FromCookies());
+                }
+                else
+                {
+                    var invoice = _appendixService.GetInvoiceById(invoiceModel.Id);
 
-            return new JsonResult
+                    invoice.CustomNumber = invoiceModel.CustomNumber.Value;
+                    invoice.Date = invoiceModel.DateTime.Value;
+                    invoice.Value = invoiceModel.Value.Value;
+
+                    _appendixService.UpdateInvoice(invoice);
+
+                    _logger.Entity(invoice, Core.Domain.Logging.LogEntityActivity.Update, _userHelper.FromCookies());
+                }
+
+                return new JsonResult
+                {
+                    Data = "success"
+                };
+            }
+            catch (Exception ex)
             {
-                Data = "success"
-            };
+                _logger.Error("Fehler bei Speicherung einer Rechnung.", ex, _userHelper.FromCookies());
+
+                return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+            }
         }
 
         /// <summary>
@@ -362,15 +395,26 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         [HttpPost]
         public ActionResult DeleteInvoice(int id)
         {
-            var invoice = _appendixService.GetInvoiceById(id);
+            try
+            {
+                var invoice = _appendixService.GetInvoiceById(id);
 
-            if (invoice != null)
-                _appendixService.DeleteInvoice(invoice);
+                if (invoice != null)
+                    _appendixService.DeleteInvoice(invoice);
 
-            return new JsonResult
+                _logger.Entity(invoice, Core.Domain.Logging.LogEntityActivity.Delete, _userHelper.FromCookies());
+
+                return new JsonResult
+                {
+                    Data = "success"
+                };
+            }
+            catch (Exception ex)
             {
-                Data = "success"
-            };
+                _logger.Error("Fehler bei Löschung einer Rechnung.", ex, _userHelper.FromCookies());
+
+                return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+            }
         }
 
         #endregion
@@ -468,42 +512,55 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         [HttpPost, ValidateInput(false)]
         public ActionResult EditState(StateDataModel stateModel)
         {
-            if (!ModelState.IsValid)
-                return PartialView("~/Views/Appendices/_StateEditPartial.cshtml", stateModel);
+            try
+            {
+                if (!ModelState.IsValid)
+                    return PartialView("~/Views/Appendices/_StateEditPartial.cshtml", stateModel);
 
-            var allStates = _appendixService.GetAllStates();
+                var allStates = _appendixService.GetAllStates();
 
-            if (stateModel.IsDefault)
-            {
-                foreach (var state in allStates)
+                if (stateModel.IsDefault)
                 {
-                    state.IsDefault = false;
+                    foreach (var state in allStates)
+                    {
+                        state.IsDefault = false;
 
-                    _appendixService.UpdateState(state);
+                        _appendixService.UpdateState(state);
+                    }
                 }
-            }
 
-            if (stateModel.Id == -1)
-            {
-                var claim = stateModel.ToState();
+                if (stateModel.Id == -1)
+                {
+                    var claim = stateModel.ToState();
 
-                _appendixService.InsertState(claim);
-            }
-            else
-            {
-                var state = _appendixService.GetStateById(stateModel.Id);
+                    _appendixService.InsertState(claim);
 
-                state.Description = stateModel.Description;
-                state.HexColor = stateModel.HexColor;
-                state.IsDefault = stateModel.IsDefault;
+                    _logger.Entity(claim, Core.Domain.Logging.LogEntityActivity.Insert, _userHelper.FromCookies());
+                }
+                else
+                {
+                    var state = _appendixService.GetStateById(stateModel.Id);
 
-                _appendixService.UpdateState(state);
-            }
+                    state.Description = stateModel.Description;
+                    state.HexColor = stateModel.HexColor;
+                    state.IsDefault = stateModel.IsDefault;
 
-            return new JsonResult
+                    _appendixService.UpdateState(state);
+
+                    _logger.Entity(state, Core.Domain.Logging.LogEntityActivity.Update, _userHelper.FromCookies());
+                }
+
+                return new JsonResult
+                {
+                    Data = "success"
+                };
+            }
+            catch (Exception ex)
             {
-                Data = "success"
-            };
+                _logger.Error("Fehler bei Speicherung eines NT-Status.", ex, _userHelper.FromCookies());
+
+                return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+            }
         }
 
         /// <summary>
@@ -513,28 +570,41 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         [HttpPost, ValidateInput(false)]
         public ActionResult EditCategory(CategoryDataModel categoryModel)
         {
-            if (!ModelState.IsValid)
-                return PartialView("~/Views/Appendices/_CategoryEditPartial.cshtml", categoryModel);
-
-            if (categoryModel.Id == -1)
+            try
             {
-                var claim = categoryModel.ToCategory();
+                if (!ModelState.IsValid)
+                    return PartialView("~/Views/Appendices/_CategoryEditPartial.cshtml", categoryModel);
 
-                _appendixService.InsertCategory(claim);
-            }
-            else
-            {
-                var category = _appendixService.GetCategoryById(categoryModel.Id);
+                if (categoryModel.Id == -1)
+                {
+                    var claim = categoryModel.ToCategory();
 
-                category.Description = categoryModel.Description;
+                    _appendixService.InsertCategory(claim);
 
-                _appendixService.UpdateCategory(category);
-            }
+                    _logger.Entity(claim, Core.Domain.Logging.LogEntityActivity.Insert, _userHelper.FromCookies());
+                }
+                else
+                {
+                    var category = _appendixService.GetCategoryById(categoryModel.Id);
 
-            return new JsonResult
+                    category.Description = categoryModel.Description;
+
+                    _appendixService.UpdateCategory(category);
+
+                    _logger.Entity(category, Core.Domain.Logging.LogEntityActivity.Update, _userHelper.FromCookies());
+                }
+
+                return new JsonResult
+                {
+                    Data = "success"
+                };
+            }
+            catch (Exception ex)
             {
-                Data = "success"
-            };
+                _logger.Error("Fehler bei Löschung einer NT-Kategorie.", ex, _userHelper.FromCookies());
+
+                return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+            }
         }
 
         /// <summary>
@@ -549,44 +619,66 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
             switch (claimType.ToLower())
             {
                 case "state":
-                    var state = _appendixService.GetStateById(id);
-                    var replaceState = _appendixService.GetStateById(replaceId);
+                    try
+                    {
+                        var state = _appendixService.GetStateById(id);
+                        var replaceState = _appendixService.GetStateById(replaceId);
 
-                    var stateAppendices = _appendixService.GetAppendicesByState(id);
+                        var stateAppendices = _appendixService.GetAppendicesByState(id);
 
-                    foreach (var appendix in stateAppendices)
-                    {
-                        appendix.StateId = replaceId;
-                        appendix.State = replaceState;
+                        foreach (var appendix in stateAppendices)
+                        {
+                            appendix.StateId = replaceId;
+                            appendix.State = replaceState;
 
-                        _appendixService.UpdateAppendix(appendix);
+                            _appendixService.UpdateAppendix(appendix);
+                        }
+
+                        if (state != null)
+                            _appendixService.DeleteState(state);
+
+                        _logger.Entity(state, Core.Domain.Logging.LogEntityActivity.Delete, _userHelper.FromCookies());
                     }
+                    catch (Exception ex)
+                    {
+                        _logger.Error("Fehler bei Löschung eines NT-Status.", ex, _userHelper.FromCookies());
 
-                    if (state != null)
-                        _appendixService.DeleteState(state);
+                        return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+                    }
                     break;
                 case "category":
-                    var category = _appendixService.GetCategoryById(id);
-                    var replaceCategory = _appendixService.GetCategoryById(replaceId);
+                    try
+                    {
+                        var category = _appendixService.GetCategoryById(id);
+                        var replaceCategory = _appendixService.GetCategoryById(replaceId);
 
-                    var allAppendices = _appendixService.GetAllAppendices();
+                        var allAppendices = _appendixService.GetAllAppendices();
 
-                    foreach (var appendix in allAppendices)
-                    {
-                        foreach (var categoryValue in appendix.CategoryValues)
+                        foreach (var appendix in allAppendices)
                         {
-                            if (categoryValue.CategoryId == id)
+                            foreach (var categoryValue in appendix.CategoryValues)
                             {
-                                categoryValue.Category = replaceCategory;
-                                categoryValue.CategoryId = replaceCategory.Id;
+                                if (categoryValue.CategoryId == id)
+                                {
+                                    categoryValue.Category = replaceCategory;
+                                    categoryValue.CategoryId = replaceCategory.Id;
+                                }
                             }
+
+                            _appendixService.UpdateAppendix(appendix);
                         }
 
-                        _appendixService.UpdateAppendix(appendix);
+                        if (category != null)
+                            _appendixService.DeleteCategory(category);
+
+                        _logger.Entity(category, Core.Domain.Logging.LogEntityActivity.Delete, _userHelper.FromCookies());
                     }
+                    catch (Exception ex)
+                    {
+                        _logger.Error("Fehler bei Löschung einer NT-Kategorie.", ex, _userHelper.FromCookies());
 
-                    if (category != null)
-                        _appendixService.DeleteCategory(category);
+                        return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+                    }
                     break;
                 default:
                     return new EmptyResult();

+ 0 - 33
GreenTree.Nachtragsmanagement.Web/Controllers/AuthController.cs

@@ -1,33 +0,0 @@
-using GreenTree.Nachtragsmanagement.Services.User;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Web;
-using System.Web.Mvc;
-
-namespace GreenTree.Nachtragsmanagement.Web.Controllers
-{
-    public abstract class AuthController : Controller
-    {
-        private readonly IAuthenticationService _authenticationService;
-        private readonly IUserService _userService;
-
-        public AuthController(
-            IAuthenticationService authenticationService,
-            IUserService userService)
-        {
-            _authenticationService = authenticationService;
-            _userService = userService;
-
-            Authenticate();
-        }
-
-        /// <summary>
-        /// Authenticates the current
-        /// </summary>
-        public void Authenticate()
-        {
-
-        }
-    }
-}

+ 243 - 139
GreenTree.Nachtragsmanagement.Web/Controllers/DeviationController.cs

@@ -1,9 +1,11 @@
 using DevExpress.Web.Mvc;
 using GreenTree.Nachtragsmanagement.Core;
+using GreenTree.Nachtragsmanagement.Core.Authentication;
 using GreenTree.Nachtragsmanagement.Core.Domain.Appendix;
 using GreenTree.Nachtragsmanagement.Core.Domain.Deviation;
 using GreenTree.Nachtragsmanagement.Services.Appendix;
 using GreenTree.Nachtragsmanagement.Services.Deviation;
+using GreenTree.Nachtragsmanagement.Services.Logging;
 using GreenTree.Nachtragsmanagement.Services.Site;
 using GreenTree.Nachtragsmanagement.Web.Framework.Authorization;
 using GreenTree.Nachtragsmanagement.Web.Models.Deviation;
@@ -22,15 +24,21 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         private readonly IDeviationService _deviationService;
         private readonly IAppendixService _appendixService;
         private readonly ISiteService _siteService;
+        private readonly IUserHelper _userHelper;
+        private readonly ILogger _logger;
 
         public DeviationController(
             IDeviationService deviationService,
             IAppendixService appendixService,
-            ISiteService siteService)
+            ISiteService siteService,
+            IUserHelper userHelper,
+            ILogger logger)
         {
             _deviationService = deviationService;
             _appendixService = appendixService;
             _siteService = siteService;
+            _userHelper = userHelper;
+            _logger = logger;
 
             ViewData["AllDisturbances"] = _deviationService.GetAllDisturbances();
             ViewData["AllStatuses"] = _deviationService.GetAllStatuses();
@@ -236,54 +244,67 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         [HttpPost, ValidateInput(false)]
         public ActionResult EditDeviation(DeviationDataModel deviationModel)
         {
-            deviationModel.DisturbanceValueEntities =
-                deviationModel.DisturbanceEntities
-                    .Select(r => JsonConvert.DeserializeObject<DisturbanceValueDataModel>(r))
-                    .ToList();
+            try
+            {
+                deviationModel.DisturbanceValueEntities =
+                    deviationModel.DisturbanceEntities
+                        .Select(r => JsonConvert.DeserializeObject<DisturbanceValueDataModel>(r))
+                        .ToList();
 
-            for (int i = 0; i < deviationModel.DisturbanceValueEntities.Count; i++)
-                deviationModel.DisturbanceValueEntities.ElementAt(i).Json = deviationModel.DisturbanceEntities.ElementAt(i);
+                for (int i = 0; i < deviationModel.DisturbanceValueEntities.Count; i++)
+                    deviationModel.DisturbanceValueEntities.ElementAt(i).Json = deviationModel.DisturbanceEntities.ElementAt(i);
 
-            deviationModel.PercentageValue = deviationModel.Value * deviationModel.Percentage;
+                deviationModel.PercentageValue = deviationModel.Value * deviationModel.Percentage;
 
-            if (!ModelState.IsValid)
-                return PartialView("~/Views/Deviations/_DeviationEditPartial.cshtml", deviationModel);
+                if (!ModelState.IsValid)
+                    return PartialView("~/Views/Deviations/_DeviationEditPartial.cshtml", deviationModel);
 
-            var disturbanceValues = deviationModel.DisturbanceValueEntities
-                .Select(r => r.ToDisturbanceValue())
-                .ToList();
+                var disturbanceValues = deviationModel.DisturbanceValueEntities
+                    .Select(r => r.ToDisturbanceValue())
+                    .ToList();
 
-            if (deviationModel.Id == -1)
-            {
-                var deviation = deviationModel.ToDeviation();
+                if (deviationModel.Id == -1)
+                {
+                    var deviation = deviationModel.ToDeviation();
 
-                deviation.SetDisturbanceValues(disturbanceValues);
+                    deviation.SetDisturbanceValues(disturbanceValues);
 
-                _deviationService.InsertDeviation(deviation);
-            }
-            else
-            {
-                var deviation = _deviationService.GetDeviationById(deviationModel.Id);
+                    _deviationService.InsertDeviation(deviation);
 
-                deviation.CustomNumber = deviationModel.CustomNumber;
-                deviation.Description = deviationModel.Description;
-                deviation.ReceiptDate = deviationModel.ReceiptDate;
-                deviation.AppendixDate = deviationModel.AppendixDate;
-                deviation.Value = deviationModel.Value;
-                deviation.AppendixId = deviationModel.AppendixId;
-                deviation.StatusId = deviationModel.StatusId;
-                deviation.KindId = deviationModel.KindId;
-                deviation.Comment = deviationModel.Comment;
+                    _logger.Entity(deviation, Core.Domain.Logging.LogEntityActivity.Insert, _userHelper.FromCookies());
+                }
+                else
+                {
+                    var deviation = _deviationService.GetDeviationById(deviationModel.Id);
 
-                deviation.SetDisturbanceValues(disturbanceValues);
+                    deviation.CustomNumber = deviationModel.CustomNumber;
+                    deviation.Description = deviationModel.Description;
+                    deviation.ReceiptDate = deviationModel.ReceiptDate;
+                    deviation.AppendixDate = deviationModel.AppendixDate;
+                    deviation.Value = deviationModel.Value;
+                    deviation.AppendixId = deviationModel.AppendixId;
+                    deviation.StatusId = deviationModel.StatusId;
+                    deviation.KindId = deviationModel.KindId;
+                    deviation.Comment = deviationModel.Comment;
 
-                _deviationService.UpdateDeviation(deviation);
-            }
+                    deviation.SetDisturbanceValues(disturbanceValues);
 
-            return new JsonResult
+                    _deviationService.UpdateDeviation(deviation);
+
+                    _logger.Entity(deviation, Core.Domain.Logging.LogEntityActivity.Update, _userHelper.FromCookies());
+                }
+
+                return new JsonResult
+                {
+                    Data = "success"
+                };
+            }
+            catch (Exception ex)
             {
-                Data = "success"
-            };
+                _logger.Error("Fehler bei Speicherung einer Vertragsabweichung.", ex, _userHelper.FromCookies());
+
+                return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+            }
         }
 
         /// <summary>
@@ -293,15 +314,26 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         [HttpPost]
         public ActionResult DeleteDeviation(int id)
         {
-            var deviation = _deviationService.GetDeviationById(id);
+            try
+            {
+                var deviation = _deviationService.GetDeviationById(id);
 
-            if (deviation != null)
-                _deviationService.DeleteDeviation(deviation);
+                if (deviation != null)
+                    _deviationService.DeleteDeviation(deviation);
 
-            return new JsonResult
+                _logger.Entity(deviation, Core.Domain.Logging.LogEntityActivity.Delete, _userHelper.FromCookies());
+
+                return new JsonResult
+                {
+                    Data = "success"
+                };
+            }
+            catch (Exception ex)
             {
-                Data = "success"
-            };
+                _logger.Error("Fehler bei Löschung einer Vertragsabweichung.", ex, _userHelper.FromCookies());
+
+                return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+            }
         }
 
         #endregion
@@ -415,41 +447,54 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         [HttpPost, ValidateInput(false)]
         public ActionResult EditStatus(StatusDataModel statusModel)
         {
-            if (!ModelState.IsValid)
-                return PartialView("~/Views/Deviations/_StatusEditPartial.cshtml", statusModel);
+            try
+            {
+                if (!ModelState.IsValid)
+                    return PartialView("~/Views/Deviations/_StatusEditPartial.cshtml", statusModel);
 
-            var allStatuses = _deviationService.GetAllStatuses();
+                var allStatuses = _deviationService.GetAllStatuses();
 
-            if (statusModel.IsDefault)
-            {
-                foreach (var status in allStatuses)
+                if (statusModel.IsDefault)
                 {
-                    status.IsDefault = false;
+                    foreach (var status in allStatuses)
+                    {
+                        status.IsDefault = false;
 
-                    _deviationService.UpdateStatus(status);
+                        _deviationService.UpdateStatus(status);
+                    }
                 }
-            }
 
-            if (statusModel.Id == -1)
-            {
-                var claim = statusModel.ToStatus();
+                if (statusModel.Id == -1)
+                {
+                    var claim = statusModel.ToStatus();
 
-                _deviationService.InsertStatus(claim);
-            }
-            else
-            {
-                var claim = _deviationService.GetStatusById(statusModel.Id);
+                    _deviationService.InsertStatus(claim);
+
+                    _logger.Entity(claim, Core.Domain.Logging.LogEntityActivity.Insert, _userHelper.FromCookies());
+                }
+                else
+                {
+                    var claim = _deviationService.GetStatusById(statusModel.Id);
 
-                claim.Description = statusModel.Description;
-                claim.IsDefault = statusModel.IsDefault;
+                    claim.Description = statusModel.Description;
+                    claim.IsDefault = statusModel.IsDefault;
 
-                _deviationService.UpdateStatus(claim);
-            }
+                    _deviationService.UpdateStatus(claim);
 
-            return new JsonResult
+                    _logger.Entity(claim, Core.Domain.Logging.LogEntityActivity.Update, _userHelper.FromCookies());
+                }
+
+                return new JsonResult
+                {
+                    Data = "success"
+                };
+            }
+            catch (Exception ex)
             {
-                Data = "success"
-            };
+                _logger.Error("Fehler bei Speicherung eines VA-Status.", ex, _userHelper.FromCookies());
+
+                return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+            }
         }
 
         /// <summary>
@@ -459,28 +504,41 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         [HttpPost, ValidateInput(false)]
         public ActionResult EditDisturbance(DisturbanceDataModel disturbanceModel)
         {
-            if (!ModelState.IsValid)
-                return PartialView("~/Views/Deviations/_DisturbanceEditPartial.cshtml", disturbanceModel);
-
-            if (disturbanceModel.Id == -1)
+            try
             {
-                var claim = disturbanceModel.ToDisturbance();
+                if (!ModelState.IsValid)
+                    return PartialView("~/Views/Deviations/_DisturbanceEditPartial.cshtml", disturbanceModel);
 
-                _deviationService.InsertDisturbance(claim);
-            }
-            else
-            {
-                var disturbance = _deviationService.GetDisturbanceById(disturbanceModel.Id);
+                if (disturbanceModel.Id == -1)
+                {
+                    var claim = disturbanceModel.ToDisturbance();
 
-                disturbance.Description = disturbanceModel.Description;
+                    _deviationService.InsertDisturbance(claim);
 
-                _deviationService.UpdateDisturbance(disturbance);
-            }
+                    _logger.Entity(claim, Core.Domain.Logging.LogEntityActivity.Insert, _userHelper.FromCookies());
+                }
+                else
+                {
+                    var disturbance = _deviationService.GetDisturbanceById(disturbanceModel.Id);
 
-            return new JsonResult
+                    disturbance.Description = disturbanceModel.Description;
+
+                    _deviationService.UpdateDisturbance(disturbance);
+
+                    _logger.Entity(disturbance, Core.Domain.Logging.LogEntityActivity.Update, _userHelper.FromCookies());
+                }
+
+                return new JsonResult
+                {
+                    Data = "success"
+                };
+            }
+            catch (Exception ex)
             {
-                Data = "success"
-            };
+                _logger.Error("Fehler bei Speicherung einer VA-Kategorie.", ex, _userHelper.FromCookies());
+
+                return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+            }
         }
 
         /// <summary>
@@ -490,41 +548,54 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         [HttpPost, ValidateInput(false)]
         public ActionResult EditKind(KindDataModel kindModel)
         {
-            if (!ModelState.IsValid)
-                return PartialView("~/Views/Deviations/_KindEditPartial.cshtml", kindModel);
+            try
+            {
+                if (!ModelState.IsValid)
+                    return PartialView("~/Views/Deviations/_KindEditPartial.cshtml", kindModel);
 
-            var allKinds = _deviationService.GetAllKinds();
+                var allKinds = _deviationService.GetAllKinds();
 
-            if (kindModel.IsDefault)
-            {
-                foreach (var kind in allKinds)
+                if (kindModel.IsDefault)
                 {
-                    kind.IsDefault = false;
+                    foreach (var kind in allKinds)
+                    {
+                        kind.IsDefault = false;
 
-                    _deviationService.UpdateKind(kind);
+                        _deviationService.UpdateKind(kind);
+                    }
                 }
-            }
 
-            if (kindModel.Id == -1)
-            {
-                var claim = kindModel.ToKind();
+                if (kindModel.Id == -1)
+                {
+                    var claim = kindModel.ToKind();
 
-                _deviationService.InsertKind(claim);
-            }
-            else
-            {
-                var kind = _deviationService.GetKindById(kindModel.Id);
+                    _deviationService.InsertKind(claim);
 
-                kind.Description = kindModel.Description;
-                kind.IsDefault = kindModel.IsDefault;
+                    _logger.Entity(claim, Core.Domain.Logging.LogEntityActivity.Insert, _userHelper.FromCookies());
+                }
+                else
+                {
+                    var kind = _deviationService.GetKindById(kindModel.Id);
 
-                _deviationService.UpdateKind(kind);
-            }
+                    kind.Description = kindModel.Description;
+                    kind.IsDefault = kindModel.IsDefault;
 
-            return new JsonResult
+                    _deviationService.UpdateKind(kind);
+
+                    _logger.Entity(kind, Core.Domain.Logging.LogEntityActivity.Update, _userHelper.FromCookies());
+                }
+
+                return new JsonResult
+                {
+                    Data = "success"
+                };
+            }
+            catch (Exception ex)
             {
-                Data = "success"
-            };
+                _logger.Error("Fehler bei Speicherung einer Art.", ex, _userHelper.FromCookies());
+
+                return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+            }
         }
 
         /// <summary>
@@ -539,61 +610,94 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
             switch (claimType.ToLower())
             {
                 case "status":
-                    var status = _deviationService.GetStatusById(id);
-                    var replaceStatus = _deviationService.GetStatusById(replaceId);
+                    try
+                    {
+                        var status = _deviationService.GetStatusById(id);
+                        var replaceStatus = _deviationService.GetStatusById(replaceId);
 
-                    var statusDeviations = _deviationService.GetDeviationsByStatus(id);
+                        var statusDeviations = _deviationService.GetDeviationsByStatus(id);
 
-                    foreach (var deviation in statusDeviations)
-                    {
-                        deviation.StatusId = replaceId;
-                        deviation.Status = replaceStatus;
+                        foreach (var deviation in statusDeviations)
+                        {
+                            deviation.StatusId = replaceId;
+                            deviation.Status = replaceStatus;
+
+                            _deviationService.UpdateDeviation(deviation);
+                        }
+
+                        if (status != null)
+                            _deviationService.DeleteStatus(status);
 
-                        _deviationService.UpdateDeviation(deviation);
+                        _logger.Entity(status, Core.Domain.Logging.LogEntityActivity.Delete, _userHelper.FromCookies());
                     }
+                    catch (Exception ex)
+                    {
+                        _logger.Error("Fehler bei Löschung eines VA-Status.", ex, _userHelper.FromCookies());
 
-                    if (status != null)
-                        _deviationService.DeleteStatus(status);
+                        return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+                    }
                     break;
                 case "kind":
-                    var kind = _deviationService.GetKindById(id);
-                    var replaceKind = _deviationService.GetKindById(replaceId);
+                    try
+                    {
+                        var kind = _deviationService.GetKindById(id);
+                        var replaceKind = _deviationService.GetKindById(replaceId);
 
-                    var kindDeviations = _deviationService.GetDeviationsByKind(id);
+                        var kindDeviations = _deviationService.GetDeviationsByKind(id);
 
-                    foreach (var deviation in kindDeviations)
-                    {
-                        deviation.KindId = replaceId;
-                        deviation.Kind = replaceKind;
+                        foreach (var deviation in kindDeviations)
+                        {
+                            deviation.KindId = replaceId;
+                            deviation.Kind = replaceKind;
 
-                        _deviationService.UpdateDeviation(deviation);
+                            _deviationService.UpdateDeviation(deviation);
+                        }
+
+                        if (kind != null)
+                            _deviationService.DeleteKind(kind);
+
+                        _logger.Entity(kind, Core.Domain.Logging.LogEntityActivity.Delete, _userHelper.FromCookies());
                     }
+                    catch (Exception ex)
+                    {
+                        _logger.Error("Fehler bei Löschung einer Art.", ex, _userHelper.FromCookies());
 
-                    if (kind != null)
-                        _deviationService.DeleteKind(kind);
+                        return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+                    }
                     break;
                 case "disturbance":
-                    var disturbance = _deviationService.GetDisturbanceById(id);
-                    var replaceDisturbance = _deviationService.GetDisturbanceById(replaceId);
+                    try
+                    {
+                        var disturbance = _deviationService.GetDisturbanceById(id);
+                        var replaceDisturbance = _deviationService.GetDisturbanceById(replaceId);
 
-                    var allDeviations = _deviationService.GetAllDeviations();
+                        var allDeviations = _deviationService.GetAllDeviations();
 
-                    foreach (var deviation in allDeviations)
-                    {
-                        foreach (var disturbanceValue in deviation.DisturbanceValues)
+                        foreach (var deviation in allDeviations)
                         {
-                            if (disturbanceValue.DisturbanceId == id)
+                            foreach (var disturbanceValue in deviation.DisturbanceValues)
                             {
-                                disturbanceValue.Disturbance = replaceDisturbance;
-                                disturbanceValue.DisturbanceId = replaceDisturbance.Id;
+                                if (disturbanceValue.DisturbanceId == id)
+                                {
+                                    disturbanceValue.Disturbance = replaceDisturbance;
+                                    disturbanceValue.DisturbanceId = replaceDisturbance.Id;
+                                }
                             }
+
+                            _deviationService.UpdateDeviation(deviation);
                         }
 
-                        _deviationService.UpdateDeviation(deviation);
+                        if (disturbance != null)
+                            _deviationService.DeleteDisturbance(disturbance);
+
+                        _logger.Entity(disturbance, Core.Domain.Logging.LogEntityActivity.Delete, _userHelper.FromCookies());
                     }
+                    catch (Exception ex)
+                    {
+                        _logger.Error("Fehler bei Löschung einer VA-Kategorie.", ex, _userHelper.FromCookies());
 
-                    if (disturbance != null)
-                        _deviationService.DeleteDisturbance(disturbance);
+                        return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+                    }
                     break;
                 default:
                     return new EmptyResult();

+ 18 - 7
GreenTree.Nachtragsmanagement.Web/Controllers/GlobalController.cs

@@ -1,6 +1,7 @@
 using GreenTree.Nachtragsmanagement.Core.Authentication;
 using GreenTree.Nachtragsmanagement.Services.Appendix;
 using GreenTree.Nachtragsmanagement.Services.Deviation;
+using GreenTree.Nachtragsmanagement.Services.Logging;
 using GreenTree.Nachtragsmanagement.Services.Site;
 using GreenTree.Nachtragsmanagement.Services.User;
 using GreenTree.Nachtragsmanagement.Web.Models.Global;
@@ -20,19 +21,22 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         private readonly IAppendixService _appendixService;
         private readonly IDeviationService _deviationService;
         private readonly ISiteService _siteService;
+        private readonly ILogger _logger;
 
         public GlobalController(
             IUserHelper userHelper,
             IUserService userService,
             IAppendixService appendixService,
             IDeviationService deviationService,
-            ISiteService siteService)
+            ISiteService siteService,
+            ILogger logger)
         {
             _userHelper = userHelper;
             _userService = userService;
             _appendixService = appendixService;
             _deviationService = deviationService;
             _siteService = siteService;
+            _logger = logger;
         }
 
         /// <summary>
@@ -75,15 +79,22 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         /// <param name="roleId">The id of the new role.</param>
         public ActionResult SetRole(int roleId = -1)
         {
-            if (roleId == -1)
-                return RedirectToAction("Index", "Home");
+            try
+            {
+                if (roleId == -1)
+                    return RedirectToAction("Index", "Home");
 
-            var user = _userHelper.FromCookies();
-            var role = _userService.GetRoleById(roleId);
+                var user = _userHelper.FromCookies();
+                var role = _userService.GetRoleById(roleId);
 
-            user.CurrentRole = role;
+                user.CurrentRole = role;
 
-            _userHelper.ToCookies(user);
+                _userHelper.ToCookies(user);
+            }
+            catch (Exception ex)
+            {
+                _logger.Error("Fehler bei Wechsel einer Rolle.", ex, _userHelper.FromCookies());
+            }
 
             return RedirectToAction("Index", "Home");
         }

+ 33 - 17
GreenTree.Nachtragsmanagement.Web/Controllers/LoginController.cs

@@ -1,5 +1,6 @@
 using GreenTree.Nachtragsmanagement.Core;
 using GreenTree.Nachtragsmanagement.Core.Authentication;
+using GreenTree.Nachtragsmanagement.Services.Logging;
 using GreenTree.Nachtragsmanagement.Services.User;
 using GreenTree.Nachtragsmanagement.Web.Models.Login;
 using System;
@@ -14,13 +15,16 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
     {
         private readonly IUserService _userService;
         private readonly IUserHelper _userHelper;
+        private readonly ILogger _logger;
 
         public LoginController(
             IUserService userService,
-            IUserHelper userHelper)
+            IUserHelper userHelper,
+            ILogger logger)
         {
             _userService = userService;
             _userHelper = userHelper;
+            _logger = logger;
         }
 
         // GET: Login
@@ -39,30 +43,42 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         {
             var user = _userService.GetUserByCustomNumber(model.Username);
 
-            if (user == null)
+            try
             {
-                ViewData["LoginFailed"] = true;
+                if (user == null)
+                {
+                    ViewData["LoginFailed"] = true;
 
-                return Index();
-            }
+                    return Index();
+                }
 
-            var password = StaticHelper.GetMD5Hash(model.Password);
+                var password = StaticHelper.GetMD5Hash(model.Password);
 
-            if (!String.Equals(user.Password, password, StringComparison.InvariantCulture))
-            {
-                ViewData["LoginFailed"] = true;
+                if (!String.Equals(user.Password, password, StringComparison.InvariantCulture))
+                {
+                    ViewData["LoginFailed"] = true;
 
-                return Index();
-            }
+                    return Index();
+                }
 
-            user.CurrentRole = user.Roles.First(r1 => r1.Level == user.Roles.Max(r2 => r2.Level));
+                user.CurrentRole = user.Roles.First(r1 => r1.Level == user.Roles.Max(r2 => r2.Level));
 
-            if (model.IsPermanent.HasValue && model.IsPermanent.Value)
-                _userHelper.ToCookies(user, DateTime.MaxValue);
-            else
-                _userHelper.ToCookies(user, DateTime.Now.AddHours(2));
+                if (model.IsPermanent.HasValue && model.IsPermanent.Value)
+                    _userHelper.ToCookies(user, DateTime.MaxValue);
+                else
+                    _userHelper.ToCookies(user, DateTime.Now.AddHours(2));
 
-            return RedirectToAction("Index", "Home");
+                _logger.Debug(
+                    String.Format("Login für Benutzer \"{0}\" erfolgreich.", model.Username));
+
+                return RedirectToAction("Index", "Home");
+            }
+            catch (Exception ex)
+            {
+                _logger.Error("Fehler bei Login eines Benutzers.", ex, user);
+
+                throw;
+            }
         }
 
         public ActionResult Logout()

+ 214 - 44
GreenTree.Nachtragsmanagement.Web/Controllers/MiscController.cs

@@ -4,10 +4,12 @@ using DevExpress.Web.ASPxThemes;
 using DevExpress.Web.Mvc;
 using DevExpress.XtraPrinting;
 using DevExpress.XtraScheduler;
+using GreenTree.Nachtragsmanagement.Core.Authentication;
 using GreenTree.Nachtragsmanagement.Core.Domain.Deviation;
 using GreenTree.Nachtragsmanagement.Core.Domain.User;
 using GreenTree.Nachtragsmanagement.Services.Appendix;
 using GreenTree.Nachtragsmanagement.Services.Deviation;
+using GreenTree.Nachtragsmanagement.Services.Logging;
 using GreenTree.Nachtragsmanagement.Services.Misc;
 using GreenTree.Nachtragsmanagement.Services.Scheduling;
 using GreenTree.Nachtragsmanagement.Services.User;
@@ -32,17 +34,23 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         private readonly IUserService _userService;
         private readonly INotificationService _notificationService;
         private readonly INotificationScheduler _notificationScheduler;
+        private readonly IUserHelper _userHelper;
+        private readonly ILogger _logger;
 
         public MiscController(
             IMiscService miscService,
             IUserService userService,
             INotificationService notificationService,
-            INotificationScheduler notificationScheduler)
+            INotificationScheduler notificationScheduler,
+            IUserHelper userHelper,
+            ILogger logger)
         {
             _miscService = miscService;
             _userService = userService;
             _notificationService = notificationService;
             _notificationScheduler = notificationScheduler;
+            _userHelper = userHelper;
+            _logger = logger;
 
             ViewData["AllUsers"] = _userService.GetAllUsers();
             ViewData["AllUsersWithRole"] =
@@ -191,53 +199,66 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         [HttpPost, ValidateInput(false)]
         public ActionResult EditMailNotification(MailNotificationDataModel mailNotificationModel)
         {
-            if (!ModelState.IsValid)
+            try
             {
-                foreach (var role in mailNotificationModel.UserValues)
-                    mailNotificationModel.UserDescriptions.Add(
-                        ((IList<User>)ViewData["AllUsers"])
-                            .First(r => r.Id == role).Lastname);
+                if (!ModelState.IsValid)
+                {
+                    foreach (var role in mailNotificationModel.UserValues)
+                        mailNotificationModel.UserDescriptions.Add(
+                            ((IList<User>)ViewData["AllUsers"])
+                                .First(r => r.Id == role).Lastname);
 
-                var notificationPlugin = _notificationService.GetNotificationPlugin(mailNotificationModel.NotificationPluginSystemName);
+                    var notificationPlugin = _notificationService.GetNotificationPlugin(mailNotificationModel.NotificationPluginSystemName);
 
-                if (notificationPlugin != null)
-                    mailNotificationModel.NotificationPlugin = notificationPlugin;
+                    if (notificationPlugin != null)
+                        mailNotificationModel.NotificationPlugin = notificationPlugin;
 
-                return PartialView("~/Views/Misc/_MailNotificationEditPartial.cshtml", mailNotificationModel);
-            }
+                    return PartialView("~/Views/Misc/_MailNotificationEditPartial.cshtml", mailNotificationModel);
+                }
 
-            if (mailNotificationModel.CronExpression.Split(' ').Length == 5)
-                mailNotificationModel.CronExpression = mailNotificationModel.CronExpression + " *";
+                if (mailNotificationModel.CronExpression.Split(' ').Length == 5)
+                    mailNotificationModel.CronExpression = mailNotificationModel.CronExpression + " *";
 
-            var selectedUsers = _userService.GetUsersByIds(mailNotificationModel.UserValues.ToArray());
+                var selectedUsers = _userService.GetUsersByIds(mailNotificationModel.UserValues.ToArray());
 
-            if (mailNotificationModel.Id == -1)
-            {
-                var mailNotification = mailNotificationModel.ToMailNotification();
+                if (mailNotificationModel.Id == -1)
+                {
+                    var mailNotification = mailNotificationModel.ToMailNotification();
 
-                mailNotification.SetUsers(selectedUsers);
+                    mailNotification.SetUsers(selectedUsers);
 
-                _miscService.InsertMailNotification(mailNotification);
-            }
-            else
-            {
-                var mailNotification = _miscService.GetMailNotificationById(mailNotificationModel.Id);
+                    _miscService.InsertMailNotification(mailNotification);
 
-                mailNotification.CronExpression = mailNotificationModel.CronExpression;
-                mailNotification.NotificationPluginSystemName = mailNotificationModel.NotificationPluginSystemName;
-                mailNotification.NotificationJobSystemName = mailNotificationModel.NotificationJobSystemName;
+                    _logger.Entity(mailNotification, Core.Domain.Logging.LogEntityActivity.Insert, _userHelper.FromCookies());
+                }
+                else
+                {
+                    var mailNotification = _miscService.GetMailNotificationById(mailNotificationModel.Id);
 
-                mailNotification.SetUsers(selectedUsers);
+                    mailNotification.CronExpression = mailNotificationModel.CronExpression;
+                    mailNotification.NotificationPluginSystemName = mailNotificationModel.NotificationPluginSystemName;
+                    mailNotification.NotificationJobSystemName = mailNotificationModel.NotificationJobSystemName;
 
-                _miscService.UpdateMailNotification(mailNotification);
-            }
+                    mailNotification.SetUsers(selectedUsers);
 
-            _notificationScheduler.Start();
+                    _miscService.UpdateMailNotification(mailNotification);
 
-            return new JsonResult
+                    _logger.Entity(mailNotification, Core.Domain.Logging.LogEntityActivity.Update, _userHelper.FromCookies());
+                }
+
+                _notificationScheduler.Start();
+
+                return new JsonResult
+                {
+                    Data = "success"
+                };
+            }
+            catch (Exception ex)
             {
-                Data = "success"
-            };
+                _logger.Error("Fehler bei Speicherung einer Mail-Benachrichtigung.", ex, _userHelper.FromCookies());
+
+                return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+            }
         }
 
         /// <summary>
@@ -247,15 +268,26 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         [HttpPost]
         public ActionResult DeleteMailNotification(int id)
         {
-            var mailNotification = _miscService.GetMailNotificationById(id);
+            try
+            {
+                var mailNotification = _miscService.GetMailNotificationById(id);
 
-            if (mailNotification != null)
-                _miscService.DeleteMailNotification(mailNotification);
+                if (mailNotification != null)
+                    _miscService.DeleteMailNotification(mailNotification);
 
-            return new JsonResult
+                _logger.Entity(mailNotification, Core.Domain.Logging.LogEntityActivity.Delete, _userHelper.FromCookies());
+
+                return new JsonResult
+                {
+                    Data = "success"
+                };
+            }
+            catch (Exception ex)
             {
-                Data = "success"
-            };
+                _logger.Error("Fehler bei Löschung einer Mail-Benachrichtigung.", ex, _userHelper.FromCookies());
+
+                return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+            }
         }
 
         /// <summary>
@@ -265,21 +297,159 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         [HttpPost]
         public ActionResult ProcessMailNotification(int id)
         {
-            var mailNotification = _miscService.GetMailNotificationById(id);
+            try
+            {
+                var mailNotification = _miscService.GetMailNotificationById(id);
+
+                if (mailNotification != null)
+                {
+                    var notificationPlugin = _notificationService.GetNotificationPlugin(mailNotification.NotificationPluginSystemName);
 
-            if (mailNotification != null)
+                    notificationPlugin.ProcessNotifications(new[] { mailNotification });
+                }
+
+                return new JsonResult
+                {
+                    Data = "success"
+                };
+            }
+            catch (Exception ex)
             {
-                var notificationPlugin = _notificationService.GetNotificationPlugin(mailNotification.NotificationPluginSystemName);
+                _logger.Error("Fehler bei Ausführung einer Mail-Benachrichtigung.", ex, _userHelper.FromCookies());
 
-                notificationPlugin.ProcessNotifications(new[] { mailNotification });
+                return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
             }
+        }
+
+        #endregion
+
+        #region Logs
+
+        /// <summary>
+        /// Basic log view function
+        /// </summary>
+        [FunctionAuthorize(true, "Misc-Logs")]
+        public ActionResult ViewLogs()
+        {
+            var logs = _logger.GetAllLogs();
+            var logModels = logs
+                .Select(u => LogDataModel.FromLog(u, false))
+                .ToList();
+
+            return View("~/Views/Misc/Logs.cshtml", logModels);
+        }
+
+        /// <summary>
+        /// Get JSON data of specific log
+        /// </summary>
+        /// <param name="id">Log id.</param>
+        public ActionResult GetLog(int id = -1)
+        {
+            var log = _logger.GetLogById(id);
+            if (log == null)
+                return new JsonResult
+                {
+                    Data = "notFound",
+                    JsonRequestBehavior = JsonRequestBehavior.AllowGet
+                };
+
+            var logModel = LogDataModel.FromLog(log, false);
 
             return new JsonResult
             {
-                Data = "success"
+                Data = JsonConvert.SerializeObject(logModel),
+                JsonRequestBehavior = JsonRequestBehavior.AllowGet
             };
         }
 
+        /// <summary>
+        /// Callback result for log grid
+        /// </summary>
+        /// <param name="scrollHeight">The height of the grid scrollable component.</param>
+        public ActionResult PartialLogs(int scrollHeight = -1)
+        {
+            var logs = _logger.GetAllLogs();
+            var logModels = logs
+                .Select(u => LogDataModel.FromLog(u, false))
+                .ToList();
+
+            ViewData["ScrollHeight"] = scrollHeight;
+
+            return PartialView("~/Views/Misc/_LogGridPartial.cshtml", logModels);
+        }
+
+        /// <summary>
+        /// Export result for log grid
+        /// </summary>
+        [HttpPost]
+        public ActionResult ExportPartialLogs(GridViewExportFormat exportformat)
+        {
+            if (exportformat == null || String.IsNullOrEmpty(exportformat.Format))
+                return new EmptyResult();
+
+            var logs = _logger.GetAllLogs();
+            var logModels = logs
+                .Select(u => LogDataModel.FromLog(u, false))
+                .ToList();
+
+            var viewContext = new ViewContext();
+            var viewPage = new ViewPage();
+            var htmlHelper = new HtmlHelper(viewContext, viewPage);
+
+            var gridViewSettings = Extensions.GridViewSettingsHelper.LogGridViewSettings(htmlHelper);
+
+            switch (exportformat.Format.ToLower())
+            {
+                case "xlsx":
+                    return GridViewExtension.ExportToXlsx(gridViewSettings, logModels);
+                case "xls":
+                    return GridViewExtension.ExportToXls(gridViewSettings, logModels);
+                case "pdf":
+                    return GridViewExtension.ExportToPdf(gridViewSettings, logModels);
+                default:
+                    return new EmptyResult();
+            }
+        }
+
+        /// <summary>
+        /// Partial edit for editing of existing or for new log
+        /// </summary>
+        /// <param name="id">Id for existing log, otherweise -1.</param>
+        public ActionResult ViewLog(int id = -1)
+        {
+            var log = _logger.GetLogById(id);
+            var logModel = LogDataModel.FromLog(log, true);
+
+            return PartialView("~/Views/Misc/_LogViewPartial.cshtml", logModel);
+        }
+
+        /// <summary>
+        /// Simple JSON result for deleting a specific log
+        /// </summary>
+        /// <param name="id">Log id.</param>
+        [HttpPost]
+        public ActionResult DeleteLog(int id)
+        {
+            try
+            {
+                var log = _logger.GetLogById(id);
+
+                if (log != null)
+                    _logger.DeleteLog(log);
+
+                return new JsonResult
+                {
+                    Data = "success"
+                };
+            }
+            catch (Exception ex)
+            {
+                _logger.Error("Fehler bei Löschung eines Logs.", ex, _userHelper.FromCookies());
+
+                return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+            }
+        }
+
         #endregion
     }
 }

+ 138 - 73
GreenTree.Nachtragsmanagement.Web/Controllers/SiteController.cs

@@ -3,10 +3,12 @@ using DevExpress.Web;
 using DevExpress.Web.ASPxThemes;
 using DevExpress.Web.Mvc;
 using DevExpress.XtraPrinting;
+using GreenTree.Nachtragsmanagement.Core.Authentication;
 using GreenTree.Nachtragsmanagement.Core.Domain.Deviation;
 using GreenTree.Nachtragsmanagement.Core.Domain.User;
 using GreenTree.Nachtragsmanagement.Services.Appendix;
 using GreenTree.Nachtragsmanagement.Services.Deviation;
+using GreenTree.Nachtragsmanagement.Services.Logging;
 using GreenTree.Nachtragsmanagement.Services.Site;
 using GreenTree.Nachtragsmanagement.Services.User;
 using GreenTree.Nachtragsmanagement.Web.Framework.Authorization;
@@ -30,17 +32,23 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         private readonly IDeviationService _deviationService;
         private readonly IAppendixService _appendixService;
         private readonly IUserService _userService;
+        private readonly IUserHelper _userHelper;
+        private readonly ILogger _logger;
 
         public SiteController(
             ISiteService siteService,
             IDeviationService deviationService,
             IAppendixService appendixService,
-            IUserService userService)
+            IUserService userService,
+            IUserHelper userHelper,
+            ILogger logger)
         {
             _siteService = siteService;
             _deviationService = deviationService;
             _appendixService = appendixService;
             _userService = userService;
+            _userHelper = userHelper;
+            _logger = logger;
 
             ViewData["AllUsers"] = _userService.GetAllUsers();
             ViewData["AllUsersWithRole"] =
@@ -186,54 +194,67 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         [HttpPost, ValidateInput(false)]
         public ActionResult EditSite(SiteDataModel siteModel)
         {
-            if (!ModelState.IsValid)
+            try
             {
-                foreach (var role in siteModel.UserValues)
-                    siteModel.UserDescriptions.Add(
-                        ((IList<User>)ViewData["AllUsers"])
-                            .First(r => r.Id == role).Lastname);
-
-                if (siteModel.Id != -1)
+                if (!ModelState.IsValid)
                 {
-                    var site = _siteService.GetSiteById(siteModel.Id);
+                    foreach (var role in siteModel.UserValues)
+                        siteModel.UserDescriptions.Add(
+                            ((IList<User>)ViewData["AllUsers"])
+                                .First(r => r.Id == role).Lastname);
+
+                    if (siteModel.Id != -1)
+                    {
+                        var site = _siteService.GetSiteById(siteModel.Id);
 
-                    var siteTreeModel = SiteTreeDataModel.TreeFromSite(site);
+                        var siteTreeModel = SiteTreeDataModel.TreeFromSite(site);
 
-                    siteModel.SiteTreeData = siteTreeModel;
+                        siteModel.SiteTreeData = siteTreeModel;
+                    }
+
+                    return PartialView("~/Views/Sites/_SiteEditPartial.cshtml", siteModel);
                 }
 
-                return PartialView("~/Views/Sites/_SiteEditPartial.cshtml", siteModel);
-            }
+                var selectedUsers = _userService.GetUsersByIds(siteModel.UserValues.ToArray());
 
-            var selectedUsers = _userService.GetUsersByIds(siteModel.UserValues.ToArray());
+                if (siteModel.Id == -1)
+                {
+                    var site = siteModel.ToSite();
 
-            if (siteModel.Id == -1)
-            {
-                var site = siteModel.ToSite();
+                    site.SetUsers(selectedUsers);
 
-                site.SetUsers(selectedUsers);
+                    _siteService.InsertSite(site);
 
-                _siteService.InsertSite(site);
-            }
-            else
-            {
-                var site = _siteService.GetSiteById(siteModel.Id);
+                    _logger.Entity(site, Core.Domain.Logging.LogEntityActivity.Insert, _userHelper.FromCookies());
+                }
+                else
+                {
+                    var site = _siteService.GetSiteById(siteModel.Id);
 
-                site.CustomNumber = siteModel.CustomNumber;
-                site.Description = siteModel.Description;
-                site.Start = siteModel.Start;
-                site.End = siteModel.End;
-                site.Comment = siteModel.Comment;
+                    site.CustomNumber = siteModel.CustomNumber;
+                    site.Description = siteModel.Description;
+                    site.Start = siteModel.Start;
+                    site.End = siteModel.End;
+                    site.Comment = siteModel.Comment;
 
-                site.SetUsers(selectedUsers);
+                    site.SetUsers(selectedUsers);
 
-                _siteService.UpdateSite(site);
-            }
+                    _siteService.UpdateSite(site);
 
-            return new JsonResult
+                    _logger.Entity(site, Core.Domain.Logging.LogEntityActivity.Update, _userHelper.FromCookies());
+                }
+
+                return new JsonResult
+                {
+                    Data = "success"
+                };
+            }
+            catch (Exception ex)
             {
-                Data = "success"
-            };
+                _logger.Error("Fehler bei Speicherung einer Baustelle.", ex, _userHelper.FromCookies());
+
+                return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+            }
         }
 
 
@@ -244,31 +265,42 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         [HttpPost, ValidateInput(false)]
         public ActionResult EditSiteForAppend(SiteDataModel siteModel)
         {
-            if (!ModelState.IsValid)
+            try
             {
-                foreach (var role in siteModel.UserValues)
-                    siteModel.UserDescriptions.Add(
-                        ((IList<User>)ViewData["AllUsers"])
-                            .First(r => r.Id == role).Lastname);
+                if (!ModelState.IsValid)
+                {
+                    foreach (var role in siteModel.UserValues)
+                        siteModel.UserDescriptions.Add(
+                            ((IList<User>)ViewData["AllUsers"])
+                                .First(r => r.Id == role).Lastname);
 
-                return PartialView("~/Views/Sites/_SiteEditPartial.cshtml", siteModel);
-            }
+                    return PartialView("~/Views/Sites/_SiteEditPartial.cshtml", siteModel);
+                }
 
-            var selectedUsers = _userService.GetUsersByIds(siteModel.UserValues.ToArray());
-            var siteId = siteModel.Id;
+                var selectedUsers = _userService.GetUsersByIds(siteModel.UserValues.ToArray());
+                var siteId = siteModel.Id;
 
-            if (siteModel.Id == -1)
-            {
-                var site = siteModel.ToSite();
+                if (siteModel.Id == -1)
+                {
+                    var site = siteModel.ToSite();
 
-                site.SetUsers(selectedUsers);
+                    site.SetUsers(selectedUsers);
 
-                _siteService.InsertSite(site);
+                    _siteService.InsertSite(site);
 
-                siteId = site.Id;
+                    siteId = site.Id;
+
+                    _logger.Entity(site, Core.Domain.Logging.LogEntityActivity.Insert, _userHelper.FromCookies());
+                }
+
+                return EditSite(siteId);
             }
+            catch (Exception ex)
+            {
+                _logger.Error("Fehler bei Speicherung einer Baustelle für einen Nachtrag.", ex, _userHelper.FromCookies());
 
-            return EditSite(siteId);
+                return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+            }
         }
 
         /// <summary>
@@ -278,15 +310,26 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         [HttpPost]
         public ActionResult DeleteSite(int id)
         {
-            var site = _siteService.GetSiteById(id);
+            try
+            {
+                var site = _siteService.GetSiteById(id);
 
-            if (site != null)
-                _siteService.DeleteSite(site);
+                if (site != null)
+                    _siteService.DeleteSite(site);
 
-            return new JsonResult
+                _logger.Entity(site, Core.Domain.Logging.LogEntityActivity.Delete, _userHelper.FromCookies());
+
+                return new JsonResult
+                {
+                    Data = "success"
+                };
+            }
+            catch (Exception ex)
             {
-                Data = "success"
-            };
+                _logger.Error("Fehler bei Löschung einer Baustelle.", ex, _userHelper.FromCookies());
+
+                return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+            }
         }
 
         #endregion
@@ -330,34 +373,56 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
 
             if (determinedTargetKey == "a_0")
             {
-                var site = _siteService.GetSiteById(siteId);
+                try
+                {
+                    var site = _siteService.GetSiteById(siteId);
 
-                if (deviation.Site != null)
-                    return new EmptyResult();
+                    if (deviation.Site != null)
+                        return new EmptyResult();
+
+                    deviation.Appendix = null;
+                    deviation.AppendixId = null;
 
-                deviation.Appendix = null;
-                deviation.AppendixId = null;
+                    site.Deviations.Add(deviation);
 
-                site.Deviations.Add(deviation);
+                    _siteService.UpdateSite(site);
+                    _deviationService.UpdateDeviation(deviation);
 
-                _siteService.UpdateSite(site);
-                _deviationService.UpdateDeviation(deviation);
+                    _logger.Entity(deviation, Core.Domain.Logging.LogEntityActivity.Update, _userHelper.FromCookies());
+                }
+                catch (Exception ex)
+                {
+                    _logger.Error("Fehler bei Zuweisung einer VA zu einer Baustelle.", ex, _userHelper.FromCookies());
+
+                    return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+                }
             }
             else
             {
-                var appendixId = Convert.ToInt32(determinedTargetKey.Replace("a_", String.Empty));
-                var appendix = _appendixService.GetAppendixById(appendixId);
+                try
+                {
+                    var appendixId = Convert.ToInt32(determinedTargetKey.Replace("a_", String.Empty));
+                    var appendix = _appendixService.GetAppendixById(appendixId);
 
-                if (deviation.AppendixId == appendixId)
-                    return new EmptyResult();
+                    if (deviation.AppendixId == appendixId)
+                        return new EmptyResult();
 
-                deviation.Site = null;
-                deviation.SiteId = null;
+                    deviation.Site = null;
+                    deviation.SiteId = null;
 
-                appendix.Deviations.Add(deviation);
+                    appendix.Deviations.Add(deviation);
 
-                _appendixService.UpdateAppendix(appendix);
-                _deviationService.UpdateDeviation(deviation);
+                    _appendixService.UpdateAppendix(appendix);
+                    _deviationService.UpdateDeviation(deviation);
+
+                    _logger.Entity(deviation, Core.Domain.Logging.LogEntityActivity.Update, _userHelper.FromCookies());
+                }
+                catch (Exception ex)
+                {
+                    _logger.Error("Fehler bei Zuweisung einer VA zu einem Nachtrag.", ex, _userHelper.FromCookies());
+
+                    return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+                }
             }
 
             return new JsonResult

+ 207 - 0
GreenTree.Nachtragsmanagement.Web/Extensions/GridViewSettingsHelper.cs

@@ -206,6 +206,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Extensions
             s.Columns.Add(column =>
             {
                 column.Caption = "Kommentar";
+                column.FieldName = "Comment";
                 column.CellStyle.Wrap = DefaultBoolean.True;
                 column.SetDataItemTemplateContent(c =>
                 {
@@ -409,6 +410,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Extensions
             s.Columns.Add(column =>
             {
                 column.Caption = "Kommentar";
+                column.FieldName = "Comment";
                 column.CellStyle.Wrap = DefaultBoolean.True;
                 column.SetDataItemTemplateContent(c =>
                 {
@@ -672,6 +674,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Extensions
             s.Columns.Add(column =>
             {
                 column.Caption = "Kommentar";
+                column.FieldName = "Comment";
                 column.CellStyle.Wrap = DefaultBoolean.True;
                 column.SetDataItemTemplateContent(c =>
                 {
@@ -874,6 +877,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Extensions
             s.Columns.Add(column =>
             {
                 column.Caption = "Mitarbeiter";
+                column.FieldName = "UserDescriptions";
                 column.MinWidth = 150;
                 column.Width = new Unit(15, UnitType.Percentage);
                 column.SetDataItemTemplateContent(c =>
@@ -924,5 +928,208 @@ namespace GreenTree.Nachtragsmanagement.Web.Extensions
 
             return s;
         }
+
+        /// <summary>
+        /// Creates GridViewSettings for the logs gridView
+        /// </summary>
+        /// <param name="html">Current HtmlHelper context.</param>
+        public static GridViewSettings LogGridViewSettings(this System.Web.Mvc.HtmlHelper html)
+        {
+            var s = new GridViewSettings();
+
+            s.Name = "devGridViewLog";
+            s.KeyFieldName = "Id";
+            s.CallbackRouteValues = new { Controller = "Misc", Action = "PartialLogs" };
+            s.Width = Unit.Percentage(99);
+            s.Settings.ShowHeaderFilterButton = true;
+            s.Settings.ShowFilterRow = true;
+            s.Settings.ShowFilterRowMenu = true;
+            s.Settings.ShowFooter = true;
+            s.Settings.ShowGroupPanel = true;
+            s.Settings.AutoFilterCondition = AutoFilterCondition.Contains;
+            s.Settings.VerticalScrollBarMode = ScrollBarMode.Auto;
+            s.Settings.VerticalScrollableHeight =
+                (html.ViewData["ScrollHeight"] == null || (int)html.ViewData["ScrollHeight"] == -1)
+                ? 400
+                : (int)html.ViewData["ScrollHeight"];
+            s.SettingsExport.Landscape = true;
+            s.SettingsExport.FileName = "Logliste";
+            s.SettingsPopup.CustomizationWindow.Width = new Unit(250, UnitType.Pixel);
+            s.SettingsPopup.CustomizationWindow.Height = new Unit(350, UnitType.Pixel);
+            s.SettingsBehavior.EnableCustomizationWindow = true;
+            s.SettingsBehavior.AllowHeaderFilter = true;
+            s.SettingsPager.AlwaysShowPager = true;
+            s.SettingsResizing.ColumnResizeMode = ColumnResizeMode.NextColumn;
+            s.SettingsCookies.Enabled = true;
+            s.SettingsCookies.CookiesID = "logGridStateCookie";
+
+            s.Toolbars.Add(t =>
+            {
+                var refreshItem = t.Items.Add(GridViewToolbarCommand.Refresh);
+                refreshItem.Text = "Aktualisieren";
+                var filterItem = t.Items.Add(GridViewToolbarCommand.ClearFilter);
+                filterItem.Text = "Filter entfernen";
+                t.Items.Add(i =>
+                {
+                    i.Text = "Spalten anzeigen / ausblenden";
+                    i.Name = "ToggleColumnChooser";
+                    i.Image.IconID = IconID.OtherViewgridlines16x16gray;
+                    i.BeginGroup = true;
+                });
+                t.Items.Add(i =>
+                {
+                    i.Text = "Exportieren nach";
+                    i.Image.IconID = IconID.ActionsDownload16x16office2013;
+                    i.BeginGroup = true;
+                    i.Items.Add(exportitem =>
+                    {
+                        exportitem.Name = "Pdf";
+                        exportitem.Text = "PDF";
+                        exportitem.Image.IconID = IconID.ExportExporttopdf16x16office2013;
+                    });
+                    i.Items.Add(exportitem =>
+                    {
+                        exportitem.Name = "Xlsx";
+                        exportitem.Text = "XLSX";
+                        exportitem.Image.IconID = IconID.ExportExporttoxlsx16x16office2013;
+                    });
+                    i.Items.Add(exportitem =>
+                    {
+                        exportitem.Name = "Xls";
+                        exportitem.Text = "XLS";
+                        exportitem.Image.IconID = IconID.ExportExporttoxls16x16office2013;
+                    });
+                });
+            });
+
+            s.Columns.Add(column =>
+            {
+                column.Caption = "#";
+                column.SetDataItemTemplateContent(c =>
+                {
+                    html.ViewContext.Writer.Write(
+                        "<a href=\"#\" onclick=\"viewLog(" + DataBinder.Eval(c.DataItem, "Id") + ")\">Betrachten</a>&nbsp;");
+
+                    if (_userContext.CurrentUser.HasFunction("Misc-Logs-Delete"))
+                    {
+                        html.ViewContext.Writer.Write(
+                            "<a href=\"#\" onclick=\"deleteLog(" + DataBinder.Eval(c.DataItem, "Id") + ")\">Löschen</a><br />");
+                    }
+                });
+                column.Settings.AllowDragDrop = DefaultBoolean.False;
+                column.Settings.AllowSort = DefaultBoolean.False;
+                column.Width = new Unit(150, UnitType.Pixel);
+            });
+            s.Columns.Add(column =>
+            {
+                column.Caption = "Level";
+                column.FieldName = "LogLevelDescription";
+                column.MinWidth = 100;
+                column.Width = new Unit(8, UnitType.Percentage);
+            });
+            s.Columns.Add(column =>
+            {
+                column.Caption = "Betreff";
+                column.FieldName = "ShortMessage";
+                column.MinWidth = 150;
+                column.Width = new Unit(12, UnitType.Percentage);
+            });
+            s.Columns.Add(column =>
+            {
+                column.Caption = "Nachricht";
+                column.FieldName = "FullMessage";
+                column.MinWidth = 200;
+                column.Width = new Unit(20, UnitType.Percentage);
+                column.SetDataItemTemplateContent(c =>
+                {
+                    var fullMessage = DataBinder.Eval(c.DataItem, "FullMessage");
+                    var text = fullMessage == null
+                        ? String.Empty
+                        : fullMessage.ToString();
+
+                    if (text.ToString().Length > 80)
+                        html.ViewContext.Writer.Write(text.Substring(0, 80) + " ...");
+                    else
+                        html.ViewContext.Writer.Write(text);
+                });
+            });
+            s.Columns.Add(column =>
+            {
+                column.Caption = "IP-Adresse";
+                column.FieldName = "IpAddress";
+                column.MinWidth = 130;
+                column.Width = new Unit(10, UnitType.Percentage);
+            });
+            s.Columns.Add(column =>
+            {
+                column.Caption = "Mitarbeiter";
+                column.FieldName = "UserDescription";
+                column.MinWidth = 130;
+                column.Width = new Unit(10, UnitType.Percentage);
+            });
+            s.Columns.Add(column =>
+            {
+                column.Caption = "Datentyp";
+                column.FieldName = "EntityType";
+                column.MinWidth = 100;
+                column.Width = new Unit(8, UnitType.Percentage);
+            });
+            s.Columns.Add(column =>
+            {
+                column.Caption = "Daten ID";
+                column.FieldName = "EntityId";
+                column.MinWidth = 100;
+                column.Width = new Unit(8, UnitType.Percentage);
+            });
+            s.Columns.Add(column =>
+            {
+                column.Caption = "Seiten URL";
+                column.FieldName = "PageUrl";
+                column.MinWidth = 150;
+                column.Width = new Unit(12, UnitType.Percentage);
+                column.Visible = false;
+            });
+            s.Columns.Add(column =>
+            {
+                column.Caption = "Relative URL";
+                column.FieldName = "ReferrerUrl";
+                column.MinWidth = 100;
+                column.Width = new Unit(8, UnitType.Percentage);
+                column.Visible = false;
+            });
+            s.Columns.Add(column =>
+            {
+                column.Caption = "Zeitpunkt";
+                column.FieldName = "CreatedOnUtc";
+                column.SetDataItemTemplateContent(c =>
+                {
+                    var dateTime = DataBinder.Eval(c.DataItem, "CreatedOnUtc") as DateTime?;
+                    var text = dateTime == null
+                        ? String.Empty
+                        : dateTime.Value.ToLocalTime().ToString("dd.MM.yyyy - HH:mm:ss");
+
+                    html.ViewContext.Writer.Write(text);
+                });
+                column.MinWidth = 150;
+                column.Width = new Unit(12, UnitType.Percentage);
+            });
+
+            s.ClientLayout = (sender, e) =>
+            {
+                if (e.LayoutMode == ClientLayoutMode.Loading)
+                {
+                    if (System.Web.HttpContext.Current.Session["LogGridState"] != null)
+                        e.LayoutData = (string)System.Web.HttpContext.Current.Session["LogGridState"];
+                }
+                else
+                    System.Web.HttpContext.Current.Session["LogGridState"] = e.LayoutData;
+            };
+            s.ClientSideEvents.BeginCallback = "function (s, e) { e.customArgs['scrollHeight'] = [ gridScrollHeight ]; }";
+            s.ClientSideEvents.ToolbarItemClick = "function (s, e) { onToolbarItemClick(s, e); }";
+
+            s.Styles.AlternatingRow.BackColor = System.Drawing.Color.FromArgb(247, 247, 247);
+
+            return s;
+        }
     }
 }

+ 46 - 19
GreenTree.Nachtragsmanagement.Web/Global.asax.cs

@@ -30,6 +30,7 @@ using GreenTree.Nachtragsmanagement.Services.Configuration;
 using GreenTree.Nachtragsmanagement.Core.Domain.Config;
 using GreenTree.Nachtragsmanagement.Services.Misc;
 using GreenTree.Nachtragsmanagement.Services.Scheduling;
+using GreenTree.Nachtragsmanagement.Services.Logging;
 
 namespace GreenTree.Nachtragsmanagement.Web
 {
@@ -40,44 +41,70 @@ namespace GreenTree.Nachtragsmanagement.Web
     {
         protected void Application_Start()
         {
-            AreaRegistration.RegisterAllAreas();
+            try
+            {
+                AreaRegistration.RegisterAllAreas();
 
-            GlobalConfiguration.Configure(WebApiConfig.Register);
-            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
-            RouteConfig.RegisterRoutes(RouteTable.Routes);
+                GlobalConfiguration.Configure(WebApiConfig.Register);
+                FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
+                RouteConfig.RegisterRoutes(RouteTable.Routes);
 
-            BundleConfig.RegisterBundles(BundleTable.Bundles);
-            
-            ModelBinders.Binders.DefaultBinder = new DevExpress.Web.Mvc.DevExpressEditorsBinder();
+                BundleConfig.RegisterBundles(BundleTable.Bundles);
 
-            ApplicationContext.InitApplication();
-            ApplicationContext.InitPluginRoutes(RouteTable.Routes);
+                ModelBinders.Binders.DefaultBinder = new DevExpress.Web.Mvc.DevExpressEditorsBinder();
 
-            FunctionConfig.RegisterFunctions();
+                ApplicationContext.InitApplication();
+                ApplicationContext.InitPluginRoutes(RouteTable.Routes);
 
-            FluentValidationModelValidatorProvider.Configure(provider =>
-            {
-                provider.ValidatorFactory = new AppendixValidatorFactory();
-            });
+                var logger = Singleton<IContainer>.Instance.Resolve<ILogger>();
 
-            var notificationScheduler = Singleton<IContainer>.Instance.Resolve<INotificationScheduler>();
+                FunctionConfig.RegisterFunctions();
 
-            if (notificationScheduler != null)
-                notificationScheduler.Start();
+                FluentValidationModelValidatorProvider.Configure(provider =>
+                {
+                    provider.ValidatorFactory = new AppendixValidatorFactory();
+                });
+
+                var notificationScheduler = Singleton<IContainer>.Instance.Resolve<INotificationScheduler>();
+
+                if (notificationScheduler != null)
+                    notificationScheduler.Start();
+
+                logger.Information("Anwendung erfolgreich gestartet.");
+            }
+            catch (Exception ex)
+            {
+                var logger = Singleton<IContainer>.Instance.Resolve<ILogger>();
+
+                if (logger != null)
+                    logger.Fatal("Fehler bei Start der Anwendung.", ex, null);
+            }
 
             DevExpress.Web.ASPxWebControl.CallbackError += Application_Error;
 
             GenerateTestData();
         }
 
+        /// <summary>
+        /// Event handler for general exceptions
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
         protected void Application_Error(object sender, EventArgs e) 
         {
-            Exception exception = System.Web.HttpContext.Current.Server.GetLastError();
-            //TODO: Handle Exception
+            var logger = Singleton<IContainer>.Instance.Resolve<ILogger>();
+
+            var exception = HttpContext.Current.Server.GetLastError();
+
+            if (logger != null)
+                logger.Error("Allgemeiner Fehler aufgetreten.", exception, null);
         }
 
         #region Test
 
+        /// <summary>
+        /// Generates test data for testing the application
+        /// </summary>
         private void GenerateTestData()
         {
             // Get services

+ 7 - 1
GreenTree.Nachtragsmanagement.Web/GreenTree.Nachtragsmanagement.Web.csproj

@@ -171,6 +171,8 @@
     <Content Include="Content\devex.css" />
     <Content Include="Content\function.css" />
     <Content Include="Content\global.css" />
+    <Content Include="Content\Images\function-Misc-Logs-32-contrast.png" />
+    <Content Include="Content\Images\function-Misc-Logs-32.png" />
     <Content Include="Content\Images\add-16-contrast.png" />
     <Content Include="Content\Images\add-16.png" />
     <Content Include="Content\Images\add-24-contrast.png" />
@@ -317,6 +319,10 @@
     <Content Include="Views\Misc\_MailNotificationEditPartial.cshtml" />
     <Content Include="Views\Misc\_MailNotificationPluginJobsPartial.cshtml" />
     <Content Include="Views\Misc\_MailNotificationCronPartial.cshtml" />
+    <Content Include="Views\Shared\_PopupError.cshtml" />
+    <Content Include="Views\Misc\Logs.cshtml" />
+    <Content Include="Views\Misc\_LogGridPartial.cshtml" />
+    <Content Include="Views\Misc\_LogViewPartial.cshtml" />
     <None Include="Web.Debug.config">
       <DependentUpon>Web.config</DependentUpon>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@@ -334,12 +340,12 @@
     <Compile Include="App_Start\WebApiConfig.cs" />
     <Compile Include="Controllers\MiscController.cs" />
     <Compile Include="Extensions\ControlHelper.cs" />
+    <Compile Include="Models\Misc\LogDataModel.cs" />
     <Compile Include="Models\Misc\MailNotificationDataModel.cs" />
     <Compile Include="Implementations\DeviationNotificationPlugin.cs" />
     <Compile Include="Implementations\AppendixNotificationPlugin.cs" />
     <Compile Include="Controllers\AdminController.cs" />
     <Compile Include="Controllers\AppendixController.cs" />
-    <Compile Include="Controllers\AuthController.cs" />
     <Compile Include="Controllers\DataCallbackController.cs" />
     <Compile Include="Controllers\DeviationController.cs" />
     <Compile Include="Controllers\GlobalController.cs" />

+ 63 - 0
GreenTree.Nachtragsmanagement.Web/Models/Misc/LogDataModel.cs

@@ -0,0 +1,63 @@
+using GreenTree.Nachtragsmanagement.Core.Domain.Logging;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+
+namespace GreenTree.Nachtragsmanagement.Web.Models.Misc
+{
+    public class LogDataModel
+    {
+        public int Id { get; set; }
+        public int LogLevelId { get; set; }
+        public string LogLevelDescription { get; set; }
+        public string ShortMessage { get; set; }
+        public string FullMessage { get; set; }
+        public string IpAddress { get; set; }
+        public int? UserId { get; set; }
+        public string UserDescription { get; set; }
+        public int? EntityId { get; set; }
+        public string EntityType { get; set; }
+        public string PageUrl { get; set; }
+        public string ReferrerUrl { get; set; }
+        public DateTime CreatedOnUtc { get; set; }
+
+        public LogDataModel()
+        {
+
+        }
+
+        public static LogDataModel FromLog(Core.Domain.Logging.Log logEntity, bool newWhenIsNull)
+        {
+            if (logEntity == null && newWhenIsNull)
+                return new LogDataModel
+                {
+                    Id = -1,
+                };
+
+            if (logEntity == null && !newWhenIsNull)
+                throw new ArgumentNullException("logEntity", "Cannot create LogDataModel from NULL log entity.");
+
+            var logDataModel = new LogDataModel
+            {
+                Id = logEntity.Id,
+                LogLevelId = logEntity.LogLevelId,
+                LogLevelDescription = Enum.GetName(typeof(LogLevel), (LogLevel)logEntity.LogLevelId),
+                ShortMessage = logEntity.ShortMessage,
+                FullMessage = logEntity.FullMessage,
+                IpAddress = logEntity.IpAddress,
+                UserId = logEntity.UserId,
+                UserDescription = logEntity.User == null
+                    ? String.Empty
+                    : logEntity.User.Lastname,
+                EntityId = logEntity.EntityId,
+                EntityType = logEntity.EntityType,
+                PageUrl = logEntity.PageUrl,
+                ReferrerUrl = logEntity.ReferrerUrl,
+                CreatedOnUtc = logEntity.CreatedOnUtc
+            };
+
+            return logDataModel;
+        }
+    }
+}

+ 15 - 4
GreenTree.Nachtragsmanagement.Web/Views/Admin/Roles/View.cshtml

@@ -10,11 +10,11 @@
 	var deleteId;
 	var deleteReplaceId = -1;
 	var gridScrollHeight;
-	var gridScrollOffset = 120;
+	var gridScrollOffset = 130;
 	var resizeFinished;
 
 	$(document).ready(function () {
-		gridScrollHeight = $(window).height() - gridScrollOffset;
+		gridScrollHeight = calculateGridScrollHeight();
 		setTimeout(function () {
 			devGridViewRole.PerformCallback();
 		}, 500);
@@ -23,11 +23,22 @@
 	$(window).resize(function () {
 		clearTimeout(window.resizedFinished);
 		window.resizedFinished = setTimeout(function () {
-			gridScrollHeight = $(window).height() - gridScrollOffset;
-			devGridViewRole.PerformCallback();
+			setGridScrollHeight();
 		}, 250);
 	});
 
+	function calculateGridScrollHeight() {
+		var windowHeight = $(window).height();
+		var gridHeaderHeight = $("#devGridViewRole_DXHeadersRow0").height();
+		var gridFooterHeight = $("#devGridViewRole_DXFooterRow").height();
+		return windowHeight - gridHeaderHeight - gridFooterHeight - gridScrollOffset;
+	}
+
+	function setGridScrollHeight() {
+		gridScrollHeight = calculateGridScrollHeight();
+		devGridViewRole.PerformCallback();
+	}
+
 	function editRole(id) {
 		if (!id) return;
 		$.ajax({

+ 2 - 1
GreenTree.Nachtragsmanagement.Web/Views/Admin/Roles/_RoleGridPartial.cshtml

@@ -9,7 +9,7 @@
 	s.Name = "devGridViewRole";
 	s.KeyFieldName = "Id";
 	s.CallbackRouteValues = new { Controller = "Admin", Action = "PartialRoles" };
-	s.Width = Unit.Percentage(100);
+	s.Width = Unit.Percentage(99);
 	s.Settings.ShowFilterRow = true;
 	s.Settings.ShowFilterRowMenu = true;
 	s.Settings.VerticalScrollBarMode = ScrollBarMode.Auto;
@@ -17,6 +17,7 @@
 		(ViewData["ScrollHeight"] == null || (int)ViewData["ScrollHeight"] == -1)
 			? 400
 			: (int)ViewData["ScrollHeight"];
+	s.SettingsPager.AlwaysShowPager = true;
 
 	if (userContext.CurrentUser.HasFunction("Administration-Roles-Edit"))
 	{

+ 15 - 4
GreenTree.Nachtragsmanagement.Web/Views/Admin/Users/View.cshtml

@@ -8,11 +8,11 @@
 	var deleteId;
 	var resizeFinished;
 	var gridScrollHeight;
-	var gridScrollOffset = 120;
+	var gridScrollOffset = 130;
 	var resizeFinished;
 
 	$(document).ready(function () {
-		gridScrollHeight = $(window).height() - gridScrollOffset;
+		gridScrollHeight = calculateGridScrollHeight();
 		setTimeout(function () {
 			devGridViewUser.PerformCallback();
 		}, 500);
@@ -21,11 +21,22 @@
 	$(window).resize(function () {
 		clearTimeout(window.resizedFinished);
 		window.resizedFinished = setTimeout(function () {
-			gridScrollHeight = $(window).height() - gridScrollOffset;
-			devGridViewUser.PerformCallback();
+			setGridScrollHeight();
 		}, 250);
 	});
 
+	function calculateGridScrollHeight() {
+		var windowHeight = $(window).height();
+		var gridHeaderHeight = $("#devGridViewUser_DXHeadersRow0").height();
+		var gridFooterHeight = $("#devGridViewUser_DXFooterRow").height();
+		return windowHeight - gridHeaderHeight - gridFooterHeight - gridScrollOffset;
+	}
+
+	function setGridScrollHeight() {
+		gridScrollHeight = calculateGridScrollHeight();
+		devGridViewUser.PerformCallback();
+	}
+
 	function editUser(id) {
 		if (!id) return;
 		$.ajax({

+ 2 - 1
GreenTree.Nachtragsmanagement.Web/Views/Admin/Users/_UserGridPartial.cshtml

@@ -9,7 +9,7 @@
 	s.Name = "devGridViewUser";
 	s.KeyFieldName = "Id";
 	s.CallbackRouteValues = new { Controller = "Admin", Action = "PartialUsers" };
-	s.Width = Unit.Percentage(100);
+	s.Width = Unit.Percentage(99);
 	s.Settings.ShowFilterRow = true;
 	s.Settings.ShowFilterRowMenu = true;
 	s.Settings.VerticalScrollBarMode = ScrollBarMode.Auto;
@@ -17,6 +17,7 @@
 		(ViewData["ScrollHeight"] == null || (int)ViewData["ScrollHeight"] == -1)
 			? 400
 			: (int)ViewData["ScrollHeight"];
+	s.SettingsPager.AlwaysShowPager = true;
 
 	if (userContext.CurrentUser.HasFunction("Administration-Users-Edit"))
 	{

+ 15 - 4
GreenTree.Nachtragsmanagement.Web/Views/Deviations/View.cshtml

@@ -7,11 +7,11 @@
 <script>
 	var deleteId;
 	var gridScrollHeight;
-	var gridScrollOffset = 350;
+	var gridScrollOffset = 240;
 	var resizeFinished;
 
 	$(document).ready(function () {
-		gridScrollHeight = $(window).height() - gridScrollOffset;
+		gridScrollHeight = calculateGridScrollHeight();
 		setTimeout(function () {
 			devGridViewDeviation.PerformCallback();
 		}, 500);
@@ -20,11 +20,22 @@
 	$(window).resize(function () {
 		clearTimeout(window.resizedFinished);
 		window.resizedFinished = setTimeout(function () {
-			gridScrollHeight = $(window).height() - gridScrollOffset;
-			devGridViewDeviation.PerformCallback();
+			setGridScrollHeight();
 		}, 250);
 	});
 
+	function calculateGridScrollHeight() {
+		var windowHeight = $(window).height();
+		var gridHeaderHeight = $("#devGridViewDeviation_DXHeadersRow0").height();
+		var gridFooterHeight = $("#devGridViewDeviation_DXFooterRow").height();
+		return windowHeight - gridHeaderHeight - gridFooterHeight - gridScrollOffset;
+	}
+
+	function setGridScrollHeight() {
+		gridScrollHeight = calculateGridScrollHeight();
+		devGridViewDeviation.PerformCallback();
+	}
+
 	function onToolbarItemClick(s, e) {
 		if (!s || !e) return;
 		if (IsExportToolbarCommand(e.item.name)) {

+ 94 - 0
GreenTree.Nachtragsmanagement.Web/Views/Misc/Logs.cshtml

@@ -0,0 +1,94 @@
+@{
+	Layout = "~/Views/Shared/_FunctionLayout.cshtml";
+}
+
+@model IEnumerable<GreenTree.Nachtragsmanagement.Web.Models.Misc.LogDataModel>
+
+<script>
+	var gridScrollHeight;
+	var gridScrollOffset = 250;
+	var resizeFinished;
+
+	$(document).ready(function () {
+		gridScrollHeight = calculateGridScrollHeight();
+		setTimeout(function () {
+			devGridViewLog.PerformCallback();
+		}, 500);
+	});
+
+	$(window).resize(function () {
+		clearTimeout(window.resizedFinished);
+		window.resizedFinished = setTimeout(function () {
+			setGridScrollHeight();
+		}, 250);
+	});
+
+	function calculateGridScrollHeight() {
+		var windowHeight = $(window).height();
+		var gridHeaderHeight = $("#devGridViewLog_DXHeadersRow0").height();
+		var gridFooterHeight = $("#devGridViewLog_DXFooterRow").height();
+		return windowHeight - gridHeaderHeight - gridFooterHeight - gridScrollOffset;
+	}
+
+	function setGridScrollHeight() {
+		gridScrollHeight = calculateGridScrollHeight();
+		devGridViewLog.PerformCallback();
+	}
+
+	function onToolbarItemClick(s, e) {
+		if (!s || !e) return;
+		if (IsExportToolbarCommand(e.item.name)) {
+			$("#Format").val(e.item.name);
+			$("#logExportForm").submit();
+		} else if (e.item.name == "ToggleColumnChooser") {
+			if (devGridViewLog.IsCustomizationWindowVisible())
+				devGridViewLog.HideCustomizationWindow();
+			else
+				devGridViewLog.ShowCustomizationWindow();
+		}
+	}
+
+	function IsExportToolbarCommand(command) {
+		return command == "Pdf" || command == "Xlsx" || command == "Xls";
+	}
+
+	function viewLog(id) {
+		if (!id) return;
+		$.ajax({
+			url: '@Url.Action("viewLog", "Misc")',
+			data: { Id: id },
+			success: function (response) {
+				setTimeout(function () {
+					$(".logViewContainer").remove();
+					$("body").append(response);
+				}, 200);
+			},
+			error: function () {
+				 alert("error occured");
+			}
+		});
+	}
+
+	function deleteLog(id) {
+		$.ajax({
+			type: "POST",
+			url: '@Url.Action("DeleteLog", "Misc")',
+			data: { Id: id },
+			success: function (response) {
+				setTimeout(function () {
+					devGridViewLog.PerformCallback();
+				}, 200);
+			},
+			error: function () {
+				 alert("error occured");
+			}
+		});
+	}
+</script>
+
+@using (Html.BeginForm("ExportPartialLogs", "Misc", FormMethod.Post, new { id = "logExportForm" }))
+{
+	@Html.Hidden("Format")
+}
+
+@Html.Partial("~/Views/Misc/_LogGridPartial.cshtml", Model)

+ 15 - 4
GreenTree.Nachtragsmanagement.Web/Views/Misc/MailNotifications.cshtml

@@ -8,11 +8,11 @@
 	var deleteId;
 	var processId;
 	var gridScrollHeight;
-	var gridScrollOffset = 350;
+	var gridScrollOffset = 240;
 	var resizeFinished;
 
 	$(document).ready(function () {
-		gridScrollHeight = $(window).height() - gridScrollOffset;
+		gridScrollHeight = calculateGridScrollHeight();
 		setTimeout(function () {
 			devGridViewMailNotifications.PerformCallback();
 		}, 500);
@@ -21,11 +21,22 @@
 	$(window).resize(function () {
 		clearTimeout(window.resizedFinished);
 		window.resizedFinished = setTimeout(function () {
-			gridScrollHeight = $(window).height() - gridScrollOffset;
-			devGridViewMailNotifications.PerformCallback();
+			setGridScrollHeight();
 		}, 250);
 	});
 
+	function calculateGridScrollHeight() {
+		var windowHeight = $(window).height();
+		var gridHeaderHeight = $("#devGridViewMailNotifications_DXHeadersRow0").height();
+		var gridFooterHeight = $("#devGridViewMailNotifications_DXFooterRow").height();
+		return windowHeight - gridHeaderHeight - gridFooterHeight - gridScrollOffset;
+	}
+
+	function setGridScrollHeight() {
+		gridScrollHeight = calculateGridScrollHeight();
+		devGridViewMailNotifications.PerformCallback();
+	}
+
 	function onToolbarItemClick(s, e) {
 		if (!s || !e) return;
 		if (IsExportToolbarCommand(e.item.name)) {

+ 9 - 0
GreenTree.Nachtragsmanagement.Web/Views/Misc/_LogGridPartial.cshtml

@@ -0,0 +1,9 @@
+@model IEnumerable<GreenTree.Nachtragsmanagement.Web.Models.Misc.LogDataModel>
+
+@using GreenTree.Nachtragsmanagement.Web.Extensions
+
+@{ 
+	var userContext = GreenTree.Nachtragsmanagement.Core.CommonHelper.UserContext();
+}
+
+@Html.DevExpress().GridView(Html.LogGridViewSettings()).Bind(Model).GetHtml()

+ 80 - 0
GreenTree.Nachtragsmanagement.Web/Views/Misc/_LogViewPartial.cshtml

@@ -0,0 +1,80 @@
+@using GreenTree.Nachtragsmanagement.Web.Extensions
+
+@model GreenTree.Nachtragsmanagement.Web.Models.Misc.LogDataModel
+
+<div class="logViewContainer">
+
+	@Html.DevExpress().PopupControl(s =>
+{
+	s.Name = "devPopupControlViewLog";
+	s.HeaderText = String.Format("Log \"{0}\" betrachten", Model.Id);
+	s.Modal = true;
+	s.Width = new Unit(500, UnitType.Pixel);
+	s.CloseAction = CloseAction.CloseButton;
+	s.PopupHorizontalAlign = PopupHorizontalAlign.WindowCenter;
+	s.PopupVerticalAlign = PopupVerticalAlign.WindowCenter;
+	s.AllowDragging = false;
+	s.AllowResize = false;
+	s.ShowFooter = false;
+	s.ShowOnPageLoad = true;
+	s.SetContent(() =>
+	{
+		ViewContext.Writer.Write("<div class='editFormWrapper'>");
+
+		ViewContext.Writer.Write(Html.CustomLabelFor(m => m.Id, "ID:").ToHtmlString() + "&nbsp;");
+		ViewContext.Writer.Write("<b>" + Html.DisplayTextFor(m => m.Id).ToHtmlString() + "</b><br/>");
+
+		ViewContext.Writer.Write(Html.CustomLabelFor(m => m.LogLevelDescription, "Level:").ToHtmlString()  + "&nbsp;");
+		ViewContext.Writer.Write("<b>" + Html.DisplayTextFor(m => m.LogLevelDescription).ToHtmlString() + "</b><br/>");
+
+		ViewContext.Writer.Write(Html.CustomLabelFor(m => m.ShortMessage, "Betreff:").ToHtmlString()  + "&nbsp;");
+		ViewContext.Writer.Write("<b>" + Html.DisplayTextFor(m => m.ShortMessage).ToHtmlString() + "</b><br/>");
+
+		ViewContext.Writer.Write(Html.CustomLabelFor(m => m.FullMessage, "Nachricht:").ToHtmlString()  + "&nbsp;");
+		ViewContext.Writer.Write(
+			"<b>" + Html.TextAreaFor(m => m.FullMessage, new { disabled = "disabled", style = "width: 100%; height: 80px" }).ToHtmlString() + "</b><br/>");
+
+		ViewContext.Writer.Write(Html.CustomLabelFor(m => m.IpAddress, "IP-Adresse:").ToHtmlString() + "&nbsp;");
+		ViewContext.Writer.Write("<b>" + Html.DisplayTextFor(m => m.IpAddress).ToHtmlString() + "</b><br/>");
+
+		ViewContext.Writer.Write(Html.CustomLabelFor(m => m.UserDescription, "Mitarbeiter:").ToHtmlString() + "&nbsp;");
+		ViewContext.Writer.Write("<b>" + Html.DisplayTextFor(m => m.UserDescription).ToHtmlString() + "</b><br/>");
+
+		ViewContext.Writer.Write(Html.CustomLabelFor(m => m.EntityType, "Datentyp:").ToHtmlString() + "&nbsp;");
+		ViewContext.Writer.Write("<b>" + Html.DisplayTextFor(m => m.EntityType).ToHtmlString() + "</b><br/>");
+
+		ViewContext.Writer.Write(Html.CustomLabelFor(m => m.EntityId, "Daten ID:").ToHtmlString() + "&nbsp;");
+		ViewContext.Writer.Write("<b>" + Html.DisplayTextFor(m => m.EntityId).ToHtmlString() + "</b><br/>");
+
+		ViewContext.Writer.Write(Html.CustomLabelFor(m => m.PageUrl, "Seiten-URL:").ToHtmlString() + "&nbsp;");
+		ViewContext.Writer.Write("<b>" + Html.DisplayTextFor(m => m.PageUrl).ToHtmlString() + "</b><br/>");
+
+		ViewContext.Writer.Write(Html.CustomLabelFor(m => m.ReferrerUrl, "Relative URL:").ToHtmlString() + "&nbsp;");
+		ViewContext.Writer.Write("<b>" + Html.DisplayTextFor(m => m.ReferrerUrl).ToHtmlString() + "</b><br/>");
+
+		ViewContext.Writer.Write(Html.CustomLabelFor(m => m.CreatedOnUtc, "Zeitpunkt:").ToHtmlString() + "&nbsp;");
+		ViewContext.Writer.Write(
+			"<b><span>" + Model.CreatedOnUtc.ToLocalTime().ToString("dd.MM.yyyy - HH:mm:ss.fff \"Uhr\"") + "</span></b><br/>");
+
+		ViewContext.Writer.Write("</div>");
+
+		Html.RenderPartial(
+			"~/Views/Shared/_PopupButtonPanelOption.cshtml",
+			new GreenTree.Nachtragsmanagement.Web.Models.Global.OptionDialogModel
+			{
+				OptionItems = new List<GreenTree.Nachtragsmanagement.Web.Models.Global.OptionDialogItemModel>
+				{
+						new GreenTree.Nachtragsmanagement.Web.Models.Global.OptionDialogItemModel
+						{
+							Name = "ViewLogAccept",
+							Text = "Ok",
+							Function = "function (s, e) { devPopupControlViewLog.Hide(); }"
+						}
+				}
+			}
+		);
+	});
+	s.Styles.Content.Paddings.Padding = new Unit(0);
+	s.Styles.ModalBackground.Opacity = 0;
+}).GetHtml()
+</div>

+ 7 - 0
GreenTree.Nachtragsmanagement.Web/Views/Shared/_FunctionLayout.cshtml

@@ -38,6 +38,13 @@
 				}
 			});
 		}
+
+		function showError(header, message) {
+			var errorPopup = MVCxClientPopupControl.Cast(devPopupControlErrorBox);
+			$("#errorContent").text(message);
+			errorPopup.SetHeaderText(header);
+			errorPopup.Show(element);
+		}
 	</script>
 </head>
 

+ 59 - 0
GreenTree.Nachtragsmanagement.Web/Views/Shared/_PopupError.cshtml

@@ -0,0 +1,59 @@
+@model Exception
+
+<div class="errorPopupContainer">
+	<script>
+		$(document).ready(function () {
+			var errorContainer = $(".errorPopupContainer");
+			if (errorContainer.length > 1) {
+				for (var i = 0; i < errorContainer.length - 1; i++) {
+					$(errorContainer[i]).remove();
+				}
+			}
+		});
+	</script>
+
+	@Html.DevExpress().PopupControl(p =>
+	{
+		p.Name = "devPopupControlErrorBox";
+		p.HeaderText = "Fehler bei Aktion";
+		p.ShowHeader = true;
+		p.ShowFooter = false;
+		p.Modal = true;
+		p.Width = new Unit(500, UnitType.Pixel);
+		p.MinHeight = new Unit(200, UnitType.Pixel);
+		p.ShowOnPageLoad = true;
+		p.SetContent(() =>
+		{
+			ViewContext.Writer.Write(
+				"<div style=\"padding: 16px\">" +
+					"Leider ist folgender Fehler aufgetreten beim Versuch die " +
+					"Aktion durchzuführen:<br /><br />" +
+					"<div id=\"errorContent\" style=\"color: red; font-style: italic\">" + Model.Message +
+					"</div><br />" +
+					"Der Fehler wurde protokolliert und kann von Ihrem Systemadministrator " +
+					"eingesehen werden." +
+				"</div>"
+			);
+			Html.RenderPartial(
+				"~/Views/Shared/_PopupButtonPanelOption.cshtml",
+				new GreenTree.Nachtragsmanagement.Web.Models.Global.OptionDialogModel
+				{
+					OptionItems = new List<GreenTree.Nachtragsmanagement.Web.Models.Global.OptionDialogItemModel>
+					{
+						new GreenTree.Nachtragsmanagement.Web.Models.Global.OptionDialogItemModel
+						{
+							Name = "ErrorAccept",
+							Text = "Ok",
+							Function = "function (s, e) { devPopupControlErrorBox.Hide(); }"
+						}
+					}
+				}
+			);
+		});
+		p.CloseAction = CloseAction.CloseButton;
+		p.PopupHorizontalAlign = PopupHorizontalAlign.WindowCenter;
+		p.PopupVerticalAlign = PopupVerticalAlign.WindowCenter;
+		p.Styles.Content.Paddings.Padding = new Unit(0, UnitType.Pixel);
+		p.Styles.ModalBackground.Opacity = 0;
+	}).GetHtml()
+</div>

+ 15 - 4
GreenTree.Nachtragsmanagement.Web/Views/Sites/View.cshtml

@@ -7,11 +7,11 @@
 <script>
 	var deleteId;
 	var gridScrollHeight;
-	var gridScrollOffset = 350;
+	var gridScrollOffset = 240;
 	var resizeFinished;
 
 	$(document).ready(function () {
-		gridScrollHeight = $(window).height() - gridScrollOffset;
+		gridScrollHeight = calculateGridScrollHeight();
 		setTimeout(function () {
 			devGridViewSite.PerformCallback();
 		}, 500);
@@ -20,11 +20,22 @@
 	$(window).resize(function () {
 		clearTimeout(window.resizedFinished);
 		window.resizedFinished = setTimeout(function () {
-			gridScrollHeight = $(window).height() - gridScrollOffset;
-			devGridViewSite.PerformCallback();
+			setGridScrollHeight();
 		}, 250);
 	});
 
+	function calculateGridScrollHeight() {
+		var windowHeight = $(window).height();
+		var gridHeaderHeight = $("#devGridViewSite_DXHeadersRow0").height();
+		var gridFooterHeight = $("#devGridViewSite_DXFooterRow").height();
+		return windowHeight - gridHeaderHeight - gridFooterHeight - gridScrollOffset;
+	}
+
+	function setGridScrollHeight() {
+		gridScrollHeight = calculateGridScrollHeight();
+		devGridViewSite.PerformCallback();
+	}
+
 	function onToolbarItemClick(s, e) {
 		if (!s || !e) return;
 		if (IsExportToolbarCommand(e.item.name)) {

+ 23 - 23
GreenTree.Nachtragsmanagement.Web/Views/Sites/_SiteEditPartial.cshtml

@@ -495,37 +495,37 @@
 	s.Styles.ModalBackground.Opacity = 0;
 }).GetHtml()
 
-	@Html.Partial("~/Views/Shared/_PopupDialogYesNo.cshtml", new GreenTree.Nachtragsmanagement.Web.Models.Global.YesNoDialogModel
+@Html.Partial("~/Views/Shared/_PopupDialogYesNo.cshtml", new GreenTree.Nachtragsmanagement.Web.Models.Global.YesNoDialogModel
 {
 	PopupName = "devPopupControlSaveForAppend",
 	Content = "<div class='dialogTextSaveForAppend' style='padding: 12px'>Damit ein Nachtrag für die Baustelle erfasst werden kann, " +
-			  "muss Sie zunächst gespeichert werden. Soll die Baustelle jetzt gespeichert werden?</div>",
+				"muss Sie zunächst gespeichert werden. Soll die Baustelle jetzt gespeichert werden?</div>",
 	HeaderText = "Baustelle speichern",
 	YesFunction = "function (s, e) { saveSiteForAppend(); }",
 	YesButtonName = "devButtonSaveForAppendYes",
 	NoButtonName = "devButtonSaveForAppendNo"
 })
 
-	@Html.Partial("~/Views/Shared/_PopupDialogYesNo.cshtml", new GreenTree.Nachtragsmanagement.Web.Models.Global.YesNoDialogModel
-	{
-		PopupName = "devPopupControlDeleteAppendix",
-		Content = "<div class='dialogTextAppendix' style='padding: 12px'>Sind Sie sicher, dass Sie den Nachtrag \"{appendix}\" und " +
-				  "alle zugehörigen Vertragsabweichungen löschen möchten? Der Vorgang kann nicht rückgängig gemacht werden.</div>",
-		HeaderText = "\"{appendix}\" löschen",
-		YesFunction = "function (s, e) { deleteAppendix(); }",
-		YesButtonName = "devButtonDeleteAppendixYes",
-		NoButtonName = "devButtonDeleteAppendixNo"
-	})
-
-	@Html.Partial("~/Views/Shared/_PopupDialogYesNo.cshtml", new GreenTree.Nachtragsmanagement.Web.Models.Global.YesNoDialogModel
-	{
-		PopupName = "devPopupControlDeleteDeviation",
-		Content = "<div class='dialogTextDeviation' style='padding: 12px'>Sind Sie sicher, dass Sie die Vertragsabweichung \"{deviation}\" " +
-				  "löschen möchten? Der Vorgang kann nicht rückgängig gemacht werden.</div>",
-		HeaderText = "\"{deviation}\" löschen",
-		YesFunction = "function (s, e) { deleteDeviation(); }",
-		YesButtonName = "devButtonDeleteDeviationYes",
-		NoButtonName = "devButtonDeleteDeviationNo"
-	})
+@Html.Partial("~/Views/Shared/_PopupDialogYesNo.cshtml", new GreenTree.Nachtragsmanagement.Web.Models.Global.YesNoDialogModel
+{
+	PopupName = "devPopupControlDeleteAppendix",
+	Content = "<div class='dialogTextAppendix' style='padding: 12px'>Sind Sie sicher, dass Sie den Nachtrag \"{appendix}\" und " +
+				"alle zugehörigen Vertragsabweichungen löschen möchten? Der Vorgang kann nicht rückgängig gemacht werden.</div>",
+	HeaderText = "\"{appendix}\" löschen",
+	YesFunction = "function (s, e) { deleteAppendix(); }",
+	YesButtonName = "devButtonDeleteAppendixYes",
+	NoButtonName = "devButtonDeleteAppendixNo"
+})
+
+@Html.Partial("~/Views/Shared/_PopupDialogYesNo.cshtml", new GreenTree.Nachtragsmanagement.Web.Models.Global.YesNoDialogModel
+{
+	PopupName = "devPopupControlDeleteDeviation",
+	Content = "<div class='dialogTextDeviation' style='padding: 12px'>Sind Sie sicher, dass Sie die Vertragsabweichung \"{deviation}\" " +
+				"löschen möchten? Der Vorgang kann nicht rückgängig gemacht werden.</div>",
+	HeaderText = "\"{deviation}\" löschen",
+	YesFunction = "function (s, e) { deleteDeviation(); }",
+	YesButtonName = "devButtonDeleteDeviationYes",
+	NoButtonName = "devButtonDeleteDeviationNo"
+})
 
 </div>