Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'release/1.5.0'

  • Loading branch information...
commit 3ee280fe34e34c7b6d6109bd141577eb03db0c86 2 parents db88812 + 8da8b7c
Alan Malloy amalloy authored
1  README.md
View
@@ -43,6 +43,7 @@ vaguely like this:
* [Anthony Grimes](https://github.com/Raynes) (raynes)
* [Carin Meier](https://github.com/gigasquid) (cmeier)
* [Clint Harrison](https://github.com/Clinteger) (clinteger)
+* [Darren Austin](https://github.com/darrenaustin) (darren)
* [David Davis](https://github.com/daviddavis) (daviddavis)
* [Devin Walters](https://github.com/devn) (devn)
* [Michael Kohl](https://github.com/citizen428) (citizen428)
2  project.clj
View
@@ -1,4 +1,4 @@
-(defproject foreclojure "1.4.3.5"
+(defproject foreclojure "1.5.0"
:description "4clojure - a website for lisp beginners"
:dependencies [[clojure "1.2.1"]
[clojure-contrib "1.2.0"]
31 resources/public/css/style.css
View
@@ -134,6 +134,27 @@ a.novisited {color: #00e;}
color: black;
}
+td a.user-profile-link {
+ display: block;
+ margin-top: 5px;
+}
+
+#profile-pic{
+ float: left;
+ padding-right: 10px;
+}
+
+#profile-pic img{
+ border: 2px solid black;
+}
+
+img.gravatar{
+ float: left;
+ display: inline;
+ margin-right: 5px;
+ border: 1px solid grey;
+}
+
#user-info{
float:right;
}
@@ -217,7 +238,6 @@ a.novisited {color: #00e;}
padding: 15px;
}
-
/*
* By default only show the text label for the "Following" column on
* the users page. If they have javascript turned on (detected
@@ -375,12 +395,12 @@ div#problems-error {
height: 250px;
}
-#editor {
+#editor {
margin: 0px;
float: left;
width: 50%;
height: 250px;
- overflow: hidden
+ overflow: hidden
}
table.my-table {
@@ -391,10 +411,11 @@ table.my-table {
table.my-table td {
text-align: left;
font-size: 12px;
+ vertical-align: middle;
}
tr.evenrow {
- background-color: #E2E4FF;
+ background-color: #E2E4FF;
}
tr.oddrow {
@@ -485,7 +506,7 @@ button:active {
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#A1CC59', endColorstr='#A7D45C');
}
-#golfgraph{
+#golfgraph{
width: 90%;
}
BIN  resources/public/images/4clj-logo-small.png
View
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
46 resources/public/script/foreclojure.js
View
@@ -59,20 +59,38 @@ $(document).ready(function() {
});
});
-var difficulty = new Array();
-difficulty["Elementary"] = 0;
-difficulty["Easy"] = 1;
-difficulty["Medium"] = 2;
-difficulty["Hard"] = 3;
-difficulty[""] = 4;
-
-jQuery.fn.dataTableExt.oSort['difficulty-asc'] = function(a, b) {
- return difficulty[a] - difficulty[b];
+var difficulty = {
+ "Elementary": 0,
+ "Easy": 1,
+ "Medium": 2,
+ "Hard": 3,
+ "": 4
};
-jQuery.fn.dataTableExt.oSort['difficulty-desc'] = function(a, b) {
- return difficulty[b] - difficulty[a];
-};
+// dataTable will call this function in preparation for sorting a column.
+// We're responsible for giving it the "real" data to sort on, for all the
+// rows at once
+jQuery.fn.dataTableExt.afnSortData['difficulty'] = function(oSettings, iColumn)
+{
+ var aData = [];
+ // fnGetTrNodes returns a context we can use in jquery to iterate over
+ // only the <td> elements for this column. General approach taken from
+ // http://datatables.net/plug-ins/sorting#functions_data_source
+ $('td:eq('+iColumn+')', oSettings.oApi._fnGetTrNodes(oSettings)).each(function () {
+ aData.push(difficulty[$(this).text()]);
+ });
+ return aData;
+}
+
+// See comments for above function to make sense of this mess
+jQuery.fn.dataTableExt.afnSortData['user-name'] = function(oSettings, iColumn)
+{
+ var aData = [];
+ $('td:eq('+iColumn+') a.user-profile-link', oSettings.oApi._fnGetTrNodes(oSettings)).each(function () {
+ aData.push($(this).text());
+ });
+ return aData;
+}
function configureDataTables(){
@@ -81,7 +99,7 @@ function configureDataTables(){
"aaSorting": [[5, "desc"], [1, "asc"], [4, "desc"]],
"aoColumns": [
{"sType": "string"},
- {"sType": "difficulty"},
+ {"sSortDataType": "difficulty", "sType": "numeric"},
{"sType": "string"},
{"sType": "string"},
{"sType": "numeric"},
@@ -105,7 +123,7 @@ function configureDataTables(){
"aaSorting": [[0, "asc"]],
"aoColumns": [
{"sType": "numeric"},
- {"sType": "string"},
+ {"sSortDataType": "user-name"},
{"sType": "numeric"},
{"sType": "string"}
]
4 src/foreclojure/core.clj
View
@@ -16,7 +16,7 @@
[foreclojure.version :only [version-routes]]
[foreclojure.graphs :only [graph-routes]]
[foreclojure.mongo :only [prepare-mongo]]
- [foreclojure.utils :only [wrap-uri-binding]]
+ [foreclojure.ring-utils :only [wrap-request-bindings]]
[foreclojure.periodic :only [schedule-task]]
[ring.adapter.jetty :only [run-jetty]]
[ring.middleware.reload :only [wrap-reload]]
@@ -47,7 +47,7 @@
#(wrap-reload % '(foreclojure.core))
identity))
session/wrap-stateful-session
- wrap-uri-binding
+ wrap-request-bindings
handler/site
wrap-strip-trailing-slash))
4 src/foreclojure/problems.clj
View
@@ -4,7 +4,8 @@
[clojure.string :as s]
[ring.util.response :as response])
(:import [org.apache.commons.mail EmailException])
- (:use [foreclojure.utils :only [from-mongo get-user get-solved login-link *url* flash-msg flash-error row-class approver? can-submit? send-email image-builder with-user as-int maybe-update escape-html]]
+ (:use [foreclojure.utils :only [from-mongo get-user get-solved login-link flash-msg flash-error row-class approver? can-submit? send-email image-builder with-user as-int maybe-update escape-html]]
+ [foreclojure.ring-utils :only [*url*]]
[foreclojure.template :only [def-page content-page]]
[foreclojure.social :only [tweet-link gist!]]
[foreclojure.feeds :only [create-feed]]
@@ -579,6 +580,7 @@ Return a map, {:message, :error, :url, :num-tests-passed}."
(defroutes problems-routes
(GET "/problems" [] (problem-list-page))
+ (GET "/problems/solved" [] (:total @solved-stats))
(GET "/problem/:id" [id]
(if-let [id-int (as-int id)]
(problem-page id-int)
21 src/foreclojure/ring.clj
View
@@ -1,12 +1,13 @@
(ns foreclojure.ring
- (:require [clojure.java.io :as io]
- [clojure.string :as s]
- [compojure.route :as route])
- (:import [java.net URL])
- (:use [compojure.core :only [GET]]
- [foreclojure.utils :only [strip-version-number]]
- [useful.debug :only [?]]
- [ring.util.response :only [response]]))
+ (:require [clojure.java.io :as io]
+ [clojure.string :as s]
+ [compojure.route :as route])
+ (:import [java.net URL])
+ (:use [compojure.core :only [GET]]
+ [foreclojure.version-utils :only [strip-version-number]]
+ [foreclojure.ring-utils :only [get-host]]
+ [useful.debug :only [?]]
+ [ring.util.response :only [response]]))
;; copied from compojure.route, modified to use File instead of Stream
(defn resources
@@ -45,11 +46,11 @@
(defn split-hosts [host-handlers]
(let [default (:default host-handlers)]
(fn [request]
- (let [host (get-in request [:headers "host"])
+ (let [host (get-host request)
handler (or (host-handlers host) default)]
(handler request)))))
(defn wrap-404 [handler]
(fn [request]
(or (handler request)
- (route/not-found "Page not found"))))
+ (route/not-found "Page not found"))))
20 src/foreclojure/ring_utils.clj
View
@@ -0,0 +1,20 @@
+(ns foreclojure.ring-utils
+ (:require [foreclojure.config :as config]))
+
+(def ^:dynamic *url* nil) ; url of current request
+(def ^:dynamic *host* nil) ; Host header sent by client
+(def ^:dynamic *http-scheme* nil) ; keyword, :http or :https
+
+(defn get-host [request]
+ (get-in request [:headers "host"]))
+
+(defn wrap-request-bindings [handler]
+ (fn [req]
+ (binding [*url* (:uri req)
+ *host* (or (get-host req) "www.4clojure.com")
+ *http-scheme* (:scheme req)]
+ (handler req))))
+
+(def static-url (if-let [host config/static-host]
+ #(str (name *http-scheme*) "://" host "/" %)
+ #(str "/" %)))
10 src/foreclojure/static.clj
View
@@ -1,9 +1,9 @@
(ns foreclojure.static
- (:use [compojure.core :only [defroutes GET]]
- [foreclojure.problems :only [solved-stats]]
- [foreclojure.config :only [repo-url]]
- [foreclojure.utils :only [static-url]]
- [foreclojure.template :only [def-page]]))
+ (:use [compojure.core :only [defroutes GET]]
+ [foreclojure.problems :only [solved-stats]]
+ [foreclojure.config :only [repo-url]]
+ [foreclojure.ring-utils :only [static-url]]
+ [foreclojure.template :only [def-page]]))
(def df
(let [df (java.text.DecimalFormat.)
14 src/foreclojure/template.clj
View
@@ -1,9 +1,11 @@
(ns foreclojure.template
- (:require [sandbar.stateful-session :as session])
- (:use [hiccup.core :only [html]]
- [hiccup.page-helpers :only [doctype javascript-tag link-to]]
- [foreclojure.config :only [config repo-url]]
- [foreclojure.utils :only [css js page-attributes rendering-info login-url approver? can-submit? static-url]]))
+ (:require [sandbar.stateful-session :as session])
+ (:use [hiccup.core :only [html]]
+ [hiccup.page-helpers :only [doctype javascript-tag link-to]]
+ [foreclojure.config :only [config repo-url]]
+ [foreclojure.utils :only [page-attributes rendering-info login-url approver? can-submit?]]
+ [foreclojure.ring-utils :only [static-url]]
+ [foreclojure.version-utils :only [css js]]))
;; Global wrapping template
(defn html-doc [body]
@@ -29,7 +31,7 @@
[:div#github-banner [:a {:href repo-url
:alt "Fork 4Clojure on Github!"}]])
[:div#top
- (link-to "/" [:img#logo {:src (static-url "images/4clj-logo.png")
+ (link-to "/" [:img#logo {:src (static-url "images/4clj-logo-small.png")
:alt "4clojure.com"}])]
[:div#content
[:div#menu
28 src/foreclojure/users.clj
View
@@ -1,14 +1,17 @@
(ns foreclojure.users
(:require [ring.util.response :as response]
+ [clojure.string :as string]
[sandbar.stateful-session :as session])
(:use [foreclojure.utils :only [from-mongo row-class rank-class get-user with-user]]
[foreclojure.template :only [def-page content-page]]
+ [foreclojure.ring-utils :only [*http-scheme*]]
[foreclojure.config :only [config repo-url]]
[somnium.congomongo :only [fetch-one fetch update!]]
[compojure.core :only [defroutes GET POST]]
[hiccup.form-helpers :only [form-to hidden-field]]
[hiccup.page-helpers :only [link-to]]
- [clojure.contrib.json :only [json-str]]))
+ [clojure.contrib.json :only [json-str]])
+ (:import org.apache.commons.codec.digest.DigestUtils))
(def golfer-tags (into [:contributor]
(when (:golfing-active config)
@@ -23,7 +26,7 @@
(defn get-users []
(from-mongo
(fetch :users
- :only [:user :solved :contributor])))
+ :only [:user :solved :contributor :email])))
(defn get-ranked-users []
(let [users (get-users)
@@ -66,6 +69,18 @@
(link-to (str "mailto:" (email-address username))
username))
+
+
+(let [canonical-email (comp string/trim string/lower-case)
+ md5 #(DigestUtils/md5Hex %)]
+ (defn gravatar-img [{:keys [email size class] :or {size 24}}]
+ (let [hash (md5 (canonical-email email))
+ url (str (name *http-scheme*) "://www.gravatar.com/avatar/"
+ hash "?s=" size "&d=identicon")]
+ [:img (conj {:src url, :alt "gravatar icon"
+ :width size :height size}
+ (when class {:class class}))])))
+
(defn format-user-ranking [{:keys [rank user contributor solved]}]
(when user
[:div
@@ -102,12 +117,12 @@
[:th {:style "width: 200px;"} "Username"]
[:th {:style "width: 180px;"} "Problems Solved"]
[:th "Following"]]]
- (map-indexed (fn [rownum {:keys [_id position rank user contributor solved]}]
+ (map-indexed (fn [rownum {:keys [_id email position rank user contributor solved]}]
[:tr (row-class rownum)
[:td (rank-class position) rank]
[:td
- (when contributor [:span.contributor "* "])
- [:a.user-profile-link {:href (str "/user/" user)} user]]
+ (gravatar-img {:email email :class "gravatar"})
+ [:a.user-profile-link {:href (str "/user/" user)} user (when contributor [:span.contributor " *"])]]
[:td.centered (count solved)]
[:td (following-checkbox user-id following _id user)]])
user-set)])))
@@ -158,10 +173,11 @@
(def-page user-profile [username]
(let [page-title (str "User: " username)
- user-id (:_id (get-user username))]
+ {user-id :_id email :email} (get-user username)]
{:title page-title
:content
(list
+ [:div#profile-pic (gravatar-img {:email email, :size 80 :class "user-profile-img"})]
[:div.user-profile-name page-title]
(if (session/session-get :user)
(with-user [{:keys [_id following]}]
31 src/foreclojure/utils.clj
View
@@ -14,15 +14,9 @@
[hiccup.form-helpers :only [label]]
[useful.fn :only [to-fix]]
[somnium.congomongo :only [fetch-one]]
+ [foreclojure.ring-utils :only [*url* static-url]]
[foreclojure.config :only [config repo-url]]))
-(def ^{:dynamic true} *url* nil)
-
-(defn wrap-uri-binding [handler]
- (fn [req]
- (binding [*url* (:uri req)]
- (handler req))))
-
(defn as-int [s]
(if (integer? s) s,
(try (Integer. s)
@@ -158,29 +152,6 @@
(>= (count (get-solved username))
(:advanced-user-count config)))))
-(let [prefix (str (when-let [host config/static-host]
- (str "http://" host))
- "/")]
- (defn static-url [url]
- (str prefix url)))
-
-(let [version-suffix (str "__" git/tag)]
- (defn add-version-number [file]
- (let [[_ path ext] (re-find #"(.*)\.(.*)$" file)]
- (str path version-suffix "." ext)))
-
- (defn strip-version-number [file]
- (string/replace file version-suffix "")))
-
-(letfn [(wrap-versioning [f]
- (fn [& files]
- (for [file files]
- (f (static-url (add-version-number file))))))]
- (def js (wrap-versioning hiccup/include-js))
- (def css (wrap-versioning hiccup/include-css)))
-
-
-
(defn image-builder
"Return a function for constructing an [:img] element from a keyword.
21 src/foreclojure/version_utils.clj
View
@@ -0,0 +1,21 @@
+(ns foreclojure.version-utils
+ (:use [foreclojure.ring-utils :only [static-url]])
+ (:require [foreclojure.git :as git]
+ [foreclojure.config :as config]
+ [clojure.string :as string]
+ [hiccup.page-helpers :as hiccup]))
+
+(let [version-suffix (str "__" git/tag)]
+ (defn add-version-number [file]
+ (let [[_ path ext] (re-find #"(.*)\.(.*)$" file)]
+ (str path version-suffix "." ext)))
+
+ (defn strip-version-number [file]
+ (string/replace file version-suffix "")))
+
+(letfn [(wrap-versioning [f]
+ (fn [& files]
+ (for [file files]
+ (f (static-url (add-version-number file))))))]
+ (def js (wrap-versioning hiccup/include-js))
+ (def css (wrap-versioning hiccup/include-css)))
Please sign in to comment.
Something went wrong with that request. Please try again.