quote of the day, added external image
This commit is contained in:
parent
86b0918ca5
commit
602a722277
@ -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) {
|
||||
|
@ -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" });
|
||||
|
||||
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
@ -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" />
|
||||
|
@ -3,45 +3,45 @@
|
||||
<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>
|
||||
<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. 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>
|
||||
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 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't stop you.
|
||||
</p>
|
||||
</div>
|
||||
<p>
|
||||
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’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’t stop you.
|
||||
</p>
|
||||
|
||||
<h3>Technical Details</h3>
|
||||
<div class="inset">
|
||||
<p>
|
||||
The site was written in C♯ 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>
|
||||
<p>
|
||||
The site was written in C♯ 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>
|
||||
|
||||
<h3>Get in Touch</h3>
|
||||
<div class="inset">
|
||||
<p>
|
||||
If you have any questions, concerns, comments, insults or death threats, don't hesitate to
|
||||
<%= Html.ActionLink("contact me", "Contact", "Home") %>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
If you have any questions, concerns, comments, insults or death threats, don’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’s
|
||||
gaming icon set</a>.
|
||||
</p>
|
||||
</asp:Content>
|
||||
|
@ -27,4 +27,6 @@
|
||||
<p>
|
||||
Here is the quote of the day:
|
||||
</p>
|
||||
|
||||
<% Html.RenderAction("QuoteOfTheDay", "Quote"); %>
|
||||
</asp:Content>
|
20
Src/VideoGameQuotes.Web/Views/Quote/QuoteOfTheDay.ascx
Normal file
20
Src/VideoGameQuotes.Web/Views/Quote/QuoteOfTheDay.ascx
Normal 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>
|
@ -37,7 +37,8 @@
|
||||
<div id="footer">
|
||||
<p>
|
||||
© <%= DateTime.UtcNow.Year %> <a href="http://tommymontgomery.com/" title="Who is this man?">Tommy Montgomery</a><br />
|
||||
<%= Html.ActionLink("about", "about", "home") %> |
|
||||
<%= 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 { %>
|
||||
|
@ -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;
|
||||
}
|
BIN
Src/VideoGameQuotes.Web/media/images/external.png
Normal file
BIN
Src/VideoGameQuotes.Web/media/images/external.png
Normal file
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 |
@ -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");
|
||||
});
|
||||
|
@ -1,11 +1,12 @@
|
||||
(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;
|
||||
};
|
||||
|
||||
|
||||
$.fn.applyModelErrors = function(errorMessage, errorData) {
|
||||
var $this = 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"); }
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
var $cancel = $("<input/>")
|
||||
.attr("type", "button")
|
||||
.attr("value", "Cancel")
|
||||
.click(function() { $dialog.remove(); });
|
||||
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"));
|
||||
}
|
||||
});
|
||||
});
|
||||
}());
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user