Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

We’re showing branches in this repository, but you can also compare across forks.

base fork: darrenaustin/4clojure
base: e452d42cab
...
head fork: lambdachops/4clojure
compare: 54c9333b4c
  • 12 commits
  • 10 files changed
  • 0 commit comments
  • 2 contributors
Commits on Sep 29, 2011
Alan Malloy amalloy Merge branch 'feature/followbox' into develop f0b263f
Alan Malloy amalloy Fix ranking 24b1716
Commits on Sep 30, 2011
Alan Malloy amalloy Version number de7dcac
Alan Malloy amalloy Merge branch 'release/1.4.0' into develop 5b86579
Alan Malloy amalloy Allow different behavior per hostname 4ef7b4e
Alan Malloy amalloy Version number b11820f
Alan Malloy amalloy Merge branch 'release/1.4.1' into develop 6f20d46
Alan Malloy amalloy Only use compojure 'site' for dynamic route; it's got cookies and stuff bf7e1f9
Alan Malloy amalloy Fix wrap-versioned-expiry.
It was never returning nil, even if it couldn't find the file.
7ccb3a2
Alan Malloy amalloy Version number 2580db5
Alan Malloy amalloy Merge branch 'release/1.4.2' into develop 611e296
Darren Austin Finished the work for adding the "following" checkboxes to the users …
…page. (Issue 117)

- In the case where the user has javascript disabled, we fall back to
just showing a "yes" in the following column instead of the checkbox.
- Optimized checking to see if a user was being followed by making the
following vector into a set.
- Hooked up the "Follow" buttons on the user profile page to use AJAX
instead of another page load (which also fixes the back button issue
with this).
54c9333
3  config.clj
View
@@ -9,6 +9,9 @@
:advanced-user-count 50
:pass ""
:repo-url "https://github.com/4clojure/4clojure"
+ :hosts {;; :static "static.4clojure.com"
+ ;; :dynamic "www.4clojure.com"
+ :redirects ["4clojure.com"]}
:golfing-active true
:heartbeat nil ; set to, eg, [1 :hour] for periodic heap information on stdout
;; this list is just for bootstrapping - the real DB is authoritative
2  project.clj
View
@@ -1,4 +1,4 @@
-(defproject foreclojure "1.3.3"
+(defproject foreclojure "1.4.2"
:description "4clojure - a website for lisp beginners"
:dependencies [[clojure "1.2.1"]
[clojure-contrib "1.2.0"]
27 resources/public/css/style.css
View
@@ -217,6 +217,33 @@ a.novisited {color: #00e;}
padding: 15px;
}
+/*
+ * If the user has javascript turned on, show the
+ * "following" checkboxes and turn off the "following"
+ * text labels.
+ */
+#user-table input.following, {
+ display: inline;
+}
+
+#user-table span.following {
+ display: none;
+}
+
+/*
+ * If the user has javascript disabled, hide the
+ * "following" checkboxes and fallback to showing
+ * the "following" text labels.
+ */
+#user-table.javascript-disabled input.following {
+ display: none;
+}
+
+#user-table.javascript-disabled span.following {
+ display: inline;
+}
+
+
span.error, div.error {
color: red;
}
19 resources/public/script/foreclojure.js
View
@@ -22,6 +22,25 @@ $(document).ready(function() {
$(this).parents("form").attr("action", "/problem/edit").submit();
});
+ $("button.user-follow-button").live("click", function(e) {
+ e.preventDefault();
+ var $form = $(this).parents("form");
+ var $button = $(this);
+ $.ajax({type: "POST",
+ url: "/rest" + $form.attr("action"),
+ dataType: "json",
+ success: function(data) {
+ if (data) {
+ $button.text(data["next-label"]);
+ $form.attr("action", data["next-action"]);
+ }
+ },
+ });
+ return false;
+ });
+
+ $("#user-table").removeClass('javascript-disabled');
+
$("form input.following").live("click", function(e) {
e.preventDefault();
var $checkbox = $(this)
6 src/foreclojure/config.clj
View
@@ -9,3 +9,9 @@
;; Defs both for convenience and compile-time verification of simple settings
(def repo-url (or (:repo-url config)
(throw (Exception. "config.clj needs a :repo-url key"))))
+
+(letfn [(host [key]
+ (get-in config [:hosts key]))]
+ (def static-host (host :static))
+ (def dynamic-host (host :dynamic))
+ (def redirect-hosts (host :redirects)))
71 src/foreclojure/core.clj
View
@@ -1,14 +1,15 @@
(ns foreclojure.core
(:require [compojure.route :as route]
[compojure.handler :as handler]
+ [foreclojure.config :as config]
[sandbar.stateful-session :as session])
- (:use [compojure.core :only [defroutes GET]]
+ (:use [compojure.core :only [defroutes routes GET]]
[foreclojure.static :only [static-routes welcome-page]]
[foreclojure.problems :only [problems-routes]]
[foreclojure.login :only [login-routes]]
[foreclojure.register :only [register-routes]]
[foreclojure.golf :only [golf-routes]]
- [foreclojure.ring :only [resources wrap-strip-trailing-slash wrap-url-as-file wrap-versioned-expiry]]
+ [foreclojure.ring :only [resources wrap-strip-trailing-slash wrap-url-as-file wrap-versioned-expiry split-hosts wrap-404 wrap-debug]]
[foreclojure.users :only [users-routes]]
[foreclojure.config :only [config]]
[foreclojure.social :only [social-routes]]
@@ -25,31 +26,55 @@
(def *block-server* false)
-(defroutes main-routes
- (GET "/" [] (welcome-page))
- login-routes
- register-routes
- problems-routes
- users-routes
- static-routes
- social-routes
- version-routes
- graph-routes
- golf-routes
+(defroutes resource-routes
(-> (resources "/*")
(wrap-url-as-file)
(wrap-file-info)
- (wrap-versioned-expiry))
- (route/not-found "Page not found"))
+ (wrap-versioned-expiry)))
-(def app (-> #'main-routes
- ((if (:wrap-reload config)
- #(wrap-reload % '(foreclojure.core))
- identity))
- session/wrap-stateful-session
- handler/site
- wrap-uri-binding
- wrap-strip-trailing-slash
+(def dynamic-routes
+ (-> (routes (GET "/" [] (welcome-page))
+ login-routes
+ register-routes
+ problems-routes
+ users-routes
+ static-routes
+ social-routes
+ version-routes
+ graph-routes
+ golf-routes)
+ ((if (:wrap-reload config)
+ #(wrap-reload % '(foreclojure.core))
+ identity))
+ session/wrap-stateful-session
+ wrap-uri-binding
+ handler/site
+ wrap-strip-trailing-slash))
+
+(let [canonical-host (or config/dynamic-host "www.4clojure.com")]
+ (defn redirect-routes [request]
+ (let [{:keys [scheme uri]} request
+ proper-uri (str (name scheme)
+ "://"
+ canonical-host
+ uri)]
+ {:status 302
+ :headers {"Location" proper-uri}
+ :body (str "<a href='" proper-uri "'>"
+ proper-uri
+ "</a>")})))
+
+(def host-handlers (reduce into
+ {:default (routes dynamic-routes resource-routes)}
+ [(for [host config/redirect-hosts]
+ [host redirect-routes])
+ (for [[host route] [[config/static-host resource-routes]
+ [config/dynamic-host dynamic-routes]]
+ :when host]
+ [host (wrap-debug route)])]))
+
+(def app (-> (split-hosts host-handlers)
+ wrap-404
wrap-gzip))
(defn register-heartbeat []
29 src/foreclojure/ring.clj
View
@@ -1,9 +1,11 @@
(ns foreclojure.ring
(:require [clojure.java.io :as io]
- [clojure.string :as s])
+ [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]]))
;; copied from compojure.route, modified to use File instead of Stream
@@ -31,8 +33,23 @@
(defn wrap-versioned-expiry [handler]
(fn [request]
- (-> request
- (update-in [:uri] strip-version-number)
- (handler)
- (assoc-in [:headers "Cache-control"]
- "public, max-age=31536000"))))
+ (when-let [resp (handler
+ (update-in request [:uri] strip-version-number))]
+ (assoc-in resp [:headers "Cache-control"]
+ "public, max-age=31536000"))))
+
+(defn wrap-debug [handler]
+ (fn [request]
+ (? (handler (? request)))))
+
+(defn split-hosts [host-handlers]
+ (let [default (:default host-handlers)]
+ (fn [request]
+ (let [host (get-in request [:headers "host"])
+ handler (or (host-handlers host) default)]
+ (handler request)))))
+
+(defn wrap-404 [handler]
+ (fn [request]
+ (or (handler request)
+ (route/not-found "Page not found"))))
19 src/foreclojure/template.clj
View
@@ -3,7 +3,7 @@
(: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?]]))
+ [foreclojure.utils :only [css js page-attributes rendering-info login-url approver? can-submit? static-url]]))
;; Global wrapping template
(defn html-doc [body]
@@ -14,22 +14,23 @@
[:html
[:head
[:title (:title attrs)]
- [:link {:rel "alternate" :type "application/atom+xml" :title "Atom" :href "http://4clojure.com/problems/rss"}]
- [:link {:rel "shortcut icon" :href "/favicon2.ico"}]
+ [:link {:rel "alternate" :type "application/atom+xml" :title "Atom" :href "/problems/rss"}]
+ [:link {:rel "shortcut icon" :href (static-url "favicon2.ico")}]
[:style {:type "text/css"}
".syntaxhighlighter { overflow-y: hidden !important; }"]
- (css "/css/style.css" "/css/demo_table.css" "/css/shCore.css" "/css/shThemeDefault.css")
- (js "/vendor/script/jquery-1.5.2.min.js" "/vendor/script/jquery.dataTables.min.js")
- (js "/script/foreclojure.js")
- (js "/vendor/script/xregexp.js" "/vendor/script/shCore.js" "/vendor/script/shBrushClojure.js")
- (js "/vendor/script/ace/ace.js" "/vendor/script/ace/mode-clojure.js")
+ (css "css/style.css" "css/demo_table.css" "css/shCore.css" "css/shThemeDefault.css")
+ (js "vendor/script/jquery-1.5.2.min.js" "vendor/script/jquery.dataTables.min.js")
+ (js "script/foreclojure.js")
+ (js "vendor/script/xregexp.js" "vendor/script/shCore.js" "vendor/script/shBrushClojure.js")
+ (js "vendor/script/ace/ace.js" "vendor/script/ace/mode-clojure.js")
[:script {:type "text/javascript"} "SyntaxHighlighter.all()"]]
[:body
(when (:fork-banner attrs)
[:div#github-banner [:a {:href repo-url
:alt "Fork 4Clojure on Github!"}]])
[:div#top
- (link-to "/" [:img#logo {:src "/images/4clj-logo.png" :alt "4clojure.com"}])]
+ (link-to "/" [:img#logo {:src (static-url "images/4clj-logo.png")
+ :alt "4clojure.com"}])]
[:div#content
[:div#menu
(for [[link text & [tabbed]]
45 src/foreclojure/users.clj
View
@@ -26,22 +26,25 @@
:only [:user :solved :contributor])))
(defn get-ranked-users []
- (let [users (get-users)]
- (mapcat
- (fn [rank tied-users]
- (for [user (sort-by :user tied-users)]
- (assoc user :rank (inc rank))))
- (range)
- (map second
- (sort-by #(-> % first -)
- (group-by #(count (or (:solved %) []))
- users))))))
+ (let [users (get-users)
+ tied-groups (map val
+ (sort-by #(-> % key -)
+ (group-by #(count (or (:solved %) []))
+ users)))]
+ (first
+ (reduce (fn [[user-list rank] new-group]
+ [(into user-list
+ (for [user (sort-by :user new-group)]
+ (assoc user :rank rank)))
+ (+ rank (count new-group))])
+ [[] 1]
+ tied-groups))))
(defn get-top-100-and-current-user [username]
(let [ranked-users (get-ranked-users)
this-user (first (filter (comp #{username} :user)
ranked-users))
- this-user-ranking (update-in this-user [:rank] #(str (or % "?") " out of " (count ranked-users)))]
+ this-user-ranking (update-in this-user [:rank] #(str (or % "?") " out of " (count ranked-users)))]
{:user-ranking this-user-ranking
:top-100 (take 100 ranked-users)}))
@@ -78,20 +81,20 @@
(defn following-checkbox [current-user-id following user-id user]
(when (and current-user-id (not= current-user-id user-id))
- (let [following? (some #{user-id} following)]
+ (let [following? (following user-id)]
(form-to [:post (follow-url user (not following?))]
[:input.following {:type "checkbox" :name "following"
- :checked following? :value following?}]))))
+ :checked following? :value following?}]
+ [:span.following (when following? "yes")]))))
(defn generate-user-list [user-set]
- (let [[user-id following]
- (if (session/session-get :user)
+ (let [[user-id following]
+ (when (session/session-get :user)
(with-user [{:keys [_id following]}]
- [_id following])
- [nil nil])]
+ [_id (set following)]))]
(list
[:br]
- [:table#user-table.my-table
+ [:table#user-table.my-table.javascript-disabled
[:thead
[:tr
[:th {:style "width: 40px;"} "Rank"]
@@ -117,14 +120,14 @@
:main (generate-user-list (get-ranked-users))})})
(def-page top-users-page []
- (let [username (session/session-get :user)
+ (let [username (session/session-get :user)
{:keys [user-ranking top-100]} (get-top-100-and-current-user username)]
{:title "Top 100 Users"
:content
(content-page
{:heading "Top 100 Users"
:heading-note (list "[show " (link-to "/users/all" "all") "]")
- :sub-heading (list (format-user-ranking user-ranking)
+ :sub-heading (list (format-user-ranking user-ranking)
[:span.contributor "*"] "&nbsp;"
(link-to repo-url "4clojure contributor"))
:main (generate-user-list top-100)})}))
@@ -195,7 +198,7 @@
(update! :users
{:_id _id}
{operation {:following follow-id}}))))
-
+
(defn static-follow-user [username follow?]
(follow-user username follow?)
(response/redirect (str "/user/" username)))
9 src/foreclojure/utils.clj
View
@@ -1,6 +1,7 @@
(ns foreclojure.utils
(:require [sandbar.stateful-session :as session]
[ring.util.response :as response]
+ [foreclojure.config :as config]
[clojure.walk :as walk]
[clojure.string :as string]
[foreclojure.git :as git]
@@ -167,6 +168,12 @@
(>= (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)]
@@ -178,7 +185,7 @@
(letfn [(wrap-versioning [f]
(fn [& files]
(for [file files]
- (f (add-version-number file)))))]
+ (f (static-url (add-version-number file))))))]
(def js (wrap-versioning hiccup/include-js))
(def css (wrap-versioning hiccup/include-css)))

No commit comments for this range

Something went wrong with that request. Please try again.