about, contact, error views

This commit is contained in:
tmont 2011-02-11 05:21:31 +00:00
parent 8153d05b1d
commit 4f98f46e1e
20 changed files with 590 additions and 27 deletions

@ -1,9 +1,86 @@
using System.Web.Mvc; using System;
using System.Net.Mail;
using System.Security.Cryptography;
using System.Text;
using System.Web.Mvc;
using VideoGameQuotes.Web.Models;
namespace VideoGameQuotes.Web.Controllers { namespace VideoGameQuotes.Web.Controllers {
public class HomeController : Controller { public class HomeController : Controller {
private static readonly string[] answers = new[] {
"I AM ERROR.",
"shyron",
"our princess is in another castle",
"the cake is a lie",
"all your base",
"ganon not gannon",
"thunderbird",
"'glad you came, pit!",
"ryu huyabasa"
};
public ActionResult Index() { public ActionResult Index() {
return View(); return View();
} }
public ActionResult About() {
return View();
}
public ActionResult Contact() {
var randomAnswer = GetRandomAnswer();
var model = new ContactModel {
UnhashedCaptchaAnswer = randomAnswer,
HashedCaptchaAnswer = GetHashedCaptcha(randomAnswer)
};
return View(model);
}
private static string GetRandomAnswer() {
return answers[new Random().Next(answers.Length)];
}
private static string GetHashedCaptcha(string value) {
return Convert.ToBase64String(MD5.Create().ComputeHash(Encoding.ASCII.GetBytes(value ?? string.Empty)));
}
private static void ResetModel(ContactModel model) {
model.UnhashedCaptchaAnswer = GetRandomAnswer();
model.HashedCaptchaAnswer = GetHashedCaptcha(model.UnhashedCaptchaAnswer);
model.CaptchaAnswer = null;
}
[HttpPost]
public ActionResult Contact(ContactModel model) {
if (GetHashedCaptcha(model.CaptchaAnswer) != model.HashedCaptchaAnswer) {
ModelState.AddModelError("CaptchaAnswer", "You are not human");
}
if (!ModelState.IsValid) {
ResetModel(model);
return View(model);
}
//send email
var fromAddress = new MailAddress("contact@tommymontgomery.com", "Contact Bot");
var subject = string.Format("[tommymontgomery.com] Message from {0}", model.Name);
var client = new SmtpClient {
Host = "localhost"
};
var message = new MailMessage(fromAddress, new MailAddress("tmont@tmont.com")) { Subject = subject, Body = model.Message };
message.ReplyToList.Add(new MailAddress(model.Email, model.Name));
try {
client.Send(message);
} catch (Exception e) {
ModelState.AddModelError("client", e.Message);
ResetModel(model);
return View(model);
}
return View("ContactSuccess");
}
} }
} }

@ -27,11 +27,9 @@ namespace VideoGameQuotes.Web {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("media/{*anything}"); routes.IgnoreRoute("media/{*anything}");
routes.MapRoute( routes.MapRoute("about", "about", new { controller = "Home", action = "About" });
"Default", routes.MapRoute("contact", "contact", new { controller = "Home", action = "Contact" });
"{controller}/{action}/{id}", routes.MapRoute("Default", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional });
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
} }
} }
} }

@ -0,0 +1,19 @@
using System.ComponentModel.DataAnnotations;
using Portoa.Validation.DataAnnotations;
namespace VideoGameQuotes.Web.Models {
public class ContactModel {
[Required]
public string Name { get; set; }
[Email]
public string Email { get; set; }
[Required]
public string Message { get; set; }
[Required]
public string CaptchaAnswer { get; set; }
[Required]
public string HashedCaptchaAnswer { get; set; }
public string UnhashedCaptchaAnswer { get; set; }
}
}

@ -86,6 +86,7 @@
<DependentUpon>Default.aspx</DependentUpon> <DependentUpon>Default.aspx</DependentUpon>
<SubType>ASPXCodeBehind</SubType> <SubType>ASPXCodeBehind</SubType>
</Compile> </Compile>
<Compile Include="Models\ContactModel.cs" />
<Compile Include="Security\SessionBasedUserProvider.cs" /> <Compile Include="Security\SessionBasedUserProvider.cs" />
<Compile Include="Global.asax.cs"> <Compile Include="Global.asax.cs">
<DependentUpon>Global.asax</DependentUpon> <DependentUpon>Global.asax</DependentUpon>
@ -95,7 +96,22 @@
<ItemGroup> <ItemGroup>
<Content Include="Default.aspx" /> <Content Include="Default.aspx" />
<Content Include="Global.asax" /> <Content Include="Global.asax" />
<EmbeddedResource Include="hibernate.cfg.xml" /> <Content Include="hibernate.cfg.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="media\css\global.css" />
<Content Include="media\css\reset.css" />
<Content Include="media\images\favicon.png" />
<Content Include="media\images\search.png" />
<Content Include="Views\Home\About.aspx" />
<Content Include="Views\Home\Contact.aspx" />
<Content Include="Views\Home\ContactSuccess.aspx" />
<Content Include="Views\Shared\ExceptionView.ascx" />
<Content Include="Views\Shared\Forbidden.aspx" />
<Content Include="Views\Shared\NotFound.aspx" />
<Content Include="Views\Shared\NotFoundContent.ascx" />
<Content Include="Views\Shared\RecursiveExceptionView.ascx" />
<Content Include="Views\Shared\Unknown.aspx" />
<Content Include="Web.config" /> <Content Include="Web.config" />
<Content Include="Web.Debug.config"> <Content Include="Web.Debug.config">
<DependentUpon>Web.config</DependentUpon> <DependentUpon>Web.config</DependentUpon>
@ -104,12 +120,12 @@
<DependentUpon>Web.config</DependentUpon> <DependentUpon>Web.config</DependentUpon>
</Content> </Content>
<Content Include="Views\Home\Index.aspx" /> <Content Include="Views\Home\Index.aspx" />
<Content Include="Views\Shared\LogOnUserControl.ascx" />
<Content Include="Views\Shared\Site.Master" /> <Content Include="Views\Shared\Site.Master" />
<Content Include="Views\Web.config" /> <Content Include="Views\Web.config" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="App_Data\" /> <Folder Include="App_Data\" />
<Folder Include="media\js\" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\VideoGameQuotes.Api\VideoGameQuotes.Api.csproj"> <ProjectReference Include="..\VideoGameQuotes.Api\VideoGameQuotes.Api.csproj">

@ -0,0 +1,47 @@
<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Views/Shared/Site.Master" %>
<asp:Content runat="server" ID="Title" ContentPlaceHolderID="TitleContent">About</asp:Content>
<asp:Content runat="server" ID="Main" ContentPlaceHolderID="MainContent">
<h2>About</h2>
<div class="inset">
<h3>Genesis</h3>
<div class="inset">
<p>
<strong>Video Game Quotes</strong> is a little project that I decided to tackle one day. I gave
myself a week to implement everything and throw it up on the internet.
</p>
<p>
The inspiration for the site was that there were a lot of awesome video game quotes, but sadly,
no central place to go to find/search/read them. That made me sad.
</p>
<p>
The infrastructure is modeled after <a href="http://bash.org/">bash.org</a>, which is a database
of IRC quotes. One thing I wanted to make sure of was that it shouldn't require you to login or
register. No email addresses, or usernames, or passwords. The site keeps track of who has voted
for what based on IP address, which admittedly is a quite fragile. But it's the only way to
unique-ish-ly identify someone without requiring a login. So, you could totally game the system
by voting, resetting your router, closing your browser, and voting again. I won&#39;t stop you.
</p>
</div>
<h3>Technical Details</h3>
<div class="inset">
<p>
The site was written in C&#9839; using <a href="http://www.asp.net/mvc">ASP.NET MVC 2</a>,
<a href="http://unity.codeplex.com/">Unity</a> and <a href="http://nhforge.org/">NHibernate</a>.
It runs on <a href="http://nginx.org/">nginx</a> using <a href="http://www.mono-project.com/">Mono</a>
via FastCGI on Ubuntu. It uses MySQL for the backend.
</p>
</div>
<h3>Get in Touch</h3>
<div class="inset">
<p>
If you have any questions, concerns, comments, insults or death threats, don&#39;t hesitate to
<%= Html.ActionLink("contact me", "Contact", "Home") %>.
</p>
</div>
</div>
</asp:Content>

@ -0,0 +1,70 @@
<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage<VideoGameQuotes.Web.Models.ContactModel>" MasterPageFile="~/Views/Shared/Site.Master" %>
<%@ Import Namespace="Portoa.Web.Util" %>
<asp:Content runat="server" ID="Title" ContentPlaceHolderID="TitleContent">Contact</asp:Content>
<asp:Content runat="server" ID="Main" ContentPlaceHolderID="MainContent">
<h2>Contact</h2>
<p><%= Html.ValidationMessage("client") %></p>
<% using (Html.BeginForm("Contact", "Home")) { %>
<div class="contact-form">
<%= Html.HiddenFor(model => model.HashedCaptchaAnswer) %>
<p>
<%= Html.LabelFor(model => model.Name) %>
<br />
<%= Html.TextBoxFor(model => model.Name) %>
</p>
<p>
<%= Html.LabelFor(model => model.Email) %>
<br />
<%= Html.TextBoxFor(model => model.Email) %>
</p>
<p>
<%= Html.LabelFor(model => model.Message) %>
<br />
<%= Html.TextAreaFor(model => model.Message) %>
</p>
<p>
Are you human? <small style="position: relative"><a href="#" id="captcha-question">[click for correct answer]</a></small>
<br />
<%= Html.TextBoxFor(model => model.CaptchaAnswer) %>
</p>
<div class="form-submit-container">
<div class="form-submit">
<%= Html.Submit("Send!")%>
</div>
</div>
</div>
<% } %>
</asp:Content>
<asp:Content ContentPlaceHolderID="DeferrableScripts" runat="server">
<script type="text/javascript">//<![CDATA[
$(document).ready(function() {
$("#captcha-question").click(function() {
if ($("#captcha-dialog").length) {
$("#captcha-dialog").remove();
} else {
$("<div/>")
.attr("id", "captcha-dialog")
.addClass("dialog")
.css({ top: "20px" })
.append(
$("<span>The answer is <strong></strong></span>")
.css({ "white-space": "nowrap" })
.find("strong")
.text("<%= Model.UnhashedCaptchaAnswer %>")
.end()
).insertAfter($("#captcha-question"));
}
return false;
});
});
//]]></script>
</asp:Content>

@ -0,0 +1,9 @@
<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Views/Shared/Site.Master" %>
<asp:Content runat="server" ID="Title" ContentPlaceHolderID="TitleContent">Contact</asp:Content>
<asp:Content runat="server" ID="Main" ContentPlaceHolderID="MainContent">
<h2>Success</h2>
<p>
A winner is you. Thank you for your correspondence.
</p>
</asp:Content>

@ -1,4 +1,29 @@
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %> <%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
<asp:Content ContentPlaceHolderID="TitleContent" runat="server">Home</asp:Content> <asp:Content ContentPlaceHolderID="TitleContent" runat="server">Home</asp:Content>
<asp:Content ContentPlaceHolderID="MainContent" runat="server">OH HAI!</asp:Content> <asp:Content ContentPlaceHolderID="MainContent" runat="server">
<h2>Welcome</h2>
<p>
Welcome to <strong>Video Game Quotes</strong>. You can <%= Html.ActionLink("browse", "index", "quote") %>,
<%= Html.ActionLink("rate", "best", "quote") %> and <%= Html.ActionLink("submit", "submit", "quote") %>
quotes from video games. You do not need to register or login (in fact, you aren&#39;t allowed to)
to perform any of these tasks. Participation is encouraged.
</p>
<p>
If you see a quote that is inaccurate, fake, spelled wrong, spam or you&#39;re just straight up
concerned about it, please use the <em>report</em> link near the quote in question. It won&#39;t
be removed immediately, but it will be flagged for review by our crack team of administrators (i.e. me).
</p>
<p>
If you have ideas to make this place suck less, or if you just want to express your admiration
for its creator (i.e. me), don&#39;t hesitate to contact him (i.e. me) using the
<%= Html.ActionLink("contact", "contact", "home") %> form.
</p>
<p>
Here is the quote of the day:
</p>
</asp:Content>

@ -0,0 +1,12 @@
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Portoa.Web.ErrorHandling.ErrorModel>" %>
<%
if (true) {
if (Model.Exception != null) { %>
<h2>Exception Details</h2> <%
Html.RenderPartial("RecursiveExceptionView", Model.Exception);
} else { %>
<p class="info">No exception was thrown.</p>
<% }
}
%>

@ -0,0 +1,11 @@
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<Portoa.Web.ErrorHandling.ErrorModel>" MasterPageFile="~/Views/Shared/Site.Master" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">Forbidden</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<p class="info">
You are not allowed to be here.
</p>
<% Html.RenderPartial("ExceptionView", Model); %>
</asp:Content>

@ -1,14 +0,0 @@
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%
if (Request.IsAuthenticated) {
%>
Welcome <b><%: Page.User.Identity.Name %></b>!
[ <%: Html.ActionLink("Log Off", "LogOff", "Account") %> ]
<%
}
else {
%>
[ <%: Html.ActionLink("Log On", "LogOn", "Account") %> ]
<%
}
%>

@ -0,0 +1,13 @@
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<Portoa.Web.ErrorHandling.ErrorModel>" MasterPageFile="~/Views/Shared/Site.Master" %>
<asp:Content ContentPlaceHolderID="TitleContent" runat="server">404</asp:Content>
<asp:Content ContentPlaceHolderID="MainContent" runat="server">
<% Html.RenderPartial("NotFoundContent"); %>
<p class="info">
Hey. Nice typing, dude. Why don&rsquo;t you spit out your sandwich and try it again?
</p>
<% Html.RenderPartial("ExceptionView", Model); %>
</asp:Content>

@ -0,0 +1,7 @@
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<h2>Not Found</h2>
<div class="error-image">
<img src="/media/images/not-found.png" alt="looking for something?" title="can I help you find something?"/>
</div>

@ -0,0 +1,18 @@
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Exception>" %>
<%@ Import Namespace="Portoa.Util" %>
<p class="exception-message"><%= Model.GetType().GetFriendlyName(false) %>: <code style="white-space: pre"><%= Html.Encode(Model.Message) %></code></p>
<pre><%= Model.StackTrace %></pre>
<hr />
<div style="margin-left: 20px">
<%
if (Model.InnerException != null) {
Html.RenderPartial("RecursiveExceptionView", Model.InnerException);
}
%>
</div>

@ -6,18 +6,54 @@
<head> <head>
<title>Video Game Quotes :: <asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title> <title>Video Game Quotes :: <asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/> <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<link rel="stylesheet" type="text/css" href="/media/css/reset.css" />
<link rel="stylesheet" type="text/css" href="/media/css/global.css" />
<link rel="shortcut icon" type="image/png" href="/media/images/favicon.png" />
</head> </head>
<body> <body>
<div id="wrapper"> <div id="wrapper">
<div id="header"> <div id="header">
<div class="content-container">
<div id="logo">
<h1>Video Game Quotes</h1>
</div>
<div id="main-menu">
<ul class="clearfix">
<li><%= Html.ActionLink("Recent", "Recent", "Quote") %></li>
<li><%= Html.ActionLink("Random", "Random", "Quote") %></li>
<li><%= Html.ActionLink("Best", "Best", "Quote") %></li>
<li><%= Html.ActionLink("Submit", "Submit", "Quote") %></li>
<li><%= Html.ActionLink("About", "About", "Home") %></li>
<li class="searchbox">
<% using (Html.BeginForm("search", "quote", FormMethod.Get)) { %>
<div>
<%= Html.TextBox("term") %>
<input type="image" src="/media/images/search.png" alt="search" title="search quotes, games, systems" />
</div>
<% } %>
</li>
</ul>
</div>
</div>
</div> </div>
<div id="main"> <div class="content-container">
<asp:ContentPlaceHolder ID="MainContent" runat="server" /> <div id="main">
<asp:ContentPlaceHolder ID="MainContent" runat="server" />
</div>
</div>
<div id="footer">
<div class="content-container">
&copy; <%= DateTime.UtcNow.Year %> <a href="http://tommymontgomery.com/" title="Who is this man?">Tommy Montgomery</a><br />
If you steal something, I&#39;ll murder your family.
</div>
</div> </div>
</div> </div>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.0/jquery.min.js"></script>
<asp:ContentPlaceHolder ID="DeferrableScripts" runat="server" />
</body> </body>
</html> </html>

@ -0,0 +1,16 @@
<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage<Portoa.Web.ErrorHandling.ErrorModel>" MasterPageFile="~/Views/Shared/Site.Master" %>
<asp:Content runat="server" ID="SubTitle" ContentPlaceHolderID="TitleContent">Unknown Error</asp:Content>
<asp:Content runat="server" ID="Main" ContentPlaceHolderID="MainContent">
<h2>WTF&#x2048;</h2>
<div class="error-image">
<img src="/media/images/unknown.png" alt="BLARGH!!" title="BLARGH!!"/>
</div>
<p class="info">
WTF&#x2048; How did this even happen&#x2048;
</p>
<% Html.RenderPartial("ExceptionView", Model); %>
</asp:Content>

@ -0,0 +1,137 @@
body {
background-color: #EEEEEE;
color: #000000;
font-family: Calibri, Corbel, Helvetica, Arial;
font-size: 1em;
}
h1 {
font-size: 5em;
text-align: center;
margin-bottom: 10px;
padding: 5px;
}
h2 {
font-size: 3em;
border-bottom: 1px dotted #999999;
margin: 5px 0 10px 0;
}
h3 {
font-size: 1.5em;
color: #999999;
margin: 5px 0;
}
p {
margin-bottom: 5px;
margin-top: 2px;
padding: 2px;
line-height: 1.2em;
}
.inset {
margin-left: 20px;
}
.searchbox {
float: right !important;
}
.searchbox input[type="text"] {
height: 15px;
font-size: 10px;
width: 200px;
padding: 0;
}
.content-container {
width: 800px;
margin: auto;
}
.centered-list {
width: 100%;
overflow: hidden;
}
.centered-list > * {
float: left;
position: relative;
left: 50%;
}
.centered-list > * > * {
float: left;
position: relative;
right: 50%;
}
.centered-list ul {
margin: 0;
}
.dialog {
z-index: 1000;
border: 1px solid #000000;
background-color: #FFFFCC;
color: #000000;
font-size: 80%;
padding: 5px;
position: absolute;
top: 0;
left: 0;
}
.input-validation-error {
background-color: #FFCCCC;
}
.contact-form textarea {
width: 100%;
}
.contact-form input[type="text"] {
width: 300px;
}
.contact-form {
width: 600px;
}
#header {
background-color: #669966;
}
#main-menu li {
float: left;
margin-right: 2px;
}
#main-menu li a {
display: block;
padding: 3px 10px;
text-align: center;
color: #FFFFFF;
text-decoration: none;
font-weight: bold;
}
#main-menu li a:hover {
color: #000000;
background-color: #EEEEEE;
}
#main {
padding: 20px;
}
#main a {
color: #669966;
font-weight: bold;
text-decoration: none;
}
#main a:hover {
border-bottom: 2px solid #000000;
}
#footer {
text-align: center;
font-size: 75%;
color: #999999;
}
#footer a {
color: #999999;
text-decoration: none;
}
#footer a:hover {
text-decoration: underline;
}

@ -0,0 +1,66 @@
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, font, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td {
margin: 0;
padding: 0;
border: 0;
outline: 0;
font-size: 100%;
vertical-align: baseline;
background: transparent;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
:focus {
outline: 0;
}
ins {
text-decoration: none;
}
del {
text-decoration: line-through;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
.clearfix:after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
* html .clearfix { zoom: 1; } /* IE6 */
*:first-child+html .clearfix { zoom: 1; } /* IE7 */
.mir { letter-spacing : -1000em; }
/*\*/html>body .mir { letter-spacing : normal; text-indent : -999em; overflow : hidden;}
html {
height: 100%;
}
body {
height: 100%;
}

Binary file not shown.

After

(image error) Size: 294 B

Binary file not shown.

After

(image error) Size: 582 B