<input type="submit" value="Save" />
@Ajax.beginform(...)
<div id="pjaxContainer"> @RenderBody() </div>
<input type="submit" value="Save" />
@Ajax.beginform(...)
<div id="pjaxContainer"> @RenderBody() </div>
گزینه | Context | توضیحات |
Copy Rule Declaration | CSS selector | CSS Rule فعلی را به همراه استایل هایش در clipboard کپی میکند |
Copy Style Declaration | CSS selector | استایلهای CSS Rule فعلی را در clipboard کپی میکند |
Copy Location | source link | آدرس فایل تعریف CSS Rule را در clipboard کپی میکند |
Open in New Tab | source link | آدرس فایل تعریف CSS Rule را در تب جدید باز میکند |
Edit Element Style... | everywhere | امکان تعریف استایلهای درون تگ (inline) را محیا میکند |
Add Rule... | everywhere | یک Rule جدید ایجاد میکند CSS Rule هایی که در حال حاظر وجود دارد را هم پیشنهاد میدهد |
Delete "<rule name>" | CSS selector | CSS Rule فعلی را حذف میکند |
New Property... | CSS rule | یک استایل جدید به CSS Rule فعلی اضافه میکند |
Edit "<property name>"... | CSS property | Property فعلی را به حالت ویرایش میبرد راه دیگر ویرایش Property ، کلیک بروی آن است |
Delete "<property name>" | CSS property | Property فعلی را حذف میکند |
Disable "<property name>" | CSS property | Property فعلی را غیر فعال میکند را سریعتر ، کلیک کردن در ناحیهی پشت Property ، بروی علامت قرمز رنگ است |
Refresh | everywhere | محتویات پنل را بروز میکند |
Inspect in DOM Panel |
CSS rule | CSS Rule فعلی را در پنل DOM برای بررسی باز میکند |
Inspect in CSS Panel | CSS rule | CSS Rule فعلی را در پنل CSS برای بررسی باز میکند |
<Default Editor Name> | CSS rule | فایل تعریف استایلها را در ادیتور تعریف شده باز میکند (این گزینه در صورت تعریف ادیتور در تنظیمات FireBug نمایش داده خواهد شد) |
گزینه | Context | توضیحات |
Expand All Styles | everywhere | CSS Rule فعلی را به همراه استایل هایش در clipboard کپی میکند |
Collapse All Styles | everywhere | استایلهای CSS Rule فعلی را در clipboard کپی میکند |
Inspect in DOM panel | styles | آدرس فایل تعریف CSS Rule را در تب جدید باز میکند |
Copy Location |
style source link | امکان تعریف استایلهای درون تگ (inline) را محیا میکند |
Open in New Tab | style source link | یک Rule جدید ایجاد میکند CSS Rule هایی که در حال حاظر وجود دارد را هم پیشنهاد میدهد |
Inspect in CSS panel | style source link | CSS Rule فعلی را حذف میکند |
<Default Editor Name> | style source link | فایل تعریف استایلها را در ادیتور تعریف شده باز میکند (این گزینه در صورت تعریف ادیتور در تنظیمات FireBug نمایش داده خواهد شد) |
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Globalization; using System.Security.Cryptography; using System.Text; using System.Web.Mvc; using System.Web.Security; namespace OpenIDExample.Models { #region Models public class ChangePasswordModel { [Required] [DataType(DataType.Password)] [Display(Name = "Current password")] public string OldPassword { get; set; } [Required] [ValidatePasswordLength] [DataType(DataType.Password)] [Display(Name = "New password")] public string NewPassword { get; set; } [DataType(DataType.Password)] [Display(Name = "Confirm new password")] [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] public string ConfirmPassword { get; set; } } public class LogOnModel { [Display(Name = "OpenID")] public string OpenID { get; set; } [Required] [Display(Name = "User name")] public string UserName { get; set; } [Required] [DataType(DataType.Password)] [Display(Name = "Password")] public string Password { get; set; } [Display(Name = "Remember me?")] public bool RememberMe { get; set; } } public class RegisterModel { [Display(Name = "OpenID")] public string OpenID { get; set; } [Required] [Display(Name = "User name")] public string UserName { get; set; } [Required] [DataType(DataType.EmailAddress)] [Display(Name = "Email address")] public string Email { get; set; } [Required] [ValidatePasswordLength] [DataType(DataType.Password)] [Display(Name = "Password")] public string Password { get; set; } [DataType(DataType.Password)] [Display(Name = "Confirm password")] [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] public string ConfirmPassword { get; set; } } #endregion Models #region Services // The FormsAuthentication type is sealed and contains static members, so it is difficult to // unit test code that calls its members. The interface and helper class below demonstrate // how to create an abstract wrapper around such a type in order to make the AccountController // code unit testable. public interface IMembershipService { int MinPasswordLength { get; } bool ValidateUser(string userName, string password); MembershipCreateStatus CreateUser(string userName, string password, string email, string OpenID); bool ChangePassword(string userName, string oldPassword, string newPassword); MembershipUser GetUser(string OpenID); } public class AccountMembershipService : IMembershipService { private readonly MembershipProvider _provider; public AccountMembershipService() : this(null) { } public AccountMembershipService(MembershipProvider provider) { _provider = provider ?? Membership.Provider; } public int MinPasswordLength { get { return _provider.MinRequiredPasswordLength; } } public bool ValidateUser(string userName, string password) { if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName"); if (String.IsNullOrEmpty(password)) throw new ArgumentException("Value cannot be null or empty.", "password"); return _provider.ValidateUser(userName, password); } public Guid StringToGUID(string value) { // Create a new instance of the MD5CryptoServiceProvider object. MD5 md5Hasher = MD5.Create(); // Convert the input string to a byte array and compute the hash. byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(value)); return new Guid(data); } public MembershipCreateStatus CreateUser(string userName, string password, string email, string OpenID) { if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName"); if (String.IsNullOrEmpty(password)) throw new ArgumentException("Value cannot be null or empty.", "password"); if (String.IsNullOrEmpty(email)) throw new ArgumentException("Value cannot be null or empty.", "email"); MembershipCreateStatus status; _provider.CreateUser(userName, password, email, null, null, true, StringToGUID(OpenID), out status); return status; } public MembershipUser GetUser(string OpenID) { return _provider.GetUser(StringToGUID(OpenID), true); } public bool ChangePassword(string userName, string oldPassword, string newPassword) { if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName"); if (String.IsNullOrEmpty(oldPassword)) throw new ArgumentException("Value cannot be null or empty.", "oldPassword"); if (String.IsNullOrEmpty(newPassword)) throw new ArgumentException("Value cannot be null or empty.", "newPassword"); // The underlying ChangePassword() will throw an exception rather // than return false in certain failure scenarios. try { MembershipUser currentUser = _provider.GetUser(userName, true /* userIsOnline */); return currentUser.ChangePassword(oldPassword, newPassword); } catch (ArgumentException) { return false; } catch (MembershipPasswordException) { return false; } } public MembershipCreateStatus CreateUser(string userName, string password, string email) { throw new NotImplementedException(); } } public interface IFormsAuthenticationService { void SignIn(string userName, bool createPersistentCookie); void SignOut(); } public class FormsAuthenticationService : IFormsAuthenticationService { public void SignIn(string userName, bool createPersistentCookie) { if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName"); FormsAuthentication.SetAuthCookie(userName, createPersistentCookie); } public void SignOut() { FormsAuthentication.SignOut(); } } #endregion Services #region Validation public static class AccountValidation { public static string ErrorCodeToString(MembershipCreateStatus createStatus) { // See http://go.microsoft.com/fwlink/?LinkID=177550 for // a full list of status codes. switch (createStatus) { case MembershipCreateStatus.DuplicateUserName: return "Username already exists. Please enter a different user name."; case MembershipCreateStatus.DuplicateEmail: return "A username for that e-mail address already exists. Please enter a different e-mail address."; case MembershipCreateStatus.InvalidPassword: return "The password provided is invalid. Please enter a valid password value."; case MembershipCreateStatus.InvalidEmail: return "The e-mail address provided is invalid. Please check the value and try again."; case MembershipCreateStatus.InvalidAnswer: return "The password retrieval answer provided is invalid. Please check the value and try again."; case MembershipCreateStatus.InvalidQuestion: return "The password retrieval question provided is invalid. Please check the value and try again."; case MembershipCreateStatus.InvalidUserName: return "The user name provided is invalid. Please check the value and try again."; case MembershipCreateStatus.ProviderError: return "The authentication provider returned an error. Please verify your entry and try again. If the problem persists, please contact your system administrator."; case MembershipCreateStatus.UserRejected: return "The user creation request has been canceled. Please verify your entry and try again. If the problem persists, please contact your system administrator."; default: return "An unknown error occurred. Please verify your entry and try again. If the problem persists, please contact your system administrator."; } } } [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)] public sealed class ValidatePasswordLengthAttribute : ValidationAttribute, IClientValidatable { private const string _defaultErrorMessage = "'{0}' must be at least {1} characters long."; private readonly int _minCharacters = Membership.Provider.MinRequiredPasswordLength; public ValidatePasswordLengthAttribute() : base(_defaultErrorMessage) { } public override string FormatErrorMessage(string name) { return String.Format(CultureInfo.CurrentCulture, ErrorMessageString, name, _minCharacters); } public override bool IsValid(object value) { string valueAsString = value as string; return (valueAsString != null && valueAsString.Length >= _minCharacters); } public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context) { return new[]{ new ModelClientValidationStringLengthRule(FormatErrorMessage(metadata.GetDisplayName()), _minCharacters, int.MaxValue) }; } } #endregion Validation }
using System.Web.Mvc; using System.Web.Routing; using System.Web.Security; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.RelyingParty; using OpenIDExample.Models; namespace OpenIDExample.Controllers { public class AccountController : Controller { private static OpenIdRelyingParty openid = new OpenIdRelyingParty(); public IFormsAuthenticationService FormsService { get; set; } public IMembershipService MembershipService { get; set; } protected override void Initialize(RequestContext requestContext) { if (FormsService == null) { FormsService = new FormsAuthenticationService(); } if (MembershipService == null) { MembershipService = new AccountMembershipService(); } base.Initialize(requestContext); } // ************************************** // URL: /Account/LogOn // ************************************** public ActionResult LogOn() { return View(); } [HttpPost] public ActionResult LogOn(LogOnModel model, string returnUrl) { if (ModelState.IsValid) { if (MembershipService.ValidateUser(model.UserName, model.Password)) { FormsService.SignIn(model.UserName, model.RememberMe); if (Url.IsLocalUrl(returnUrl)) { return Redirect(returnUrl); } else { return RedirectToAction("Index", "Home"); } } else { ModelState.AddModelError("", "The user name or password provided is incorrect."); } } // If we got this far, something failed, redisplay form return View(model); } // ************************************** // URL: /Account/LogOff // ************************************** public ActionResult LogOff() { FormsService.SignOut(); return RedirectToAction("Index", "Home"); } // ************************************** // URL: /Account/Register // ************************************** public ActionResult Register(string OpenID) { ViewBag.PasswordLength = MembershipService.MinPasswordLength; ViewBag.OpenID = OpenID; return View(); } [HttpPost] public ActionResult Register(RegisterModel model) { if (ModelState.IsValid) { // Attempt to register the user MembershipCreateStatus createStatus = MembershipService.CreateUser(model.UserName, model.Password, model.Email, model.OpenID); if (createStatus == MembershipCreateStatus.Success) { FormsService.SignIn(model.UserName, false /* createPersistentCookie */); return RedirectToAction("Index", "Home"); } else { ModelState.AddModelError("", AccountValidation.ErrorCodeToString(createStatus)); } } // If we got this far, something failed, redisplay form ViewBag.PasswordLength = MembershipService.MinPasswordLength; return View(model); } // ************************************** // URL: /Account/ChangePassword // ************************************** [Authorize] public ActionResult ChangePassword() { ViewBag.PasswordLength = MembershipService.MinPasswordLength; return View(); } [Authorize] [HttpPost] public ActionResult ChangePassword(ChangePasswordModel model) { if (ModelState.IsValid) { if (MembershipService.ChangePassword(User.Identity.Name, model.OldPassword, model.NewPassword)) { return RedirectToAction("ChangePasswordSuccess"); } else { ModelState.AddModelError("", "The current password is incorrect or the new password is invalid."); } } // If we got this far, something failed, redisplay form ViewBag.PasswordLength = MembershipService.MinPasswordLength; return View(model); } // ************************************** // URL: /Account/ChangePasswordSuccess // ************************************** public ActionResult ChangePasswordSuccess() { return View(); } [ValidateInput(false)] public ActionResult Authenticate(string returnUrl) { var response = openid.GetResponse(); if (response == null) { //Let us submit the request to OpenID provider Identifier id; if (Identifier.TryParse(Request.Form["openid_identifier"], out id)) { try { var request = openid.CreateRequest(Request.Form["openid_identifier"]); return request.RedirectingResponse.AsActionResult(); } catch (ProtocolException ex) { ViewBag.Message = ex.Message; return View("LogOn"); } } ViewBag.Message = "Invalid identifier"; return View("LogOn"); } //Let us check the response switch (response.Status) { case AuthenticationStatus.Authenticated: LogOnModel lm = new LogOnModel(); lm.OpenID = response.ClaimedIdentifier; //check if user exist MembershipUser user = MembershipService.GetUser(lm.OpenID); if (user != null) { lm.UserName = user.UserName; FormsService.SignIn(user.UserName, false); } return View("LogOn", lm); case AuthenticationStatus.Canceled: ViewBag.Message = "Canceled at provider"; return View("LogOn"); case AuthenticationStatus.Failed: ViewBag.Message = response.Exception.Message; return View("LogOn"); } return new EmptyResult(); } } }
@model OpenIDExample.Models.LogOnModel @{ ViewBag.Title = "Log On"; } <h2> Log On</h2> <p> Please enter your username and password. @Html.ActionLink("Register", "Register") if you don't have an account. </p> <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script> <form action="Authenticate?ReturnUrl=@HttpUtility.UrlEncode(Request.QueryString["ReturnUrl"])" method="post" id="openid_form"> <input type="hidden" name="action" value="verify" /> <div> <fieldset> <legend>Login using OpenID</legend> <div class="openid_choice"> <p> Please click your account provider:</p> <div id="openid_btns"> </div> </div> <div id="openid_input_area"> @Html.TextBox("openid_identifier") <input type="submit" value="Log On" /> </div> <noscript> <p> OpenID is service that allows you to log-on to many different websites using a single indentity. Find out <a href="http://openid.net/what/">more about OpenID</a> and <a href="http://openid.net/get/">how to get an OpenID enabled account</a>.</p> </noscript> <div> @if (Model != null) { if (String.IsNullOrEmpty(Model.UserName)) { <div class="editor-label"> @Html.LabelFor(model => model.OpenID) </div> <div class="editor-field"> @Html.DisplayFor(model => model.OpenID) </div> <p class="button"> @Html.ActionLink("New User ,Register", "Register", new { OpenID = Model.OpenID }) </p> } else { //user exist <p class="buttonGreen"> <a href="@Url.Action("Index", "Home")">Welcome , @Model.UserName, Continue..." </a> </p> } } </div> </fieldset> </div> </form> @Html.ValidationSummary(true, "Login was unsuccessful. Please correct the errors and try again.") @using (Html.BeginForm()) { <div> <fieldset> <legend>Or Login Normally</legend> <div class="editor-label"> @Html.LabelFor(m => m.UserName) </div> <div class="editor-field"> @Html.TextBoxFor(m => m.UserName) @Html.ValidationMessageFor(m => m.UserName) </div> <div class="editor-label"> @Html.LabelFor(m => m.Password) </div> <div class="editor-field"> @Html.PasswordFor(m => m.Password) @Html.ValidationMessageFor(m => m.Password) </div> <div class="editor-label"> @Html.CheckBoxFor(m => m.RememberMe) @Html.LabelFor(m => m.RememberMe) </div> <p> <input type="submit" value="Log On" /> </p> </fieldset> </div> }
<authentication mode="Forms"> <forms loginUrl="~/Account/LogOn" timeout="2880" /> </authentication>
PM> Install-Package DNTFrameworkCore.Web
public class BlogsController : CrudController<IBlogService, int, BlogModel> { public BlogsController(IBlogService service) : base(service) { } protected override string CreatePermissionName => PermissionNames.Blogs_Create; protected override string EditPermissionName => PermissionNames.Blogs_Edit; protected override string ViewPermissionName => PermissionNames.Blogs_View; protected override string DeletePermissionName => PermissionNames.Blogs_Delete; protected override string ViewName => "_BlogModal"; }
<form asp-action="@(Model.IsNew() ? "Create" : "Edit")" asp-controller="Blogs" asp-modal-form="BlogForm"> <div> <input type="hidden" name="save-continue" value="true"/> <input asp-for="RowVersion" type="hidden"/> <input asp-for="Id" type="hidden"/> <div> <div> <label asp-for="Title"></label> <input asp-for="Title" autocomplete="off"/> <span asp-validation-for="Title"></span> </div> </div> <div> <div> <label asp-for="Url"></label> <input asp-for="Url" type="url"/> <span asp-validation-for="Url"></span> </div> </div> </div> ... </form>
<div> <a asp-modal-delete-link asp-model-id="@Model.Id" asp-modal-toggle="false" asp-controller="Blogs" asp-action="Delete" asp-if="!Model.IsNew()" asp-permission="@PermissionNames.Blogs_Delete" title="Delete Blog"> <i></i> </a> <a title="Refresh Blog" asp-if="!Model.IsNew()" asp-modal-link asp-modal-toggle="false" asp-controller="Blogs" asp-action="Edit" asp-route-id="@Model.Id"> <i></i> </a> <a title="New Blog" asp-modal-link asp-modal-toggle="false" asp-controller="Blogs" asp-action="Create"> <i></i> </a> <button type="button" data-dismiss="modal"> <i></i> Cancel </button> <button type="submit"> <i></i> Save Changes </button> </div>
public class RoleModalViewModel : RoleModel { public IReadOnlyList<LookupItem> PermissionList { get; set; } }
protected override IActionResult RenderView(RoleModel role) { var model = _mapper.Map<RoleModalViewModel>(role); model.PermissionList = ReadPermissionList(); return PartialView(ViewName, model); }
برای مدیریت سناریوهای Master-Detail به مانند قسمت مدیریت دسترسیها در تب Permissions فرم بالا، امکاناتی در زیرساخت تعبیه شده است ولی پیادهسازی آن را به عنوان یک تمرین و با توجه به سری مطالب «Editing Variable Length Reorderable Collections in ASP.NET MVC» به شما واگذار میکنم.
نکته تکمیلی: برای ارسال اطلاعات اضافی به ویو Index متناظر با یک موجودیت میتوانید متد RenderIndex را به شکل زیر بازنویسی کنید:
protected override IActionResult RenderIndex(IPagedQueryResult<RoleReadModel> model) { var indexModel = new RoleIndexViewModel { Items = model.Items, TotalCount = model.TotalCount, Permissions = ReadPermissionList() }; return Request.IsAjaxRequest() ? (IActionResult) PartialView(indexModel) : View(indexModel); }
مدل RoleIndexViewModel استفاده شده در تکه کد بالا نیز به شکل زیر خواهد بود:
public class RoleIndexViewModel : PagedQueryResult<RoleReadModel> { public IReadOnlyList<LookupItem> Permissions { get; set; } }
فرآیند بارگذاری یک پارشالویو در مودال
به عنوان مثال برای استفاده از مودالهای بوت استرپ، ایده کار به این شکل است که یک مودال را به شکل زیر در فایل Layout قرار دهید:
<div class="modal fade" @*tabindex="-1"*@ id="main-modal" data-keyboard="true" data-backdrop="static" role="dialog" aria-hidden="true"> <div class="modal-dialog modal-dialog-centered" role="document"> <div class="modal-content"> <div class="modal-body"> Loading... </div> </div> </div> </div>
سپس در زمان کلیک بروی یک دکمه Ajaxای، ابتدا main-modal را نمایش داده و بعد از دریافت پارشالویو از سرور، آن را با محتوای modal-content جایگزین میکنیم. به همین دلیل Tag Halperهای مطرح شده در مطلب جاری، callbackهای failure/complete/success متناظر با unobtrusive-ajax را نیز مقداردهی میکنند. برای این منظور نیاز است تا متدهای جاوااسکریپتی زیر نیز در سطح شیء window تعریف شده باشند:
/*---------------------------------- asp-modal-link ---------------------------*/ window.handleModalLinkLoaded = function (data, status, xhr) { prepareForm('#main-modal.modal form'); }; window.handleModalLinkFailed = function (xhr, status, error) { //.... }; /*---------------------------------- asp-modal-form ---------------------------*/ window.handleModalFormBegin = function (xhr) { $('#main-modal a').addClass('disabled'); $('#main-modal button').attr('disabled', 'disabled'); }; window.handleModalFormComplete = function (xhr, status) { $('#main-modal a').removeClass('disabled'); $('#main-modal button').removeAttr('disabled'); }; window.handleModalFormSucceeded = function (data, status, xhr) { if (xhr.getResponseHeader('Content-Type') === 'text/html; charset=utf-8') { prepareForm('#main-modal.modal form'); } else { hideMainModal(); } }; window.handleModalFormFailed = function (xhr, status, error, formId) { if (xhr.status === 400) { handleBadRequest(xhr, formId); } };