Merge branch 'feature/60' into develop

This commit is contained in:
Simon Brooke 2021-05-21 10:17:40 +01:00
commit 644dfcb517
No known key found for this signature in database
GPG key ID: A7A4F18D1D4DF987
24 changed files with 10254 additions and 158 deletions

79
.classpath Normal file
View file

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/clj">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/test-classes" path="resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/test-classes" path="target/cljsbuild">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="test/clj">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/test-classes" path="env/test/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/test-classes" path="env/dev/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/classes" path="src/cljc">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry exported="true" kind="lib" path="classes">
<attributes>
<attribute name="optional" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" path="target/generated-sources/annotations">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="ignore_optional_problems" value="true"/>
<attribute name="m2e-apt" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="ignore_optional_problems" value="true"/>
<attribute name="m2e-apt" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

8
.gitignore vendored
View file

@ -49,3 +49,11 @@ youyesyet\.dump\.20180816
*.tar *.tar
src/clj/youyesyet/cache\.clj src/clj/youyesyet/cache\.clj
.lsp/sqlite.db
.settings/
.classpath
.project

40
.project Normal file
View file

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>youyesyet</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>ccw.builder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>ccw.nature</nature>
</natures>
<filteredResources>
<filter>
<id>1621149723161</id>
<name></name>
<type>30</type>
<matcher>
<id>org.eclipse.core.resources.regexFilterMatcher</id>
<arguments>node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
</matcher>
</filter>
</filteredResources>
</projectDescription>

View file

@ -0,0 +1,9 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
org.eclipse.jdt.core.compiler.compliance=1.5
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore
org.eclipse.jdt.core.compiler.processAnnotations=disabled
org.eclipse.jdt.core.compiler.release=disabled
org.eclipse.jdt.core.compiler.source=1.5

View file

@ -187,5 +187,4 @@ Copyright © 2016 Simon Brooke for the Radical Independence Campaign.
Licensed under the GNU General Public License, version 2.0 or (at your option) any later version. Licensed under the GNU General Public License, version 2.0 or (at your option) any later version.
**NOTE THAT** files which are directly created by the Luminus template do not currently have a GPL header **NOTE THAT** files which are directly created by the Luminus template do not currently have a GPL header
at the top; files which are new in this project or which have been substantially modified for this project at the top; files which are new in this project or which have been substantially modified for this project should have a GPL header at the top.
do have a GPL header at the top.

View file

@ -30,6 +30,6 @@
(.bind "java:comp/env/jdbc/testdb" (.bind "java:comp/env/jdbc/testdb"
(doto (org.postgresql.ds.PGSimpleDataSource.) (doto (org.postgresql.ds.PGSimpleDataSource.)
(.setServerName "localhost") (.setServerName "localhost")
(.setDatabaseName "youyesyet_dev") (.setDatabaseName "loriner") ;; "youyesyet_dev")
(.setUser "youyesyet") (.setUser "youyesyet")
(.setPassword "thisisnotsecure")))) (.setPassword "thisisnotsecure"))))

View file

@ -6,50 +6,53 @@
:url "https://github.com/simon-brooke/youyesyet" :url "https://github.com/simon-brooke/youyesyet"
:dependencies [[adl-support "0.1.6"] :dependencies [[adl-support "0.1.6"]
[binaryage/devtools "1.0.3"]
[bouncer "1.0.1"] [bouncer "1.0.1"]
[ch.qos.logback/logback-classic "1.2.3"] [ch.qos.logback/logback-classic "1.2.3"]
[clj-oauth "1.5.5"] [clj-oauth "1.5.5"]
[cljsjs/react-leaflet "1.6.5-0"] ;; [cljsjs/react-leaflet "1.6.5-0"]
;; [cljsjs/react-leaflet "2.0.1-0"] is available but doesn't seem to work fully ;; [cljsjs/react-leaflet "2.4.0-0"] is available but doesn't seem to work fully
[cljsjs/leaflet "1.5.1-0"] ;; leaflet-reagent-reframe uses this and it works with Firefox!
[cljs-ajax "0.8.0"] [cljs-ajax "0.8.0"]
[com.andrewmcveigh/cljs-time "0.5.2"] [com.andrewmcveigh/cljs-time "0.5.2"]
[clojure.java-time "0.3.2"] [clojure.java-time "0.3.2"]
[com.cemerick/url "0.1.1"] [com.cemerick/url "0.1.1"]
[compojure "1.6.1"] [compojure "1.6.1"]
[conman "0.8.3"] [conman "0.8.3"]
[cprop "0.1.13"] [cprop "0.1.14"]
[day8.re-frame/http-fx "0.1.6"] [day8.re-frame/http-fx "0.1.6"]
[korma "0.4.3"] [korma "0.4.3"]
[lib-noir "0.9.9" :exclusions [org.clojure/tools.reader]] [lib-noir "0.9.9" :exclusions [org.clojure/tools.reader]]
[luminus/ring-ttl-session "0.3.2"] [luminus/ring-ttl-session "0.3.3"]
[luminus-nrepl "0.1.6"] [luminus-nrepl "0.1.6"]
[luminus-migrations "0.6.5"] [luminus-migrations "0.6.6"]
[luminus-immutant "0.2.5"] [luminus-immutant "0.2.5"]
[markdown-clj "1.0.8"] [markdown-clj "1.10.0"]
[metosin/compojure-api "1.1.12"] [metosin/compojure-api "1.1.13"]
[metosin/ring-http-response "0.9.1"] [metosin/ring-http-response "0.9.1"]
[migratus "1.2.3"] [migratus "1.2.7"]
[mount "0.1.16"] [mount "0.1.16"]
[org.clojure/clojure "1.9.0"] [org.clojure/clojure "1.9.0"]
[org.clojure/clojurescript "1.10.520" :scope "provided"] [org.clojure/clojurescript "1.10.520" :scope "provided"]
[org.clojure/core.memoize "0.7.1"] [org.clojure/core.memoize "0.8.2"]
;;[org.clojure/spec.alpha "0.2.168"] ;;[org.clojure/spec.alpha "0.2.168"]
[org.clojure/tools.cli "0.4.2"] [org.clojure/tools.cli "0.4.2"]
[org.clojure/tools.logging "0.4.1"] [org.clojure/tools.logging "0.5.0"]
[org.postgresql/postgresql "42.2.5"] [org.postgresql/postgresql "42.2.8"]
[org.webjars/bootstrap "4.3.1"] [org.webjars/bootstrap "4.3.1"]
[org.webjars/font-awesome "5.8.1"] [org.webjars/font-awesome "5.11.2"]
[org.webjars.bower/tether "1.4.4"] [org.webjars.bower/tether "1.4.4"]
[postgre-types "0.0.4"] [postgre-types "0.0.4"]
[re-frame "0.10.6"] [re-frame "0.10.9"]
[reagent "0.8.1"] [reagent "0.8.1"]
[reagent-utils "0.3.2"] [reagent-utils "0.3.3"]
[recalcitrant "0.1.2"]
[ring-middleware-format "0.7.4"] [ring-middleware-format "0.7.4"]
[ring/ring-defaults "0.3.2"] [ring/ring-defaults "0.3.2"]
[ring/ring-servlet "1.7.1"] [ring/ring-servlet "1.7.1"]
[ring-webjars "0.2.0"] [ring-webjars "0.2.0"]
[secretary "1.2.3"] [secretary "1.2.3"]
[selmer "1.12.12"]] [selmer "1.12.40"]]
:deploy-repositories [["releases" :clojars] :deploy-repositories [["releases" :clojars]
["snapshots" :clojars]] ["snapshots" :clojars]]
@ -64,9 +67,9 @@
:main ^:skip-aot youyesyet.core :main ^:skip-aot youyesyet.core
:migratus {:store :database :db ~(get (System/getenv) "DATABASE_URL")} :migratus {:store :database :db ~(get (System/getenv) "DATABASE_URL")}
:plugins [[lein-adl "0.1.7"] :plugins [[lein-adl "0.1.8-SNAPSHOT"]
[lein-cljsbuild "1.1.7"] [lein-cljsbuild "1.1.7"]
[lein-codox "0.10.7-multilang"] [lein-codox "0.10.7"]
[lein-cprop "1.0.3"] [lein-cprop "1.0.3"]
[lein-kibit "0.1.6"] [lein-kibit "0.1.6"]
[lein-less "1.7.5"] [lein-less "1.7.5"]
@ -153,15 +156,15 @@
[ring/ring-devel "1.7.1"] [ring/ring-devel "1.7.1"]
[org.webjars/webjars-locator-jboss-vfs "0.1.0"] [org.webjars/webjars-locator-jboss-vfs "0.1.0"]
[luminus-immutant "0.2.5"] [luminus-immutant "0.2.5"]
[pjstadig/humane-test-output "0.9.0"] [pjstadig/humane-test-output "0.10.0"]
[binaryage/devtools "0.9.10"] [binaryage/devtools "0.9.10"]
[com.cemerick/piggieback "0.2.2"] [com.cemerick/piggieback "0.2.2"]
[directory-naming/naming-java "0.8"] [directory-naming/naming-java "0.8"]
[doo "0.1.11"] [doo "0.1.11"]
[figwheel-sidecar "0.5.18"]] [figwheel-sidecar "0.5.19"]]
:plugins [[com.jakemccrary/lein-test-refresh "0.23.0"] :plugins [[com.jakemccrary/lein-test-refresh "0.23.0"]
[lein-doo "0.1.10"] [lein-doo "0.1.10"]
[lein-figwheel "0.5.16"] [lein-figwheel "0.5.19"]
[org.clojure/clojurescript "1.10.520"]] [org.clojure/clojurescript "1.10.520"]]
:cljsbuild {:builds :cljsbuild {:builds
{:app {:app

Binary file not shown.

Before

Width:  |  Height:  |  Size: 333 B

View file

@ -42,13 +42,13 @@
{% endblock %} {% endblock %}
{% block extra-tail %} {% block extra-tail %}
<!-- scripts and styles --> <!-- scripts and styles -->
<!-- ATTENTION \/ -->
<!-- ATTENTION /\ -->
<!-- Leaflet --> <!-- Leaflet -->
<link rel="stylesheet" href="js/lib/node_modules/leaflet/dist/leaflet.css" /> <link rel="stylesheet" href="{{servlet-context}}/js/lib/node_modules/leaflet/dist/leaflet.css" />
{% script "js/lib/node_modules/signature_pad/dist/signature_pad.min.js" %} <script src="{{servlet-context}}/js/lib/node_modules/leaflet/dist/leaflet.js"></script>
{% script "js/lib/node_modules/leaflet/dist/leaflet.js" %} <!-- Signature pad -->
{% script "/js/app.js" %} <script src="{{servlet-context}}/js/lib/node_modules/signature_pad/dist/signature_pad.min.js"></script>
<!-- our actual app -->
<script src="{{servlet-context}}/js/app.js"></script>
{% endblock %} {% endblock %}

View file

@ -17,6 +17,9 @@
<!-- extra-head: put any additional markup to go into the head of your document into this block --> <!-- extra-head: put any additional markup to go into the head of your document into this block -->
<!-- e.g. script tags, link tags --> <!-- e.g. script tags, link tags -->
{% endblock %} {% endblock %}
{% if servlet-context %}
<!-- Servlet context is {{servlet-context}} -->
{% endif %}
</head> </head>
<body> <body>
{% block whole-page %} {% block whole-page %}
@ -26,7 +29,7 @@
<div id="nav"> <div id="nav">
<img id="nav-icon" src="{{servlet-context}}/img/threelines.png" alt="Menu"/> <img id="nav-icon" src="{{servlet-context}}/img/threelines.png" alt="Menu"/>
<menu id="nav-menu" class="nav"> <menu id="nav-menu" class="nav">
<li class=""><a href="{{servlet-context}}/home">Home</a></li> <li class=""><a href="{{servlet-context}}/">Home</a></li>
<li class=""><a href="https://library.projecthope.scot/">Library</a></li> <li class=""><a href="https://library.projecthope.scot/">Library</a></li>
{% if user %} {% if user %}
<li class=""><a href="{{servlet-context}}/roles">Roles</a></li> <li class=""><a href="{{servlet-context}}/roles">Roles</a></li>
@ -87,12 +90,12 @@
<!-- foot: override this block if you don't want the standard footer --> <!-- foot: override this block if you don't want the standard footer -->
<footer> <footer>
<div id="credits"> <div id="credits">
<!-- div> <div>
<img src="{{servlet-context}}/img/credits/ric-logo.png" width="24" height="24"/> <img src="{{servlet-context}}/img/credits/ric-logo.png" width="24" height="24"/>
A project of the A project of the
<a href="https://radical.scot/">Radical Independence Campaign</a> || <a href="https://radical.scot/">Radical Independence Campaign</a> ||
Version {{version}} Version {{version}}
</div --> </div>
<div> <div>
<img height="16" width="16" alt="Clojure" src="{{servlet-context}}/img/credits/luminus-logo.png"/>Built with <a href="http://www.luminusweb.net/">LuminusWeb</a> || <img height="16" width="16" alt="Clojure" src="{{servlet-context}}/img/credits/luminus-logo.png"/>Built with <a href="http://www.luminusweb.net/">LuminusWeb</a> ||
<img height="16" width="16" alt="Clojure" src="{{servlet-context}}/img/credits/clojure-icon.gif"/> Powered by <a href="http://clojure.org">Clojure</a> || <img height="16" width="16" alt="Clojure" src="{{servlet-context}}/img/credits/clojure-icon.gif"/> Powered by <a href="http://clojure.org">Clojure</a> ||

View file

@ -22,7 +22,8 @@
"Primary connection to the main database. TODO: this does not yet enable "Primary connection to the main database. TODO: this does not yet enable
sharding." sharding."
:start (conman/connect! {:jdbc-url-env (env :database-url) :start (conman/connect! {:jdbc-url-env (env :database-url)
:jdbc-url "jdbc:postgresql://127.0.0.1/youyesyet_dev?user=youyesyet&password=thisisnotsecure" ;; :jdbc-url "jdbc:postgresql://127.0.0.1/youyesyet_dev?user=youyesyet&password=thisisnotsecure"
:jdbc-url "jdbc:postgresql://127.0.0.1/loriner?user=youyesyet&password=thisisnotsecure"
:driver-class-name "org.postgresql.Driver"}) :driver-class-name "org.postgresql.Driver"})
:stop (conman/disconnect! *db*)) :stop (conman/disconnect! *db*))

View file

@ -2,6 +2,7 @@
youyesyet.middleware youyesyet.middleware
(:require [clojure.tools.logging :as log] (:require [clojure.tools.logging :as log]
[ring.middleware.anti-forgery :refer [wrap-anti-forgery]] [ring.middleware.anti-forgery :refer [wrap-anti-forgery]]
[ring.middleware.content-type :refer [wrap-content-type]]
[ring.middleware.defaults :refer [site-defaults wrap-defaults]] [ring.middleware.defaults :refer [site-defaults wrap-defaults]]
[ring.middleware.format :refer [wrap-restful-format]] [ring.middleware.format :refer [wrap-restful-format]]
[ring.middleware.webjars :refer [wrap-webjars]] [ring.middleware.webjars :refer [wrap-webjars]]
@ -29,7 +30,11 @@
(do (do
(log/debug "Taking '" (:app-context env) "' as *app-context* from env") (log/debug "Taking '" (:app-context env) "' as *app-context* from env")
(:app-context env)))] (:app-context env)))]
(handler (assoc request :servlet-context *app-context*))))) (handler (assoc
request
;; bind both possible keys just to save grief
:selmer/context *app-context*
:servlet-context *app-context*)))))
(defn wrap-internal-error [handler] (defn wrap-internal-error [handler]
@ -83,5 +88,6 @@
(assoc-in [:security :anti-forgery] false) (assoc-in [:security :anti-forgery] false)
(assoc-in [:session :store] (ttl-memory-store (* 60 30))))) (assoc-in [:session :store] (ttl-memory-store (* 60 30)))))
wrap-context wrap-context
wrap-content-type
wrap-internal-error)) wrap-internal-error))

View file

@ -1,19 +1,16 @@
(ns ^{:doc "Routes/pages available to unauthenticated users." (ns ^{:doc "Routes/pages available to unauthenticated users."
:author "Simon Brooke"} youyesyet.routes.home :author "Simon Brooke"} youyesyet.routes.home
(:require [adl-support.utils :refer [safe-name]] (:require [clojure.java.io :as io]
[clojure.java.io :as io]
[clojure.string :as s]
[clojure.tools.logging :as log] [clojure.tools.logging :as log]
[clojure.walk :refer [keywordize-keys]] [clojure.walk :refer [keywordize-keys]]
[markdown.core :refer [md-to-html-string]] [markdown.core :refer [md-to-html-string]]
[noir.util.route :as route]
[ring.util.http-response :as response] [ring.util.http-response :as response]
[youyesyet.config :refer [env]] [youyesyet.config :refer [env]]
[youyesyet.db.core :as db-core] [youyesyet.db.core :as db-core]
[youyesyet.layout :as layout] [youyesyet.layout :as layout]
[youyesyet.oauth :as oauth] [youyesyet.oauth :as oauth]
[compojure.core :refer [defroutes GET POST]] [youyesyet.routes.utils :refer [with-servlet-context]]
)) [compojure.core :refer [defroutes GET POST]]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; ;;;;
@ -48,27 +45,41 @@
(if (.exists motd) (slurp motd) ""))) (if (.exists motd) (slurp motd) "")))
(defn about-page [] (defn about-page
"Render a page describing the app and its purpose."
[request]
(layout/render "md.html" {:title (layout/render "md.html" {:title
(str "About " (:site-title env)) (str "About " (:site-title env))
:content (md-to-html-string (slurp (io/resource "about.md")))})) :servlet-context (:servlet-context request)
:content (md-to-html-string (slurp (io/resource "about.md")))}))
(defn call-me-page [request] (defn call-me-page
"Render a page requesting a call to a particular elector from a member of the
telephone canvassing team."
[request]
(if (if
request request
(do (do
;; do something to store it in the database ;; do something to store it in the database
(layout/render "call-me-accepted.html" (:session request) (:params request))) (layout/render
(layout/render "call-me.html" (:session request) "call-me-accepted.html"
{:title "Please call me!" (:session request)
(assoc (:params request) :servlet-context (:servlet-context request)))
(layout/render "call-me.html" (:session request)
(with-servlet-context {:title "Please call me!"
;; TODO: Issues need to be fetched from the database ;; TODO: Issues need to be fetched from the database
:concerns (db-core/list-issues db-core/*db* {})}))) :concerns (db-core/list-issues db-core/*db* {})}
request)))))
(defn home-page [] (defn home-page
(layout/render "home.html" {:title "You yes yet?" "Render the default page."
:motd (md-to-html-string (motd))})) [request]
(layout/render "home.html" (with-servlet-context
{:title "You yes yet?"
:motd (md-to-html-string (motd))}
request)))
(defn login-page (defn login-page
@ -77,58 +88,68 @@
(let [params (keywordize-keys (:params request)) (let [params (keywordize-keys (:params request))
session (:session request) session (:session request)
username (:username params) username (:username params)
user (if username (db-core/get-canvasser-by-username db-core/*db* {:username username})) user (when username (db-core/get-canvasser-by-username db-core/*db* {:username username}))
password (:password params) password (:password params)
redirect-to (or redirect-to (or
(:redirect-to params) (:redirect-to params)
(str (:servlet-context request) "/roles"))] (str (:servlet-context request) "/roles"))]
(cond (cond
(:authority params) (:authority params)
(let [auth (oauth/authority! (:authority params))] (let [auth (oauth/authority! (:authority params))]
(if auth (if auth
(do (do
(log/info "Attempting to authorise with authority " (:authority params)) (log/info "Attempting to authorise with authority " (:authority params))
(oauth/fetch-request-token (oauth/fetch-request-token
(assoc request :session (assoc session :authority auth)) (assoc request :session (assoc session :authority auth))
auth)) auth))
(throw (Exception. (str "No such authority: " (:authority params)))))) (throw (Exception. (str "No such authority: " (:authority params))))))
;; this is obviously, ABSURDLY, insecure. I don't want to put just-about-good-enough, ;; this is obviously, ABSURDLY, insecure. I don't want to put just-about-good-enough,
;; it-will-do-for-now security in place; instead, I want this to be test code only ;; it-will-do-for-now security in place; instead, I want this to be test code only
;; until we have o-auth properly working. ;; until we have o-auth properly working.
(and user (= username password)) (and user (= username password))
(let (let
[roles (layout/get-user-roles user)] [roles (layout/get-user-roles user)]
(log/info (str "Logged in user '" username "' with roles " roles)) (log/info (str "Logged in user '" username "' with roles " roles))
(assoc (assoc
(response/found redirect-to) (response/found redirect-to)
:session :session
(assoc session :user (assoc user :roles roles)))) (assoc session :user (assoc user :roles roles))))
;; if we've got a username but either no user object or else ;; if we've got a username but either no user object or else
;; the password doesn't match ;; the password doesn't match
username username
(layout/render (layout/render
"login.html" "login.html"
{:title (str "User " username " is unknown") (with-servlet-context
:redirect-to redirect-to {:title (str "User " username " is unknown")
:warnings ["Your user name was not recognised or your password did not match"]}) :redirect-to redirect-to
:warnings ["Your user name was not recognised or your password did not match"]}
request))
;; if we've no username, just invite the user to log in ;; if we've no username, just invite the user to log in
true :else
(layout/render (layout/render
"login.html" "login.html"
{:title "Please log in" (with-servlet-context
:redirect-to redirect-to {:title "Please log in"
:authorities (db-core/list-authorities db-core/*db*)})))) :redirect-to redirect-to
:authorities (db-core/list-authorities db-core/*db*)}
request)))))
(defroutes home-routes (defroutes home-routes
(GET "/" [] (home-page)) (GET "/" [request] (home-page request))
(GET "/home" [] (home-page)) (GET "/home" [request] (home-page request))
(GET "/about" [] (about-page)) (GET "/about" [request] (about-page request))
(GET "/call-me" [] (call-me-page nil)) (GET "/call-me" [request] (call-me-page request))
(POST "/call-me" request (call-me-page request)) (POST "/call-me" request (call-me-page request))
(GET "/login" request (login-page request)) (GET "/login" request (login-page request))
(POST "/login" request (login-page request)) (POST "/login" request (login-page request))
(GET "/notyet" [] (layout/render "notyet.html" (GET "/notyet" [request] (layout/render
{:title "Can we persuade you?"})) "notyet.html"
(GET "/supporter" [] (layout/render "supporter.html" (with-servlet-context
{:title "Have you signed up as a canvasser yet?"}))) {:title "Can we persuade you?"}
request)))
(GET "/supporter" [request] (layout/render
"supporter.html"
(with-servlet-context
{:title "Have you signed up as a canvasser yet?"}
request))))

View file

@ -1,23 +1,15 @@
(ns ^{:doc "Manually maintained routes which handle data transfer to/from the canvasser app." (ns ^{:doc "Manually maintained routes which handle data transfer to/from the canvasser app."
:author "Simon Brooke"} youyesyet.routes.rest :author "Simon Brooke"} youyesyet.routes.rest
(:require [adl-support.rest-support :refer :all] (:require [adl-support.core :refer [massage-params]]
[adl-support.core :refer [massage-params do-or-log-error [adl-support.rest-support :refer [do-or-server-fail valid-user-or-forbid with-params-or-error]]
do-or-return-reason]]
[clojure.core.memoize :as memo] [clojure.core.memoize :as memo]
[clojure.data.json :as json] [clojure.data.json :as json]
[clojure.java.io :as io]
[clojure.string :as s]
[clojure.tools.logging :as log] [clojure.tools.logging :as log]
[clojure.walk :refer [keywordize-keys]] [compojure.core :refer [defroutes GET]]
[compojure.core :refer [defroutes GET POST]]
[java-time :as jt] [java-time :as jt]
[mount.core :as mount]
[noir.response :as nresponse]
[noir.util.route :as route] [noir.util.route :as route]
[ring.util.http-response :as response]
[youyesyet.locality :as l] [youyesyet.locality :as l]
[youyesyet.db.core :as db] [youyesyet.db.core :as db]
[youyesyet.utils :refer :all]
)) ))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View file

@ -3,42 +3,43 @@
(:require [adl-support.core :as support] (:require [adl-support.core :as support]
[adl-support.utils :refer [safe-name]] [adl-support.utils :refer [safe-name]]
[clojure.tools.logging :as log] [clojure.tools.logging :as log]
[clojure.walk :refer [keywordize-keys]]
[compojure.core :refer [defroutes GET POST]] [compojure.core :refer [defroutes GET POST]]
[noir.util.route :as route] [noir.util.route :as route]
[ring.util.http-response :as response] [ring.util.http-response :as response]
[youyesyet.config :refer [env]]
[youyesyet.db.core :as db-core] [youyesyet.db.core :as db-core]
[youyesyet.routes.issue-experts :as expert] [youyesyet.routes.issue-experts :as expert]
[youyesyet.layout :as layout] [youyesyet.layout :as layout]
[youyesyet.oauth :as oauth] [youyesyet.oauth :as oauth]
[youyesyet.routes.auto :as auto])) [youyesyet.routes.auto :as auto]
[youyesyet.routes.utils :refer [with-servlet-context]]))
(defn roles-page [request] (defn roles-page
"Render the routing page for the roles the currently logged in user is member of." "Render the routing page for the roles the currently logged in user is member of."
[request]
(let (let
[session (:session request) [session (:session request)
user (-> request :session :user) user (-> request :session :user)
roles (if roles (if
user user
(db-core/list-roles-by-canvasser db-core/*db* {:id (:id user)}))] (db-core/list-roles-by-canvasser db-core/*db* {:id (:id user)}))]
(log/info (str "Roles routing page; user is " user "; roles are " roles)) (log/info (str "Roles routing page; user is " user "; roles are " roles))
(if (if
roles roles
(layout/render (layout/render
"roles.html" "roles.html"
{:title (str "Welcome " (:fullname user) ", what do you want to do?") {:title (str "Welcome " (:fullname user) ", what do you want to do?")
:user user :servlet-context (:servlet-context request)
:roles (map #(assoc % :link (safe-name (:name %) :sql)) roles)}) :user user
:roles (map #(assoc % :link (safe-name (:name %) :sql)) roles)})
(assoc (response/found "/login") :session (dissoc session :user))))) (assoc (response/found "/login") :session (dissoc session :user)))))
(defn admins-page (defn admins-page
[request] [request]
(layout/render (layout/render
(support/resolve-template "application-index.html") (support/resolve-template "application-index.html")
{:title "Administrative menu"})) (with-servlet-context {:title "Administrative menu"} request)))
(defn analysts-page (defn analysts-page
@ -47,22 +48,22 @@
anything sophisticated here." anything sophisticated here."
[request] [request]
(layout/render (layout/render
(support/resolve-template "application-index.html") (support/resolve-template "application-index.html")
{:title "Administrative menu"})) (with-servlet-context {:title "Administrative menu"} request)))
(defn canvassers-page (defn canvassers-page
[request] [request]
(layout/render (layout/render
"roles/canvasser.html" "roles/canvasser.html"
{})) (with-servlet-context {} request)))
(defn team-organisers-page (defn team-organisers-page
[request] [request]
(layout/render (layout/render
"roles/team-orgenisers.html" "roles/team-orgenisers.html"
{})) (with-servlet-context {} request)))
(defroutes roles-routes (defroutes roles-routes

View file

@ -0,0 +1,12 @@
(ns youyesyet.routes.utils)
(defn with-servlet-context
"Returns a map like `m` into which the servlet context from `request`,
expected to be a request map, has been merged. When an app is served from
Tomcat, there will be a `:servlet-context` key in the map, and it matters;
when served from Jetty or other lightweight servlet containers, there won't
be. TODO: should probably be moved (or copied) to adl-support."
[m request]
(if (:servlet-context m)
(assoc m :servlet-context (:servlet-context request))
m))

View file

@ -1,14 +1,16 @@
(ns ^{:doc "Canvasser app navigation and routing." (ns ^{:doc "Canvasser app navigation and routing."
:author "Simon Brooke"} :author "Simon Brooke"}
youyesyet.canvasser-app.core youyesyet.canvasser-app.core
(:require cljsjs.react-leaflet (:require [ajax.core :refer [GET POST]]
[ajax.core :refer [GET POST]] [cljsjs.leaflet]
[devtools.core :as devtools]
[goog.events :as events] [goog.events :as events]
[goog.history.EventType :as HistoryEventType] [goog.history.EventType :as HistoryEventType]
[markdown.core :refer [md->html]] [markdown.core :refer [md->html]]
[reagent.core :as r] [reagent.core :as r]
[re-frame.core :as rf] [re-frame.core :as rf]
[re-frame.fx] [re-frame.fx]
[recalcitrant.core :refer [error-boundary]] ;; may not be needed here
[secretary.core :as secretary] [secretary.core :as secretary]
[youyesyet.canvasser-app.ajax :refer [load-interceptors!]] [youyesyet.canvasser-app.ajax :refer [load-interceptors!]]
[youyesyet.canvasser-app.gis :refer [get-current-location]] [youyesyet.canvasser-app.gis :refer [get-current-location]]
@ -49,6 +51,11 @@
;;;; ;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; install tools to aid debugging in Chrome/Chromium.
(devtools/install!)
;;; So that we can do debug logging! ;;; So that we can do debug logging!
(enable-console-print!) (enable-console-print!)
@ -121,20 +128,22 @@
(if content [content] (if content [content]
[:div.error (str "No content in page " @(rf/subscribe [:page]))]) [:div.error (str "No content in page " @(rf/subscribe [:page]))])
[:footer [:footer
[:div.error {:style [:display (if (empty? error) :none :block)]} (apply str error)] (if-not (empty? error)
[:div.feedback [:div.error
{:style [:display (if (empty? feedback) :none :block)]} error])
(apply str (map #(h/feedback-messages %) (distinct feedback)))] (if-not (empty? feedback)
[:div.queue (if [:div.feedback
(nil? outqueue) "" (apply str (map #(h/feedback-messages %) (distinct feedback)))])
(str (count outqueue) " items queued to send"))]]])) ;; (if-not (empty? outqueue)
;; [:div.queue (str (count outqueue) " items queued to send")])
]]))
;; ------------------------- ;; -------------------------
;; Routes ;; Routes
(secretary/set-config! :prefix "#") (secretary/set-config! :prefix "#")
(secretary/defroute "/" [] (secretary/defroute "/" []
(ui/log-and-dispatch [:set-active-page :map])) (ui/log-and-dispatch [:set-active-page :about]))
(secretary/defroute "/about" [] (secretary/defroute "/about" []
(ui/log-and-dispatch [:set-active-page :about])) (ui/log-and-dispatch [:set-active-page :about]))
@ -213,7 +222,7 @@
(rf/dispatch [:fetch-options]) (rf/dispatch [:fetch-options])
(rf/dispatch [:fetch-issues]) (rf/dispatch [:fetch-issues])
(rf/dispatch [:fetch-followupmethods]) (rf/dispatch [:fetch-followupmethods])
(rf/dispatch [:dispatch-later [{:ms 60000 :dispatch [:process-queue]}]]) ;; (rf/dispatch [:dispatch-later [{:ms 60000 :dispatch [:process-queue]}]])
(load-interceptors!) (load-interceptors!)
(hook-browser-navigation!) (hook-browser-navigation!)
(mount-components)) (mount-components))

View file

@ -45,13 +45,23 @@
(.getCurrentPosition (.getCurrentPosition
(.-geolocation js/navigator) (.-geolocation js/navigator)
(fn [position] (fn [position]
(let [lat (.-latitude (.-coords position)) (let [view @(subscribe [:view])
lat (.-latitude (.-coords position))
lng (.-longitude (.-coords position))] lng (.-longitude (.-coords position))]
(js/console.log (str "Current location is: " lat ", " lng)) (js/console.log (str "Current location is: " lat ", " lng))
(dispatch [:set-latitude lat]) (if
(dispatch [:set-longitude lng]) (and view (float? lat) (float? lng))
(.panTo @(subscribe [:view]) (.latLng js/L lat lng)) (do
(locality lat lng)))) (dispatch [:set-latitude lat])
(dispatch [:set-longitude lng])
(.panTo view (.latLng js/L lat lng))
(locality lat lng))
(do
(js/console.log
(if view
(str "Geolocation failed lat: '" lat "'; lng '" lng "'")
"No value for subscription to [:view]"))
0)))))
(do (do
(js/console.log "Geolocation not available") (js/console.log "Geolocation not available")
0)) 0))
@ -84,12 +94,14 @@
so back links work." so back links work."
[id] [id]
(js/console.log (str "Click handler for address #" id)) (js/console.log (str "Click handler for address #" id))
(let [view @(subscribe [:view]) ;; (let [view @(subscribe [:view])
centre (.getCenter view)] ;; centre (.getCenter view)]
(dispatch [:set-zoom (.getZoom view)]) ;; (dispatch [:set-zoom (.getZoom view)])
(dispatch [:set-latitude (.-lat centre)]) ;; (dispatch [:set-latitude (.-lat centre)])
(dispatch [:set-longitude (.-lng centre)])) ;; (dispatch [:set-longitude (.-lng centre)]))
(set! window.location.href (str "#building/" id))) (js/console.log (str "Navigating to " "#building/" id))
(set! window.location.href (str "#building/" id))
)
(defn add-map-pin (defn add-map-pin

View file

@ -77,6 +77,7 @@
{:fetch-locality "Fetching local data." {:fetch-locality "Fetching local data."
:send-request "Request has been queued." :send-request "Request has been queued."
:send-intention-and-visit "Voting intention has been sent" :send-intention-and-visit "Voting intention has been sent"
:bad-address "No valid address has been selected"
}) })

View file

@ -59,7 +59,7 @@
(reg-sub (reg-sub
:error :error
(fn [db _] (fn [db _]
(:error db))) (apply str (:error db))))
(reg-sub (reg-sub
:feedback :feedback

View file

@ -38,17 +38,19 @@
[] []
(let [address @(subscribe [:address]) (let [address @(subscribe [:address])
dwellings (:dwellings address)] dwellings (:dwellings address)]
[:div (js/console.log (str "Address: address"))
[:h1 (str "Flats at " (:address address))] (if address
[:div.container {:id "main-container"} [:div
(ui/back-link "#map") [:h1 (str "Flats at " (:address address))]
[:div {:id "dwelling-list"} [:div.container {:id "main-container"}
(map (ui/back-link "#map")
(fn [:div {:id "dwelling-list"}
[dwelling] (map
(ui/big-link (fn
(:sub_address dwelling) [dwelling]
:target (str "#/dwelling/" (:id dwelling))) ) (ui/big-link
(sort-by (:sub_address dwelling)
:sub_address :target (str "#/dwelling/" (:id dwelling))) )
(:dwellings address)))]]])) (sort-by
:sub_address
(:dwellings address)))]]])))

View file

@ -1,8 +1,10 @@
(ns ^{:doc "Canvasser app map view panel." (ns ^{:doc "Canvasser app map view panel."
:author "Simon Brooke"} :author "Simon Brooke"}
youyesyet.canvasser-app.views.map youyesyet.canvasser-app.views.map
(:require [re-frame.core :refer [reg-sub subscribe dispatch dispatch-sync]] (:require [cljsjs.leaflet]
[re-frame.core :refer [reg-sub subscribe dispatch dispatch-sync]]
[reagent.core :as reagent] [reagent.core :as reagent]
[recalcitrant.core :refer [error-boundary]]
[youyesyet.canvasser-app.gis :refer [refresh-map-pins get-current-location]])) [youyesyet.canvasser-app.gis :refer [refresh-map-pins get-current-location]]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -48,7 +50,7 @@
;;; thought. ;;; thought.
;; which provider to use ;; which provider to use
(def ^dynamic *map-provider* :osm) (def ^:dynamic *map-provider* :osm)
(def osm-url "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png") (def osm-url "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png")
(def osm-attrib "Map data &copy; <a href='http://openstreetmap.org'>OpenStreetMap</a> contributors") (def osm-attrib "Map data &copy; <a href='http://openstreetmap.org'>OpenStreetMap</a> contributors")
@ -58,7 +60,8 @@
"Did-mount function loading map tile data from MapBox (proprietary)." "Did-mount function loading map tile data from MapBox (proprietary)."
[] []
(get-current-location) (get-current-location)
(let [view (.setView (.map js/L "map" (clj->js {:zoomControl "false"})) #js [55.82 -4.25] 40)] (let [view (.setView
(.map js/L "map" (clj->js {:zoomControl "false"})))]
;; NEED TO REPLACE FIXME with your mapID! ;; NEED TO REPLACE FIXME with your mapID!
(.addTo (.tileLayer js/L "http://{s}.tiles.mapbox.com/v3/FIXME/{z}/{x}/{y}.png" (.addTo (.tileLayer js/L "http://{s}.tiles.mapbox.com/v3/FIXME/{z}/{x}/{y}.png"
(clj->js {:attribution "Map data &copy; [...]" (clj->js {:attribution "Map data &copy; [...]"
@ -69,7 +72,7 @@
(defn map-did-mount-osm (defn map-did-mount-osm
"Did-mount function loading map tile data from Open Street Map." "Did-mount function loading map tile data from Open Street Map."
[] []
(get-current-location) (get-current-location) ;; - [Violation] Only request geolocation information in response to a user gesture.
(let [view (.setView (let [view (.setView
(.map js/L (.map js/L
"map" "map"
@ -110,6 +113,6 @@
(defn panel (defn panel
"A reagent class for the map object." "A reagent class for the map object."
[] []
(get-current-location) ;; (get-current-location)
(reagent/create-class {:reagent-render map-render (reagent/create-class {:reagent-render map-render
:component-did-mount map-did-mount})) :component-did-mount map-did-mount}))

View file

@ -242,7 +242,7 @@ version="0.1.1">
<property type="real" name="longitude" column="longitude"> <property type="real" name="longitude" column="longitude">
<prompt prompt="Longitude" locale="en_GB.UTF-8"/> <prompt prompt="Longitude" locale="en_GB.UTF-8"/>
</property> </property>
<property type="list" name="dwellings" entity="dwellings"/> <property type="list" name="dwellings" entity="dwellings" farkey="address_id"/>
<property type="integer" name="locality" column="locality"> <property type="integer" name="locality" column="locality">
<documentation>Locality indexing; see issue #44. Note that <documentation>Locality indexing; see issue #44. Note that
this property should be generated automatically from the this property should be generated automatically from the
@ -320,7 +320,7 @@ version="0.1.1">
<prompt prompt="Canvasser" locale="en_GB.UTF-8"/> <prompt prompt="Canvasser" locale="en_GB.UTF-8"/>
</property> </property>
<property required="true" type="timestamp" name="date" <property required="true" type="timestamp" name="date"
column="date" default="CURRENT_TIMESTAMP" distinct="user"> column="date" default="now()" distinct="user">
<prompt prompt="Date" locale="en_GB.UTF-8"/> <prompt prompt="Date" locale="en_GB.UTF-8"/>
</property> </property>
<permission group="canvassers" permission="noedit"> <permission group="canvassers" permission="noedit">
@ -1000,7 +1000,7 @@ version="0.1.1">
column="actor" entity="canvassers" farkey="id"> column="actor" entity="canvassers" farkey="id">
<prompt prompt="Actor" locale="en_GB.UTF-8"/> <prompt prompt="Actor" locale="en_GB.UTF-8"/>
</property> </property>
<property default="CURRENT_TIMESTAMP" type="timestamp" <property default="now()" type="timestamp"
name="date" column="date" distinct="user"> name="date" column="date" distinct="user">
<prompt prompt="Date" locale="en_GB.UTF-8"/> <prompt prompt="Date" locale="en_GB.UTF-8"/>
</property> </property>

9895
youyesyet.dump.20191116 Normal file

File diff suppressed because it is too large Load diff