Преглед изворни кода

Benuterverwaltung so gut wie fertiggestellt!

Arne Diekmann пре 8 година
родитељ
комит
8dfd04c41f
22 измењених фајлова са 725 додато и 126 уклоњено
  1. 13 0
      GreenTree.Nachtragsmanagement.Core/Domain/User/User.cs
  2. 3 1
      GreenTree.Nachtragsmanagement.Web/App_Start/BundleConfig.cs
  3. 82 1
      GreenTree.Nachtragsmanagement.Web/Content/function.css
  4. 94 10
      GreenTree.Nachtragsmanagement.Web/Controllers/AdminController.cs
  5. 52 0
      GreenTree.Nachtragsmanagement.Web/Extensions/HtmlHelper.cs
  6. 7 0
      GreenTree.Nachtragsmanagement.Web/Global.asax.cs
  7. 12 0
      GreenTree.Nachtragsmanagement.Web/GreenTree.Nachtragsmanagement.Web.csproj
  8. 23 2
      GreenTree.Nachtragsmanagement.Web/Models/Admin/User/UserDataModel.cs
  9. 16 0
      GreenTree.Nachtragsmanagement.Web/Models/Global/DialogModel.cs
  10. 1 1
      GreenTree.Nachtragsmanagement.Web/Models/Global/PopupModel.cs
  11. 41 0
      GreenTree.Nachtragsmanagement.Web/Validation/Admin/User/UserDataModelValidator.cs
  12. 30 0
      GreenTree.Nachtragsmanagement.Web/Validation/AppendixValidatorFactory.cs
  13. 89 2
      GreenTree.Nachtragsmanagement.Web/Views/Admin/Users/View.cshtml
  14. 142 18
      GreenTree.Nachtragsmanagement.Web/Views/Admin/Users/_UserEditPartial.cshtml
  15. 51 64
      GreenTree.Nachtragsmanagement.Web/Views/Admin/Users/_UserGridPartial.cshtml
  16. 1 18
      GreenTree.Nachtragsmanagement.Web/Views/Home/Index.cshtml
  17. 2 2
      GreenTree.Nachtragsmanagement.Web/Views/Shared/_Footer.cshtml
  18. 0 5
      GreenTree.Nachtragsmanagement.Web/Views/Shared/_FunctionLayout.cshtml
  19. 4 2
      GreenTree.Nachtragsmanagement.Web/Views/Shared/_PopupButtonPanel.cshtml
  20. 29 0
      GreenTree.Nachtragsmanagement.Web/Views/Shared/_PopupButtonPanelYesNo.cshtml
  21. 31 0
      GreenTree.Nachtragsmanagement.Web/Views/Shared/_PopupDialog.cshtml
  22. 2 0
      GreenTree.Nachtragsmanagement.Web/packages.config

+ 13 - 0
GreenTree.Nachtragsmanagement.Core/Domain/User/User.cs

@@ -70,5 +70,18 @@ namespace GreenTree.Nachtragsmanagement.Core.Domain.User
             get { return _mailNotifications ?? ( _mailNotifications = new List<MailNotification>()); }
             protected set { _mailNotifications = value; }
         }
+
+        #region Helper
+
+        /// <summary>
+        /// Adds missing roles and removes not selected roles
+        /// </summary>
+        /// <param name="roles">User roles.</param>
+        public void SetRoles(ICollection<Role> roles)
+        {
+            Roles = roles;
+        }
+
+        #endregion
     }
 }

+ 3 - 1
GreenTree.Nachtragsmanagement.Web/App_Start/BundleConfig.cs

@@ -15,12 +15,14 @@ namespace GreenTree.Nachtragsmanagement.Web.App_Start
             jqueryBundle.Include("~/Scripts/jquery-1.11.3.min.js");
             jqueryBundle.Include("~/Scripts/jquery-ui-1.11.4.min.js");
             jqueryBundle.Include("~/Scripts/jquery.unobtrusive-ajax.min.js");
+            jqueryBundle.Include("~/Scripts/jquery.validate-vsdoc.js");
             jqueryBundle.Include("~/Scripts/jquery.validate.min.js");
+            jqueryBundle.Include("~/Scripts/jquery.validate.unobtrusive.min.js");
 
             var globalizeBundle = new ScriptBundle("~/bundles/globalize");
+            globalizeBundle.Include("~/Scripts/globalize.js");
             globalizeBundle.Include("~/Scripts/globalize.currency.js");
             globalizeBundle.Include("~/Scripts/globalize.date.js");
-            globalizeBundle.Include("~/Scripts/globalize.js");
             globalizeBundle.Include("~/Scripts/globalize.message.js");
             globalizeBundle.Include("~/Scripts/globalize.number.js");
 

+ 82 - 1
GreenTree.Nachtragsmanagement.Web/Content/function.css

@@ -1,3 +1,84 @@
 html {
     background: white;
-}
+}
+
+/* Label and validation customization */
+
+.inlineModelPropertyContainer {
+    width: 100%;
+    overflow: auto;
+}
+
+.inlineModelProperty {
+    overflow: auto;
+    float: left;
+}
+
+.inlineModelPropertyLeft {
+    overflow: auto;
+    float: left;
+    padding-right: 4px;
+}
+
+.inlineModelPropertyCenter {
+    overflow: auto;
+    float: left;
+    padding-left: 4px;
+    padding-right: 4px;
+}
+
+.inlineModelPropertyRight {
+    overflow: auto;
+    float: left;
+    padding-left: 4px;
+}
+
+.modelPropertyLabel {
+    margin: 10px 0 4px 0;
+    display: inline-block;
+}
+
+.field-validation-error {
+    color: red;
+    margin-left: 8px;
+}
+
+.validated {
+    border-color: #DCE4EC !important;
+}
+
+textarea, input[type="text"], input[type="password"],
+input[type="datetime"],
+input[type="datetime-local"], input[type="date"],
+input[type="month"],
+input[type="time"], input[type="week"],
+input[type="number"], input[type="email"],
+input[type="url"], input[type="search"],
+input[type="tel"], input[type="color"],
+.uneditable-input {
+    padding: 3px 3px;
+    border: 1px solid #DCE4EC;
+}
+
+.tip {
+    background: none repeat scroll 0 0 #FFFFFF;
+    border: 1px solid #808080;
+    border-radius: 10px;
+    box-shadow: 0 1px 10px rgba(32, 32, 32, 0.5);
+    color: red;
+    display: none;
+    font-size: 12px;
+    font-style: normal;
+    margin-left: 10px;
+    margin-top: -24px;
+    padding: 4px;
+    position: absolute;
+    z-index: 999999;
+}
+
+.tip_trigger {
+    width: 10px;
+    display: inline-block;
+    color: red;
+    margin-left: 3px;
+} 

+ 94 - 10
GreenTree.Nachtragsmanagement.Web/Controllers/AdminController.cs

@@ -1,11 +1,14 @@
-using GreenTree.Nachtragsmanagement.Core.Authentication;
-using GreenTree.Nachtragsmanagement.Services.User;
-using GreenTree.Nachtragsmanagement.Web.Models.Admin.User;
-using System;
+using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Web;
 using System.Web.Mvc;
+using GreenTree.Nachtragsmanagement.Core.Authentication;
+using GreenTree.Nachtragsmanagement.Services.User;
+using GreenTree.Nachtragsmanagement.Web.Models.Admin.User;
+using GreenTree.Nachtragsmanagement.Core.Domain.User;
+using Newtonsoft.Json;
+using GreenTree.Nachtragsmanagement.Core;
 
 namespace GreenTree.Nachtragsmanagement.Web.Controllers
 {
@@ -20,20 +23,65 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
         {
             _userService = userService;
             _userHelper = userHelper;
+
+            ViewData["AllRoles"] = _userService.GetAllRoles();
         }
 
         #region Users
 
-        // GET: Admin Users
+        /// <summary>
+        /// Basic user view function
+        /// </summary>
         public ActionResult ViewUsers()
         {
             var users = _userService.GetAllUsers();
             var userModels = users
-                .Select(u => UserDataModel.FromUser(u, false));
+                .Select(u => UserDataModel.FromUser(u, false))
+                .ToList();
 
             return View("~/Views/Admin/Users/View.cshtml", userModels);
         }
 
+        /// <summary>
+        /// Get JSON data of specific user
+        /// </summary>
+        /// <param name="id">User id.</param>
+        public ActionResult GetUser(int id = -1)
+        {
+            var user = _userService.GetUserById(id);
+            if (user == null)
+                return new JsonResult
+                {
+                    Data = "notFound",
+                    JsonRequestBehavior = JsonRequestBehavior.AllowGet
+                };
+
+            var userModel = UserDataModel.FromUser(user, false);
+
+            return new JsonResult
+            {
+                Data = JsonConvert.SerializeObject(userModel),
+                JsonRequestBehavior = JsonRequestBehavior.AllowGet
+            };
+        }
+
+        /// <summary>
+        /// Callback result for user grid
+        /// </summary>
+        public ActionResult PartialUsers()
+        {
+            var users = _userService.GetAllUsers();
+            var userModels = users
+                .Select(u => UserDataModel.FromUser(u, false))
+                .ToList();
+
+            return PartialView("~/Views/Admin/Users/_UserGridPartial.cshtml", userModels);
+        }
+
+        /// <summary>
+        /// Partial edit for editing of existing or for new user
+        /// </summary>
+        /// <param name="id">Id for existing user, otherweise -1.</param>
         public ActionResult EditUser(int id = -1)
         {
             var user = _userService.GetUserById(id);
@@ -42,16 +90,32 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
             return PartialView("~/Views/Admin/Users/_UserEditPartial.cshtml", userModel);
         }
 
+        /// <summary>
+        /// Partial edit result if ModelState is valid, otherwise simple JSON result for success
+        /// </summary>
+        /// <param name="userModel">User model to be saved.</param>
         [HttpPost, ValidateInput(false)]
         public ActionResult EditUser(UserDataModel userModel)
         {
             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);
+            }
+
+            var selectedRoles = _userService.GetRolesByIds(userModel.RoleValues.ToArray());
 
             if (userModel.Id == -1)
             {
                 var user = userModel.ToUser();
 
+                user.SetRoles(selectedRoles);
+                user.Password = StaticHelper.GetMD5Hash(userModel.Password);
+
                 _userService.InsertUser(user);
             }
             else
@@ -63,12 +127,25 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
                 user.Lastname = userModel.Lastname;
                 user.MailAddress = userModel.MailAddress;
 
+                if (!String.IsNullOrEmpty(userModel.Password))
+                    user.Password = StaticHelper.GetMD5Hash(userModel.Password);
+
+                user.SetRoles(selectedRoles);
+
                 _userService.UpdateUser(user);
             }
 
-            return RedirectToAction("ViewUsers");
+            return new JsonResult
+            {
+                Data = "success"
+            };
         }
 
+        /// <summary>
+        /// Simple JSON result for deleting a specific user
+        /// </summary>
+        /// <param name="id">User id.</param>
+        [HttpPost]
         public ActionResult DeleteUser(int id)
         {
             var user = _userService.GetUserById(id);
@@ -76,14 +153,19 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
             if (user != null)
                 _userService.DeleteUser(user);
 
-            return RedirectToAction("ViewUsers");
+            return new JsonResult
+            {
+                Data = "success"
+            };
         }
 
         #endregion
 
         #region Roles
 
-        // GET: Admin Roles
+        /// <summary>
+        /// Basic role view function
+        /// </summary>
         public ActionResult ViewRoles()
         {
             return View("~/Views/Admin/Roles/View.cshtml");
@@ -93,7 +175,9 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
 
         #region Plugins
 
-        // GET: Admin Plugins
+        /// <summary>
+        /// Basic plugin view function
+        /// </summary>
         public ActionResult ViewPlugins()
         {
             return View("~/Views/Admin/Plugins/View.cshtml");

+ 52 - 0
GreenTree.Nachtragsmanagement.Web/Extensions/HtmlHelper.cs

@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Web;
+using System.Web.Mvc;
+using System.Web.Mvc.Html;
+
+namespace GreenTree.Nachtragsmanagement.Web.Extensions
+{
+    public static class HtmlHelper
+    {
+        #region Label
+
+        public static MvcHtmlString CustomLabelFor<TModel, TProperty>(
+            this HtmlHelper<TModel> helper,
+            Expression<Func<TModel, TProperty>> expression,
+            string text)
+        {
+            TagBuilder containerDivBuilder = new TagBuilder("div");
+            containerDivBuilder.AddCssClass("modelPropertyLabel");
+
+            containerDivBuilder.SetInnerText(text);
+
+            return MvcHtmlString.Create(containerDivBuilder.ToString(TagRenderMode.Normal));
+        }
+
+        #endregion
+
+        #region Validation
+
+        public static MvcHtmlString CustomValidationMessageFor<TModel, TProperty>(
+            this HtmlHelper<TModel> helper,
+            Expression<Func<TModel, TProperty>> expression)
+        {
+            TagBuilder containerDivBuilder = new TagBuilder("div");
+            containerDivBuilder.AddCssClass("tip_trigger");
+            containerDivBuilder.InnerHtml = "*";
+
+            TagBuilder midDivBuilder = new TagBuilder("div");
+            midDivBuilder.AddCssClass("classic");
+            midDivBuilder.AddCssClass("tip");
+            midDivBuilder.InnerHtml = helper.ValidationMessageFor(expression).ToString();
+
+            containerDivBuilder.InnerHtml += midDivBuilder.ToString(TagRenderMode.Normal);
+
+            return MvcHtmlString.Create(containerDivBuilder.ToString(TagRenderMode.Normal));
+        }
+
+        #endregion
+    }
+}

+ 7 - 0
GreenTree.Nachtragsmanagement.Web/Global.asax.cs

@@ -13,6 +13,8 @@ using Autofac;
 using GreenTree.Nachtragsmanagement.Web.Framework.Mvc.Routes;
 using GreenTree.Nachtragsmanagement.Web.App_Start;
 using System.Web.Optimization;
+using FluentValidation.Mvc;
+using GreenTree.Nachtragsmanagement.Web.Validation;
 
 namespace GreenTree.Nachtragsmanagement.Web
 {
@@ -36,6 +38,11 @@ namespace GreenTree.Nachtragsmanagement.Web
             ApplicationContext.InitApplication();
             ApplicationContext.InitPluginRoutes(RouteTable.Routes);
 
+            FluentValidationModelValidatorProvider.Configure(provider =>
+            {
+                provider.ValidatorFactory = new AppendixValidatorFactory();
+            });
+
             DevExpress.Web.ASPxWebControl.CallbackError += Application_Error;
         }
 

+ 12 - 0
GreenTree.Nachtragsmanagement.Web/GreenTree.Nachtragsmanagement.Web.csproj

@@ -57,6 +57,12 @@
     <Reference Include="EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
       <HintPath>..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll</HintPath>
     </Reference>
+    <Reference Include="FluentValidation, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7de548da2fbae0f0, processorArchitecture=MSIL">
+      <HintPath>..\packages\FluentValidation.7.1.1\lib\net45\FluentValidation.dll</HintPath>
+    </Reference>
+    <Reference Include="FluentValidation.Mvc, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7de548da2fbae0f0, processorArchitecture=MSIL">
+      <HintPath>..\packages\FluentValidation.Mvc5.7.1.1\lib\net45\FluentValidation.Mvc.dll</HintPath>
+    </Reference>
     <Reference Include="Microsoft.CSharp" />
     <Reference Include="Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
       <HintPath>..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
@@ -221,6 +227,8 @@
     <Content Include="Views\Admin\Users\_UserGridPartial.cshtml" />
     <Content Include="Views\Admin\Users\_UserEditPartial.cshtml" />
     <Content Include="Views\Global\SuccessMessage.cshtml" />
+    <Content Include="Views\Shared\_PopupDialog.cshtml" />
+    <Content Include="Views\Shared\_PopupButtonPanelYesNo.cshtml" />
     <None Include="Web.Debug.config">
       <DependentUpon>Web.config</DependentUpon>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@@ -240,10 +248,13 @@
     <Compile Include="Controllers\GlobalController.cs" />
     <Compile Include="Controllers\HomeController.cs" />
     <Compile Include="Controllers\LoginController.cs" />
+    <Compile Include="Extensions\HtmlHelper.cs" />
     <Compile Include="Global.asax.cs">
       <DependentUpon>Global.asax</DependentUpon>
     </Compile>
     <Compile Include="Models\Admin\User\UserDataModel.cs" />
+    <Compile Include="Models\Global\DialogModel.cs" />
+    <Compile Include="Validation\Admin\User\UserDataModelValidator.cs" />
     <Compile Include="Models\Global\FooterModel.cs" />
     <Compile Include="Models\Global\PopupModel.cs" />
     <Compile Include="Models\Home\HomeModel.cs" />
@@ -251,6 +262,7 @@
     <Compile Include="Models\Test\DbRelationModel.cs" />
     <Compile Include="Models\Test\PluginModel.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Validation\AppendixValidatorFactory.cs" />
   </ItemGroup>
   <ItemGroup>
     <Folder Include="App_Data\" />

+ 23 - 2
GreenTree.Nachtragsmanagement.Web/Models/Admin/User/UserDataModel.cs

@@ -1,7 +1,11 @@
 using System;
 using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
 using System.Linq;
 using System.Web;
+using GreenTree.Nachtragsmanagement.Core.Domain.User;
+using FluentValidation.Attributes;
+using GreenTree.Nachtragsmanagement.Web.Validation.Admin.User;
 
 namespace GreenTree.Nachtragsmanagement.Web.Models.Admin.User
 {
@@ -12,6 +16,15 @@ namespace GreenTree.Nachtragsmanagement.Web.Models.Admin.User
         public string Forename { get; set; }
         public string Lastname { get; set; }
         public string MailAddress { get; set; }
+        public string Password { get; set; }
+        public ICollection<int> RoleValues { get; set; }
+        public ICollection<string> RoleDescriptions { get; set; }
+
+        public UserDataModel()
+        {
+            RoleValues = new List<int>();
+            RoleDescriptions = new List<string>();
+        }
 
         public static UserDataModel FromUser(Core.Domain.User.User userEntity, bool newWhenUserIsNull)
         {
@@ -30,7 +43,15 @@ namespace GreenTree.Nachtragsmanagement.Web.Models.Admin.User
                 CustomerNumber = userEntity.CustomNumber,
                 Forename = userEntity.Forename,
                 Lastname = userEntity.Lastname,
-                MailAddress = userEntity.MailAddress
+                MailAddress = userEntity.MailAddress,
+                RoleValues = 
+                    userEntity.Roles
+                        .Select(r => r.Id)
+                        .ToList(),
+                RoleDescriptions =
+                    userEntity.Roles
+                        .Select(r => r.Description)
+                        .ToList()
             };
         }
 
@@ -42,7 +63,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Models.Admin.User
                 CustomNumber = this.CustomerNumber,
                 Forename = this.Forename,
                 Lastname = this.Lastname,
-                MailAddress = this.MailAddress,
+                MailAddress = this.MailAddress
             };
         }
     }

+ 16 - 0
GreenTree.Nachtragsmanagement.Web/Models/Global/DialogModel.cs

@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+
+namespace GreenTree.Nachtragsmanagement.Web.Models.Global
+{
+    public class YesNoDialogModel
+    {
+        public string PopupName { get; set; }
+        public string YesFunction { get; set; }
+        public string NoFunction { get; set; }
+        public string HeaderText { get; set; }
+        public string Content { get; set; }
+    }
+}

+ 1 - 1
GreenTree.Nachtragsmanagement.Web/Models/Global/PopupModel.cs

@@ -8,6 +8,6 @@ namespace GreenTree.Nachtragsmanagement.Web.Models.Global
     public class PopupModel
     {
         public string PopupName { get; set; }
-        public string AcceptFunctionName { get; set; }
+        public string AcceptFunction { get; set; }
     }
 }

+ 41 - 0
GreenTree.Nachtragsmanagement.Web/Validation/Admin/User/UserDataModelValidator.cs

@@ -0,0 +1,41 @@
+using FluentValidation;
+using GreenTree.Nachtragsmanagement.Web.Models.Admin.User;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+
+namespace GreenTree.Nachtragsmanagement.Web.Validation.Admin.User
+{
+    public class UserDataModelValidator : AbstractValidator<UserDataModel>
+    {
+        public UserDataModelValidator()
+        {
+            RuleFor(m => m.Forename)
+                .NotEmpty()
+                    .WithMessage("Vorname wird benötigt")
+                .MaximumLength(50)
+                    .WithMessage("Muss unter 50 Zeichen sein");
+
+            RuleFor(m => m.Lastname)
+                .NotEmpty()
+                    .WithMessage("Nachname wird benötigt")
+                .MaximumLength(50)
+                    .WithMessage("Muss unter 50 Zeichen sein");
+
+            RuleFor(m => m.MailAddress)
+                .NotEmpty()
+                    .WithMessage("Mail-Adresse wird benötigt")
+                .EmailAddress()
+                    .WithMessage("Keine gültige E-Mail-Adresse");
+
+            RuleFor(m => m.RoleValues)
+                .Must(r => r.Count > 0)
+                    .WithMessage("Eine Rolle muss ausgewählt werden");
+
+            RuleFor(m => m.Password)
+                .NotEmpty()
+                    .WithMessage("Passwort wird benötigt");
+        }
+    }
+}

+ 30 - 0
GreenTree.Nachtragsmanagement.Web/Validation/AppendixValidatorFactory.cs

@@ -0,0 +1,30 @@
+using FluentValidation;
+using GreenTree.Nachtragsmanagement.Web.Models.Admin.User;
+using GreenTree.Nachtragsmanagement.Web.Validation.Admin.User;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+
+namespace GreenTree.Nachtragsmanagement.Web.Validation
+{
+    public class AppendixValidatorFactory : ValidatorFactoryBase
+    {
+        private static Dictionary<Type, IValidator> validators = new Dictionary<Type, IValidator>();
+
+        static AppendixValidatorFactory()
+        {
+            validators.Add(typeof(IValidator<UserDataModel>), new UserDataModelValidator());
+        }
+
+        public override IValidator CreateInstance(Type validatorType)
+        {
+            IValidator validator;
+            if (validators.TryGetValue(validatorType, out validator))
+            {
+                return validator;
+            }
+            return validator;
+        }
+    }
+}

+ 89 - 2
GreenTree.Nachtragsmanagement.Web/Views/Admin/Users/View.cshtml

@@ -1,7 +1,94 @@
 @{
-    Layout = "~/Views/Shared/_FunctionLayout.cshtml";
+	Layout = "~/Views/Shared/_FunctionLayout.cshtml";
 }
 
 @model IEnumerable<GreenTree.Nachtragsmanagement.Web.Models.Admin.User.UserDataModel>
 
-@Html.Partial("~/Views/Admin/Users/_UserGridPartial.cshtml", Model)
+<script>
+	var deleteId;
+
+	function saveUser() {
+		var form = $("#userEditForm");
+		$(form).submit(function (e) {
+			$.ajax({
+				type: "POST",
+				url: '@Url.Action("EditUser", "Admin")',
+				data: form.serialize(),
+				success: function (response) {
+					setTimeout(function () {
+						$(".userEditContainer").remove();
+						if (response == "success") {
+							devGridViewUser.PerformCallback();
+						} else {
+							$("body").append(response);
+						}
+					}, 200);
+				}
+			});
+			e.preventDefault();
+		});
+		form.submit();
+	}
+
+	function editUser(id) {
+		if (!id) return;
+		$.ajax({
+			url: '@Url.Action("EditUser", "Admin")',
+			data: { Id: id },
+			success: function (response) {
+				setTimeout(function () {
+					$(".userEditContainer").remove();
+					$("body").append(response);
+				}, 200);
+			},
+			error: function () {
+				 alert("error occured");
+			}
+		});
+	}
+
+	function confirmDelete(id) {
+		if (!id) return;
+		deleteId = id;
+		$.ajax({
+			type: "GET",
+			url: '@Url.Action("GetUser", "Admin")',
+			data: { Id: deleteId },
+			success: function (response) {
+				if (response == "notFound") return;
+				var user = JSON.parse(response);
+				var popupControl = MVCxClientPopupControl.Cast(devPopupControlDeleteUser);
+				popupControl.SetHeaderText(popupControl.GetHeaderText().replace("{user}", user.Lastname + ", " + user.Forename));
+				$(".dialogText").text($(".dialogText").text().replace("{user}", user.Lastname + ", " + user.Forename));
+				popupControl.Show();
+			}
+		});
+	}
+
+	function deleteUser() {
+		$.ajax({
+			type: "POST",
+			url: '@Url.Action("DeleteUser", "Admin")',
+			data: { Id: deleteId },
+			success: function (response) {
+				var popupControl = MVCxClientPopupControl.Cast(devPopupControlDeleteUser);
+				popupControl.Hide();
+				setTimeout(function () {
+					devGridViewUser.PerformCallback();
+				}, 200);
+			},
+			error: function () {
+				 alert("error occured");
+			}
+		});
+	}
+</script>
+
+@Html.Partial("~/Views/Admin/Users/_UserGridPartial.cshtml", Model)
+@Html.Partial("~/Views/Shared/_PopupDialog.cshtml", new GreenTree.Nachtragsmanagement.Web.Models.Global.YesNoDialogModel
+{
+	PopupName = "devPopupControlDeleteUser",
+	Content = "<div class='dialogText' style='padding: 12px'>Sind Sie sicher, dass Sie den Benutzer \"{user}\" löschen möchten?</div>",
+	HeaderText = "\"{user}\" löschen",
+	YesFunction = "function (s, e) { deleteUser(); }"
+})

+ 142 - 18
GreenTree.Nachtragsmanagement.Web/Views/Admin/Users/_UserEditPartial.cshtml

@@ -1,6 +1,57 @@
-@model GreenTree.Nachtragsmanagement.Web.Models.Admin.User.UserDataModel
+@using GreenTree.Nachtragsmanagement.Web.Extensions
+
+@model GreenTree.Nachtragsmanagement.Web.Models.Admin.User.UserDataModel
+
+<div class="userEditContainer">
+	<script>
+		var textSeparator = ", ";
+
+		function onListBoxSelectionChanged(s, e) {
+			if (e.index == 0)
+				e.isSelected ? s.SelectAll() : s.UnselectAll();
+			updateSelectAllItemState();
+			updateText();
+		}
+		function updateSelectAllItemState() {
+			isAllSelected() ? RoleValues.SelectIndices([0]) : RoleValues.UnselectIndices([0]);
+		}
+		function isAllSelected() {
+			for (var i = 1; i < RoleValues.GetItemCount(); i++)
+				if (!RoleValues.GetItem(i).selected)
+					return false;
+			return true;
+		}
+		function updateText() {
+			var selectedItems = RoleValues.GetSelectedItems();
+			devDropDownListRoleValues.SetText(getSelectedItemsText(selectedItems));
+		}
+		function synchronizeListBoxValues(s, e) {
+			RoleValues.UnselectAll();
+			var texts = s.GetText().split(textSeparator);
+			var values = getValuesByTexts(texts);
+			RoleValues.SelectValues(values);
+			updateSelectAllItemState();
+			updateText();
+		}
+		function getSelectedItemsText(items) {
+			var texts = [];
+			for (var i = 0; i < items.length; i++)
+				if (items[i].index != 0)
+					texts.push(items[i].text);
+			return texts.join(textSeparator);
+		}
+		function getValuesByTexts(texts) {
+			var actualValues = [];
+			var item;
+			for (var i = 0; i < texts.length; i++) {
+				item = RoleValues.FindItemByText(texts[i]);
+				if (item != null)
+					actualValues.push(item.value);
+			}
+			return actualValues;
+		}
+	</script>
 
-<div class="userEditPopupContainer">
 	@Html.DevExpress().PopupControl(s =>
 {
 	s.Name = "devPopupControlEditUser";
@@ -8,36 +59,107 @@
 	if (Model.Id == -1)
 		s.HeaderText = "Neuen Benutzer erstellen";
 	else
-		s.HeaderText = Model.Lastname + ", " + Model.Forename + " bearbeiten";
+		s.HeaderText = "\"" + Model.Lastname + ", " + Model.Forename + "\" bearbeiten";
 
-	s.Modal = false;
-	s.Width = new Unit(350, UnitType.Pixel);
+	s.Modal = true;
+	s.Width = new Unit(400, UnitType.Pixel);
 	s.CloseAction = CloseAction.CloseButton;
-	s.ShowOnPageLoad = true;
 	s.PopupHorizontalAlign = PopupHorizontalAlign.WindowCenter;
 	s.PopupVerticalAlign = PopupVerticalAlign.WindowCenter;
 	s.AllowDragging = false;
 	s.AllowResize = false;
 	s.ShowFooter = false;
+	s.ShowOnPageLoad = true;
 	s.SetContent(() =>
 	{
-		using (Html.BeginForm("EditUser", "Admin", FormMethod.Post))
+		using (Html.BeginForm("EditUser", "Admin", FormMethod.Post, new { id = "userEditForm" }))
 		{
-			ViewContext.Writer.Write("<div class=\"editFormWrapper\">");
+			ViewContext.Writer.Write("<div class='editFormWrapper'>");
 
 			ViewContext.Writer.Write("<input type=\"hidden\" value=\"" + Model.Id + "\" id=\"Id\" name=\"Id\" />");
 
-			ViewContext.Writer.Write("<div>Personalnummer</div>");
-			Html.DevExpress().TextBoxFor(m => m.CustomerNumber, t => { t.Width = new Unit(100, UnitType.Percentage); }).Render();
+			ViewContext.Writer.Write(Html.CustomLabelFor(m => m.Forename, "Personalnummer:"));
+			Html.DevExpress().TextBoxFor(m => m.CustomerNumber, t =>
+			{
+				t.Width = new Unit(60, UnitType.Percentage);
+			}).Render();
 
-			ViewContext.Writer.Write("<div>Vorname</div>");
-			Html.DevExpress().TextBoxFor(m => m.Forename, t => { t.Width = new Unit(100, UnitType.Percentage); }).Render();
+			ViewContext.Writer.Write("<div class='inlineModelPropertyContainer'>");
+				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 50%'>");
+					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.Forename, "Vorname:"));
+					ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.Forename).ToHtmlString());
+					Html.DevExpress().TextBoxFor(m => m.Forename, t =>
+					{
+						t.Width = new Unit(95, UnitType.Percentage);
+					}).Render();
+				ViewContext.Writer.Write("</div>");
 
-			ViewContext.Writer.Write("<div>Nachname</div>");
-			Html.DevExpress().TextBoxFor(m => m.Lastname, t => { t.Width = new Unit(100, UnitType.Percentage); }).Render();
+				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 50%'>");
+					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.Lastname, "Nachname:"));
+					ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.Lastname).ToHtmlString());
+					Html.DevExpress().TextBoxFor(m => m.Lastname, t =>
+					{
+						t.Width = new Unit(100, UnitType.Percentage);
+					}).Render();
+				ViewContext.Writer.Write("</div>");
+			ViewContext.Writer.Write("</div>");
+
+			ViewContext.Writer.Write(Html.CustomLabelFor(m => m.MailAddress, "E-Mail:"));
+			ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.MailAddress).ToHtmlString());
+			Html.DevExpress().TextBoxFor(m => m.MailAddress, t =>
+			{
+				t.Width = new Unit(100, UnitType.Percentage);
+			}).Render();
+
+			ViewContext.Writer.Write(Html.CustomLabelFor(m => m.MailAddress, "Passwort:"));
+			ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.Password).ToHtmlString());
+			Html.DevExpress().TextBoxFor(m => m.Password, t =>
+			{
+				t.Width = new Unit(60, UnitType.Percentage);
+				t.Properties.Password = true;
+			}).Render();
+
+			ViewContext.Writer.Write(Html.CustomLabelFor(m => m.RoleValues, "Rollen:"));
+			ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.RoleValues).ToHtmlString());
+			Html.DevExpress().DropDownEdit(t =>
+			{
+				t.Name = "devDropDownListRoleValues";
+				t.Width = new Unit(100, UnitType.Percentage);
+
+				if (Model.RoleDescriptions != null && Model.RoleDescriptions.Any())
+					t.Text = String.Join(", ", Model.RoleDescriptions);
+
+				t.SetDropDownWindowTemplateContent(l =>
+				{
+					Html.DevExpress().ListBox(lb =>
+					{
+						lb.Name = "RoleValues";
+						lb.Width = new Unit(100, UnitType.Percentage);
+						lb.Properties.TextField = "Description";
+						lb.Properties.ValueField = "Id";
+						lb.Properties.ValueType = typeof(int);
+						lb.Properties.SelectionMode = ListEditSelectionMode.CheckColumn;
+						lb.ControlStyle.Border.BorderStyle = BorderStyle.None;
+						lb.PreRender = (sender, e) =>
+						{
+							var listBox = sender as MVCxListBox;
+
+							foreach (ListEditItem listItem in listBox.Items)
+							{
+								if (Model.RoleValues == null || !Model.RoleValues.Any(m => m == (int)listItem.Value)) continue;
+
+								listItem.Selected = true;
+							}
 
-			ViewContext.Writer.Write("<div>Mail-Adresse</div>");
-			Html.DevExpress().TextBoxFor(m => m.MailAddress, t => { t.Width = new Unit(100, UnitType.Percentage); }).Render();
+							listBox.Items.Insert(0, new ListEditItem("(Alle auswählen)", -1));
+						};
+						lb.Properties.ClientSideEvents.SelectedIndexChanged = "function (s, e) { onListBoxSelectionChanged(s, e); }";
+					}).BindList(ViewData["AllRoles"]).Render();
+
+					t.Properties.ClientSideEvents.TextChanged = "function (s, e) { synchronizeListBoxValues(s, e); }";
+					t.Properties.ClientSideEvents.DropDown = "function (s, e) { synchronizeListBoxValues(s, e); }";
+				});
+			}).Render();
 
 			ViewContext.Writer.Write("</div>");
 
@@ -46,10 +168,12 @@
 				new GreenTree.Nachtragsmanagement.Web.Models.Global.PopupModel
 				{
 					PopupName = "devPopupControlEditUser",
-					AcceptFunctionName = ""
-				});
+					AcceptFunction = "function (s, e) { saveUser(); }"
+				}
+			);
 		}
 	});
 	s.Styles.Content.Paddings.Padding = new Unit(0);
+	s.Styles.ModalBackground.Opacity = 0;
 }).GetHtml()
 </div>

+ 51 - 64
GreenTree.Nachtragsmanagement.Web/Views/Admin/Users/_UserGridPartial.cshtml

@@ -1,72 +1,59 @@
 @model IEnumerable<GreenTree.Nachtragsmanagement.Web.Models.Admin.User.UserDataModel>
 
-<script>
-	function editUser(id) {
-		if (!id) return;
-		$(".userEditPopupContainer").remove();
-		$.ajax({
-			url: '@Url.Action("EditUser", "Admin")',
-			data: { Id: id },
-			success: function (response) {
-				 $("body").append(response);
-			},
-			error: function () {
-				 alert("error occured");
-			}
-		});
-	}
-</script>
+@Html.DevExpress().GridView(s =>
+{
+	s.Name = "devGridViewUser";
+	s.KeyFieldName = "Id";
+	s.CallbackRouteValues = new { Controller = "Admin", Action = "PartialUsers" };
+	s.Width = Unit.Percentage(100);
 
-@Html.DevExpress().GridView(
-	settings =>
+	s.Columns.Add(column =>
 	{
-		settings.Name = "devGridViewUser";
-		settings.KeyFieldName = "ID";
-		settings.CallbackRouteValues = new { Controller = "Admin", Action = "ViewUsers" };
-		settings.Width = Unit.Percentage(100);
-
-		settings.Columns.Add(column => {
-			column.Caption = "#";
-			column.SetDataItemTemplateContent(c =>
-			{
-				ViewContext.Writer.Write(
-					"<a href=\"#\" onclick=\"editUser(" + DataBinder.Eval(c.DataItem, "Id") + ")\">Bearbeiten</a>&nbsp;" +
-					Html.ActionLink("Löschen", "",
-						new { Id = DataBinder.Eval(c.DataItem, "Id") },
-						new { onclick = "return confirm('Möchten Sie den Benutzer wirklich löschen?')" })
-				);
-			});
-			column.SetHeaderTemplateContent(c =>
-			{
-				ViewContext.Writer.Write(
-					"<a href=\"#\" onclick=\"editUser(-1)\">Neu</a>&nbsp;");
-			});
-			column.Settings.AllowDragDrop = DefaultBoolean.False;
-			column.Settings.AllowSort = DefaultBoolean.False;
-			column.Width = 70;
+		column.Caption = "#";
+		column.SetDataItemTemplateContent(c =>
+		{
+			ViewContext.Writer.Write(
+				"<a href=\"#\" onclick=\"editUser(" + DataBinder.Eval(c.DataItem, "Id") + ")\">Bearbeiten</a>&nbsp;" +
+				"<a href=\"#\" onclick=\"confirmDelete(" + DataBinder.Eval(c.DataItem, "Id") + ")\">Löschen</a>"
+			);
 		});
-		settings.Columns.Add("Forename");
-		settings.Columns.Add("Lastname");
-		settings.Columns.Add("MailAddress");
-		//settings.Columns.Add(column => {
-		//    column.FieldName = "CategoryID";
-		//    column.Caption = "Category";
+		column.SetHeaderTemplateContent(c =>
+		{
+			ViewContext.Writer.Write(
+				"<a href=\"#\" onclick=\"editUser(-1)\">Neu</a>&nbsp;");
+		});
+		column.Settings.AllowDragDrop = DefaultBoolean.False;
+		column.Settings.AllowSort = DefaultBoolean.False;
+		column.Width = 70;
+	});
+	s.Columns.Add("Forename", "Vorname");
+	s.Columns.Add("Lastname", "Nachname");
+	s.Columns.Add("MailAddress", "Mail-Adresse");
+	s.Columns.Add(column =>
+	{
+		column.Caption = "Rollen";
 
-		//    column.ColumnType = MVCxGridViewColumnType.ComboBox;
-		//    var comboBoxProperties = column.PropertiesEdit as ComboBoxProperties;
-		//    comboBoxProperties.DataSource = NorthwindDataProvider.GetCategories();
-		//    comboBoxProperties.TextField = "CategoryName";
-		//    comboBoxProperties.ValueField = "CategoryID";
-		//    comboBoxProperties.ValueType = typeof(int);
-		//});
+		column.SetDataItemTemplateContent(r =>
+		{
+			var modelItem = Model
+				.ElementAt(r.ItemIndex).RoleDescriptions;
+
+			ViewContext.Writer.Write(
+				String.Join(", ", String.Join(", ", modelItem))
+			);
+		});
+	});
 
-		settings.ClientLayout = (s, e) =>
+	s.ClientLayout = (sender, e) =>
+	{
+		if (e.LayoutMode == ClientLayoutMode.Loading)
 		{
-			if(e.LayoutMode == ClientLayoutMode.Loading) {
-				if(Session["UserGridState"] != null)
-					e.LayoutData = (string)Session["UserGridState"];
-			}
-			else
-				Session["UserGridState"] = e.LayoutData;
-		};
-	}).Bind(Model).GetHtml()
+			if (Session["UserGridState"] != null)
+				e.LayoutData = (string)Session["UserGridState"];
+		}
+		else
+			Session["UserGridState"] = e.LayoutData;
+	};
+
+	s.Styles.AlternatingRow.BackColor = System.Drawing.Color.FromArgb(247, 247, 247);
+}).Bind(Model).GetHtml()

+ 1 - 18
GreenTree.Nachtragsmanagement.Web/Views/Home/Index.cshtml

@@ -1,5 +1,5 @@
 @{
-    ViewBag.Title = "Index";
+    ViewBag.Title = "Nachtragsmanagement";
     Layout = "~/Views/Shared/_Layout.cshtml";
 }
 
@@ -84,21 +84,6 @@
 		}
 	})
 
-	//function checkResizeBorders(s, e) {
-	//	var controls = ASPxClientControl.GetControlCollection();
-	//	var popupName = s.name.replace("-Popup", "");
-	//	var popupElement = controls.GetByName(s.name);
-	//	var popupControl = MVCxClientPopupControl.Cast(popupElement);
-	//	var curWidth = popupControl.GetWidth();
-	//	var curHeight = popupControl.GetHeight();
-	//	if (popupLocationsX[popupName] + curWidth >= contentX + $(".functionContentContainer").width()) {
-	//		popupControl.SetWidth($(".functionContentContainer").width() - popupLocationsX[popupName]);
-	//	}
-	//	if (popupLocationsY[popupName] + curHeight >= contentY + $(".functionContentContainer").height()) {
-	//		popupControl.SetHeight($(".functionContentContainer").height() - popupLocationsY[popupName]);
-	//	}
-	//}
-
 	function showFunction(e) {
 		if (!e) return;
 		var controls = ASPxClientControl.GetControlCollection();
@@ -242,8 +227,6 @@
 				s.MinHeight = new Unit(i.MinHeight.Value, UnitType.Pixel);
 				s.CloseAction = CloseAction.None;
 				s.AllowDragging = false;
-				//s.AllowResize = true;
-				//s.ClientSideEvents.Resize = "function (s, e) { checkResizeBorders(s, e); }";
 				s.Styles.Header.CssClass += "devExAllowDrag";
 			}).GetHtml();
 		}

+ 2 - 2
GreenTree.Nachtragsmanagement.Web/Views/Shared/_Footer.cshtml

@@ -30,10 +30,10 @@
 		}).BindList(ViewData["Roles"]).GetHtml();
 		Html.RenderPartial(
 			"~/Views/Shared/_PopupButtonPanel.cshtml",
-			new GreenTree.Nachtragsmanagement.Web.Models.Global.PopupModel()
+			new GreenTree.Nachtragsmanagement.Web.Models.Global.PopupModel
 			{
 				PopupName = "devPopupControlRoles",
-				AcceptFunctionName = "acceptFooterRoleSelection"
+				AcceptFunction = "function (s, e) { acceptFooterRoleSelection(); }"
 			});
 	});
 	s.Styles.Content.Paddings.Padding = new Unit(0);

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

@@ -19,11 +19,6 @@
 		new Script { ExtensionSuite = ExtensionSuite.GridView }
 	)
 
-	@*@System.Web.Optimization.Scripts.Render("~/bundles/jquery")
-	@System.Web.Optimization.Scripts.Render("~/bundles/globalize")
-	@System.Web.Optimization.Scripts.Render("~/bundles/knockout")
-	@System.Web.Optimization.Scripts.Render("~/bundles/modernizr")*@
-
 	<link rel="stylesheet" type="text/css" href="~/Content/global.css" />
 	<link rel="stylesheet" type="text/css" href="~/Content/devex.css" />
 	<link rel="stylesheet" type="text/css" href="~/Content/function.css" />

+ 4 - 2
GreenTree.Nachtragsmanagement.Web/Views/Shared/_PopupButtonPanel.cshtml

@@ -7,6 +7,7 @@
 		b.Text = "Abbrechen";
 		b.Width = new Unit(100, UnitType.Pixel);
 		b.ControlStyle.CssClass += "devExFloatRight devExPopupPanelButton";
+		b.UseSubmitBehavior = false;
 		b.ClientSideEvents.Click = "function (s, e) { " + Model.PopupName + ".Hide(); }";
 	}).GetHtml()
 	@Html.DevExpress().Button(b =>
@@ -15,13 +16,14 @@
 		b.Text = "Ok";
 		b.Width = new Unit(100, UnitType.Pixel);
 		b.ControlStyle.CssClass += "devExFloatRight devExPopupPanelButton";
-		if (String.IsNullOrEmpty(Model.AcceptFunctionName)) 
+		if (String.IsNullOrEmpty(Model.AcceptFunction)) 
 		{
 			b.UseSubmitBehavior = true;
 		}
 		else
 		{
-			b.ClientSideEvents.Click = "function (s, e) { " + Model.AcceptFunctionName + "(); }";
+			b.UseSubmitBehavior = false;
+			b.ClientSideEvents.Click = Model.AcceptFunction;
 		}
 	}).GetHtml()
 </div>

+ 29 - 0
GreenTree.Nachtragsmanagement.Web/Views/Shared/_PopupButtonPanelYesNo.cshtml

@@ -0,0 +1,29 @@
+@model GreenTree.Nachtragsmanagement.Web.Models.Global.YesNoDialogModel
+
+<div class="popupButtonPanel">
+	@Html.DevExpress().Button(b =>
+	{
+		b.Name = "devButtonDialogNo";
+		b.Text = "Nein";
+		b.Width = new Unit(100, UnitType.Pixel);
+		b.ControlStyle.CssClass += "devExFloatRight devExPopupPanelButton";
+		b.UseSubmitBehavior = false;
+		if (String.IsNullOrEmpty(Model.NoFunction))
+		{
+			b.ClientSideEvents.Click = "function (s, e) { " + Model.PopupName + ".Hide(); }";
+		}
+		else
+		{
+			b.ClientSideEvents.Click = Model.NoFunction;
+		}
+	}).GetHtml()
+	@Html.DevExpress().Button(b =>
+	{
+		b.Name = "devButtonDialogYes";
+		b.Text = "Ja";
+		b.Width = new Unit(100, UnitType.Pixel);
+		b.ControlStyle.CssClass += "devExFloatRight devExPopupPanelButton";
+		b.UseSubmitBehavior = false;
+		b.ClientSideEvents.Click = Model.YesFunction;
+	}).GetHtml()
+</div>

+ 31 - 0
GreenTree.Nachtragsmanagement.Web/Views/Shared/_PopupDialog.cshtml

@@ -0,0 +1,31 @@
+@model GreenTree.Nachtragsmanagement.Web.Models.Global.YesNoDialogModel
+
+@Html.DevExpress().PopupControl(s =>
+{
+	s.Name = Model.PopupName;
+	s.HeaderText = Model.HeaderText;
+	s.Modal = false;
+	s.Width = new Unit(350, UnitType.Pixel);
+	s.CloseAction = CloseAction.CloseButton;
+	s.PopupHorizontalAlign = PopupHorizontalAlign.WindowCenter;
+	s.PopupVerticalAlign = PopupVerticalAlign.WindowCenter;
+	s.AllowDragging = false;
+	s.AllowResize = false;
+	s.ShowFooter = false;
+	s.SetContent(() =>
+	{
+		ViewContext.Writer.Write(Model.Content);
+
+		if (String.IsNullOrEmpty(Model.NoFunction))
+			Model.NoFunction = "function (s, e) { " + Model.PopupName + ".Hide(); }";
+
+		if (String.IsNullOrEmpty(Model.YesFunction))
+			Model.YesFunction = "function (s, e) { " + Model.PopupName + ".Hide(); }";
+
+		Html.RenderPartial(
+			"~/Views/Shared/_PopupButtonPanelYesNo.cshtml",
+			Model
+		);
+	});
+	s.Styles.Content.Paddings.Padding = new Unit(0, UnitType.Pixel);
+}).GetHtml()

+ 2 - 0
GreenTree.Nachtragsmanagement.Web/packages.config

@@ -3,6 +3,8 @@
   <package id="Antlr" version="3.4.1.9004" targetFramework="net452" />
   <package id="Autofac" version="4.0.1" targetFramework="net452" />
   <package id="EntityFramework" version="6.1.3" targetFramework="net452" />
+  <package id="FluentValidation" version="7.1.1" targetFramework="net452" />
+  <package id="FluentValidation.Mvc5" version="7.1.1" targetFramework="net452" />
   <package id="Microsoft.AspNet.Mvc" version="5.2.3" targetFramework="net452" />
   <package id="Microsoft.AspNet.Mvc.de" version="5.2.3" targetFramework="net452" />
   <package id="Microsoft.AspNet.Razor" version="3.2.3" targetFramework="net452" />