Browse Source

Rough, incomplete first version of the account management UI

master
parent
commit
b63565ff9f
4 changed files with 117 additions and 26 deletions
  1. +5
    -3
      resources/index.html
  2. +26
    -3
      src/clj/character_suite/server.clj
  3. +12
    -0
      src/clj/character_suite/store.clj
  4. +74
    -20
      src/cljs/character_suite/core.cljs

+ 5
- 3
resources/index.html View File

@ -31,9 +31,11 @@
</div>
</div -->
<!-- footer class="footer">
<p>&copy; Company 2014</p>
</footer -->
<footer class="footer">
<p>&copy; Esteban Manchado Velázquez 2015 &mdash; See
the <a href="https://github.com/emanchado/character-suite">full
source code</a> on GitHub.</p>
</footer>
<!-- <script src="//fb.me/react-0.9.0.js"></script> -- -->
<!-- <script src="//code.jquery.com/jquery-1.11.0.js"></script> -- -->


+ 26
- 3
src/clj/character_suite/server.clj View File

@ -9,7 +9,7 @@
org.apache.commons.io.IOUtils)
(:require [clojure.java.io :as io]
[character-suite.dev :refer [is-dev? browser-repl start-figwheel]]
[compojure.core :refer [GET POST defroutes]]
[compojure.core :refer [GET POST DELETE defroutes]]
[compojure.route :refer [resources not-found]]
[ring.middleware.reload :as reload]
[environ.core :refer [env]]
@ -62,7 +62,7 @@
saved-character (store/save-character-data (env :database-url)
incoming-character)]
{:body (pr-str {:id saved-character})
:headers {"Content-Type" "text/plain"}
:headers {"Content-Type" "application/edn"}
:session (assoc (:session request) :character (:id saved-character))}))
(deftemplate login-page (io/resource "login.html") [request]
@ -98,6 +98,27 @@
{:status 401
:body "The current password is wrong"})))
(defn- get-users-handler [request]
{:status 200
:headers {"Content-Type" "application/edn"}
:body (pr-str (store/get-users (env :database-url)))})
(defn- delete-users-handler [request]
(let [current-user (-> request :session :logged-in-as)
is-admin? (store/is-user-admin? (env :database-url) current-user)
user-to-delete (-> request :params :username)]
(if is-admin?
(if (= current-user user-to-delete)
{:status 403
:body "You cannot delete yourself"}
(if (store/delete-user (env :database-url) user-to-delete)
{:status 200
:body (str "User '" user-to-delete "' deleted")}
{:status 500
:body (str "Could not delete user '" user-to-delete "'")}))
{:status 403
:body "You don't have permissions to delete users"})))
(defn- post-login-handler [request]
(let [username (get (:form-params request) "username")
password (get (:form-params request) "password")
@ -115,7 +136,9 @@
(GET "/api/characters" [] (logged-in-handler get-api-characters-handler))
(GET "/characters/:id/pdf" [id] (logged-in-handler get-character-pdf-handler))
(POST "/api/characters" [] (logged-in-handler post-characters-handler))
(POST "/account" [] (logged-in-handler post-account-handler))
(POST "/api/account" [] (logged-in-handler post-account-handler))
(GET "/api/users" [] (logged-in-handler get-users-handler))
(DELETE "/api/users/:username" [] (logged-in-handler delete-users-handler))
(POST "/login" [] post-login-handler)
(GET "/logout" [] get-logout-handler)
(GET "/" [] (logged-in-handler (io/resource "index.html")))


+ 12
- 0
src/clj/character_suite/store.clj View File

@ -131,3 +131,15 @@
:users
{:password hash-password}
["username = ?" username])))
(defn get-users [db-spec]
(vec (query db-spec "SELECT username, role FROM users")))
(defn is-user-admin? [db-spec username]
(let [result (first (query db-spec
["SELECT role FROM users WHERE username = ?"
username]))]
(= (:role result) "admin")))
(defn delete-user [db-spec username]
(delete! db-spec :users ["username = ?" username]))

+ 74
- 20
src/cljs/character_suite/core.cljs View File

@ -1,10 +1,10 @@
(ns character-suite.core
(:require [ajax.core :refer [GET POST]]
(:require [ajax.core :refer [GET POST DELETE]]
[character-suite.character :as c]
[character-suite.character.coc :as coc]
[character-suite.character.soy :as soy]
[character-suite.views :as views]
[cljs.core.async :refer [chan <!]]
[cljs.core.async :refer [chan put! <!]]
[cljs.reader :as reader]
[clojure.set :as set]
[clojure.string :as str]
@ -18,6 +18,7 @@
(defonce app-state (atom {:mode :init}))
(def ch (chan))
(def deletion-ch (chan))
(defn- post-character [character & [handler]]
(let [plain-character (into {:rule-system (c/rulesystem character)}
@ -30,7 +31,6 @@
(go
(while true
(let [new-value (<! ch)]
(println (str "Receiving update " new-value))
(swap! app-state update-in [:character] (fn [character]
(c/patch character new-value)))
(when-not (-> @app-state :character :props :next-step)
@ -40,6 +40,13 @@
{:mode :init,
:available-characters nil})))))))
(go
(while true
(let [user-to-delete (<! deletion-ch)]
(DELETE (str "/api/users/" user-to-delete)
{:handler (fn [_]
(swap! app-state assoc-in [:users] nil))}))))
(defn- character-edit-view [app owner]
(reify om/IRender
(render [_]
@ -78,7 +85,7 @@
:text "Oh, come on, it's empty"})
(if (= new-password confirm-new-password)
(do
(POST "/account"
(POST "/api/account"
{:format :url
:params {:old-password old-password
:new-password new-password}
@ -125,17 +132,61 @@
:onClick (update-password-fn app)}
"Change password")))))))
(defn about-view [app owner]
(defn user-view [user owner]
(reify om/IRenderState
(render-state [_ {:keys [deletion-ch]}]
(dom/tr nil
(dom/td nil
(if (= (:role user) "admin")
(dom/strong nil (:username user))
(:username user)))
(dom/td nil
(dom/button #js {:type "submit"
:className "btn btn-default btn-sm"
:onClick (fn [e]
)}
" Edit"))
(dom/td nil
(if (not= (:username user)
"admin")
(dom/button #js {:type "submit"
:className "btn btn-danger btn-sm"
:onClick (fn [_]
(when (.confirm js/window
(str "Delete user '" (:username user) "'?"))
(put! deletion-ch (:username user))))}
(dom/span #js {:className "glyphicon glyphicon-remove"})
" Delete user")))))))
(defn users-view [app owner]
(reify om/IRender
(render [_]
(dom/div nil
(dom/p nil
"RPG Character Suite was written by Esteban
Manchado Velázquez in Clojure and
ClojureScript. The "
(dom/a #js {:href "https://github.com/emanchado/character-suite"}
"full source code")
" is open source and available on GitHub.")))))
(when (nil? (:users app))
(om/update! app :users [])
(GET "/api/users"
{:format :edn
:handler (fn [available-users]
(om/update! app
:users
available-users))}))
(dom/div #js {:className "row marketing"}
(dom/h2 nil "Users")
(dom/div #js {:className (str "alert alert-"
(get-in app [:users :message :type]))
:role "alert"}
(get-in app [:users :message :text]))
(dom/table #js {:className "table table-striped"}
(dom/thead nil
(dom/tr nil
(dom/th nil "Username")
(dom/th #js {:colSpan "2"}
"Actions")))
(apply dom/tbody #js {:className "table-hover"}
(om/build-all user-view
(:users app)
{:init-state {:deletion-ch deletion-ch}})))))))
(defn- character-load [app owner]
(reify om/IRenderState
@ -206,7 +257,7 @@
:init (om/build character-list-view app)
:edit (om/build character-edit-view app)
:account (om/build account-view app)
:about (om/build about-view app)))))
:users (om/build users-view app)))))
(defn menu-item-classname [current-mode this-mode]
(if (= current-mode this-mode)
@ -220,7 +271,8 @@
(dom/ul #js {:className "nav nav-pills pull-right"}
(dom/li #js {:role "presentation"
:className (menu-item-classname (:mode app) :init)}
(dom/a #js {:onClick (fn [e]
(dom/a #js {:href "#/"
:onClick (fn [e]
(swap! app-state
merge
{:mode :init}))}
@ -228,7 +280,8 @@
(dom/li #js {:role "presentation"
:className (menu-item-classname (:mode app)
:account)}
(dom/a #js {:onClick (fn [e]
(dom/a #js {:href "#/account"
:onClick (fn [e]
(swap! app-state
merge
{:settings {}})
@ -237,12 +290,13 @@
{:mode :account}))}
"Account"))
(dom/li #js {:role "presentation"
:className (menu-item-classname (:mode app) :about)}
(dom/a #js {:onClick (fn [e]
:className (menu-item-classname (:mode app) :users)}
(dom/a #js {:href "#/users"
:onClick (fn [e]
(swap! app-state
merge
{:mode :about}))}
"About"))
{:mode :users}))}
"Users"))
(dom/li #js {:role "presentation"
:className (menu-item-classname (:mode app) :logout)}
(dom/a #js {:href "/logout"}


Loading…
Cancel
Save