Starting to test some controller

* BlogUnitTest.cs: Should test the user registration

* NpgsqlBlogProvider.cs: Fixes usage of Npgsql upgrade to latest
  version

* TestAPI.csproj: switch to .Net framework 4.5.1

* AccountController.cs: refactoring password validation

* CalendarController.cs:
* WorkFlowController.cs: SendActivationMessage became an extension
  method

* style.css: menu items already have a background and color, since
  they're `<A>` tags

* Unregister.aspx:
* AccountController.cs: refactoring user registration

* BlogsController.cs: Fixes a confusion between Author and reader ...

* YavscHelpers.cs: refactoring the password reset

* App.master: no more <div class="menuitem">, they're hyperlinks

* Login.aspx:
* Profile.aspx: refactoring the user registration

* BlogEntryCollection.cs: implements a method to filter a given post
  collection in order to be displayed tu a given user or anonymous
vnext
Paul Schneider 9 years ago
parent 8a2113a69a
commit 303aaa5e1b
19 changed files with 218 additions and 65 deletions

@ -1,3 +1,8 @@
2015-10-07 Paul Schneider <paul@pschneider.fr>
* NpgsqlBlogProvider.cs: Fixes usage of Npgsql upgrade to
latest version
2015-10-04 Paul Schneider <paul@pschneider.fr>
* packages.config:

@ -407,7 +407,7 @@ namespace Npgsql.Web.Blog
if (circles.Length>0)
using (NpgsqlCommand cmd = cnx.CreateCommand ()) {
cmd.CommandText = "insert into blog_access (post_id,circle_id) values (:pid,:cid)";
cmd.Parameters.AddWithValue ("pid", pid);
cmd.Parameters.AddWithValue ("pid", NpgsqlTypes.NpgsqlDbType.Bigint, pid);
cmd.Parameters.Add ("cid", NpgsqlTypes.NpgsqlDbType.Bigint);
cmd.Prepare ();
foreach (long ci in circles) {

@ -0,0 +1,76 @@
//
// BlogUnitTest.cs
//
// Author:
// Paul Schneider <paul@pschneider.fr>
//
// Copyright (c) 2015 GNU GPL
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using NUnit.Framework;
using System;
using Yavsc.Model.Blogs;
using Yavsc.Controllers;
using System.Web.Mvc;
using System.Web.Security;
namespace TestAPI
{
[TestFixture ()]
public class BlogUnitTest
{
public string UserName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
AccountController accountController;
[TestFixtureSetUp]
public void Init()
{
accountController = new AccountController ();
}
[Test ()]
public void Register ()
{
ViewResult actionResult = accountController.Register (
new Yavsc.Model.RolesAndMembers.RegisterViewModel () {
UserName = UserName, Email = Email,
Password = "tpwd", ConfirmPassword = Password,
IsApprouved = true
},
"/testreturnurl") as ViewResult;
Assert.AreEqual (actionResult.ViewName, "RegistrationPending");
MembershipUser u = Membership.GetUser (UserName, false);
Assert.NotNull (u);
Assert.False (u.IsApproved);
// TODO : check mail for test,
// get the validation key from its body,
// and use the accountController.Validate(username,key)
u.IsApproved = true;
Membership.UpdateUser (u);
Assert.True (u.IsApproved);
}
[TestFixtureTearDown()]
public void Unregister()
{
ViewResult actionResult =
accountController.Unregister (UserName, true) as ViewResult;
Assert.AreEqual (actionResult.ViewName, "Index");
}
}
}

@ -1,3 +1,9 @@
2015-10-07 Paul Schneider <paul@pschneider.fr>
* BlogUnitTest.cs: Should test the user registration
* TestAPI.csproj: switch to .Net framework 4.5.1
2015-10-04 Paul Schneider <paul@pschneider.fr>
* TestAPI.csproj:

@ -9,7 +9,7 @@
<OutputType>Library</OutputType>
<RootNamespace>TestAPI</RootNamespace>
<AssemblyName>TestAPI</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -55,10 +55,14 @@
<Reference Include="Spark">
<HintPath>..\packages\Spark.1.8.1.0\lib\NET45\Spark.dll</HintPath>
</Reference>
<Reference Include="System.Web.Mvc" />
<Reference Include="System.Web.ApplicationServices" />
<Reference Include="System.Web" />
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TestAutomate.cs" />
<Compile Include="BlogUnitTest.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
@ -66,6 +70,10 @@
<Project>{68F5B80A-616E-4C3C-91A0-828AA40000BD}</Project>
<Name>YavscModel</Name>
</ProjectReference>
<ProjectReference Include="..\web\Web.csproj">
<Project>{77044C92-D2F1-45BD-80DD-AA25B311B027}</Project>
<Name>Web</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

@ -72,7 +72,7 @@ namespace Yavsc.ApiControllers
break;
case MembershipCreateStatus.Success:
if (!model.IsApprouved)
YavscHelpers.SendActivationMessage (user);
Url.SendActivationMessage (user);
ProfileBase prtu = ProfileBase.Create (model.UserName);
prtu.SetPropertyValue("Name",model.Name);
prtu.SetPropertyValue("Address",model.Address);
@ -99,9 +99,12 @@ namespace Yavsc.ApiControllers
public void ResetPassword(LostPasswordModel model)
{
StringDictionary errors;
YavscHelpers.ResetPassword (model, out errors);
MembershipUser user;
YavscHelpers.ValidatePasswordReset (model, out errors, out user);
foreach (string key in errors.Keys)
ModelState.AddModelError (key, errors [key]);
if (user != null && ModelState.IsValid)
Url.SendActivationMessage (user);
}
}
}

@ -27,6 +27,7 @@ using Yavsc.Helpers;
using System.Web.Profile;
using Yavsc.Model.Circles;
using Yavsc.Model.Calendar;
using System.Web.Http.Routing;
namespace Yavsc.ApiControllers
@ -160,7 +161,7 @@ namespace Yavsc.ApiControllers
"déjà enregistré");
break;
case MembershipCreateStatus.Success:
YavscHelpers.SendActivationMessage (user);
Url.SendActivationMessage (user);
// TODO set registration id
throw new NotImplementedException ();
}

@ -85,7 +85,8 @@ namespace Yavsc.ApiControllers
return ;
case MembershipCreateStatus.Success:
if (!userModel.IsApprouved)
YavscHelpers.SendActivationMessage (user);
Url.SendActivationMessage (user);
return;
default:
throw new InvalidOperationException (string.Format("Unexpected user creation code :{0}",mcs));

@ -235,7 +235,6 @@ input, select {
.menuitem {
background-color: rgba(0,0,40,.8);
border-radius:5px;
margin:.5em;
padding:.5em;

@ -1,3 +1,28 @@
2015-10-07 Paul Schneider <paul@pschneider.fr>
* AccountController.cs: refactoring password validation
* CalendarController.cs:
* WorkFlowController.cs: SendActivationMessage became an
extension method
* style.css: menu items already have a background and color,
since they're `<A>` tags
* Unregister.aspx:
* AccountController.cs: refactoring user registration
* BlogsController.cs: Fixes a confusion between Author and
reader ...
* YavscHelpers.cs: refactoring the password reset
* App.master: no more <div class="menuitem">, they're
hyperlinks
* Login.aspx:
* Profile.aspx: refactoring the user registration
2015-10-06 Paul Schneider <paul@pschneider.fr>
* style.css: reactive margins

@ -126,11 +126,17 @@ namespace Yavsc.Controllers
return View ();
}
public ActionResult RegisterForm()
{
return View ("Register");
}
/// <summary>
/// Register the specified model and returnUrl.
/// </summary>
/// <param name="model">Model.</param>
/// <param name="returnUrl">Return URL.</param>
[HttpPost]
public ActionResult Register (RegisterViewModel model, string returnUrl)
{
ViewData ["returnUrl"] = returnUrl;
@ -138,7 +144,8 @@ namespace Yavsc.Controllers
foreach (string k in ModelState.Keys)
ModelState [k].Errors.Clear ();
return View (model);
}
}
if (ModelState.IsValid) {
if (model.ConfirmPassword != model.Password) {
ModelState.AddModelError ("ConfirmPassword", "Veuillez confirmer votre mot de passe");
@ -164,7 +171,7 @@ namespace Yavsc.Controllers
"déjà enregistré");
return View (model);
case MembershipCreateStatus.Success:
YavscHelpers.SendActivationMessage (user);
Url.SendActivationMessage (user);
ViewData ["username"] = user.UserName;
ViewData ["email"] = user.Email;
return View ("RegistrationPending");
@ -203,14 +210,20 @@ namespace Yavsc.Controllers
}
/// <summary>
/// Unregister the specified confirmed.
/// Unregister the specified id and confirmed.
/// </summary>
/// <param name="id">Identifier.</param>
/// <param name="confirmed">If set to <c>true</c> confirmed.</param>
[Authorize]
public ActionResult Unregister (bool confirmed = false)
public ActionResult Unregister (string id, bool confirmed = false)
{
ViewData ["UserName"] = id;
if (!confirmed)
return View ();
string logged = Membership.GetUser ().UserName;
if (logged != id)
if (!Roles.IsUserInRole ("Admin"))
throw new Exception ("Unregister another user");
Membership.DeleteUser (
Membership.GetUser ().UserName);
@ -381,9 +394,13 @@ namespace Yavsc.Controllers
{
if (Request.HttpMethod == "POST") {
StringDictionary errors;
YavscHelpers.ResetPassword (model, out errors);
MembershipUser user;
YavscHelpers.ValidatePasswordReset (model, out errors, out user);
foreach (string key in errors.Keys)
ModelState.AddModelError (key, errors [key]);
if (user != null && ModelState.IsValid)
Url.SendActivationMessage (user);
}
return View (model);
}

@ -185,8 +185,8 @@ namespace Yavsc.Controllers
ViewData ["Avatar"] = pr.avatar;
ViewData ["BlogTitle"] = pr.BlogTitle;
MembershipUser u = Membership.GetUser ();
if (u != null)
ViewData ["Author"] = u.UserName;
ViewData ["Author"] = bec.Author;
if (!pr.BlogVisible) {
// only deliver to admins or owner
if (u == null)
@ -197,6 +197,13 @@ namespace Yavsc.Controllers
return View ("NotAuthorized");
}
}
if (u == null || (u.UserName != bec.Author) && !Roles.IsUserInRole (u.UserName, "Admin")) {
// Filer on allowed posts
BlogEntryCollection filtered = bec.FilterFor((u == null)?null : u.UserName);
UUTBlogEntryCollection nbec = new UUTBlogEntryCollection (bec.Author, bec.Title);
nbec.AddRange (filtered);
View ("UserPost",nbec);
}
}
return View ("UserPost",bec);
}

@ -5,11 +5,9 @@ using System.Web.Security;
using System.IO;
using System.Web.Configuration;
using System.Net.Mail;
using System.Web.Http.ModelBinding;
using Yavsc.Model.RolesAndMembers;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Web.Mvc;
using Yavsc.Model.Circles;
using System.Web.UI;
using System.Linq.Expressions;
@ -22,31 +20,6 @@ namespace Yavsc.Helpers
/// </summary>
public static class YavscHelpers
{
/// <summary>
/// Formats the circle.
/// </summary>
/// <returns>The circle.</returns>
/// <param name="c">C.</param>
public static HtmlString FormatCircle (Circle c)
{
if (c.Members!=null)
if (c.Members.Length > 0) {
TagBuilder i = new TagBuilder ("i");
i.SetInnerText (String.Join (", ", c.Members));
return new HtmlString (c.Title+"<br/>\n"+i.ToString());
}
return new HtmlString (c.Title);
}
/// <summary>
/// Formats the circle.
/// </summary>
/// <returns>The circle as Html string.</returns>
/// <param name="helper">Helper.</param>
/// <param name="c">C.</param>
public static HtmlString FormatCircle(this HtmlHelper helper, Circle c)
{
return FormatCircle (c);
}
private static string siteName = null;
/// <summary>
@ -76,19 +49,33 @@ namespace Yavsc.Helpers
/// <summary>
/// Sends the activation message.
/// </summary>
/// <param name="helper">Helper.</param>
/// <param name="user">User.</param>
public static void SendActivationMessage(MembershipUser user)
public static void SendActivationMessage(this System.Web.Http.Routing.UrlHelper helper, MembershipUser user)
{
SendEmail (WebConfigurationManager.AppSettings ["RegistrationMessage"],
SendActivationMessage (helper.Route("~/Account/Validate/",new { id=user.UserName, key=user.ProviderUserKey.ToString() } )
, WebConfigurationManager.AppSettings ["RegistrationMessage"],
user);
}
/// <summary>
/// Sends the activation message.
/// </summary>
/// <param name="helper">Helper.</param>
/// <param name="user">User.</param>
public static void SendActivationMessage(this System.Web.Mvc.UrlHelper helper, MembershipUser user)
{
SendActivationMessage (helper.Content("~/Account/Validate/"+user.UserName+"/?key="+user.ProviderUserKey.ToString())
, WebConfigurationManager.AppSettings ["RegistrationMessage"],
user);
}
/// <summary>
/// Sends the email.
/// Sends the activation message.
/// </summary>
/// <param name="validationUrl">Validation URL.</param>
/// <param name="registrationMessage">Registration message.</param>
/// <param name="user">User.</param>
public static void SendEmail(string registrationMessage, MembershipUser user) {
public static void SendActivationMessage(string validationUrl, string registrationMessage, MembershipUser user) {
FileInfo fi = new FileInfo (
HttpContext.Current.Server.MapPath (registrationMessage));
if (!fi.Exists) {
@ -103,12 +90,8 @@ namespace Yavsc.Helpers
string body = sr.ReadToEnd ();
body = body.Replace ("<%SiteName%>", YavscHelpers.SiteName);
body = body.Replace ("<%UserName%>", user.UserName);
body = body.Replace ("<%UserActivatonUrl%>",
string.Format ("<{0}://{1}/Account/Validate/{2}?key={3}>",
HttpContext.Current.Request.Url.Scheme,
HttpContext.Current.Request.Url.Authority,
user.UserName,
user.ProviderUserKey.ToString ()));
body = body.Replace ("<%UserActivatonUrl%>", validationUrl);
using (MailMessage msg = new MailMessage (
Admail, user.Email,
string.Format ("Validation de votre compte {0}", YavscHelpers.SiteName),
@ -122,15 +105,16 @@ namespace Yavsc.Helpers
}
/// <summary>
/// Resets the password.
/// Validates the password reset.
/// </summary>
/// <param name="model">Model.</param>
/// <param name="errors">Errors.</param>
public static void ResetPassword(LostPasswordModel model, out StringDictionary errors)
/// <param name="user">User.</param>
public static void ValidatePasswordReset(LostPasswordModel model, out StringDictionary errors, out MembershipUser user)
{
MembershipUserCollection users = null;
errors = new StringDictionary ();
user = null;
if (!string.IsNullOrEmpty (model.UserName)) {
users =
Membership.FindUsersByName (model.UserName);
@ -158,10 +142,7 @@ namespace Yavsc.Helpers
if (users==null)
return;
// Assert users.Count == 1
if (users.Count != 1)
throw new InvalidProgramException ("Emails and user's names are uniques, and we find more than one result here, aborting.");
foreach (MembershipUser u in users)
YavscHelpers.SendActivationMessage (u);
foreach (MembershipUser u in users) user = u;
}
/// <summary>
/// Avatars the URL.
@ -169,7 +150,7 @@ namespace Yavsc.Helpers
/// <returns>The URL.</returns>
/// <param name="helper">Helper.</param>
/// <param name="username">Username.</param>
public static string AvatarUrl (this HtmlHelper helper, string username) {
public static string AvatarUrl (this System.Web.WebPages.Html.HtmlHelper helper, string username) {
ProfileBase pr = ProfileBase.Create (username);
var a = pr.GetPropertyValue("Avatar") ;
if (a == null || a is DBNull) return "/avatars/" + helper.Encode(username)+".png";

@ -103,11 +103,9 @@ $(document).ready(function(){
</div>
</a>
<% } else { %>
<a href="/Blog/<%= HttpContext.Current.User.Identity.Name%>" accesskey = "B" >
<div class="menuitem">
<a href="/Blog/<%= HttpContext.Current.User.Identity.Name%>" accesskey = "B" class="menuitem">
<img src="<%=Html.AvatarUrl(HttpContext.Current.User.Identity.Name)%>" alt="vos billets" class="iconsmall" />
<div class="hint">Vos billets</div>
</div>
</a>
<a href="<%= Url.Content("~/Account/Profile/" + HttpContext.Current.User.Identity.Name) %>" accesskey="L" class="menuitem">
<i class="fa fa-user"></i> <%= HttpContext.Current.User.Identity.Name %>

@ -20,7 +20,7 @@
<input type="submit"/>
<% } %></div>
<div class="panel">
<%= Html.ActionLink("S'enregistrer","Register",new {returnUrl=ViewData["returnUrl"]}, new { @class="actionlink" }) %>
<%= Html.ActionLink("S'enregistrer","RegisterForm",new {returnUrl=ViewData["returnUrl"]}, new { @class="actionlink" }) %>
</div>
<div class="panel">
<a href="<%=Request.Url.Scheme + "://" + Request.Url.Authority + "/Google/Login"%>?returnUrl=<%=ViewData["returnUrl"]==null?Request.Url.PathAndQuery:(string)ViewData["returnUrl"]%>" class="actionlink">

@ -112,7 +112,7 @@ Avatar : <img class="avatar" src="<%=Model.avatar%>?version=<%=Html.Encode(DateT
<% } %>
<aside>
<%= Html.ActionLink("Changer de mot de passe","ChangePassword", "Account",null, new { @class="actionlink" })%>
<%= Html.ActionLink("Désincription","Unregister", "Account",null, new { @class="actionlink" })%>
<%= Html.ActionLink("Désincription","Unregister", "Account", new { id=ViewData["UserName"] } , new { @class="actionlink" })%>
</aside>
<aside>
<% if (Roles.IsUserInRole((string)ViewData ["UserName"],"Admin")) { %>

@ -7,5 +7,6 @@ Warning: This will delete all of your data here, your profile, your posts and ot
<label for="confirmed">Unregister</label>
<%=Html.CheckBox("confirmed")%>
<input type="submit"/>
<%= Html.Hidden("UserName") %>
<% } %>
</asp:Content>

@ -3,6 +3,7 @@ using System.Configuration;
using System.Collections.Generic;
using Yavsc.Model.Blogs;
using System.Linq;
using Yavsc.Model.Circles;
namespace Yavsc.Model.Blogs
{
@ -56,7 +57,25 @@ namespace Yavsc.Model.Blogs
{
return this.Where (x => x.Title == title).ToArray ();
}
/// <summary>
/// Filters the current collection for a given user by its name.
/// Assumes that this user is not an author of any of these posts.
/// </summary>
/// <param name="username">Username.</param>
public BlogEntryCollection FilterFor(string username)
{
BlogEntryCollection res = new BlogEntryCollection ();
foreach (BlogEntry be in this) {
if (be.Visible &&
(be.AllowedCircles == null ||
(username!= null && CircleManager.DefaultProvider.Matches
(be.AllowedCircles,username))))
{
res.Add(be);
}
}
return res;
}
/// <summary>
/// Base post info.
/// </summary>

@ -1,3 +1,9 @@
2015-10-07 Paul Schneider <paul@pschneider.fr>
* BlogEntryCollection.cs: implements a method to filter a
given post collection in order to be displayed tu a given user
or anonymous
2015-10-04 Paul Schneider <paul@pschneider.fr>
* MarkdownHelper.cs:

Loading…