(ns admin (:import java.io.File) (:require [clojure.contrib.str-utils2 :as s]) (:use compojure email scheduled-agent user utils)) ;; Muting (def *mute-refresh-period-sec* 60) (def fetch-active-mutes-query " SELECT m.*, (m.set_on + m.duration) AS expiry, a.nick AS admin_nick, o.nick AS nick FROM mutes m, users a, users o WHERE (m.set_on + m.duration) > now() AND a.user_id = m.admin_id AND o.user_id = m.user_id AND NOT m.cancelled ") (def fetch-inactive-mutes-query " SELECT m.*, (m.set_on + m.duration) AS expiry, a.nick AS admin_nick, o.nick AS nick, c.nick AS cancel_nick FROM mutes m LEFT OUTER JOIN users o ON (m.user_id = o.user_id) LEFT OUTER JOIN users a ON (m.admin_id = a.user_id) LEFT OUTER JOIN users c ON (m.cancel_admin_id = c.user_id) WHERE m.cancelled OR (m.set_on + m.duration < now()) ") (def fetch-mute-query " SELECT m.*, (m.set_on + m.duration) AS expiry, (m.set_on + m.duration) < now() as expired, a.nick AS admin_nick FROM mutes m, users a WHERE mute_id = ? AND a.user_id = m.admin_id LIMIT 1 ") (defn fetch-active-mutes [] (do-select [fetch-active-mutes-query])) (defn fetch-active-mute-map [] (let [res (fetch-active-mutes)] (zipmap (map :user_id res) res))) (defn fetch-inactive-mutes [] (do-select [fetch-inactive-mutes-query])) (defn fetch-mute [mute-id] (first (do-select [fetch-mute-query mute-id]))) (def *active-mutes* (scheduled-agent fetch-active-mute-map *mute-refresh-period-sec* nil)) (defn parse-pos-interval [time unit] (let [t (maybe-parse-int time 0) u (lower-case unit)] (and (> t 0) (contains? #{"minutes" "hours" "days"} u) (str time " " (pluralize (s/butlast u 1) t))))) (defn insert-mute! [user-id admin-id reason duration] (do-prepared! "INSERT INTO mutes (user_id, admin_id, duration, reason) VALUES (?, ?, CAST (? AS INTERVAL), ?)" [user-id admin-id duration reason]) (update! *active-mutes*)) (defn mute! [session params] (if-vip (let [nick (params :nick) user-id (:user_id (fetch-nick nick)) time (params :time) unit (params :unit) duration (parse-pos-interval time unit) reason (params :reason) admin-id (session :user_id) admin-nick (session :nick)] (cond (not user-id) [400 "INVALID_NICK"] (not duration) [400 "INVALID_DURATION"] :else (do (insert-mute! user-id admin-id reason duration) (send-mute-email nick admin-nick reason duration) "OK"))))) (def mute-cancel-query " UPDATE mutes SET cancelled=true, cancel_admin_id=? WHERE mute_id = ? AND cancelled = false ") (defn cancel-mute! [mute-id admin-id reason] (let [mute (fetch-mute mute-id) active (nor (:expired mute) (:cancelled mute)) qry "mute_id = ? AND cancelled = false AND (set_on + duration) > now()"] (cond (not mute) (resp-error "INVALID_MUTE_ID") (not active) (resp-error "EXPIRED_MUTE") (not reason) (resp-error "NO_REASON") :else (assert-update (do-update :mutes [qry mute-id] {:cancelled true :cancel_admin_id admin-id :cancel_reason reason}) (do (update! *active-mutes*) (resp-success "OK")) (resp-error "UPDATE_ERROR"))))) (defn handle-cancel-mute! [session params] (if-vip (let [mute-id (maybe-parse-int (params :mute_id) 0) reason (params :reason) admin-id (session :user_id)] (cancel-mute! mute-id admin-id reason)))) (defn format-mute [mute] (format (str "I'm sorry, you've been muted for %s. " "You'll be able to post again on %s EST.") (mute :reason) (mute :expiry))) (def mute-formatter {:duration format-interval :set_on format-date-first-timestamp :expiry format-date-first-timestamp :cancelled #(if % "Cancelled" "Expired")}) (defn show-mutes [session] (if-vip (let [st (fetch-template "mutes" session) active (fetch-active-mutes) inactive (fetch-inactive-mutes) formatter (partial apply-formats mute-formatter) f #(map (comp stringify-and-escape formatter) %)] (.setAttribute st "active" (f active)) (.setAttribute st "inactive" (f inactive)) (.toString st)))) ;; Debug Page (defn error-header [& text] (html [:h2 {"color" "red"} text])) (defn exception-to-string [e] (let [sw (java.io.StringWriter.) pw (java.io.PrintWriter. sw)] (.printStackTrace e pw) (.toString sw))) (defn lookup-templates [dir selected] (for [f (.listFiles (File. dir)) :when (and (.isFile f) (.endsWith (.getName f) ".st"))] (let [n (s/butlast (.getName f) 3)] {"template" n "selected" (= selected n)}))) (defn debug-page [session flash] (if-vip (let [mutes (poll *active-mutes*) st (fetch-template "debug" session)] (.setAttribute st "flash" (:msg flash)) (.setAttribute st "mailtemps" (lookup-templates "template/mail" "welcome")) (.setAttribute st "resettemps" (lookup-templates "template/mail" "reset")) (.toString st)))) (defn debug-reg-email [session params] (send-registration-email (params :nick) (params :to) (params :template)) (str "Sent registration mail to " (params :to))) (defn debug-reset-email [session {nick :nick to :to template :template}] (if-let [info (fetch-nick nick)] (let [nick (info :nick) hash (info :hash) ts (System/currentTimeMillis) token (reset-token nick hash ts) link (reset-link nick token ts)] (do (send-reset-email nick to link template) (str "Send reset email to " to))) (error-header "Unknown user " nick))) (def *debug-action-map* {"regemail" debug-reg-email "resetemail" debug-reset-email}) (defn format-unknown-action [action] (error-header "Unknown action " action)) (defn format-debug-exception [action e] (html [:h2 {"color" "red"} ["Caught exception in " action " -- " (.getMessage e)]] :br [:pre (exception-to-string e)])) (defn debug-commmand! [session params] (if-vip (let [action (s/lower-case (:action params)) func (*debug-action-map* action) msg (if func (try (func session params) (catch Exception e (format-debug-exception action e))) (format-unknown-action action))] [(flash-assoc :msg msg) (redirect-to "/debug")]))) (def report-dir "/var/reports") (defn get-reports [] (reverse (sort (for [f (.listFiles (new File report-dir))] (.replaceAll (.getName f) ".html" ""))))) (defn list-reports-dir [session] (if-super-vip (let [reports (get-reports)] (html [:html [:head [:title "dump.fm reports"]] [:body [:ul (for [r reports] [:li [:a {:href (str "/reports/" r)} r]])]]])))) (defn show-report [session f] (if-super-vip (if (contains? (set (get-reports)) f) (serve-file report-dir (str f ".html")) "ERROR")))