|
Thank you I'll do that.
AF Matambo
|
|
|
|
|
I need to validate a Delete Action using a function, the requirements are:
1) No one is allowed to modify nor delete some ones else information, needed verification if the logged in user and a creator user is a same user. Added CreatedBy Property in base Model
2) For a same user Modification is allowed if there is no more than 2 hours from the created date time
3) Delete is allowed only if there is no more the 1 hour from the created date time
I created a function for verification of all those requirements.
private Boolean IsAllowedAction(RoleView view, string caller)
{
if (view.CreatedBy != HttpContext.Current.User.Identity.Name)
{
ModelState.AddModelError<RoleView>(role => role.Name, Validations.RoleNameIsAlreadyUsed);
return false;
}
if (DateTime.Now.Subtract(view.CreationDate).TotalDays > 1)
{
ModelState.AddModelError<RoleView>(role => role.Name, Validations.RoleNameIsAlreadyUsed);
return false;
}
if (DateTime.Now.Subtract(view.CreationDate).TotalHours > 2 && caller == "CanEdit")
{
ModelState.AddModelError<RoleView>(role => role.Name, Validations.RoleNameIsAlreadyUsed);
return false;
}
if (DateTime.Now.Subtract(view.CreationDate).TotalHours > 1 && caller == "CanDelete")
{
ModelState.AddModelError<RoleView>(role => role.Name, Validations.RoleNameIsAlreadyUsed);
return false;
}
return true;
}
}
I created de CanDelete for calling the function
public bool CanDelete(RoleView view)
{
Boolean isValid = ModelState.IsValid;
isValid &= IsAllowedAction(view, "CanDelete");
return isValid;
}
Since these validation are shared among all classes, I created the Validations resources file in shared folder of resource project with these entries:
DateGreaterThanToday => The date entered is greater than today's date
DateIsNotInRange => The Selected date is not in permitted range
DeleteIsNotAllowed => Delete is not allowed passed 1 hours of creation date
EditIsNotAllowed => Edit is not allowed passed 2 hours of creation date
EditOrDeleteAllowedToCreatorUser => Edit or Delete is allowed only to the creator user
EditOrDeleteNotAllowed => Edit or Delete is not allowed passed 1 day of creation date
SelectedDateGreaterThanSixMonths => The selected date is greater than six months
In controllers for the Department class implemented like this
[HttpGet]
public ActionResult Delete(String id)
{
return NotEmptyView(Service.Get<DepartmentView>(id));
}
[HttpPost]
[ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(String id, DepartmentView department)
{
if (!Validator.CanDelete(department))
return RedirectIfAuthorized("Index");
Service.Delete(id);
return RedirectIfAuthorized("Index");
}
I’m trying to test like so
[Fact]
public void Delete_ReturnsSameModelIfCanNotEdit()
{
validator.Delete(department).Returns(false);
Object actual = (controller.Delete(department) as ViewResult).Model;
Object expected = department;
Assert.Same(expected, actual);
}
[Fact]
public void Delete_DeleteDepartmentView()
{
validator.Delete(department).Returns(true);
controller.Delete(department);
service.Received().Delete(department);
}
I’m not succeeded, can you help me please for implementing a Delete validation.
Thank you so much
AF Matambo
|
|
|
|
|
What are you not succeeding in? I don't know if I should or can help you with something not template related
modified 25-Mar-21 21:01pm.
|
|
|
|
|
First of all let me apologize for making a non-sense questions, without any kind of contribution and posting a lot of lines of code that you know because you have coded it, please accept my apologizes and I promise that will never occur again.
Having said that, my question is: Can you show me and help me on how to implement the delete validation as you do it in the Edit Module?
I’ll appreciate if you give me the steps to follow like in your reply to the “General approach using scaffolding” question.
Thank you so much.
AF Matambo
|
|
|
|
|
But as I see from your code you already created your delete validation? And you called it from controller and that 's it :?
Of course your validation messages doesn't seems right, but that's about it.
modified 25-Mar-21 21:01pm.
|
|
|
|
|
Yes I created the validations and are being called from controller class, it looks like that all is ok in controller and validators classes, now the errors are coming from a test project, by the descriptions of the errors I guess that is caused by the method names definition and types mismatch, I'll investigate the error source and try to fix it and then I'll came back to you
Thank you for your time and patience
AF Matambo
|
|
|
|
|
I added entries to the Mvc.sitemap:
<?xml version="1.0" encoding="utf-8" ?>
<siteMap>
<siteMapNode icon="fa fa-home" controller="Home" action="Index">
<siteMapNode icon="fa fa-ban" controller="Home" action="Error" />
<siteMapNode icon="fa fa-search" controller="Home" action="NotFound" />
<siteMapNode icon="fa fa-lock" controller="Home" action="Unauthorized" />
<siteMapNode menu="true" icon="fa fa-home" controller="Products" action="Index">
<siteMapNode icon="fa fa-file-o" controller="Products" action="Create" />
<siteMapNode icon="fa fa-info" controller="Products" action="Details" />
<siteMapNode icon="fa fa-pencil" controller="Products" action="Edit" />
<siteMapNode icon="fa fa-times" controller="Products" action="Delete" />
</siteMapNode>
<siteMapNode controller="Profile" action="Edit" />
<siteMapNode controller="Profile" action="Delete" />
<siteMapNode menu="true" icon="fa fa-gears" area="Administration">
<siteMapNode menu="true" icon="fa fa-user" area="Administration" controller="Accounts" action="Index">
<siteMapNode icon="fa fa-file-o" area="Administration" controller="Accounts" action="Create" />
<siteMapNode icon="fa fa-info" area="Administration" controller="Accounts" action="Details" />
<siteMapNode icon="fa fa-pencil" area="Administration" controller="Accounts" action="Edit" />
</siteMapNode>
<siteMapNode menu="true" icon="fa fa-users" area="Administration" controller="Roles" action="Index">
<siteMapNode icon="fa fa-file-o" area="Administration" controller="Roles" action="Create" />
<siteMapNode icon="fa fa-info" area="Administration" controller="Roles" action="Details" />
<siteMapNode icon="fa fa-pencil" area="Administration" controller="Roles" action="Edit" />
<siteMapNode icon="fa fa-times" area="Administration" controller="Roles" action="Delete" />
</siteMapNode>
</siteMapNode>
</siteMapNode>
</siteMap>
Added Privileges and assigned them to the Sys_Admin Role:
...
new Privilege { Area = "", Controller = "Products", Action = "Index" },
new Privilege { Area = "", Controller = "Products", Action = "Create" },
new Privilege { Area = "", Controller = "Products", Action = "Details" },
new Privilege { Area = "", Controller = "Products", Action = "Edit" },
new Privilege { Area = "", Controller = "Products", Action = "Delete" }
...
What am I missing?
|
|
|
|
|
I found the problem.
New Privileges must be added with no Area if you dont need it.
new Privilege { Controller = "Products", Action = "Index" },
new Privilege { Controller = "Products", Action = "Create" },
new Privilege { Controller = "Products", Action = "Details" },
new Privilege { Controller = "Products", Action = "Edit" },
new Privilege { Controller = "Products", Action = "Delete" }
Otherwise an empty string is inserted into the table and the Cache query in AuthorizationProvider.cs
return Cache[accountId]
.Any(privilege =>
String.Equals(privilege.Area, area, StringComparison.OrdinalIgnoreCase) &&
String.Equals(privilege.Action, action, StringComparison.OrdinalIgnoreCase) &&
String.Equals(privilege.Controller, controller, StringComparison.OrdinalIgnoreCase));
will give you false since the area variable is explicitly set to null if empty, but the Cache contains the empty string.
|
|
|
|
|
Yep, this is one of confusing MVC parts, because you create urls while specifying area = "", but generated route values give null instead of an empty string.
modified 25-Mar-21 21:01pm.
|
|
|
|
|
Hi there
I found that in some ways all views they need a resource file for validation, some’s for text displaying others for some messages, can you include in Scaffolding procedures the creation of views structure in resources project and at least include the validation resource file in each view folder? And also include the name space of that file resource for example.
using VumbaSoft.AdventureWorks.Resources.Views.Administration.AccountView;
I mean to generate the hierarchical structure same as the structure that is being generated in view folder of Web project
Thank you so much
AF Matambo
|
|
|
|
|
I wonder how complicated is it to update the site with one of the themes from lets say: wrapbootstrap?
Have you tried this already and if yes, is there something special to consider?
best regards,
Darko
|
|
|
|
|
I'm actually changing site theme for every project.
No, it not hard you would just need to change everything in Content/Shared folder or delete everything and add your site.css files. Of course div structure in views may have to be changed, but it's okay after changing scaffolding too.
modified 25-Mar-21 21:01pm.
|
|
|
|
|
I must say, this template is the one of the most complete ones I came across.
As I started implementing my solution I tried the manual approach first just to get hands dirty.
Reading the questions and comments and seeing the T4 templates I created a new solution, added a POCO Product and tried to fire up scaffolding with:
scaffold Product Products
This generated a whole bunch of new files:
Added KDSoft.BuyersConnection.Tests\Unit\Controllers\Products\ProductsControllerTests.cs.
Added KDSoft.BuyersConnection.Tests\Unit\Validators\Products\ProductValidatorTests.cs.
Added KDSoft.BuyersConnection.Tests\Unit\Services\Products\ProductServiceTests.cs.
Added KDSoft.BuyersConnection.Objects\Models\Products\Product.cs.
Added KDSoft.BuyersConnection.Objects\Views\Products\ProductView.cs.
Added KDSoft.BuyersConnection.Validators\Products\IProductValidator.cs.
Added KDSoft.BuyersConnection.Validators\Products\ProductValidator.cs.
Added KDSoft.BuyersConnection.Services\Products\IProductService.cs.
Added KDSoft.BuyersConnection.Services\Products\ProductService.cs.
Added KDSoft.BuyersConnection.Controllers\Products\ProductsController.cs.
Added KDSoft.BuyersConnection.Web\Views\Products\Index.cshtml.
Added KDSoft.BuyersConnection.Web\Views\Products\Create.cshtml.
Added KDSoft.BuyersConnection.Web\Views\Products\Details.cshtml.
Added KDSoft.BuyersConnection.Web\Views\Products\Edit.cshtml.
Added KDSoft.BuyersConnection.Web\Views\Products\Delete.cshtml.
Added model/view mapping tests to ObjectMapperTests.
Added tests object creation functions to ObjectFactory.
Added model/view mapping to ObjectMapper.
Added DbSet<Product> member to Context.
So far so good, but then I realize that this approach assumes that the class Product does not exist yet and creates alle files necessary to implement it.
So now I am thinking about what is the best approach to have my POCOs in place and go from there using as much of scaffolding as possible.
Can I get some hints please?
thx and regards,
Darko
|
|
|
|
|
|
That was fast. Thank you!
Yeah, I figured out that is the other way around, I forgot module when writing my call for scaffolder.
It was
Scafold Module Product Products
So my question is: lets say I follow your approach and start implementing after scaffolding finishes.
What would be the steps in your opinion:
1. Add Properties with Validation and DB Attributes to the POCO
2. Create the privileges in Configuration.cs
3. Add migration and update the new Entities to the database
4. Create View classes
5. Implement Validation classes
6. Update sitemap and routing stuff
7. Update the cshtml views (was there a scaffolding for that?)
Am I missing something?
Never the less I am curious how the approach the other way around will go for you.
best regards!
|
|
|
|
|
Generally it's:
1) Add map call in ObjectMapper.cs
2) Add properties to model and view.
3) Add migrations to data and test projects.
4) Add privileges to Configuration.cs.
5) Add resources in Privileges - Action, Controller, Area (optional).
6) Add resources for ContentTitles and Headers.
7) Add resource to SiteMap.
8) Add resources for all views in Views folder.
9) Register your service and validator in MainContainer.cs.
10) Add sitemap to Mvc.sitemap
11) Make your UI in razor views.
12) Add privileges to initial data tests.
13) Add your service and validator tests in MainContainerTests.cs
Everything else is optional I believe. It may seem a lot but must of the time it's just translating resources. I'm making a working "module" with basic validations in like 30 minutes (practice makes perfect ).
modified 25-Mar-21 21:01pm.
|
|
|
|
|
Nr 1. has been scaffolded
private static void MapProducts()
{
Mapper.CreateMap<Product, ProductView>();
Mapper.CreateMap<ProductView, Product>();
}
|
|
|
|
|
Yes it's scaffolded but it's not "called"
modified 25-Mar-21 21:01pm.
|
|
|
|
|
|
Did you have time yet to look at the "other way around" scaffolding?
|
|
|
|
|
Yes I did, and it's not that easy to do, so it may take some time
And I'm still wondering about approach to this too, should I create new "module xxx" or leave the current one with "existing model check" ~~
modified 25-Mar-21 21:01pm.
|
|
|
|
|
How do I add migrations and update the test project Database?, I’ve executed the Add-Migration and Update-Database command in the Test project, the database was created and added the Migration too , of course the database is empty, but even after succeeded Migration, I’m still having 227 test failing because of Backing Model changed.
“The model backing the 'TestingContext' context has changed since the database was created. Consider using Code First Migrations to update the database”
Can you help me please, I will be happy to have all my test passed.
Second question.
There is more than tree failing test caused by bad date format of data picker, I supposed that the actual value will be retrieved from the translated string in script folder, but it look like it is being retrieved from the system date time format.
Help please, than you very much.
Expected: <input autocomplete="off" class="form-control datepicker" id="Relation_Date" name="Relation.Date" type="text" value="2011.01.01" />
Actual: <input autocomplete="off" class="form-control datepicker" id="Relation_Date" name="Relation.Date" type="text" value="2011-01-01" />
$.datepicker.regional['lt-LT'] = {
closeText: 'Uždaryti',
prevText: '<Atgal',
nextText: 'Pirmyn>',
currentText: 'Šiandien',
monthNames: ['Sausis', 'Vasaris', 'Kovas', 'Balandis', 'Gegužė', 'Birželis', 'Liepa', 'Rugpjūtis', 'Rugsėjis', 'Spalis', 'Lapkritis', 'Gruodis'],
monthNamesShort: ['Sau', 'Vas', 'Kov', 'Bal', 'Geg', 'Bir', 'Lie', 'Rugp', 'Rugs', 'Spa', 'Lap', 'Gru'],
dayNames: ['sekmadienis', 'pirmadienis', 'antradienis', 'trečiadienis', 'ketvirtadienis', 'penktadienis', 'šeštadienis'],
dayNamesShort: ['sek', 'pir', 'ant', 'tre', 'ket', 'pen', 'šeš'],
dayNamesMin: ['Se', 'Pr', 'An', 'Tr', 'Ke', 'Pe', 'Še'],
weekHeader: 'Sav',
dateFormat: 'yy.mm.dd',
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: ''
};
$.timepicker.regional['lt-LT'] = {
timeOnlyTitle: 'Pasirinkite laiką',
timeText: 'Laikas',
hourText: 'Valandos',
minuteText: 'Minutės',
secondText: 'Sekundės',
millisecText: 'Milisekundės',
microsecText: 'Mikrosekundės',
timezoneText: 'Laiko zona',
currentText: 'Dabar',
closeText: 'Uždaryti',
timeFormat: 'HH:mm',
amNames: ['priešpiet', 'AM', 'A'],
pmNames: ['popiet', 'PM', 'P'],
isRTL: false
};
$.datepicker.setDefaults($.datepicker.regional['lt-LT']);
$.timepicker.setDefaults($.timepicker.regional['lt-LT']);
/***********************************************************************
AM: null,
PM: null,
patterns: {
d: "yyyy.MM.dd",
dt: "yyyy.MM.dd HH:mm",
D: "yyyy 'm.' MMMM d 'd.'",
t: "HH:mm",
T: "HH:mm:ss",
f: "yyyy 'm.' MMMM d 'd.' HH:mm",
F: "yyyy 'm.' MMMM d 'd.' HH:mm:ss",
M: "MMMM d 'd.'",
Y: "yyyy 'm.' MMMM"
}
}
}
});
}( this ));
Globalize.culture("lt-LT");
AF Matambo
|
|
|
|
|
1) Just try deleting database file and it's connection in SQL explorer and rerun update-database on both Data and Test projects. Because there are a lot of stuff which can brake migrations.
2) Could you provide failing test names? I'm 99% sure that it's machine culture related.
modified 25-Mar-21 21:01pm.
|
|
|
|
|
Ok I'll do it but need time to collect all test names.
AF Matambo
|
|
|
|
|
I’m having problems when I add more languages in the globalization, when it starts the foreach loop after selecting the third element it is being returned null string in abbreviation attribute. The error is occurring in the GlobalizationProvider method when adding elements to the LanguageDictionary.
According to your reply on this issue in my previous question, I have everything in place, but I had a problem this time in updated application to 1.3.0 version of the template, if I comment all the additional languages, the application runs smoothly with the 2 languages.
Where did I am doing something wrong?
Thank you in advance.
="1.0"="utf-8"
<globalization>
<language name="English" culture="en-GB" abbreviation="en" default="true" />
<language name="Lietuvių" culture="lt-LT" abbreviation="lt" />
<language name="Español" culture="es-ES" abbrevation="es" />
<language name="Português" culture="pt-PT" abbrevation="pt" />
<language name="Français" culture="fr-FR" abbrevation="fr" />
<language name="Italiano" culture="it-IT" abbrevation="it" />
<language name="Deutsch" culture="de-DE" abbrevation="de" />
</globalization>
public void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes
.MapRoute(
"DefaultMultilingual",
"{language}/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new { language = "es|pt|fr|it|de|lt" },
new[] { "VumbaSoft.AdventureWorks.Controllers" })
.DataTokens["UseNamespaceFallback"] = false;
routes
.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { language = "en", controller = "Home", action = "Index", id = UrlParameter.Optional },
new { language = "en" },
new[] { "VumbaSoft.AdventureWorks.Controllers" })
.DataTokens["UseNamespaceFallback"] = false;
}
public override void RegisterArea(AreaRegistrationContext context)
{
context
.MapRoute(
"AdministrationMultilingual",
"{language}/Administration/{controller}/{action}/{id}",
new { area = "Administration", action = "Index", id = UrlParameter.Optional },
new { language = "es|pt|fr|it|de|lt" },
new[] { "VumbaSoft.AdventureWorks.Controllers.Administration" });
context
.MapRoute(
"Administration",
"Administration/{controller}/{action}/{id}",
new { language = "en", area = "Administration", action = "Index", id = UrlParameter.Optional },
new { language = "en" },
new[] { "VumbaSoft.AdventureWorks.Controllers.Administration" });
}
}
public GlobalizationProvider(String path)
{
XElement languagesXml = XElement.Load(path);
LanguageDictionary = new Dictionary<String, Language>();
foreach (XElement languageNode in languagesXml.Elements("language"))
{
Language language = new Language();
language.Culture = new CultureInfo((String)languageNode.Attribute("culture"));
language.IsDefault = (Boolean?)languageNode.Attribute("default") == true;
language.Abbreviation = (String)languageNode.Attribute("abbreviation");
language.Name = (String)languageNode.Attribute("name");
LanguageDictionary.Add(language.Abbreviation, language);
}
Languages = LanguageDictionary.Select(language => language.Value).ToArray();
DefaultLanguage = Languages.Single(language => language.IsDefault);
}
AF Matambo
|
|
|
|
|