quote of the day, added external image

This commit is contained in:
tmont 2011-02-28 00:38:47 +00:00
parent 86b0918ca5
commit 602a722277
13 changed files with 164 additions and 70 deletions

View File

@ -135,6 +135,13 @@ namespace VideoGameQuotes.Web.Controllers {
return RedirectToAction("Quote", new { id = quote.Id, text = quote.GetUrlFriendlyText() });
}
[ChildActionOnly]
public ActionResult QuoteOfTheDay() {
var today = DateTime.UtcNow.DayOfYear;
var quote = quoteService.GetQuoteForDayOfYear(today);
return PartialView("QuoteOfTheDay", quote);
}
[HttpPost, VerifyUser(Group = UserGroup.Admin)]
public ActionResult DismissFlag([GreaterThanZero]int quoteId, [GreaterThanZero]int flagId) {
if (!ModelState.IsValid) {

View File

@ -63,6 +63,7 @@ namespace VideoGameQuotes.Web {
//bullshit route so that RenderAction works
routes.MapRoute("mainmenu", "home/mainmenu", new { controller = "Home", action = "MainMenu" });
routes.MapRoute("quote-of-the-day", "quote/quoteoftheday", new { controller = "Quote", action = "QuoteOfTheDay" });
routes.MapRoute("crud-default", "{controller}/{action}", null, new { controller = "system|publisher|game|category", action = "create|edit|delete" });

View File

@ -24,6 +24,7 @@ namespace VideoGameQuotes.Web.Services {
IEnumerable<Quote> GetBestQuotes(int start, int end, out int totalCount);
Vote GetVoteOrCreateNew(Quote quote, User voter);
IEnumerable<Quote> GetBrowsableQuotes(BrowseModel model, int start, int end, out int totalCount);
Quote GetQuoteForDayOfYear(int day);
}
public class QuoteService : IQuoteService {
@ -34,6 +35,7 @@ namespace VideoGameQuotes.Web.Services {
private readonly IRepository<Category> categoryRepository;
private readonly IRepository<Vote> voteRepository;
private static readonly Random random = new Random();
private static readonly IDictionary<int, Quote> quoteOfTheDayMap = new Dictionary<int, Quote>();
public QuoteService(
IRepository<Quote> quoteRepository,
@ -118,8 +120,16 @@ namespace VideoGameQuotes.Web.Services {
return null;
}
return quoteRepository
.Records
return GetRandomQuote(quoteRepository.Records);
}
private static Quote GetRandomQuote(IEnumerable<Quote> quotes) {
var length = quotes.Count();
if (length == 0) {
return null;
}
return quotes
.Skip(random.Next(length))
.Take(1)
.Single();
@ -164,5 +174,15 @@ namespace VideoGameQuotes.Web.Services {
totalCount = quotes.Count();
return quotes.Skip(start - 1).Take(end - start + 1);
}
[UnitOfWork, CanBeNull]
public Quote GetQuoteForDayOfYear(int day) {
if (!quoteOfTheDayMap.ContainsKey(day)) {
quoteOfTheDayMap[day] = GetRandomQuote(quoteRepository.Records.Where(quote => quote.Score > 0));
}
return quoteOfTheDayMap[day];
}
}
}

View File

@ -149,6 +149,7 @@
<Content Include="media\images\cancel.png" />
<Content Include="media\images\delete.png" />
<Content Include="media\images\error.png" />
<Content Include="media\images\external.png" />
<Content Include="media\images\favicon.png" />
<Content Include="media\images\flag_red.png" />
<Content Include="media\images\link.png" />
@ -172,6 +173,7 @@
<Content Include="Views\Home\Contact.aspx" />
<Content Include="Views\Home\ContactSuccess.aspx" />
<Content Include="Views\Quote\PagingMenu.ascx" />
<Content Include="Views\Quote\QuoteOfTheDay.ascx" />
<Content Include="Views\Shared\BadPaging.aspx" />
<Content Include="Views\Quote\Best.aspx" />
<Content Include="Views\Quote\Edit.aspx" />

View File

@ -3,12 +3,11 @@
<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.
myself a week to implement everything and throw it up on the internet. It actually took three
weeks as I struggled with <a href="http://incubator.apache.org/lucene.net/" class="external">Lucene</a> and my
obviously poor graphic design skills.
</p>
<p>
@ -17,31 +16,32 @@
</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
The infrastructure is modeled after <a href="http://bash.org/" class="external">bash.org</a>, which is a database
of IRC quotes. One thing I wanted to make sure of was that it shouldn&rsquo;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
for what based on IP address, which admittedly is a quite fragile. But it&rsquo;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.
by voting, resetting your router, closing your browser, and voting again. I won&rsquo;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.
The site was written in C&#9839; using <a href="http://www.asp.net/mvc" class="external">ASP.NET MVC 2</a>,
<a href="http://unity.codeplex.com/" class="external">Unity</a> and <a href="http://nhforge.org/" class="external">NHibernate</a>.
It runs on <a href="http://nginx.org/" class="external">nginx</a> using <a href="http://www.mono-project.com/" class="external">Mono</a>
via FastCGI on Ubuntu. It uses MySQL for the backend and
<a href="http://incubator.apache.org/lucene.net/" class="external">Lucene.NET</a> for search indexing.
</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") %>.
If you have any questions, concerns, comments, insults or death threats, don&rsquo;t hesitate to
<%= Html.ActionLink("contact me", "Contact", "Home") %> and you will be ignored immediately.
</p>
<h3><a name="credits"></a>Credits</h3>
<p>
Some of the icons and images used on this site were taken from the
<a href="http://www.famfamfam.com/lab/icons/silk/" class="external">silk icon set</a> and
<a href="http://deleket.deviantart.com/art/Gaming-Icons-Pack-42723812" class="external">deleket&rsquo;s
gaming icon set</a>.
</p>
</div>
</div>
</asp:Content>

View File

@ -27,4 +27,6 @@
<p>
Here is the quote of the day:
</p>
<% Html.RenderAction("QuoteOfTheDay", "Quote"); %>
</asp:Content>

View File

@ -0,0 +1,20 @@
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<VideoGameQuotes.Api.Quote>" %>
<%@ Import Namespace="VideoGameQuotes.Api" %>
<div class="quote-container">
<div class="quote-data clearfix">
<div class="quote-score-container">
<div class="vote-container">
</div>
<div class="quote-score" title="+<%= Model.UpVotes %>, -<%= Model.DownVotes %>"><%= Model.Score %></div>
<div class="vote-container">
</div>
</div>
<div class="quote-text">
<a href="<%= Url.Action("quote", "quote", new { id = Model.Id, text = Model.GetUrlFriendlyText() }) %>">
<%= Model.FormatTextForHtml() %>
</a>
</div>
</div>
</div>

View File

@ -38,6 +38,7 @@
<p>
&copy; <%= DateTime.UtcNow.Year %> <a href="http://tommymontgomery.com/" title="Who is this man?">Tommy Montgomery</a><br />
<%= Html.ActionLink("about", "about", "home") %> |
<%= Html.ActionLink("credits", "about", "home", null, null, "credits", null, null) %> |
<% if (!Request.IsAuthenticated) { %>
<a href="#" id="login-link">login</a>
<% } else { %>

View File

@ -50,6 +50,12 @@ ul.menu {
padding: 0;
list-style: none;
}
a.external {
background-image: url(/media/images/external.png);
background-position: right center;
background-repeat: no-repeat;
padding-right: 14px;
}
.error-message {
padding-left: 18px;
@ -252,7 +258,7 @@ ul.menu {
}
#main {
padding: 20px 20px 100px 20px;
padding: 20px 20px 120px 20px;
}
#main a {
color: #669966;
@ -306,6 +312,9 @@ ul.menu {
color: inherit;
text-decoration: none;
}
#footer p {
margin-top: 30px;
}
#footer a:hover {
text-decoration: underline;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 582 B

After

Width:  |  Height:  |  Size: 615 B

View File

@ -48,7 +48,12 @@
var $container = $("#create-system-form");
var $link = $(this);
var data = { SystemName: $("#SystemName").val(), SystemAbbreviation: $("#SystemAbbreviation").val(), SystemReleaseDate: $("#SystemReleaseDate").val() };
var data = {
SystemName: $("#SystemName").val(),
SystemAbbreviation: $("#SystemAbbreviation").val(),
SystemReleaseDate: $("#SystemReleaseDate").val(),
SystemIcon: $("#SystemIcon").val()
};
var url = "/system/create";
var systemId = $container.find("> .edit-mode").val();
@ -111,6 +116,7 @@
$("#SystemName").val(system.Name);
$("#SystemAbbreviation").val(system.Abbreviation);
$("#SystemReleaseDate").val($.vgquotes.parseAndFormatDate(system.ReleaseDate));
$("#SystemIcon").val(system.Icon);
});
return false;
@ -218,6 +224,7 @@
//populate game form with game data
$("#GameName").val(game.Name);
$("#GameWebsite").val(game.Website);
$("#GameIcon").val(game.Icon);
$.each(game.Regions, function() {
$("input[name='GameRegions'][value='" + this + "']").attr("checked", "checked");
});

View File

@ -1,8 +1,9 @@
(function($, window, undefined){
$.fn.center = function() {
this.css("top", ($(window).height() - this.height()) / 2 + $(window).scrollTop() + "px");
this.css("left", ($(window).width() - this.width()) / 2 + $(window).scrollLeft() + "px");
var fixed = this.css("position") === "fixed";
this.css("top", ($(window).height() - this.height()) / 2 + (fixed ? 0 : $(window).scrollTop()) + "px");
this.css("left", ($(window).width() - this.width()) / 2 + (fixed ? 0 : $(window).scrollLeft()) + "px");
return this;
};
@ -70,7 +71,6 @@
alert("An error occurred (" + xhr.status + ")");
},
preload: function(images) {
//MM_preloadImages(lulz)
$.each(images, function() {
$('<img/>')[0].src = this;
});
@ -83,6 +83,17 @@
success: ajaxCallback(type, callback),
error: ajaxCallback(type, callback)
});
},
createDialog: function($dialog) {
$("body")
.append($("<div/>").attr("id", "modal-background"))
.append($dialog);
$dialog.center();
},
closeDialog: function($dialog) {
$("#modal-background").remove();
$dialog.remove();
}
};
}();
@ -203,11 +214,12 @@
});
};
var setupReportLink = function() {
$(".quote-flag-link").click(function() {
if ($(".report-dialog").length > 0) {
var setupFlagLink = function() {
$("a.quote-flag-link").click(function() {
if ($(".flag-dialog").length > 0) {
return false;
}
var $link = $(this);
var $container = $link.parents(".quote-container");
var quoteId = $container.find("input.quote-id").val();
@ -222,42 +234,51 @@
$row.append($(html));
}
var $dialog = $("<div/>").addClass("dialog report-dialog");
var $dialog = $("<div/>").addClass("dialog flag-dialog");
var $submit = $("<input/>")
.attr("type", "button")
.attr("value", "Submit Report")
var $submit = $("<a/>")
.attr({ href: "#", title: "submit" })
.addClass("submit-link button-link")
.text("Submit")
.click(function() {
$.ajax("/report", {
var $link = $(this);
$link.toggleClass("submit-link loading-link");
$.ajax("/flag", {
type: "POST",
data: { QuoteId: quoteId, Comment: $dialog.find("textarea").val(), FlagType: $dialog.find("input[name='flagType']:checked").val() },
complete: function() { $dialog.remove(); },
success: function(data, status, $xhr) {
if (data.Error !== null) {
alert(data.Error);
return;
}
}
});
$.vgquotes.closeDialog($dialog);
},
complete: function() { $link.toggleClass("submit-link loading-link"); }
});
var $cancel = $("<input/>")
.attr("type", "button")
.attr("value", "Cancel")
.click(function() { $dialog.remove(); });
return false;
});
var $cancel = $("<a/>")
.attr({ href: "#", title: "cancel" })
.addClass("cancel-link button-link")
.text("Cancel")
.click(function() { $.vgquotes.closeDialog($dialog); return false; });
$dialog
.append($("<p/>").text("Flag as:"))
.append($("<p/>").addClass("label").text("Flag as:"))
.append($("<table/>").append($row))
.append($("<p/>").text("Comment"))
.append($("<p/>").addClass("label").text("Comment"))
.append($("<textarea/>"))
.append($("<div/>").css("text-align", "center").append($submit).append($cancel));
.append($("<div/>").addClass("submit-container").append($submit).append($cancel));
//"other" should be checked by default
$dialog.find("#flag-type-0").attr("checked", "checked");
$("body").append($dialog);
$dialog.center();
$.vgquotes.createDialog($dialog);
return false;
});
@ -267,7 +288,7 @@
var showLoginForm = function() {
var $dialog = $("#login-dialog");
if ($dialog.length > 0) {
$dialog.remove();
$.vgquotes.closeDialog($dialog);
return false;
}
@ -292,13 +313,12 @@
return false;
});
var $dialog = $("<div/>").addClass("dialog").attr("id", "login-dialog");
$dialog = $("<div/>").addClass("dialog").attr("id", "login-dialog");
$form.append($usernameInput).append($passwordInput).append($submit);
$dialog.append($form);
$("body").append($dialog);
$dialog.center();
$.vgquotes.createDialog($dialog);
$usernameInput.focus();
return false;
@ -315,7 +335,7 @@
$(document).ready(function() {
$.vgquotes.preload([$.vgquotes.loadingGif]);
setupReportLink();
setupFlagLink();
setupVoting();
$("body").ajaxError(function(e, xhr, options, error){
@ -323,6 +343,11 @@
});
$("#search-query").focus();
$(document).keyup(function(e) {
if (e.keyCode === 27) {
$.vgquotes.closeDialog($(".dialog"));
}
});
});
}());