From 8958e5c159ce00cfdeb1d934396d6337010e0e12 Mon Sep 17 00:00:00 2001 From: Alex McNamara Date: Sat, 24 Sep 2011 00:46:31 -0400 Subject: [PATCH] More style fixes. Templated unapproved problems, settings/golf, login --- resources/public/css/style.css | 8 +- src/foreclojure/golf.clj | 40 ++++++---- src/foreclojure/login.clj | 135 +++++++++++++++++---------------- src/foreclojure/problems.clj | 56 +++++++------- src/foreclojure/template.clj | 21 ++--- 5 files changed, 145 insertions(+), 115 deletions(-) diff --git a/resources/public/css/style.css b/resources/public/css/style.css index 39cdf1a6..d27a1d98 100644 --- a/resources/public/css/style.css +++ b/resources/public/css/style.css @@ -239,7 +239,7 @@ div#heading-note { margin: 11px 0 0 13px; } -div#subheading { +div#sub-heading { font-size: 13px; font-weight: normal; display: block; @@ -248,6 +248,12 @@ div#subheading { margin-bottom: 10px; } +div#main { + display: block; + float: left; + clear: both; +} + div.message #flash-text {} div.message #error-text {} diff --git a/src/foreclojure/golf.clj b/src/foreclojure/golf.clj index f1b52638..97b37889 100644 --- a/src/foreclojure/golf.clj +++ b/src/foreclojure/golf.clj @@ -4,7 +4,7 @@ (:use [hiccup.form-helpers :only [form-to check-box]] [hiccup.page-helpers :only [link-to]] [foreclojure.utils :only [with-user]] - [foreclojure.template :only [def-page]] + [foreclojure.template :only [def-page content-page]] [foreclojure.users :only [golfer?]] [compojure.core :only [defroutes POST GET]] [somnium.congomongo :only [update!]])) @@ -12,20 +12,32 @@ (def-page golfer-page [] "Your preferences have been saved.") +(defn golf-opt-in-box [user-obj] + (list + [:table + (form-to [:post "/golf/opt-in"] + [:tr + [:td + (check-box :opt-in + (golfer? user-obj)) + [:label {:for "opt-in"} + "I want to join the golf league and compete to find the shortest solutions"]]] + [:tr [:td [:button {:type "submit"} "Update"]]])])) + (def-page opt-in-page [] - (with-user [user-obj] - [:div - [:h2 "League sign-up"] - [:div#explain "While the primary purpose of 4clojure.com is to teach Clojure \"by doing\", you may also choose to compete for the shortest solution. This is affectionately known as " (link-to "http://lbrandy.com/blog/2008/09/what-code-golf-taught-me-about-python/" "code golf") ": the lower your score the better, get it? If you choose to participate, we'll score your correct solutions based on the number of non-whitespace characters (and some more metrics in the future). We'll also provide a chart showing how you stack up compared to everyone else on the site."] - [:table - (form-to [:post "/golf/opt-in"] - [:tr - [:td - (check-box :opt-in - (golfer? user-obj)) - [:label {:for "opt-in"} - "I want to join the golf league and compete to find the shortest solutions"]]] - [:tr [:td [:button {:type "submit"} "Update"]]])]])) + (with-user [{:keys [user] :as user-obj}] + {:title "Account Settings" + :content + (content-page + {:heading "League sign-up" + :sub-heading (list "While the primary purpose of 4clojure.com is to teach Clojure \"by doing\", you may " + "also choose to compete for the shortest solution. This is affectionately known as " + (link-to "http://lbrandy.com/blog/2008/09/what-code-golf-taught-me-about-python/" "code golf") + ": the lower your score the better, get it? If you choose to participate, we'll score " + "your correct solutions based on the number of non-whitespace characters (and some more " + "metrics in the future). We'll also provide a chart showing how you stack up compared " + "to everyone else on the site.") + :main (golf-opt-in-box user-obj)})})) (defn set-golfer [opt-in] (with-user [{:keys [_id]}] diff --git a/src/foreclojure/login.clj b/src/foreclojure/login.clj index f308f66c..a149b09d 100644 --- a/src/foreclojure/login.clj +++ b/src/foreclojure/login.clj @@ -4,7 +4,7 @@ (:import [org.jasypt.util.password StrongPasswordEncryptor]) (:use [hiccup.form-helpers :only [form-to label text-field password-field check-box]] [foreclojure.utils :only [from-mongo flash-error flash-msg with-user form-row assuming send-email login-url]] - [foreclojure.template :only [def-page]] + [foreclojure.template :only [def-page content-page]] [foreclojure.users :only [disable-codebox? set-disable-codebox hide-solutions? set-hide-solutions]] [compojure.core :only [defroutes GET POST]] [useful.map :only [keyed]] @@ -12,31 +12,30 @@ [clojure.stacktrace :only [print-cause-trace]] [somnium.congomongo :only [update! fetch-one]])) +(def login-box + (form-to [:post "/login"] + [:table + [:tr + [:td (label :user "Username")] + [:td (text-field :user)]] + [:tr + [:td (label :pwd "Password")] + [:td (password-field :pwd)]] + [:tr + [:td] + [:td [:button {:type "submit"} "Log In"]]] + [:tr + [:td] + [:td + [:a {:href "/login/reset"} "Forgot your password?"]]]])) + (def-page my-login-page [location] - {:title "4clojure - login" - :content - (list - (when location - (session/session-put! :login-to location) - nil) ;; don't include this in HTML output - [:div.error - (session/flash-get :error) - (session/flash-get :message)] - (form-to [:post "/login"] - [:table - [:tr - [:td (label :user "Username")] - [:td (text-field :user)]] - [:tr - [:td (label :pwd "Password")] - [:td (password-field :pwd)]] - [:tr - [:td] - [:td [:button {:type "submit"} "Log In"]]] - [:tr - [:td] - [:td - [:a {:href "/login/reset"} "Forgot your password?"]]]]))}) + (do + (if location (session/session-put! :login-to location)) + {:title "4clojure - login" + :content + (content-page + {:main login-box})})) (defn do-login [user pwd] (let [user (.toLowerCase user) @@ -51,48 +50,56 @@ (response/redirect (or location "/problems"))) (flash-error "Error logging in." "/login")))) -;; TODO this page is getting hella gross. Need a real Settings page soon. +(defn account-settings-box [user] + [:table + (form-to [:post "/login/update"] + (map form-row + [[text-field :new-username "Username" user] + [password-field :old-pwd "Current password"] + [password-field :pwd "New password"] + [password-field :repeat-pwd "Repeat password"]]) + [:tr + [:td [:button {:type "submit"} "Reset now"]]])]) + +(defn js-settings-box [user-obj] + (list + [:p "Selecting this will disable the JavaScript code entry box and just give you plain text entry"] + (form-to [:post "/users/set-disable-codebox"] + (check-box :disable-codebox + (disable-codebox? user-obj)) + [:label {:for "disable-codebox"} + "Disable JavaScript in code entry box"] + [:br] + [:div#button-div + [:button {:type "submit"} "Submit"]]))) + +(defn hide-settings-box [user-obj] + (list + [:p "When you solve a problem, we allow any user who has solved a problem to view your solutions to that problem. Check this box to keep your solutions private."] + (form-to [:post "/users/set-hide-solutions"] + (check-box :hide-solutions + (hide-solutions? user-obj)) + [:label {:for "hide-solutions"} + "Hide my solutions"] + [:br] + [:div#button-div + [:button {:type "submit"} "Submit"]]))) + (def-page update-credentials-page [] - {:title "Change password" - :content - (with-user [{:keys [user] :as user-obj}] - [:div#account-settings - [:div#update-pwd - [:h2 "Change password for " user] - [:span.error (session/flash-get :error)] - [:table - (form-to [:post "/login/update"] - (map form-row - [[text-field :new-username "Username" user] - [password-field :old-pwd "Current password"] - [password-field :pwd "New password"] - [password-field :repeat-pwd "Repeat password"]]) - [:tr - [:td [:button {:type "submit"} "Reset now"]]])] - [:hr] - [:div#settings-codebox + (with-user [{:keys [user] :as user-obj}] + {:title "Change password" + :content + (content-page + {:main + (list + [:h2 "Change password for " user] + [:div#account-settings (account-settings-box user)] + [:hr] [:h2 "Disable JavaScript Code Box"] - [:p "Selecting this will disable the JavaScript code entry box and just give you plain text entry"] - (form-to [:post "/users/set-disable-codebox"] - (check-box :disable-codebox - (disable-codebox? user-obj)) - [:label {:for "disable-codebox"} - "Disable JavaScript in code entry box"] - [:br] - [:div#button-div - [:button {:type "submit"} "Submit"]])] - [:hr] - [:div#settings-follow + [:div#settings-codebox (js-settings-box user-obj)] + [:hr] [:h2 "Hide My Solutions"] - [:p "When you solve a problem, we allow any user who has solved a problem to view your solutions to that problem. Check this box to keep your solutions private."] - (form-to [:post "/users/set-hide-solutions"] - (check-box :hide-solutions - (hide-solutions? user-obj)) - [:label {:for "hide-solutions"} - "Hide my solutions"] - [:br] - [:div#button-div - [:button {:type "submit"} "Submit"]])]]])}) + [:div#settings-follow (hide-settings-box user-obj)])})})) (defn do-update-credentials! [new-username old-pwd new-pwd repeat-pwd] (with-user [{:keys [user pwd]}] diff --git a/src/foreclojure/problems.clj b/src/foreclojure/problems.clj index b7a1a95b..238090f5 100644 --- a/src/foreclojure/problems.clj +++ b/src/foreclojure/problems.clj @@ -5,7 +5,7 @@ [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]] - [foreclojure.template :only [def-page]] + [foreclojure.template :only [def-page content-page]] [foreclojure.social :only [tweet-link gist!]] [foreclojure.feeds :only [create-feed]] [foreclojure.users :only [golfer? get-user-id disable-codebox?]] @@ -400,34 +400,36 @@ Return a map, {:message, :error, :url, :num-tests-passed}." [:td.centered (checkbox-img (contains? solved id))]]) problems))])})) +(defn generate-unapproved-problems-list [] + (let [problems (get-problem-list {:approved false})] + (list + [:table#unapproved-problems.my-table + [:thead + [:tr + [:th "Title"] + [:th "Difficulty"] + [:th "Topics"] + [:th "Submitted By"]]] + (map-indexed + (fn [x {:keys [title difficulty tags user], id :_id}] + [:tr (row-class x) + [:td.titlelink + [:a {:href (str "/problem/" id)} + title]] + [:td.centered difficulty] + [:td.centered + (s/join " " (map #(str "" % "") + tags))] + [:td.centered user]]) + problems)]))) + (def-page unapproved-problem-list-page [] {:title "Unapproved problems" :content - (list - [:div.message (session/flash-get :message)] - [:div#problems-error.error (session/flash-get :error)] - [:table#unapproved-problems.my-table - [:thead - [:tr - [:th "Title"] - [:th "Difficulty"] - [:th "Topics"] - [:th "Submitted By"]]] - (let [problems (get-problem-list {:approved false})] - (map-indexed - (fn [x {:keys [title difficulty tags user], id :_id}] - [:tr (row-class x) - [:td.titlelink - [:a {:href (str "/problem/" id)} - title]] - [:td.centered difficulty] - [:td.centered - (s/join " " (map #(str "" % "") - tags))] - [:td.centered user]]) - problems))])}) - -(defn unapproved-problem-list [] + (content-page + {:main (generate-unapproved-problems-list)})}) + +(defn access-unapproved-problem-list-page [] (let [user (session/session-get :user)] (if (approver? user) (unapproved-problem-list-page) @@ -563,7 +565,7 @@ Return a map, {:message, :error, :url, :num-tests-passed}." (GET "/problems/submit" [] (problem-submission-page)) (POST "/problems/submit" [prob-id author title difficulty tags restricted description code] (create-problem title difficulty tags restricted description code (when (not= "" prob-id) (Integer. prob-id)) author)) - (GET "/problems/unapproved" [] (unapproved-problem-list)) + (GET "/problems/unapproved" [] (access-unapproved-problem-list-page)) (GET "/problem/:id/edit" [id] (edit-problem (Integer. id))) (POST "/problem/edit" [id] diff --git a/src/foreclojure/template.clj b/src/foreclojure/template.clj index 5e17c455..4faf4e2c 100644 --- a/src/foreclojure/template.clj +++ b/src/foreclojure/template.clj @@ -84,15 +84,18 @@ )]]]))) ;; Content templates -(defn content-page [body-map] - (list - [:div#heading (:heading body-map)] - [:div#heading-note (:heading-note body-map)] - [:div#subheading (:sub-heading body-map)] - [:div.message - [:span#flash-text (session/flash-get :message)] - [:span#error-text (session/flash-get :error)]] - [:div.main (:main body-map)])) +(defn content-page [{:keys [heading heading-note sub-heading main]}] + (let [flash-message (session/flash-get :message) + flash-error (session/flash-get :error)] + (list + (when heading [:div#heading heading]) + (when heading-note [:div#heading-note heading-note]) + (when sub-heading [:div#sub-heading sub-heading]) + (when flash-message [:div.message + [:span#flash-text flash-message]]) + (when flash-error [:div.message + [:span#error-text flash-error]]) + (when main [:div#main main])))) (defmacro def-page [page-name [& args] & code] `(defn ~page-name [~@args]