Permalink
Browse files

Merge branch 'develop' of https://github.com/gigasquid/4clojure into …

…develop
  • Loading branch information...
2 parents abcd8b2 + 5c25707 commit e2911e35d0f43990abe81c2a6b3629352c5805ae @amcnamara committed Oct 6, 2011
View
@@ -9,3 +9,5 @@ pom.xml
.cake
.DS_Store
.#*
+.emacs-project
+.lein-failures
View
@@ -1,4 +1,4 @@
-(defproject foreclojure "1.4.3.4"
+(defproject foreclojure "1.5.0.2"
:description "4clojure - a website for lisp beginners"
:dependencies [[clojure "1.2.1"]
[clojure-contrib "1.2.0"]
@@ -8,16 +8,18 @@
[sandbar "0.4.0-SNAPSHOT"]
[org.clojars.christophermaier/congomongo "0.1.4-SNAPSHOT"]
[org.jasypt/jasypt "1.7"]
- [useful "0.7.0-beta1"]
+ [cheshire "2.0.2"]
+ [useful "0.7.0-beta5"]
[amalloy/ring-gzip-middleware "[0.1.0,)"]
[clj-github "1.0.1"]
[ring "0.3.7"]
[clj-config "0.1.0"]
[incanter/incanter-core "1.2.3"]
[incanter/incanter-charts "1.2.3"]
+ [commons-lang "2.6"]
[org.apache.commons/commons-email "1.2"]]
:dev-dependencies [[lein-ring "0.4.5"]
- [swank-clojure "1.2.1"]
+ [swank-clojure "1.3.3"]
[midje "1.1.1"]]
:main foreclojure.core
:ring {:handler foreclojure.core/app
@@ -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,6 +238,12 @@ a.novisited {color: #00e;}
padding: 15px;
}
+#server-user-table{
+ float: left;
+ width: 100%;
+ padding: 15px;
+}
+
/*
* By default only show the text label for the "Following" column on
@@ -375,12 +402,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 +418,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 +513,7 @@ button:active {
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#A1CC59', endColorstr='#A7D45C');
}
-#golfgraph{
+#golfgraph{
width: 90%;
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -39,6 +39,8 @@ $(document).ready(function() {
return false;
});
+ $("#all-users-link").html("[show <a href=\"/users/all\">all</a>]");
+
$("#user-table").addClass("js-enabled");
$("#user-table input.following").live("click", function(e) {
@@ -59,20 +61,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 +101,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,12 +125,20 @@ function configureDataTables(){
"aaSorting": [[0, "asc"]],
"aoColumns": [
{"sType": "numeric"},
- {"sType": "string"},
+ {"sSortDataType": "user-name"},
{"sType": "numeric"},
{"sType": "string"},
{"sType": "string"}
]
} );
+
+
+ $('#server-user-table').dataTable( {
+ "iDisplayLength":10,
+ "bProcessing": true,
+ "bServerSide": true,
+ "sAjaxSource": "/datatable/users"
+ } );
}
function setIconColor(element, color, timeOut) {
View
@@ -0,0 +1,18 @@
+(ns foreclojure.api
+ (:require [cheshire.core :as json])
+ (:use [foreclojure.ring :only [wrap-json wrap-debug]]
+ [foreclojure.utils :only [as-int]]
+ [compojure.core :only [routes GET]]
+ [somnium.congomongo :only [fetch-one]]
+ [useful.map :only [update-each]]))
+
+(def api-routes
+ (-> (routes
+ (GET "/api/problem/:id" [id]
+ (when-let [problem (fetch-one :problems :where {:_id (as-int id)
+ :approved true})]
+ {:body (-> problem
+ (dissoc :_id :approved)
+ (update-each [:restricted :tags]
+ #(or % ())))})))
+ (wrap-json)))
View
@@ -5,6 +5,8 @@
[sandbar.stateful-session :as session])
(:use [compojure.core :only [defroutes routes GET]]
[foreclojure.static :only [static-routes welcome-page]]
+ [foreclojure.api :only [api-routes]]
+ [foreclojure.datatable :only [datatable-routes]]
[foreclojure.problems :only [problems-routes]]
[foreclojure.login :only [login-routes]]
[foreclojure.register :only [register-routes]]
@@ -16,7 +18,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]]
@@ -42,12 +44,14 @@
social-routes
version-routes
graph-routes
+ api-routes
+ datatable-routes
golf-routes)
((if (:wrap-reload config)
#(wrap-reload % '(foreclojure.core))
identity))
session/wrap-stateful-session
- wrap-uri-binding
+ wrap-request-bindings
handler/site
wrap-strip-trailing-slash))
@@ -0,0 +1,10 @@
+(ns foreclojure.datatable
+ (:require [cheshire.core :as json])
+ (:use [foreclojure.ring :only [wrap-json wrap-debug]]
+ [foreclojure.users :only [user-datatable-query]]
+ [compojure.core :only [routes GET]]))
+
+(def datatable-routes
+ (-> (routes
+ (GET "/datatable/users" [& more] {:body (user-datatable-query more)}))
+ (wrap-json)))
@@ -2,9 +2,11 @@
(:require [foreclojure.users :as users]
[sandbar.stateful-session :as session]
[clojure.string :as s]
- [ring.util.response :as response])
+ [ring.util.response :as response]
+ [cheshire.core :as json])
(: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]]
+ (: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]]
@@ -18,8 +20,7 @@
[hiccup.core :only [html]]
[useful.debug :only [?]]
[amalloy.utils :only [defcomp]]
- [compojure.core :only [defroutes GET POST]]
- [clojure.contrib.json :only [json-str]]))
+ [compojure.core :only [defroutes GET POST]]))
(def solved-stats (agent {:total 0}))
@@ -212,7 +213,7 @@ Return a map, {:message, :error, :url, :num-tests-passed}."
(let [light-img (image-builder {:red ["red" "test failed"]
:green ["green" "test passed"]
:blue ["blue" "test not run"]}
- :src #(str "/images/" % "light.png"))]
+ :src #(str "images/" % "light.png"))]
(defn render-test-cases [tests]
[:table {:class "testcases"}
(let [fail (session/flash-get :failing-test)]
@@ -248,11 +249,11 @@ Return a map, {:message, :error, :url, :num-tests-passed}."
(defn rest-run-code [id raw-code]
(let [{:keys [message error url num-tests-passed]} (run-code id raw-code)]
- (json-str {:failingTest num-tests-passed
- :message message
- :error error
- :golfScore (html (render-golf-score))
- :golfChart (html (render-golf-chart))})))
+ (json/generate-string {:failingTest num-tests-passed
+ :message message
+ :error error
+ :golfScore (html (render-golf-score))
+ :golfChart (html (render-golf-chart))})))
(defn wants-no-javascript-codebox? []
(when (session/session-get :user)
@@ -308,10 +309,11 @@ Return a map, {:message, :error, :url, :num-tests-passed}."
(when (wants-no-javascript-codebox?) [:span#disable-javascript-codebox])
(text-area {:id "code-box"
:spellcheck "false"}
- :code (or (session/flash-get :code)
- (-> (session/session-get :user)
- (get-user-id)
- (get-solution ,,, _id))))
+ :code (escape-html
+ (or (session/flash-get :code)
+ (-> (session/session-get :user)
+ (get-user-id)
+ (get-solution ,,, _id)))))
[:div#golfgraph
(render-golf-chart)]
(hidden-field :id id)
@@ -342,7 +344,7 @@ Return a map, {:message, :error, :url, :num-tests-passed}."
(with-user [{:keys [_id following]}]
(list
(let [user-code (get-solution :public _id problem-id)]
- [:pre.solution-code.solution-user-code user-code])
+ [:pre.solution-code.solution-user-code (escape-html user-code)])
(if (empty? following)
[:p "You can only see solutions of users whom you follow. Click on any name from the " (link-to "/users" "users") " listing page to see their profile, and click follow from there."]
(if (some (complement nil?) (map #(get-solution :public % problem-id) following))
@@ -357,7 +359,7 @@ Return a map, {:message, :error, :url, :num-tests-passed}."
:when f-code]
[:div.follower-solution
[:div.solution-username (str f-user "'s solution:")]
- [:pre.solution-code f-code]]))
+ [:pre.solution-code (escape-html f-code)]]))
[:p "None of the users you follow have solved this problem yet!"])))))})
(defn show-solutions [id]
@@ -373,8 +375,8 @@ Return a map, {:message, :error, :url, :num-tests-passed}."
(session/session-put! :login-to *url*)
(flash-error "/login" "You must log in to see solutions!")))))
-(let [checkbox-img (image-builder {true ["/images/checkmark.png" "completed"]
- false ["/images/empty-sq.png" "incomplete"]})]
+(let [checkbox-img (image-builder {true ["images/checkmark.png" "completed"]
+ false ["images/empty-sq.png" "incomplete"]})]
(def-page problem-list-page []
{:title "4clojure - Problem Listing"
:content
@@ -578,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)
Oops, something went wrong.

0 comments on commit e2911e3

Please sign in to comment.