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

View File

@ -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");
}
} }
} }

View File

@ -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 }
);
} }
} }
} }

View File

@ -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; }
}
}

View File

@ -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">

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>
<% }
}
%>

View File

@ -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>

View File

@ -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") %> ]
<%
}
%>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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;
}

View File

@ -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

Width:  |  Height:  |  Size: 294 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B