Sitemap, Admin tweaks, MembershipProvider tweaks

This commit is contained in:
Thomas Hounsell 2014-10-31 19:28:16 +00:00
parent 40f04a1013
commit 48acb8d39c
16 changed files with 304 additions and 17 deletions

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace BuildFeed.Areas.admin.Controllers
{
[Authorize(Users = "hounsell")]
public class baseController : Controller
{
// GET: admin/base
public ActionResult index()
{
return View();
}
}
}

View File

@ -12,19 +12,19 @@ namespace BuildFeed.Areas.admin.Controllers
public class usersController : Controller public class usersController : Controller
{ {
// GET: admin/users // GET: admin/users
public ActionResult Index() public ActionResult index()
{ {
return View(Membership.GetAllUsers().Cast<MembershipUser>().OrderBy(m => m.IsApproved).ThenBy(m => m.UserName)); return View(Membership.GetAllUsers().Cast<MembershipUser>().OrderByDescending(m => m.IsApproved).ThenBy(m => m.UserName));
} }
public ActionResult Approve(Guid id) public ActionResult approve(Guid id)
{ {
var provider = (Membership.Provider as RedisMembershipProvider); var provider = (Membership.Provider as RedisMembershipProvider);
provider.ChangeApproval(id, true); provider.ChangeApproval(id, true);
return RedirectToAction("Index"); return RedirectToAction("Index");
} }
public ActionResult Unapprove(Guid id) public ActionResult unapprove(Guid id)
{ {
var provider = (Membership.Provider as RedisMembershipProvider); var provider = (Membership.Provider as RedisMembershipProvider);
provider.ChangeApproval(id, false); provider.ChangeApproval(id, false);

View File

@ -0,0 +1,11 @@

@{
ViewBag.Title = "Administration | BuildFeed";
}
<h2>Administration</h2>
<ul>
<li>@Html.ActionLink("Manage users", "index", "users")</li>
</ul>

View File

@ -6,7 +6,7 @@
<h2>User Administration</h2> <h2>User Administration</h2>
<table class="table table-striped table-bordered"> <table class="table table-striped table-bordered table-admin">
<tr> <tr>
<th> <th>
Username Username
@ -14,6 +14,9 @@
<th> <th>
Email Address Email Address
</th> </th>
<td>
Last Login Time
</td>
<th style="width:108px;"></th> <th style="width:108px;"></th>
</tr> </tr>
@ -26,6 +29,9 @@
<td> <td>
@Html.DisplayFor(modelItem => mu.Email) @Html.DisplayFor(modelItem => mu.Email)
</td> </td>
<td>
@Html.DisplayFor(modelItem => mu.LastLoginDate)
</td>
<td class="text-right"> <td class="text-right">
@if (mu.IsApproved) @if (mu.IsApproved)
{ {

View File

@ -15,9 +15,9 @@ public override string AreaName
public override void RegisterArea(AreaRegistrationContext context) public override void RegisterArea(AreaRegistrationContext context)
{ {
context.MapRoute( context.MapRoute(
"admin_default", "Admin (Default)",
"admin/{controller}/{action}/{id}", "admin/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional } new { action = "index", controller = "base", id = UrlParameter.Optional }
); );
} }
} }

View File

@ -355,6 +355,12 @@ public override bool ValidateUser(string username, string password)
isFail |= (hash[i] != rm.PassHash[i]); isFail |= (hash[i] != rm.PassHash[i]);
} }
if(!isFail)
{
rm.LastLoginDate = DateTime.Now;
client.Store(rm);
}
return !isFail; return !isFail;
} }
} }

View File

@ -145,6 +145,7 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Areas\admin\Controllers\baseController.cs" />
<Compile Include="Code\BuildDateTimeModelBinder.cs" /> <Compile Include="Code\BuildDateTimeModelBinder.cs" />
<Compile Include="Code\DisplayHelpers.cs" /> <Compile Include="Code\DisplayHelpers.cs" />
<Compile Include="App_Start\BundleConfig.cs" /> <Compile Include="App_Start\BundleConfig.cs" />
@ -165,6 +166,7 @@
<Compile Include="Models\ViewModel\LoginUser.cs" /> <Compile Include="Models\ViewModel\LoginUser.cs" />
<Compile Include="Models\ViewModel\ChangePassword.cs" /> <Compile Include="Models\ViewModel\ChangePassword.cs" />
<Compile Include="Models\ViewModel\RegistrationUser.cs" /> <Compile Include="Models\ViewModel\RegistrationUser.cs" />
<Compile Include="Models\ViewModel\SitemapData.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -177,6 +179,7 @@
<Content Include="Areas\admin\Views\_ViewStart.cshtml" /> <Content Include="Areas\admin\Views\_ViewStart.cshtml" />
<Content Include="content\tile\wide.png" /> <Content Include="content\tile\wide.png" />
<Content Include="content\Web.config" /> <Content Include="content\Web.config" />
<Content Include="Areas\admin\Views\base\index.cshtml" />
<None Include="Properties\PublishProfiles\Milestone 1 FTP.pubxml" /> <None Include="Properties\PublishProfiles\Milestone 1 FTP.pubxml" />
<None Include="Scripts\jquery-2.1.1.intellisense.js" /> <None Include="Scripts\jquery-2.1.1.intellisense.js" />
<Content Include="googleacffc6da14c53e15.html" /> <Content Include="googleacffc6da14c53e15.html" />
@ -215,6 +218,7 @@
<Content Include="Views\support\password.cshtml" /> <Content Include="Views\support\password.cshtml" />
<Content Include="Views\support\thanks_register.cshtml" /> <Content Include="Views\support\thanks_register.cshtml" />
<Content Include="Views\support\rss.cshtml" /> <Content Include="Views\support\rss.cshtml" />
<Content Include="Views\support\sitemap.cshtml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="App_Data\" /> <Folder Include="App_Data\" />

View File

@ -10,7 +10,7 @@ namespace BuildFeed.Controllers
{ {
public class buildController : Controller public class buildController : Controller
{ {
public int pageSize { get { return 15; } } public static int pageSize { get { return 15; } }
// //
// GET: /build/ // GET: /build/
@ -103,6 +103,7 @@ public ActionResult create(Build build)
try try
{ {
build.Added = DateTime.Now; build.Added = DateTime.Now;
build.Modified = DateTime.Now;
Build.Insert(build); Build.Insert(build);
} }
catch catch

View File

@ -85,12 +85,7 @@ public async Task<ActionResult> added()
public async Task<ActionResult> version() public async Task<ActionResult> version()
{ {
var builds = Build.Select() var builds = Build.SelectInVersionOrder()
.OrderByDescending(b => b.MajorVersion)
.ThenByDescending(b => b.MinorVersion)
.ThenByDescending(b => b.Number)
.ThenByDescending(b => b.Revision)
.ThenByDescending(b => b.BuildTime)
.Take(20); .Take(20);

View File

@ -3,7 +3,9 @@
using System.Linq; using System.Linq;
using System.Web; using System.Web;
using System.Web.Mvc; using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Security; using System.Web.Security;
using BuildFeed.Models;
using BuildFeed.Models.ViewModel; using BuildFeed.Models.ViewModel;
namespace BuildFeed.Controllers namespace BuildFeed.Controllers
@ -116,5 +118,103 @@ public ActionResult rss()
{ {
return View(); return View();
} }
public ActionResult sitemap()
{
IEnumerable<Build> builds = Build.SelectInVersionOrder();
Dictionary<string, SitemapPagedAction[]> actions = new Dictionary<string, SitemapPagedAction[]>();
actions.Add("Pages", new SitemapPagedAction[] { new SitemapPagedAction()
{
UrlParams = new RouteValueDictionary(new {
controller = "build",
action = "index",
page = 1
}),
Pages = (builds.Count() + (buildController.pageSize - 1)) / buildController.pageSize
} });
actions.Add("Versions", (from b in builds
group b by new BuildVersion() { Major = b.MajorVersion, Minor = b.MinorVersion } into bv
orderby bv.Key.Major descending,
bv.Key.Minor descending
select new SitemapPagedAction()
{
Name = string.Format("Windows NT {0}.{1}", bv.Key.Major, bv.Key.Minor),
UrlParams = new RouteValueDictionary(new
{
controller = "build",
action = "version",
major = bv.Key.Major,
minor = bv.Key.Minor,
page = 1
}),
Pages = (bv.Count() + (buildController.pageSize - 1)) / buildController.pageSize
}).ToArray());
actions.Add("Labs", (from b in builds
where !string.IsNullOrEmpty(b.Lab)
group b by b.Lab into bv
orderby bv.Key
select new SitemapPagedAction()
{
Name = bv.Key,
UrlParams = new RouteValueDictionary(new
{
controller = "build",
action = "version",
lab = bv.Key,
page = 1
}),
Pages = (bv.Count() + (buildController.pageSize - 1)) / buildController.pageSize
}).ToArray());
actions.Add("Years", (from b in builds
where b.BuildTime.HasValue
group b by b.BuildTime.Value.Year into bv
orderby bv.Key
select new SitemapPagedAction()
{
Name = bv.Key.ToString(),
UrlParams = new RouteValueDictionary(new
{
controller = "build",
action = "year",
year = bv.Key,
page = 1
}),
Pages = (bv.Count() + (buildController.pageSize - 1)) / buildController.pageSize
}).ToArray());
actions.Add("Sources", (from b in builds
group b by b.SourceType into bv
orderby bv.Key
select new SitemapPagedAction()
{
Name = DisplayHelpers.GetDisplayTextForEnum(bv.Key),
UrlParams = new RouteValueDictionary(new
{
controller = "build",
action = "version",
source = bv.Key,
page = 1
}),
Pages = (bv.Count() + (buildController.pageSize - 1)) / buildController.pageSize
}).ToArray());
SitemapData model = new SitemapData()
{
Builds = (from b in builds
select new SitemapDataBuild()
{
Id = b.Id,
Name = b.FullBuildString
}).ToArray(),
Actions = actions
};
return View(model);
}
} }
} }

View File

@ -45,9 +45,13 @@ public class Build : IHasId<long>
[@Required] [@Required]
[DisplayName("Time Added")] [DisplayName("Time Created")]
public DateTime Added { get; set; } public DateTime Added { get; set; }
[@Required]
[DisplayName("Time Modified")]
public DateTime Modified { get; set; }
[@Required] [@Required]
[DisplayName("Source Type")] [DisplayName("Source Type")]
[EnumDataType(typeof(TypeOfSource))] [EnumDataType(typeof(TypeOfSource))]
@ -85,7 +89,7 @@ public bool IsLeaked
{ {
get get
{ {
switch(SourceType) switch (SourceType)
{ {
case TypeOfSource.PublicRelease: case TypeOfSource.PublicRelease:
case TypeOfSource.InternalLeak: case TypeOfSource.InternalLeak:
@ -158,6 +162,21 @@ public static IEnumerable<Build> SelectInBuildOrder()
} }
} }
[DataObjectMethod(DataObjectMethodType.Select, false)]
public static IEnumerable<Build> SelectInVersionOrder()
{
using (RedisClient rClient = new RedisClient(DatabaseConfig.Host, DatabaseConfig.Port, db: DatabaseConfig.Database))
{
var client = rClient.As<Build>();
return client.GetAll()
.OrderByDescending(b => b.MajorVersion)
.ThenByDescending(b => b.MinorVersion)
.ThenByDescending(b => b.Number)
.ThenByDescending(b => b.Revision)
.ThenByDescending(b => b.BuildTime);
}
}
[DataObjectMethod(DataObjectMethodType.Select, false)] [DataObjectMethod(DataObjectMethodType.Select, false)]
public static IEnumerable<BuildVersion> SelectBuildVersions() public static IEnumerable<BuildVersion> SelectBuildVersions()
{ {
@ -235,6 +254,7 @@ public static void Update(Build item)
{ {
Build old = Build.SelectById(item.Id); Build old = Build.SelectById(item.Id);
item.Added = old.Added; item.Added = old.Added;
item.Modified = DateTime.Now;
using (RedisClient rClient = new RedisClient(DatabaseConfig.Host, DatabaseConfig.Port, db: DatabaseConfig.Database)) using (RedisClient rClient = new RedisClient(DatabaseConfig.Host, DatabaseConfig.Port, db: DatabaseConfig.Database))
{ {

View File

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Routing;
namespace BuildFeed.Models.ViewModel
{
public class SitemapData
{
public SitemapDataBuild[] Builds { get; set; }
public Dictionary<string, SitemapPagedAction[]> Actions { get; set; }
}
public class SitemapDataBuild
{
public long Id { get; set; }
public string Name { get; set; }
}
public class SitemapPagedAction
{
public string Name { get; set; }
public RouteValueDictionary UrlParams { get; set; }
public int Pages { get; set; }
public string Action
{
get { return UrlParams["action"].ToString(); }
}
public string UniqueId
{
get { return UrlParams.GetHashCode().ToString("X8").ToLower(); }
}
}
}

View File

@ -214,6 +214,11 @@
<ul class="nav nav-pills nav-stacked"> <ul class="nav nav-pills nav-stacked">
@if (User.Identity.IsAuthenticated) @if (User.Identity.IsAuthenticated)
{ {
if (User.Identity.Name == "hounsell")
{
<li>@Html.ActionLink("Administration", "index", new { controller = "base", area = "admin" })</li>
<li>&nbsp;</li>
}
<li>@Html.ActionLink("Add a build", "create", "build")</li> <li>@Html.ActionLink("Add a build", "create", "build")</li>
<li>&nbsp;</li> <li>&nbsp;</li>
<li>@Html.ActionLink("Change your password", "password", "support")</li> <li>@Html.ActionLink("Change your password", "password", "support")</li>

View File

@ -52,7 +52,12 @@
</article> </article>
<footer id="page-footer"> <footer id="page-footer">
<div class="row"> <div class="row">
<div class="col-sm-4 col-sm-offset-8 text-right"> <div class="col-sm-8">
<p>
<a href="@Url.Action("sitemap", new { controller = "support" })">Sitemap</a>
</p>
</div>
<div class="col-sm-4 text-right">
<p> <p>
&copy; 2013 - 2014, BuildFeed<br /> &copy; 2013 - 2014, BuildFeed<br />
Developed by <a href="https://twitter.com/tomhounsell" target="_blank">Thomas Hounsell</a> Developed by <a href="https://twitter.com/tomhounsell" target="_blank">Thomas Hounsell</a>

View File

@ -0,0 +1,73 @@
@model BuildFeed.Models.ViewModel.SitemapData
@{
ViewBag.Title = "Sitemap | BuildFeed";
}
<h2>Sitemap</h2>
<ul>
<li>
@Html.ActionLink("BuildFeed", "index", "build")
<ul>
<li>
<a href="#sitemap-builds" data-toggle="collapse">Builds</a>
<ul id="sitemap-builds" class="collapse">
@foreach (var build in Model.Builds)
{
<li>@Html.ActionLink(build.Name, "info", new { controller = "build", id = build.Id })</li>
}
</ul>
</li>
@foreach (var item in Model.Actions)
{
<li>
<a href="#sitemap-@item.Key.ToLower()" data-toggle="collapse">@item.Key</a>
<ul id="sitemap-@item.Key.ToLower()" class="collapse">
@foreach (var action in item.Value)
{
if (string.IsNullOrEmpty(action.Name))
{
for (int i = 1; i <= action.Pages; i++)
{
var urlParams = action.UrlParams;
urlParams["page"] = i;
<li>@Html.ActionLink("Page " + i.ToString(), action.Action, urlParams)</li>
}
}
else
{
<li>
<a href="#sitemap-@action.UniqueId" data-toggle="collapse">@action.Name</a>
<ul id="sitemap-@action.UniqueId" class="collapse">
@for (int i = 1; i <= action.Pages; i++)
{
var urlParams = action.UrlParams;
urlParams["page"] = i;
<li>@Html.ActionLink("Page " + i.ToString(), action.Action, urlParams)</li>
}
</ul>
</li>
}
}
</ul>
</li>
}
<li>
<a href="#sitemap-rss" data-toggle="collapse">RSS Feeds</a>
<ul id="sitemap-rss" class="collapse">
<li><a href="@Url.Action("index", new { controller = "rss" })" title="Recently compiled"><i class="fa fa-sm fa-rss"></i> Recently compiled</a></li>
<li><a href="@Url.Action("added", new { controller = "rss" })" title="Recently added to BuildFeed"><i class="fa fa-sm fa-rss"></i> Recently added to BuildFeed</a></li>
<li><a href="@Url.Action("version", new { controller = "rss" })" title="Highest version"><i class="fa fa-sm fa-rss"></i> Highest version</a></li>
<li>
Flight levels
<ul>
<li><a href="@Url.Action("flight", new { controller = "rss", id = "low" })" title="Low flight level"><i class="fa fa-sm fa-rss"></i> Low flight level</a></li>
<li><a href="@Url.Action("flight", new { controller = "rss", id = "medium" })" title="Medium flight level"><i class="fa fa-sm fa-rss"></i> Medium flight level</a></li>
<li><a href="@Url.Action("flight", new { controller = "rss", id = "high" })" title="High flight level"><i class="fa fa-sm fa-rss"></i> High flight level</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>

View File

@ -97,3 +97,9 @@ label, .control-label, .help-block, .checkbox, .radio {
.table .btn { .table .btn {
padding: 4px 9px; padding: 4px 9px;
} }
.table-admin > tbody > tr > th,
.table-admin > tbody > tr > td
{
vertical-align: middle;
}