diff --git a/project.clj b/project.clj
index cb7c458..1a41985 100644
--- a/project.clj
+++ b/project.clj
@@ -54,7 +54,7 @@
:main ^:skip-aot youyesyet.core
:migratus {:store :database :db ~(get (System/getenv) "DATABASE_URL")}
- :plugins [[lein-adl ["0.1.1"]]
+ :plugins [;;[lein-adl ["0.1.1"]]
[lein-cljsbuild "1.1.4"]
[lein-codox "0.10.3"]
[lein-cprop "1.0.1"]
diff --git a/resources/sql/youyesyet.postgres.sql b/resources/sql/youyesyet.postgres.sql
index d50afd7..0c9f60c 100644
--- a/resources/sql/youyesyet.postgres.sql
+++ b/resources/sql/youyesyet.postgres.sql
@@ -5,7 +5,7 @@
--
-- auto-generated by [Application Description Language framework]
--
--- (https://github.com/simon-brooke/adl) at 20180721T111020.637Z
+-- (https://github.com/simon-brooke/adl) at 20180721T133846.002Z
--
-- A web-app intended to be used by canvassers
-- campaigning for a 'Yes' vote in the second independence
@@ -72,7 +72,7 @@ CREATE TABLE addresses
(
id SERIAL NOT NULL PRIMARY KEY,
address VARCHAR(256) NOT NULL,
- postcode VARCHAR(16) CONSTRAINT pattern_7470 CHECK (postcode ~* '^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z]))))[0-9][A-Za-z]{2})$'),
+ postcode VARCHAR(16) CONSTRAINT pattern_1 CHECK (postcode ~* '^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z]))))[0-9][A-Za-z]{2})$'),
phone VARCHAR(16),
district_id INTEGER,
latitude DOUBLE PRECISION,
@@ -985,31 +985,33 @@ ALTER TABLE ln_roles_canvassers_roles ADD CONSTRAINT ri_ln_roles_canvassers_role
ON DELETE NO ACTION ;
------------------------------------------------------------------------
--- link table joining roles with canvassers
+-- link table joining events with teams
------------------------------------------------------------------------
-CREATE TABLE ln_members_roles_canvassers
+CREATE TABLE ln_teams_events_teams
(
- role_id INTEGER,
- canvasser_id INTEGER
+ event_id INTEGER,
+ team_id INTEGER
);
-GRANT SELECT ON ln_members_roles_canvassers TO admin,
+GRANT SELECT ON ln_teams_events_teams TO admin,
analysts,
canvassers,
issueeditors,
issueexperts,
teamorganisers ;
-GRANT INSERT ON ln_members_roles_canvassers TO admin ;
-GRANT UPDATE ON ln_members_roles_canvassers TO admin ;
-GRANT DELETE ON ln_members_roles_canvassers TO admin ;
+GRANT INSERT ON ln_teams_events_teams TO admin,
+ teamorganisers ;
+GRANT UPDATE ON ln_teams_events_teams TO admin,
+ teamorganisers ;
+GRANT DELETE ON ln_teams_events_teams TO admin ;
-ALTER TABLE ln_members_roles_canvassers ADD CONSTRAINT ri_ln_members_roles_canvassers_canvassers_canvasser_id
- FOREIGN KEY( canvasser_id )
- REFERENCES canvassers(id)
+ALTER TABLE ln_teams_events_teams ADD CONSTRAINT ri_ln_teams_events_teams_events_event_id
+ FOREIGN KEY( event_id )
+ REFERENCES events(id)
ON DELETE NO ACTION ;
-ALTER TABLE ln_members_roles_canvassers ADD CONSTRAINT ri_ln_members_roles_canvassers_roles_role_id
- FOREIGN KEY( role_id )
- REFERENCES roles(id)
+ALTER TABLE ln_teams_events_teams ADD CONSTRAINT ri_ln_teams_events_teams_teams_team_id
+ FOREIGN KEY( team_id )
+ REFERENCES teams(id)
ON DELETE NO ACTION ;
------------------------------------------------------------------------
diff --git a/resources/templates/base.html b/resources/templates/base.html
index 54b4f02..26ecb00 100644
--- a/resources/templates/base.html
+++ b/resources/templates/base.html
@@ -63,6 +63,9 @@
{% endblock %}
+
+ User: {{user}}
+
{% block foot %}
diff --git a/src/clj/youyesyet/layout.clj b/src/clj/youyesyet/layout.clj
index a784cc6..06f56f3 100644
--- a/src/clj/youyesyet/layout.clj
+++ b/src/clj/youyesyet/layout.clj
@@ -39,28 +39,29 @@
(declare ^:dynamic *app-context*)
+(def ^:dynamic *user* nil)
+
(parser/set-resource-path! (clojure.java.io/resource "templates"))
(parser/add-tag! :csrf-field (fn [_ _] (anti-forgery-field)))
(filters/add-filter! :markdown (fn [content] [:safe (md-to-html-string content)]))
(tags/add-tags)
-(defn raw-get-user-roles [user]
- "Return, as a set, the names of the roles of which this user is a member."
- (if
- user
- (do
- (log/debug (str "seeking roles for user " user))
- (let [roles
- (set (map #(lower-case (:name %)) (db/list-roles-by-canvasser db/*db* user)))]
- (log/debug (str "found roles " roles " for user " user))
- roles))))
-
-
;; role assignments change only rarely.
-(def get-user-roles (memoize raw-get-user-roles))
+(def get-user-roles
+ "Return, as a set, the names of the roles of which this `user` is a member."
+ (memoize
+ (fn [user]
+ (if
+ user
+ (do
+ (log/debug (str "seeking roles for user " user))
+ (let [roles
+ (set (map #(lower-case (:name %)) (db/list-roles-by-canvasser db/*db* user)))]
+ (log/debug (str "found roles " roles " for user " user))
+ roles))))))
-(defn render
+(defn render-with-session
"renders the HTML `template` located relative to resources/templates in
the context of this session and with these parameters."
;; TODO: I'm passing `session` through into render. The default luminus
@@ -68,7 +69,7 @@
;; than me so there's almost certainly a reason it doesn't.
[template session & [params]]
(let [user (:user session)]
- (log/debug (str "layout/render: template: '" template "'"))
+ (log/debug (str "layout/render-with-session: template: '" template "'"))
(content-type
(ok
(parser/render-file
@@ -83,6 +84,26 @@
"text/html; charset=utf-8")))
+(defn render
+ "renders the HTML `template` located relative to resources/templates in
+ the context of this session and with these parameters."
+ [template & [params]]
+ (log/debug (str "layout/render: template: '" template "'"))
+ (content-type
+ (ok
+ (parser/render-file
+ template
+ (merge params
+ {:page template
+ :csrf-token *anti-forgery-token*
+ :user *user*
+ :user-roles (get-user-roles *user*)
+ :site-title (:site-title env)
+ :version (System/getProperty "youyesyet.version")})))
+ "text/html; charset=utf-8"))
+
+
+
(defn error-page
"error-details should be a map containing the following keys:
:status - error status
diff --git a/src/clj/youyesyet/middleware.clj b/src/clj/youyesyet/middleware.clj
index 7cbad0a..5e28726 100644
--- a/src/clj/youyesyet/middleware.clj
+++ b/src/clj/youyesyet/middleware.clj
@@ -8,7 +8,7 @@
[ring-ttl-session.core :refer [ttl-memory-store]]
[youyesyet.env :refer [defaults]]
[youyesyet.config :refer [env]]
- [youyesyet.layout :refer [*app-context* error-page]])
+ [youyesyet.layout :refer [*app-context* *user* error-page]])
(:import [javax.servlet ServletContext]))
@@ -63,6 +63,17 @@
((if (:websocket? request) handler wrapped) request))))
+(defn wrap-user
+ "Dynamically bind *user* to the user in the session, if any, so that it
+ is available in layout/render, q.v."
+ [handler]
+ (fn [request]
+ (if-let [user (-> request :session :user)]
+ (binding [*user* user]
+ (handler request))
+ (handler request))))
+
+
(defn wrap-base [handler]
(-> ((:middleware defaults) handler)
wrap-webjars
@@ -71,4 +82,6 @@
(assoc-in [:security :anti-forgery] false)
(assoc-in [:session :store] (ttl-memory-store (* 60 30)))))
wrap-context
- wrap-internal-error))
+ wrap-internal-error
+ wrap-user))
+
diff --git a/src/clj/youyesyet/routes/home.clj b/src/clj/youyesyet/routes/home.clj
index 2ad737f..68408f5 100644
--- a/src/clj/youyesyet/routes/home.clj
+++ b/src/clj/youyesyet/routes/home.clj
@@ -49,7 +49,7 @@
(defn about-page []
- (layout/render "about.html" {} {:title
+ (layout/render "about.html" {:title
(str "About " (:site-title env))
:motd (md-to-html-string (slurp (io/resource "about.md")))}))
@@ -67,7 +67,7 @@
(defn home-page []
- (layout/render "home.html" {} {:title "You yes yet?"
+ (layout/render "home.html" {:title "You yes yet?"
:motd (md-to-html-string (motd))}))
@@ -100,13 +100,12 @@
(assoc
(response/found redirect-to)
:session
- (assoc session :user user :roles roles)))
+ (assoc session :user (assoc user :roles roles))))
;; if we've got a username but either no user object or else
;; the password doesn't match
username
(layout/render
"login.html"
- session
{:title (str "User " username " is unknown")
:redirect-to redirect-to
:warnings ["Your user name was not recognised or your password did not match"]})
@@ -114,7 +113,6 @@
true
(layout/render
"login.html"
- session
{:title "Please log in"
:redirect-to redirect-to
:authorities (db-core/list-authorities db-core/*db*)}))))
diff --git a/src/clj/youyesyet/routes/issue_experts.clj b/src/clj/youyesyet/routes/issue_experts.clj
index 8a54566..f73b801 100644
--- a/src/clj/youyesyet/routes/issue_experts.clj
+++ b/src/clj/youyesyet/routes/issue_experts.clj
@@ -42,7 +42,7 @@
(defn list-page [request]
(layout/render
"issue-expert/list.html"
- (:session request)
+;; (:session request)
(let [user (:user (:session request))]
{:title "Open requests"
:user user
@@ -69,7 +69,7 @@
db/*db* {:id (:visit_id record)})))]
(layout/render
"issue-expert/request.html"
- (:session request)
+ ;; (:session request)
{:title (str "Request from " (:name elector) " at " (:date visit))
:user (:user (:session request))
:visit visit
diff --git a/src/clj/youyesyet/routes/logged_in.clj b/src/clj/youyesyet/routes/logged_in.clj
index c334d08..1c4c140 100644
--- a/src/clj/youyesyet/routes/logged_in.clj
+++ b/src/clj/youyesyet/routes/logged_in.clj
@@ -42,7 +42,6 @@
(defn app-page [request]
(layout/render "app.html"
- (:session request)
{:title "Canvasser app"}))
@@ -51,7 +50,7 @@
(let [record (-> request :session :user)]
(layout/render
"auto/form-canvassers-Canvasser.html"
- (:session request)
+;; (:session request)
{:title (str "Profile for " (-> request :session :user :fullname))
:record record
:elector_id
diff --git a/src/clj/youyesyet/routes/roles.clj b/src/clj/youyesyet/routes/roles.clj
index e2cae2b..851bc58 100644
--- a/src/clj/youyesyet/routes/roles.clj
+++ b/src/clj/youyesyet/routes/roles.clj
@@ -19,14 +19,14 @@
"Render the routing page for the roles the currently logged in user is member of."
(let
[session (:session request)
- user (:user session)
+ user (-> request :session :user)
roles (if
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))
(cond
roles (layout/render "roles.html"
- (:session request)
+;; ;; (:session request)
{:title (str "Welcome " (:fullname user) ", what do you want to do?")
:user user
:roles (map #(assoc % :link (safe-name (:name %) :sql)) roles)})
@@ -38,7 +38,7 @@
[request]
(layout/render
(support/resolve-template "application-index.html")
- (:session request)
+;; (:session request)
{:title "Administrative menu"}))
@@ -49,18 +49,24 @@
[request]
(layout/render
(support/resolve-template "application-index.html")
- (:session request)
+;; (:session request)
{:title "Administrative menu"}))
(defn canvassers-page
[request]
- (layout/render "roles/canvasser.html" (:session request) {}))
+ (layout/render
+ "roles/canvasser.html"
+;; (:session request)
+ {}))
(defn team-organisers-page
[request]
- (layout/render "roles/team-orgenisers.html" request {}))
+ (layout/render
+ "roles/team-orgenisers.html"
+;; request
+ {}))
(defroutes roles-routes
diff --git a/youyesyet.canonical.adl.xml b/youyesyet.canonical.adl.xml
index fb58539..08e3a63 100644
--- a/youyesyet.canonical.adl.xml
+++ b/youyesyet.canonical.adl.xml
@@ -14,8 +14,7 @@
* Generated using adl2canonical.xslt 1.10 $
*
***************************************************************************
- -->
- A web-app intended to be used by canvassers
+ -->A web-app intended to be used by canvassers
campaigning for a 'Yes' vote in the second independence
referendum. The web-app will be delivered to canvassers out
knocking doors primarily through an HTML5/React single-page app
@@ -26,225 +25,236 @@
set the system up and authorise canvassers, and a 'followup'
interface through which issue-expert specialist canvassers can
address particular electors' queries.
-
- See
+
+ See
https://assets.publishing.service.gov.uk/government/uploads/system/uploads/attachment_data/file/488478/Bulk_Data_Transfer_-_additional_validation_valid_from_12_November_2015.pdf,
section 3
- A valid postcode.
+ A valid postcode.
All users
-
- All users of the canvasser app Able to read and
+
+ All users of the canvasser app Able to read and
add canvassing data in a limited radius around their current
position.
-
- Organisers of canvassing teams Able to see and
+
+ Organisers of canvassing teams Able to see and
modify data on the canvassers in the team(s) they organise;
able to add canvassers to their team; able to update canvassers
in their team, including resetting passwords and locking
accounts; able to see canvass data over the whole area in which
their team operates.
-
- People expert on particular issues. Able to read
+
+ People expert on particular issues. Able to read
followup requests, and the electors to which they relate; able
to access (read/write) the issues wiki; able to write followuop
action records.
-
- Users entitled to see an overview of the
+
+ Users entitled to see an overview of the
canvassing data collected. Able to read canvassing data over
the whole map, including historical data.
-
- Users responsible for determining what issues
+
+ Users responsible for determining what issues
should be current at any time. Able to set current issues; able
to add issues.
-
- Able to read and update canvasser records, team
+
+ Able to read and update canvasser records, team
membership records, team organisership records, issue expertise
records; able to add and update reference data
generally.
-
- All electors known to the system; electors are
+ -->
+ All electors known to the system; electors are
people believed to be entitled to vote in the current
campaign.
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- The signature of this elector, captured as SVG text,
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The signature of this elector, captured as SVG text,
as evidence they have consented to us holding data on them.
Null if they have not.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
- All genders which may be assigned to
+ -->
+ All genders which may be assigned to
electors.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
- All dwellings within addresses in the system; a
+ -->
+ All dwellings within addresses in the system; a
dwelling is a house, flat or appartment in which electors live.
Every address should have at least one dwelling; essentially,
an address maps onto a street door and dwellings map onto
what's behind that door. So a tenement or a block of flats
would be one address with many dwellings.
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
The part of the address which identifies the flat or
apartment within the building, if in a multiple occupancy
building.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
- Addresses of all buildings which contain
+ -->
+ Addresses of all buildings which contain
dwellings.
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- Locality indexing; see issue #44. Note that
+ Locality indexing; see issue #44. Note that
this property should be generated automatically from the
latitude and longitude: (+ (* 10000 ;; left-shift the
latitude component four digits (integer (* latitude 1000)))
@@ -254,802 +264,845 @@
don't think it will ever appear in the user interface; it's
an implementation detail, not of interest to
users.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
- All visits made by canvassers to dwellings in
+ -->
+ All visits made by canvassers to dwellings in
which opinions were recorded.
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
- But only in their immediate
+
+
+
+
+
+
+
+ But only in their immediate
area.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
- Authorities which may authenticate canvassers to
+ -->
+ Authorities which may authenticate canvassers to
the system.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
- Issues believed to be of interest to electors,
+ -->
+ Issues believed to be of interest to electors,
about which they may have questions.
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
- Intentions of electors to vote for options
+ -->
+ Intentions of electors to vote for options
elicited in visits.
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
- The locality at which the intention was
+
+
+
+
+
+
+
+ The locality at which the intention was
recorded; used where an elector does not consent to have
polling intention stored against them. This is a locality as
described in
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- The locality at which the intention was
- recorded; used where an elector does not consent to have
- polling intention stored against them. This is a locality
- as described in
-
-
-
-
-
-
- Primary users of the system: those actually
- interviewing electors.
-
-
-
-
-
-
-
-
-
-
-
- An image of the canvasser, so that other members of their
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The locality at which the intention was
+ recorded; used where an elector does not consent to have
+ polling intention stored against them. This is a locality
+ as described in
+
+
+
+
+
+ Primary users of the system: those actually
+ interviewing electors.
+
+
+
+
+
+
+
+
+
+
+
+
+ An image of the canvasser, so that other members of their
team can recognise them.
-
-
-
- Information the canvasser supplies about themselves; an introduction.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Only relevant to issue experts.
-
-
-
- But only their own record
-
-
- But only canvassers in their own
+
+
+
+ Information the canvasser supplies about themselves; an introduction.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Only relevant to issue experts.
+
+
+
+ But only their own record
+
+
+ But only canvassers in their own
team.
-
-
- All canvassers
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- But should only be able to edit their own
+
+
+ All canvassers
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ But should only be able to edit their own
record.
-
-
-
-
-
-
+
+
+
+
+
+
-
- Requests for a followup with an issue
+ -->
+ Requests for a followup with an issue
expert
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
- A role (essentially, the same as a group, but
+ -->
+ A role (essentially, the same as a group, but
application layer rather than database layer) of which a user
may be a member.
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+ -->
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- But only their own group(s)
-
-
-
-
-
- All groups
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
+ -->
+
An event to which a team or teams are invited. Typically created by the team organiser(s).
May be a training event, a social event or a canvassing session.
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
- Electoral districts: TODO: Shape (polygon)
+ -->
+ Electoral districts: TODO: Shape (polygon)
information will need to be added, for use in
maps.
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
- Actions taken on followup
+ -->
+ Actions taken on followup
requests.
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- But only for electors in their immediate
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ But only for electors in their immediate
vicinity
-
-
-
-
-
+
+
+
+
+
-
- Options in the election or referendum being
+ -->
+ Options in the election or referendum being
canvassed on
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
- Methods which may be used to follow up a followup request. Reference data.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ -->
+ Methods which may be used to follow up a followup request. Reference data.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file