Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: darrenaustin/4clojure
base: e452d42cab
...
head fork: okofish/4clojure
compare: 54c9333b4c
  • 12 commits
  • 10 files changed
  • 0 commit comments
  • 2 contributors
Commits on Sep 29, 2011
@amalloy amalloy Merge branch 'feature/followbox' into develop f0b263f
@amalloy amalloy Fix ranking 24b1716
Commits on Sep 30, 2011
@amalloy amalloy Version number de7dcac
@amalloy amalloy Merge branch 'release/1.4.0' into develop 5b86579
@amalloy amalloy Allow different behavior per hostname 4ef7b4e
@amalloy amalloy Version number b11820f
@amalloy amalloy Merge branch 'release/1.4.1' into develop 6f20d46
@amalloy amalloy Only use compojure 'site' for dynamic route; it's got cookies and stuff bf7e1f9
@amalloy amalloy Fix wrap-versioned-expiry.
It was never returning nil, even if it couldn't find the file.
7ccb3a2
@amalloy amalloy Version number 2580db5
@amalloy amalloy Merge branch 'release/1.4.2' into develop 611e296
@darrenaustin 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
View
3  config.clj
@@ -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
View
2  project.clj
@@ -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"]
View
27 resources/public/css/style.css
@@ -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;
}
View
19 resources/public/script/foreclojure.js
@@ -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)
View
6 src/foreclojure/config.clj
@@ -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)))
View
71 src/foreclojure/core.clj
@@ -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 []
View
29 src/foreclojure/ring.clj
@@ -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"))))
View
19 src/foreclojure/template.clj
@@ -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]]
View
45 src/foreclojure/users.clj
@@ -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)))
View
9 src/foreclojure/utils.clj
@@ -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.