Merge lp:~mike-amy/sahana-eden/LA into lp:sahana-eden

Proposed by Mike Amy
Status: Needs review
Proposed branch: lp:~mike-amy/sahana-eden/LA
Merge into: lp:sahana-eden
Diff against target: 38687 lines (+17935/-14451)
214 files modified
.bzrignore (+2/-0)
VERSION (+1/-1)
clean_la.cmd (+11/-0)
controllers/admin.py (+52/-1)
controllers/default.py (+638/-379)
controllers/don.py (+693/-0)
controllers/event.py (+11/-1)
controllers/gis.py (+3/-2)
controllers/hrm.py (+193/-0)
controllers/master.py (+22/-0)
controllers/org.py (+83/-0)
controllers/project.py (+2/-59)
controllers/req.py (+13/-9)
controllers/supply.py (+1/-0)
controllers/vol.py (+2350/-176)
deployment-templates/models/000_config.py (+223/-205)
languages/es.py (+917/-6599)
models/00_db.py (+0/-1)
models/00_settings.py (+71/-29)
models/00_tables.py (+41/-61)
models/00_utils.py (+11/-5)
models/01_menu.py (+654/-340)
models/02_pr.py (+2/-0)
models/03_gis.py (+2/-1)
models/04_pr.py (+21/-4)
models/05_irs.py (+0/-1)
models/05_org.py (+513/-95)
models/06_hrm.py (+100/-31)
models/06_supply.py (+44/-35)
models/08_inv.py (+7/-1)
models/09_project.py (+108/-11)
models/don.py (+613/-0)
models/event.py (+98/-2)
models/req.py (+3064/-1922)
models/vol.py (+1889/-342)
models/zzz_1st_roles.py (+169/-89)
models/zzz_1st_run.py (+86/-185)
modules/s3/add_object_inline.py (+108/-0)
modules/s3/s3aaa.py (+103/-27)
modules/s3/s3cfg.py (+4/-0)
modules/s3/s3export.py (+4/-4)
modules/s3/s3gis.py (+5/-7)
modules/s3/s3import.py (+57/-20)
modules/s3/s3msg.py (+18/-2)
modules/s3/s3pdf.py (+65/-14)
modules/s3/s3search.py (+7/-2)
modules/s3/s3tools.py (+138/-258)
modules/s3/s3utils.py (+0/-1)
modules/s3/s3widgets.py (+526/-382)
modules/test_utils/__init__.py (+5/-0)
modules/test_utils/load_unit.py (+32/-0)
modules/test_utils/login.py (+19/-0)
modules/test_utils/run.py (+13/-10)
modules/test_utils/setup_request_environment.py (+23/-0)
private/prepopulate/default/StandardItems.csv (+4/-0)
private/prepopulate/demo/LA/California_L2.csv (+59/-0)
private/prepopulate/demo/LA/LASkillList.csv (+62/-64)
private/prepopulate/demo/LA/USA_L1.csv (+52/-0)
private/prepopulate/demo/LA/office.csv (+16/-0)
private/prepopulate/demo/LA/organisation.csv (+8/-0)
private/prepopulate/demo/LA/people.csv (+86/-0)
private/prepopulate/demo/LA/req_req.csv (+15/-4)
private/prepopulate/demo/LA/req_skill.csv (+12/-0)
private/prepopulate/demo/LA/sector.csv (+21/-0)
private/prepopulate/demo/LA/tasks.cfg (+17/-1)
private/prepopulate/demo/LA/users.csv (+86/-0)
private/prepopulate/demo/LA/vol_application.csv (+41/-0)
private/prepopulate/demo/LA/vol_assignment.csv (+41/-0)
private/prepopulate/regression/office.csv (+1/-1)
static/formats/edrm/export.xsl (+18/-0)
static/formats/edrm/import.xsl (+14/-2)
static/formats/s3csv/hrm/person.xsl (+44/-33)
static/formats/s3csv/org/office.xsl (+47/-5)
static/formats/s3csv/org/officela.xsl (+261/-0)
static/formats/s3csv/org/organisation.xsl (+6/-1)
static/formats/s3csv/org/sector.xsl (+38/-0)
static/formats/s3csv/req/req.xsl (+32/-21)
static/formats/s3csv/req/req_skill.xsl (+70/-0)
static/formats/s3csv/vol/application.xsl (+76/-0)
static/formats/s3csv/vol/assignment.xsl (+74/-0)
static/formats/s3csv/vol/contact.xsl (+75/-0)
static/formats/s3csv/vol/req.xsl (+77/-0)
static/scripts/S3/S3.js (+4/-3)
static/scripts/S3/S3.min.js (+4/-4)
static/scripts/S3/anytimec.js (+0/-789)
static/scripts/S3/jquery.ba-resize.js (+249/-0)
static/scripts/S3/jquery.ba-resize.min.js (+9/-0)
static/scripts/S3/jquery.hoverIntent.js (+106/-0)
static/scripts/S3/jquery.hoverIntent.minified.js (+9/-0)
static/scripts/S3/s3.dataTables.js (+10/-4)
static/scripts/S3/s3.dataTables.min.js (+5/-5)
static/scripts/S3/s3.select_object.js (+136/-0)
static/scripts/S3/s3.select_person.js (+1/-1)
static/scripts/S3/s3.select_person.min.js (+1/-1)
static/scripts/S3/ui.multiselect.js (+3/-0)
static/styles/S3/anytime.css (+0/-777)
static/styles/S3/anytimec.css (+0/-44)
static/styles/S3/sahana.css (+949/-833)
static/styles/S3/sahana.min.css (+1/-1)
tests/selenium/selenium-version (+2/-2)
tests/smoke_tests.cmd (+2/-0)
tests/unit_tests/controllers/hrm.py (+233/-0)
tests/unit_tests/controllers/org.py (+121/-0)
tests/unit_tests/models/01_menu.py (+128/-0)
tests/unit_tests/modules/s3/s3gis/FeatureLayer.py (+7/-1)
tests/unit_tests/modules/s3/s3gis/FeatureQueries.py (+7/-2)
tests/unit_tests/modules/s3/s3gis/GPXLayer.py (+3/-1)
tests/unit_tests/modules/s3/s3gis/GeoJSONLayer.py (+3/-1)
tests/unit_tests/modules/s3/s3gis/GeoRSSLayer.py (+3/-1)
tests/unit_tests/modules/s3/s3gis/KMLLayer.py (+1/-5)
tests/unit_tests/modules/s3/s3gis/TrueCodePaths.py (+97/-135)
tests/unit_tests/modules/s3/s3gis/WFSLayer.py (+2/-0)
tests/unit_tests/modules/s3/s3gis/__init__.py (+1/-1)
views/_create.html (+2/-2)
views/_delete.html (+1/-1)
views/_display.html (+2/-2)
views/_list.html (+30/-27)
views/_list_create.html (+4/-4)
views/_merge.html (+2/-2)
views/_ocr_page_upload.html (+1/-1)
views/_ocr_upload.html (+1/-1)
views/_search.html (+2/-2)
views/_update.html (+2/-2)
views/admin/export_data.html (+1/-1)
views/admin/groups.html (+1/-1)
views/admin/import_data.html (+1/-1)
views/admin/index.html (+1/-1)
views/admin/role_edit.html (+1/-1)
views/admin/role_list.html (+1/-1)
views/admin/role_users.html (+1/-1)
views/admin/settings.html (+2/-1)
views/admin/users.html (+1/-1)
views/assess/basic_assess.html (+1/-1)
views/assess/index.html (+1/-1)
views/asset/index.html (+1/-1)
views/auth/_login.html (+1/-1)
views/breadcrumbs.html (+6/-0)
views/budget/budget_staff_bundle_header.html (+1/-1)
views/budget/bundle_kit_item_header.html (+1/-1)
views/budget/kit_item_header.html (+1/-1)
views/building/adminLevel.html (+1/-1)
views/building/index.html (+1/-1)
views/building/report.html (+1/-1)
views/building/timeline.html (+1/-1)
views/cr/index.html (+1/-1)
views/dataTables.html (+37/-62)
views/default/about.html (+1/-1)
views/default/contact.html (+7/-17)
views/default/disclaimer.html (+13/-0)
views/default/faq.html (+9/-0)
views/default/help.html (+11/-2)
views/default/index.html (+24/-59)
views/default/register.html (+14/-0)
views/default/sitemap.html (+5/-0)
views/default/user.html (+1/-1)
views/default/why.html (+8/-0)
views/delphi/index.html (+1/-1)
views/don/index.html (+69/-0)
views/dvi/index.html (+1/-1)
views/dvr/index.html (+1/-1)
views/errors/index.html (+1/-1)
views/event/index.html (+1/-1)
views/flood/index.html (+1/-1)
views/footer.html (+15/-7)
views/formats.html (+7/-6)
views/gis/catalogue_toolbar.html (+1/-1)
views/gis/legend.html (+11/-11)
views/gis/location_duplicates.html (+1/-1)
views/gis/location_resolve.html (+1/-1)
views/hrm/index.html (+1/-1)
views/importer/index.html (+1/-1)
views/inner_form.html (+109/-0)
views/inv/index.html (+1/-1)
views/irs/index.html (+1/-1)
views/key.html (+1/-1)
views/layout.html (+56/-88)
views/list_create_ext.html (+1/-1)
views/list_ext.html (+1/-1)
views/master/index.html (+15/-0)
views/mobile/settings_update.html (+1/-1)
views/mpr/index.html (+1/-1)
views/msg/_compose.html (+1/-1)
views/msg/api_settings_update.html (+1/-1)
views/msg/contacts.html (+1/-1)
views/msg/email_settings_update.html (+1/-1)
views/msg/group_user_header.html (+1/-1)
views/msg/index.html (+1/-1)
views/msg/modem_settings_update.html (+1/-1)
views/msg/outbox_create.html (+1/-1)
views/msg/setting_display.html (+1/-1)
views/msg/setting_update.html (+1/-1)
views/msg/sms_to_smtp_settings_update.html (+1/-1)
views/msg/tropo_settings_update.html (+1/-1)
views/org/index.html (+1/-1)
views/patient/index.html (+1/-1)
views/pr/index.html (+1/-1)
views/pr/person_duplicates.html (+1/-1)
views/pr/person_presence_list.html (+1/-1)
views/pr/person_presence_list_create.html (+1/-1)
views/pr/person_resolve.html (+1/-1)
views/project/index.html (+1/-1)
views/project/project_search.html (+1/-1)
views/req/index.html (+2/-2)
views/req/req_assignment_update.html (+33/-0)
views/req/req_item_inv_item.html (+1/-1)
views/scenario/index.html (+1/-1)
views/supply/index.html (+26/-0)
views/survey/index.html (+1/-1)
views/ticket/index.html (+1/-1)
views/vehicle/index.html (+1/-1)
views/vol/index.html (+5/-29)
views/vol/profile.html (+10/-0)
views/vol/register.html (+11/-0)
views/vol/req_assignment_update.html (+32/-0)
To merge this branch: bzr merge lp:~mike-amy/sahana-eden/LA
Reviewer Review Type Date Requested Status
Fran Boon Pending
Review via email: mp+74669@code.launchpad.net

This proposal supersedes a proposal from 2011-09-08.

Description of the change

Add site (office) widget.

Implemented requested changes to the Add Human resource widget:

* Moved Close and Reset next to the Submit button
* Changed Close -> Cancel; Reset -> Clear; Submit -> Save
* Display throbber (static/img/ajax-loader.gif) when loading/saving form
* Hide Main widget when displaying the inner form.

Please note revision 2586. Had some problems and got past it by commenting out a debug line. Not sure if that line is in error, but caused a missing page for me.

To post a comment you must log in.
lp:~mike-amy/sahana-eden/LA updated
2594. By Mike Amy

Add xyz widget refactoring and tests

2595. By Mike Amy

merged from la

2596. By Mike Amy

fixed 'Additional 2' test => 'Address 2' :). Cleaned up 01_menu.py

2597. By Mike Amy

merged from LA

Unmerged revisions

2597. By Mike Amy

merged from LA

2596. By Mike Amy

fixed 'Additional 2' test => 'Address 2' :). Cleaned up 01_menu.py

2595. By Mike Amy

merged from la

2594. By Mike Amy

Add xyz widget refactoring and tests

2593. By Mike Amy

fixed a problem stopping offices being created from the inner forms

2592. By Mike Amy

merged from trunk

2591. By Mike Amy

Display throbber when loading inner form iframe to show something is happening, and hide main widget when it is displayed.

2590. By Mike Amy

Moved inner form buttons near to submit button and renamed as 'Cancel' and 'Close'

2589. By Mike Amy

stopped link showing for autocompleted sites (offices)

2588. By Mike Amy

merged from trunk

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2011-08-10 21:33:23 +0000
3+++ .bzrignore 2011-09-14 11:00:06 +0000
4@@ -14,6 +14,8 @@
5 !static/fonts/setfonts.py
6 !static/fonts/generatefontmapping.py
7 *.ttf
8+!static/fonts/la/
9+!static/fonts/la/*.ttf
10 static/scripts/ext/.docs/*
11 static/scripts/ext/.examples/*
12 static/scripts/ext/.test/*
13
14=== modified file 'VERSION'
15--- VERSION 2011-09-14 07:02:18 +0000
16+++ VERSION 2011-09-14 11:00:06 +0000
17@@ -1,1 +1,1 @@
18-r2745 (2011-09-14 08:02:17)
19\ No newline at end of file
20+r2778 (2011-09-14 08:44:36)
21\ No newline at end of file
22
23=== added file 'clean_la.cmd'
24--- clean_la.cmd 1970-01-01 00:00:00 +0000
25+++ clean_la.cmd 2011-09-14 11:00:06 +0000
26@@ -0,0 +1,11 @@
27+@echo off
28+rem CHOICE Do you want to delete and reinitialise the Sahana Eden Database
29+rem if ERRORLEVEL N goto end
30+
31+rd /Q /S compiled
32+
33+del databases\*.* /Q
34+del errors\*.* /Q
35+del sessions\*.* /Q
36+python ..\..\web2py.py -S la -M -R applications\la\static\scripts\tools\noop.py
37+PAUSE
38\ No newline at end of file
39
40=== modified file 'controllers/admin.py'
41--- controllers/admin.py 2011-09-13 14:56:01 +0000
42+++ controllers/admin.py 2011-09-14 11:00:06 +0000
43@@ -73,6 +73,37 @@
44
45
46 # -----------------------------------------------------------------------------
47+def register_onaccept(form):
48+ """
49+ LA-specific:
50+ Ensure manually-created users get added to 'Staff' role &
51+ have an HRM record created.
52+ """
53+ # Usual Registration Tasks
54+ # (PR record, Authenticated Role, Contacts)
55+ person = auth.s3_register(form)
56+
57+ # LA-specific
58+ # Add to 'Staff' role
59+ table = db.auth_group
60+ STAFF = db(table.uuid == "STAFF").select(table.id,
61+ limitby=(0, 1)).first().id
62+ table = db.pr_person
63+ person_uuid = db(table.id == person).select(table.uuid,
64+ limitby=(0, 1)).first().uuid
65+ table = db.auth_user
66+ query = (table.person_uuid == person_uuid)
67+ user = db(query).select(table.id,
68+ limitby=(0, 1)).first().id
69+ table = db.auth_membership
70+ table.insert(user_id = user,
71+ group_id = STAFF)
72+
73+ # Create an HRM record so that the user appears in human_resource_id() lookups
74+ table = db.hrm_human_resource
75+ table.insert(person_id=person)
76+
77+# -----------------------------------------------------------------------------
78 @auth.s3_requires_membership(1)
79 def user():
80 """ RESTful CRUD controller """
81@@ -84,7 +115,10 @@
82 s3mgr.configure(tablename,
83 main="first_name",
84 # Add users to Person Registry & 'Authenticated' role:
85- create_onaccept = auth.s3_register)
86+ create_onaccept = register_onaccept)
87+
88+ # Staff don't need a Mobile Phone
89+ deployment_settings.auth.registration_requests_mobile_phone = False
90
91 def disable_user(r):
92 if not r.id:
93@@ -591,10 +625,13 @@
94 @auth.s3_requires_membership(1)
95 def portable():
96 """ Portable app creator"""
97+
98+
99 from gluon.admin import apath
100 import os
101 from operator import itemgetter, attrgetter
102
103+
104 app = request.application
105 uploadfolder=os.path.join(apath("%s" % app,r=request),'cache')
106 web2py_source = None
107@@ -715,6 +752,20 @@
108 return response.stream(portable_app)
109
110 # =============================================================================
111+# LA Code
112+# =============================================================================
113+@auth.s3_requires_membership(1)
114+def rostermail():
115+ """
116+ Email Addresses for the Volunteer Roster to be sent to
117+ """
118+
119+ return s3_rest_controller("vol", resourcename)
120+
121+
122+
123+
124+# =============================================================================
125 # Deprecated Code below here
126 # =============================================================================
127 @auth.s3_requires_membership(1)
128
129=== modified file 'controllers/default.py'
130--- controllers/default.py 2011-09-12 13:46:38 +0000
131+++ controllers/default.py 2011-09-14 11:00:06 +0000
132@@ -26,350 +26,14 @@
133 return response.download(request, db)
134
135 # -----------------------------------------------------------------------------
136-# Check the validity of entered Mobile Phone data
137-def register_validation(form):
138- """ Validate the fields in registration form """
139- # Mobile Phone
140- if "mobile" in form.vars and form.vars.mobile:
141- regex = re.compile(single_phone_number_pattern)
142- if not regex.match(form.vars.mobile):
143- form.errors.mobile = T("Invalid phone number")
144- elif deployment_settings.get_auth_registration_mobile_phone_mandatory():
145- form.errors.mobile = T("Phone number is required")
146- return
147-
148-auth.settings.register_onvalidation = register_validation
149-# Add newly-registered users to Person Registry, add 'Authenticated' role
150-# If Organisation is provided, then: add HRM record & add to 'Org_X_Access' role
151-auth.settings.register_onaccept = auth.s3_register
152-
153-_table_user = auth.settings.table_user
154-_table_user.first_name.label = T("First Name")
155-_table_user.first_name.comment = SPAN("*", _class="req")
156-_table_user.last_name.label = T("Last Name")
157-if deployment_settings.get_L10n_mandatory_lastname():
158- _table_user.last_name.comment = SPAN("*", _class="req")
159-_table_user.email.label = T("E-mail")
160-_table_user.email.comment = SPAN("*", _class="req")
161-_table_user.password.comment = SPAN("*", _class="req")
162-_table_user.language.label = T("Language")
163-_table_user.language.comment = DIV(_class="tooltip",
164- _title="%s|%s" % (T("Language"),
165- T("The language you wish the site to be displayed in.")))
166-_table_user.language.represent = lambda opt: s3_languages.get(opt, UNKNOWN_OPT)
167-
168-# Organisation widget for use in Registration Screen
169-# NB User Profile is only editable by Admin - using User Management
170-org_widget = IS_ONE_OF(db, "org_organisation.id",
171- organisation_represent,
172- orderby="org_organisation.name",
173- sort=True)
174-if deployment_settings.get_auth_registration_organisation_mandatory():
175- _table_user.organisation_id.requires = org_widget
176-else:
177- _table_user.organisation_id.requires = IS_NULL_OR(org_widget)
178-
179-# For the User Profile:
180-_table_user.organisation_id.represent = organisation_represent
181-_table_user.organisation_id.comment = DIV(_class="tooltip",
182- _title="%s|%s|%s" % (T("Organization"),
183- T("The default Organization for whom you are acting."),
184- T("This setting can only be controlled by the Administrator.")))
185-
186-_table_user.site_id.represent = shn_site_represent
187-_table_user.site_id.comment = DIV(_class="tooltip",
188- _title="%s|%s|%s" % (T("Facility"),
189- T("The default Facility for which you are acting."),
190- T("This setting can only be controlled by the Administrator.")))
191-
192-# -----------------------------------------------------------------------------
193 def index():
194 """ Main Home Page """
195
196 title = deployment_settings.get_system_name()
197 response.title = title
198
199- if deployment_settings.has_module("cr"):
200- s3mgr.load("cr_shelter")
201- SHELTERS = s3.crud_strings["cr_shelter"].subtitle_list
202- else:
203- SHELTERS = ""
204-
205- # Menu Boxes
206- menu_btns = [#div, label, app, function
207- ["facility", SHELTERS, "cr", "shelter"],
208- ["facility", T("Warehouses"), "inv", "warehouse"],
209- ["facility", T("Hospitals"), "hms", "hospital"],
210- ["facility", T("Offices"), "org", "office"],
211- ["sit", T("Incidents"), "irs", "ireport"],
212- ["sit", T("Assessments"), "assess", "assess"],
213- ["sit", T("Assets"), "asset", "asset"],
214- ["sit", T("Inventory Items"), "inv", "inv_item"],
215- ["dec", T("Gap Map"), "project", "gap_map"],
216- ["dec", T("Gap Report"), "project", "gap_report"],
217- ["dec", T("Requests"), "req", "req"],
218- ["res", T("Projects"), "project", "project"],
219- ["res", T("Activities"), "project", "assess"],
220- ["res", T("Commitments"), "req", "commit"],
221- ["res", T("Sent Shipments"), "inv", "send"],
222- ["res", T("Received Shipments"), "inv", "recv"],
223- ]
224-
225- # Change to (Mitigation)/Preparedness/Response/Recovery?
226- menu_divs = {"facility": DIV( H3(T("Facilities")),
227- _id = "facility_box", _class = "menu_box"),
228- "sit": DIV( H3(T("Situation")),
229- _id = "menu_div_sit", _class = "menu_div"),
230- "dec": DIV( H3(T("Decision")),
231- _id = "menu_div_dec", _class = "menu_div"),
232- "res": DIV( H3(T("Response")),
233- _id = "menu_div_res", _class = "menu_div"),
234- }
235-
236- for div, label, app, function in menu_btns:
237- if deployment_settings.has_module(app):
238- # @ToDo: Also check permissions (e.g. for anonymous users)
239- menu_divs[div].append(A( DIV(label,
240- _class = "menu-btn-r"),
241- _class = "menu-btn-l",
242- _href = URL(app,function)
243- )
244- )
245-
246- div_arrow = DIV(IMG(_src = "/%s/static/img/arrow_blue_right.png" % \
247- request.application),
248- _class = "div_arrow")
249- sit_dec_res_box = DIV(menu_divs["sit"],
250- div_arrow,
251- menu_divs["dec"],
252- div_arrow,
253- menu_divs["res"],
254- _id = "sit_dec_res_box",
255- _class = "menu_box fleft swidth"
256- #div_additional,
257- )
258- facility_box = menu_divs["facility"]
259- facility_box.append( A( IMG(_src = "/%s/static/img/map_icon_128.png" % \
260- request.application),
261- _href = URL(c="gis", f="index"),
262- _title = T("Map")
263- )
264- )
265-
266- datatable_ajax_source = ""
267- # Check logged in AND permissions
268- if AUTHENTICATED in session.s3.roles and \
269- auth.s3_has_permission("read", db.org_organisation):
270- org_items = organisation()
271- datatable_ajax_source = "/%s/default/organisation.aaData" % \
272- request.application
273- response.s3.actions = None
274- response.view = "default/index.html"
275- auth.permission.controller = "org"
276- auth.permission.function = "site"
277- permitted_facilities = auth.permission.permitted_facilities(redirect_on_error=False)
278- manage_facility_box = ""
279- if permitted_facilities:
280- facility_list = s3_represent_facilities(db, permitted_facilities,
281- link=False)
282- facility_opts = [OPTION(opt[1], _value = opt[0])
283- for opt in facility_list]
284- if facility_list:
285- manage_facility_box = DIV(H3(T("Manage Your Facilities")),
286- SELECT(_id = "manage_facility_select",
287- _style = "max-width:400px;",
288- *facility_opts
289- ),
290- A(T("Go"),
291- _href = URL(c="default", f="site",
292- args=[facility_list[0][0]]),
293- #_disabled = "disabled",
294- _id = "manage_facility_btn",
295- _class = "action-btn"
296- ),
297- _id = "manage_facility_box",
298- _class = "menu_box fleft")
299- response.s3.jquery_ready.append( """
300-$('#manage_facility_select').change(function() {
301- $('#manage_facility_btn').attr('href', S3.Ap.concat('/default/site/', $('#manage_facility_select').val()));
302-})""" )
303- else:
304- manage_facility_box = DIV()
305-
306- org_box = DIV( H3(T("Organizations")),
307- A(T("Add Organization"),
308- _href = URL(c="org", f="organisation",
309- args=["create"]),
310- _id = "add-btn",
311- _class = "action-btn",
312- _style = "margin-right: 10px;"),
313- org_items["items"],
314- _id = "org_box",
315- _class = "menu_box fleft"
316- )
317- else:
318- manage_facility_box = ""
319- org_box = ""
320-
321- # @ToDo: Replace this with an easily-customisable section on the homepage
322- #settings = db(db.s3_setting.id == 1).select(limitby=(0, 1)).first()
323- #if settings:
324- # admin_name = settings.admin_name
325- # admin_email = settings.admin_email
326- # admin_tel = settings.admin_tel
327- #else:
328- # # db empty and prepopulate is false
329- # admin_name = T("Sahana Administrator").xml(),
330- # admin_email = "support@Not Set",
331- # admin_tel = T("Not Set").xml(),
332-
333- # Login/Registration forms
334- self_registration = deployment_settings.get_security_self_registration()
335- registered = False
336- login_form = None
337- login_div = None
338- register_form = None
339- register_div = None
340- if AUTHENTICATED not in session.s3.roles:
341- # This user isn't yet logged-in
342- if request.cookies.has_key("registered"):
343- # This browser has logged-in before
344- registered = True
345-
346- # Provide a login box on front page
347- request.args = ["login"]
348- auth.messages.submit_button = T("Login")
349- login_form = auth()
350- login_div = DIV(H3(T("Login")),
351- P(XML("%s <b>%s</b> %s" % (T("Registered users can"),
352- T("login"),
353- T("to access the system")))))
354-
355- if self_registration:
356- # Provide a Registration box on front page
357- request.args = ["register"]
358- if deployment_settings.get_terms_of_service():
359- auth.messages.submit_button = T("I accept. Create my account.")
360- else:
361- auth.messages.submit_button = T("Register")
362- register_form = auth()
363- register_div = DIV(H3(T("Register")),
364- P(XML("%s <b>%s</b>" % (T("If you would like to help, then please"),
365- T("sign-up now")))))
366-
367- # Add client-side validation
368- s3_register_validation()
369-
370- if session.s3.debug:
371- response.s3.scripts.append( "%s/jquery.validate.js" % s3_script_dir )
372- else:
373- response.s3.scripts.append( "%s/jquery.validate.min.js" % s3_script_dir )
374- if request.env.request_method == "POST":
375- post_script = """// Unhide register form
376- $('#register_form').removeClass('hide');
377- // Hide login form
378- $('#login_form').addClass('hide');"""
379- else:
380- post_script = ""
381- register_script = """
382- // Change register/login links to avoid page reload, make back button work.
383- $('#register-btn').attr('href', '#register');
384- $('#login-btn').attr('href', '#login');
385- %s
386- // Redirect Register Button to unhide
387- $('#register-btn').click(function() {
388- // Unhide register form
389- $('#register_form').removeClass('hide');
390- // Hide login form
391- $('#login_form').addClass('hide');
392- });
393-
394- // Redirect Login Button to unhide
395- $('#login-btn').click(function() {
396- // Hide register form
397- $('#register_form').addClass('hide');
398- // Unhide login form
399- $('#login_form').removeClass('hide');
400- });""" % post_script
401- response.s3.jquery_ready.append(register_script)
402-
403- if deployment_settings.frontpage.rss:
404- response.s3.external_stylesheets.append( "http://www.google.com/uds/solutions/dynamicfeed/gfdynamicfeedcontrol.css" )
405- response.s3.scripts.append( "http://www.google.com/jsapi?key=notsupplied-wizard" )
406- response.s3.scripts.append( "http://www.google.com/uds/solutions/dynamicfeed/gfdynamicfeedcontrol.js" )
407- counter = 0
408- feeds = ""
409- for feed in deployment_settings.frontpage.rss:
410- counter += 1
411- feeds = "".join((feeds,
412- "{title: '%s',\n" % feed["title"],
413- "url: '%s'}" % feed["url"]))
414- # Don't add a trailing comma for old IEs
415- if counter != len(deployment_settings.frontpage.rss):
416- feeds += ",\n"
417- feed_control = "".join(("""
418-function LoadDynamicFeedControl() {
419- var feeds = [
420- """, feeds, """
421- ];
422- var options = {
423- // milliseconds before feed is reloaded (5 minutes)
424- feedCycleTime : 300000,
425- numResults : 5,
426- stacked : true,
427- horizontal : false,
428- title : '""", str(T("News")), """'
429- };
430- new GFdynamicFeedControl(feeds, 'feed-control', options);
431-}
432-// Load the feeds API and set the onload callback.
433-google.load('feeds', '1');
434-google.setOnLoadCallback(LoadDynamicFeedControl);"""))
435- response.s3.js_global.append( feed_control )
436-
437- return dict(title = title,
438-
439- sit_dec_res_box = sit_dec_res_box,
440- facility_box = facility_box,
441- manage_facility_box = manage_facility_box,
442- org_box = org_box,
443-
444- r = None, # Required for dataTable to work
445- datatable_ajax_source = datatable_ajax_source,
446- #admin_name=admin_name,
447- #admin_email=admin_email,
448- #admin_tel=admin_tel,
449- self_registration=self_registration,
450- registered=registered,
451- login_form=login_form,
452- login_div=login_div,
453- register_form=register_form,
454- register_div=register_div
455- )
456-
457-# -----------------------------------------------------------------------------
458-def organisation():
459- """
460- Function to handle pagination for the org list on the homepage
461- """
462-
463- table = db.org_organisation
464- table.id.label = T("Organization")
465- table.id.represent = organisation_represent
466-
467- response.s3.dataTable_sPaginationType = "two_button"
468- response.s3.dataTable_sDom = "rtip" #"frtip" - filter broken
469- response.s3.dataTable_iDisplayLength = 25
470-
471- s3mgr.configure("org_organisation",
472- listadd = False,
473- addbtn = True,
474- super_entity = db.pr_pentity,
475- linkto = "/%s/org/organisation/%s" % (request.application,
476- "%s"),
477- list_fields = ["id",])
478-
479- return s3_rest_controller("org", "organisation")
480+ return dict(title = title)
481+
482 # -----------------------------------------------------------------------------
483 def site():
484 """
485@@ -388,7 +52,7 @@
486 args = [id]))
487 else:
488 raise HTTP(404)
489-
490+
491 # -----------------------------------------------------------------------------
492 def message():
493 #if "verify_email_sent" in request.args:
494@@ -425,12 +89,13 @@
495 _table_user = auth.settings.table_user
496 if request.args and request.args(0) == "profile":
497 #_table_user.organisation.writable = False
498- _table_user.utc_offset.readable = True
499- _table_user.utc_offset.writable = True
500+ #_table_user.utc_offset.readable = True
501+ #_table_user.utc_offset.writable = True
502+ pass
503
504 login_form = register_form = None
505 if request.args and request.args(0) == "login":
506- auth.messages.submit_button = T("Login")
507+ auth.messages.submit_button = T("Sign In")
508 form = auth()
509 login_form = form
510 if s3.crud.submit_style:
511@@ -555,46 +220,640 @@
512 xlwt_version=xlwt_version
513 )
514
515+# =============================================================================
516+# LA Custom Views
517+# =============================================================================
518+def why():
519+ """ Custom View """
520+ response.title = T("Why?")
521+ return dict()
522+
523+# -----------------------------------------------------------------------------
524+def contact():
525+ """ Custom View """
526+ response.title = T("Contact us")
527+ return dict()
528+
529 # -----------------------------------------------------------------------------
530 def help():
531 """ Custom View """
532 response.title = T("Help")
533- return dict()
534-
535-# -----------------------------------------------------------------------------
536-def contact():
537- """
538- Give the user options to contact the site admins.
539- Either:
540- An internal Support Requests database
541- or:
542- Custom View
543- """
544- if auth.is_logged_in() and deployment_settings.get_options_support_requests():
545- # Provide an internal Support Requests ticketing system.
546- prefix = "support"
547- resourcename = "req"
548- tablename = "%s_%s" % (prefix, resourcename)
549- table = db[tablename]
550-
551- # Pre-processor
552- def prep(r):
553- # Only Admins should be able to update ticket status
554- if not auth.s3_has_role(ADMIN):
555- table.status.writable = False
556- table.actions.writable = False
557- if r.interactive and r.method == "create":
558- table.status.readable = False
559- table.actions.readable = False
560- return True
561- response.s3.prep = prep
562-
563- output = s3_rest_controller(prefix, resourcename)
564- return output
565- else:
566- # Default: Simple Custom View
567- response.title = T("Contact us")
568- return dict()
569-
570+ entries = []
571+ question = T("How do I register as a volunteer?")
572+ reply = T("Follow these steps, to register:")
573+ list = []
574+ list.append(T("On the Home Page, click the ‘Volunteer’ link. Review the ‘List of Requests for Volunteers’. To select a %(request)s (Volunteer Task), click on %(apply)s; OR You can register by clicking the ‘Register’ link located on the top of the web page.") % dict(request="<i>%s</i>" % T("Request for Volunteers"),
575+ apply = "<strong>%s</strong>" % T("‘APPLY’")))
576+ list.append(T("Enter your information into the Required Fields. After you have entered your information, click %(accept)s to register.") % dict(accept="<strong>%s</strong" % T("‘I ACCEPT, CREATE MY ACCOUNT’")))
577+ entry = (question, reply, list)
578+ entries.append(entry)
579+ question = T("How do I update my Profile, Skills and Emergency Details?")
580+ reply = T("After you are signed-in, please follow these steps to edit your profile: ")
581+ list = []
582+ list.append(T("On the top menu, click %(volunteer)s.") % dict(volunteer="<strong>%s</strong>" % T("VOLUNTEER")))
583+ list.append(T("On the left menu, click %(profile)s .") % dict(profile="<strong>%s</strong>" % T("'My Profile'")))
584+ list.append(T("On this page, you can update your profile."))
585+ list.append(T("Additional information like %(notifications)s and other volunteer agency affiliations also can be updated in this page.") % dict(notifications="<strong>%s</strong>" % T("Notifications")))
586+ list.append(T("Then click %(save)s") % dict(save="<strong>%s</strong>" % T("Save")))
587+ list.append(T("On the left menu, click %(skills)s.") % dict(skills="<strong>%s</strong>" % T("Skills")))
588+ list.append(T("Click the %s sign next to the skill(s) that best describes how you can support the City.") % "<strong>'+'</strong>")
589+ list.append(T("You may also search for a skill by providing the first few characters in the search box. (Example: When searching for ‘Driving’ skills, enter the first letters of the word)."))
590+ list.append(T("If your skill(s) is not listed, you may enter it in the %(extraSkills)s field below.") % dict(extraSkills="<strong>%s</strong>" % T("Skills")))
591+ list.append(T("Then click %(save)s") % dict(save="<strong>%s</strong>") % T("Save"))
592+ list.append(T("You may also update your Emergency Contacts information by clicking on 'Emergency Contacts' located on the left menu."))
593+ list.append(T("Then click %(save)s") % dict(save="<strong>%s</strong>") % T("Save"))
594+ entry = (question, reply, list)
595+ entries.append(entry)
596+ question = T("How do I apply for a Volunteer Request after I have registered with Give2LA?")
597+ reply = T("You can apply by clicking the %(signin)s link located on the top of the web page.") % dict(signin="<strong>%s</strong>" % T("‘Sign-In’"))
598+ list = []
599+ list.append(T("Enter your E-Mail and Password information."))
600+ list.append(T("Review the ‘List of Requests for Volunteers’."))
601+ list.append(T("To select a Volunteer Task, click on %(apply)s.") % dict(apply="<strong>%s</strong>") % T("‘APPLY’"))
602+ list.append(T("Review the ‘Volunteer Assignment Details’. Enter your Emergency Contact information."))
603+ list.append(T("Download the Give2LA Volunteer Registration Forms, complete the forms, and take them with you to your Volunteer Assignment."))
604+ list.append(T("Then click %(commit)s.") % dict(commit="<strong>%s</strong>") % T("Commit"))
605+ list.append(T("Review the Volunteer Application details."))
606+ list.append(T("Then click %(form)s and take them with you to your Volunteer Assignment.") % dict(form="<strong>%s</strong>" % T("'Print Volunteer Assignment Form'")))
607+ entry = (question, reply, list)
608+ entries.append(entry)
609+ question = T("Can I provide feedback or evaluation for an assignment after volunteering?")
610+ reply = T("Yes. After you have signed-in, follow these steps:")
611+ list = []
612+ list.append(T("On the top menu, click %(volunteer)s.") % dict(volunteer="<strong>%s</strong>") % T("VOLUNTEER"))
613+ list.append(T("On the left menu, click %(assignments)s.") % dict(assignments="<strong>%s</strong>") % T("‘My Assignments’"))
614+ list.append(T("Click on the ‘Details’ button of the Volunteer Task you have completed."))
615+ list.append(T("Scroll down to the bottom of the screen to get to ‘Evaluation of Event’."))
616+ list.append(T("Enter the evaluation details; and click %(save)s") % dict(save="<strong>%s</strong>") % T("Save"))
617+ entry = (question, reply, list)
618+ entries.append(entry)
619+ question = T("I would like to register my corporation for donations, what is the process?")
620+ reply = T("Follow these steps, to register")
621+ list = []
622+ list.append(T("Locate the %(register)s for Corporations and Organizations on the Home Page and click on it.") % dict(register="<strong>%s</strong>" % T("‘CLICK HERE TO REGISTER’")))
623+ list.append(T("Enter the Mandatory details and click on the button %(accept)s at the bottom of the screen to register") % dict(accept="<strong>%s</strong" % T("‘I ACCEPT, CREATE MY ACCOUNT’")))
624+ entry = (question, reply, list)
625+ entries.append(entry)
626+
627+ return dict(entries=entries)
628+
629+# -----------------------------------------------------------------------------
630+def faq():
631+ """ Custom View """
632+ donateLink = A(T("Donate Page"), _href=URL(c="don", f="index"))
633+
634+ response.title = T("Frequently Asked Questions")
635+ entries = []
636+ question = T("Is there a minimum age limit to volunteer?")
637+ reply = T("Yes, you have to be at least 18 years of age to Volunteer.")
638+ entry = (question, reply)
639+ entries.append(entry)
640+ question = T("What is the Privacy policy of Give2LA?")
641+ reply = T("Please refer to the %(privacy)s") % dict(privacy=A(T("Privacy Policy"),
642+ _href=URL(c="default", f="disclaimer")))
643+ entry = (question, reply)
644+ entries.append(entry)
645+ question = T("Can I bring a friend to volunteer?")
646+ reply = T("Yes, but your friend must also %(register)s and apply to the same Volunteer Assignment.") % dict(register=A(T("Register"),
647+ _href=URL(c="vol", f="register")))
648+ entry = (question, reply)
649+ entries.append(entry)
650+ question = T("Will I get any food or reimbursements for expenses while volunteering?")
651+ reply = T("No, unless it is otherwise stated on the Volunteer Assignment.")
652+ entry = (question, reply)
653+ entries.append(entry)
654+ question = T("Can I donate cash to support the City of Los Angeles?")
655+ reply = T("Yes, you can donate cash to the %(laepf)s who support the City of Los Angeles or other partner organizations listed on the %(donate)s. These links will take you directly to the organization’s website to complete the cash donation transaction.") % dict(laepf="<a href=http://laemergencypreparednessfoundation.org/>LAEPF</a>", donate=donateLink)
656+ entry = (question, reply)
657+ entries.append(entry)
658+ question = T("What is the process for donating in-kind Items?")
659+ reply = T("The City of Los Angeles prefers cash donations. In-kind items can be donated directly to the organization(s) listed under Donate Items or through any Upcoming Donation Drive on the %(donate)s") % dict(donate=donateLink)
660+ entry = (question, reply)
661+ entries.append(entry)
662+ question = T("Do I have to be a U.S. citizen or legal U.S. resident to volunteer?")
663+ reply = T("Yes. You must be a U.S. citizen, legal U.S. resident, or have gained legal entry into the United States.")
664+ entry = (question, reply)
665+ entries.append(entry)
666+ question = T("Do I have to live in the City of Los Angeles to volunteer?")
667+ reply = T("No, anyone may register to support the City of Los Angeles’ volunteer efforts.")
668+ entry = (question, reply)
669+ entries.append(entry)
670+ question = T("Is my donation tax deductible?")
671+ reply = T("Yes. All donations are tax deductible. The partner organization that receives your donation will provide a receipt for your donation. Please make sure you obtain your tax receipt from the organization and keep it in a safe place for tax purposes. The City of Los Angeles will not issue receipts for your donation.")
672+ entry = (question, reply)
673+ entries.append(entry)
674+
675+ return dict(entries=entries)
676+
677+# -----------------------------------------------------------------------------
678+def sitemap():
679+ """ Custom View """
680+ response.title = T("Site Map")
681+ return dict()
682+
683+# -----------------------------------------------------------------------------
684+def disclaimer():
685+ """ Custom View """
686+ response.title = T("Disclaimer")
687+ return dict()
688+
689+# -----------------------------------------------------------------------------
690+def register():
691+ """
692+ Registration for Organisations
693+ - custom form
694+ """
695+
696+ # Which type of organisation are we registering?
697+ don = False
698+ vol = False
699+ if "type" in request.vars:
700+ if request.vars.type == "don":
701+ don = True
702+ elif request.vars.type == "vol":
703+ vol = True
704+ auth.settings.registration_requires_approval = True
705+
706+ auth.messages.submit_button = T("I accept. Create my account.")
707+ request.args = ["register"]
708+ _table_user.language.default = T.accepted_language
709+ _table_user.language.readable = False
710+ _table_user.language.writable = False
711+ form = auth()
712+ form.attributes["_id"] = "regform"
713+ # Custom class for Submit Button
714+ form[0][-1][0][0]["_class"] = "accept-button"
715+
716+ # Cancel button
717+ form[0][-1][0].append(BR())
718+ #form[0][-1][1].append(INPUT(_type="reset", _value=T("Cancel")))
719+ form[0][-1][0].append(INPUT(_type="button",
720+ _value=T("Cancel"),
721+ _class="wide-grey-button",
722+ _onClick="javascript: history.go(-1)"))
723+
724+ formstyle = s3.crud.formstyle
725+
726+ # Organisation
727+ if form.errors.organisation:
728+ organisation_error = DIV(form.errors.organisation,
729+ _id="organisation__error",
730+ _class="error",
731+ _style="display: block;")
732+ else:
733+ organisation_error = ""
734+ if don:
735+ label = T("Corporation/Organization Name")
736+ else:
737+ label = T("Organization Name")
738+ row = formstyle(id = "organisation",
739+ label = LABEL("%s:" % label,
740+ SPAN(" *", _class="req")),
741+ widget = DIV(INPUT(_name="organisation",
742+ _id="organisation",
743+ _class="string"),
744+ organisation_error),
745+ comment = "")
746+ form[0].insert(0, row)
747+
748+ # Industry Sector
749+ if vol:
750+ hidden = True
751+ widget = INPUT(_name="sector_id",
752+ _id="sector_id",
753+ _class="string")
754+ else:
755+ from gluon.sqlhtml import OptionsWidget
756+ hidden = False
757+ widget = OptionsWidget.widget(db.org_organisation.sector_id,
758+ value="")
759+ # dropdown
760+ row = formstyle(id = "sector_id",
761+ label = LABEL("%s:" % T("Industry Sector")),
762+ widget = widget,
763+ comment = "",
764+ hidden = hidden)
765+ form[0].insert(1, row)
766+ # freetext box for not listed
767+ row = formstyle(id = "sector_other",
768+ label = LABEL("%s:" % T("Other Sector not listed")),
769+ widget = INPUT(_name="sector_other",
770+ _id="sector_other",
771+ _class="string"),
772+ comment = "",
773+ hidden = hidden)
774+ form[0].insert(2, row)
775+
776+
777+ # Primary Contact Person section
778+ row = TR(TD(LABEL(T("Primary Contact")),
779+ _colspan="3",
780+ _class="subheading"))
781+ form[0][2].append(row)
782+ row = formstyle(id = "middle_name",
783+ label = LABEL("%s:" % T("Middle Name")),
784+ widget = INPUT(_name="middle_name",
785+ _id="middle_name",
786+ _class="string"),
787+ comment = "")
788+ form[0][4].append(row)
789+
790+ row = formstyle(id = "secondary_email",
791+ label = LABEL("%s:" % T("Secondary Email")),
792+ widget = INPUT(_name="secondary_email",
793+ _id="secondary_email",
794+ _class="string"),
795+ comment = "")
796+ form[0][12].append(row)
797+
798+ # What are you offering?
799+ if don or vol:
800+ hidden = True
801+ else:
802+ hidden = False
803+ row = formstyle(id = "offer",
804+ hidden = hidden,
805+ label = LABEL("%s:" % T("We can offer"),
806+ SPAN(" *", _class="req")),
807+ widget = (T("Items"),
808+ INPUT(_type="checkbox",
809+ _value="on",
810+ value="on" if don else "",
811+ _name="has_items",
812+ _id="has_items",
813+ _class="boolean"),
814+ T("Volunteers"),
815+ INPUT(_type="checkbox",
816+ _value="on",
817+ value="on" if vol else "",
818+ _name="vols",
819+ _id="vols",
820+ _class="boolean"),
821+ ),
822+ comment = "")
823+ form[0][12].append(row)
824+
825+ # Phone
826+ if form.errors.mobile_phone:
827+ mobile_phone_error = DIV(form.errors.mobile_phone,
828+ _id="mobile_phone__error",
829+ _class="error",
830+ _style="display: block;")
831+ else:
832+ mobile_phone_error = ""
833+ if form.errors.work_phone:
834+ work_phone_error = DIV(form.errors.work_phone,
835+ _id="work_phone__error",
836+ _class="error",
837+ _style="display: block;")
838+ else:
839+ work_phone_error = ""
840+ row = formstyle(id = "phone",
841+ label = LABEL("%s:" % T("Work Phone")),
842+ widget = DIV(INPUT(_name="work_phone",
843+ _id="",
844+ _class="string"),
845+ work_phone_error),
846+ comment = "")
847+ form[0][12].append(row)
848+ row = formstyle(id = "phone",
849+ label = LABEL("%s:" % current.deployment_settings.get_ui_label_mobile_phone(),
850+ SPAN(" *", _class="req")),
851+ widget = DIV(INPUT(_name="mobile_phone",
852+ _id="",
853+ _class="string"),
854+ mobile_phone_error),
855+ comment = "")
856+ form[0][12].append(row)
857+
858+ # Address
859+ row = TR(TD(LABEL(T("Corporation/Organization Address")),
860+ _colspan="3",
861+ _class="subheading"))
862+ form[0][12].append(row)
863+ if form.errors.address1:
864+ address1_error = DIV(form.errors.address1,
865+ _id="address1__error",
866+ _class="error",
867+ _style="display: block;")
868+ else:
869+ address1_error = ""
870+ row = formstyle(id = "address1",
871+ label = LABEL("%s:" % T("Address 1"),
872+ SPAN(" *", _class="req")
873+ ),
874+ widget = (INPUT(_name="address1",
875+ _id="address1",
876+ _class="string"),
877+ address1_error),
878+ comment = "")
879+ form[0][12].append(row)
880+ row = formstyle(id = "address2",
881+ label = LABEL("%s:" % T("Address 2")),
882+ widget = INPUT(_name="address2",
883+ _id="address2",
884+ _class="string"),
885+ comment = "")
886+ form[0][12].append(row)
887+ row = formstyle(id = "city",
888+ label = LABEL("%s:" % T("City"),
889+ SPAN(" *", _class="req")),
890+ widget = INPUT(_name="city",
891+ _id = "city",
892+ _class = "string"),
893+ comment = "")
894+ form[0][12].append(row)
895+ states = S3LocationDropdownWidget(level="L1",
896+ default="California",
897+ empty=False)
898+ widget = states(db.pr_address.location_id, None)
899+ row = formstyle(id = "state",
900+ label = LABEL("%s:" % T("State"),
901+ SPAN(" *", _class="req")),
902+ widget = widget,
903+ comment = "")
904+ form[0][12].append(row)
905+ if form.errors.zip:
906+ zip_error = DIV(form.errors.zip,
907+ _id="zip__error",
908+ _class="error",
909+ _style="display: block;")
910+ else:
911+ zip_error = ""
912+ row = formstyle(id = "zip",
913+ label = LABEL("%s:" % T("Zip"),
914+ SPAN(" *", _class="req")
915+ ),
916+ widget = ( INPUT(_name="zip",
917+ _id="zip",
918+ _class="string"),
919+ zip_error
920+ ),
921+ comment = "")
922+ form[0][12].append(row)
923+
924+
925+ #form[0][-2].append(TR(TD(LABEL(T("Terms of Service:"),
926+ # _id="terms_of_service__label"),
927+ # _class="w2p_fl"),
928+ # TD(LABEL(TEXTAREA(deployment_settings.get_terms_of_service(),
929+ # _onfocus="this.rows=10",
930+ # _readonly="readonly",
931+ # _style="width:100%;text-align:",
932+ # _cols="80", _rows="10"),
933+ # _id="terms_of_service"),
934+ # _class="w2p_fw",
935+ # _colspan="2"),
936+ # _id="terms_of_service__row"))
937+
938+ # Add client-side validation
939+ # simplified copy of s3_register_validation()
940+ script = "".join(( """
941+$('#regform').validate({
942+ errorClass: 'req',
943+ rules: {
944+ first_name: {
945+ required: true
946+ },
947+ last_name: {
948+ required: true
949+ },
950+ email: {
951+ required: true,
952+ email: true
953+ },
954+ organisation: {
955+ required: true
956+ },
957+ mobile_phone: {
958+ required: true
959+ },
960+ address1: {
961+ required: true
962+ },
963+ city: {
964+ required: true
965+ },
966+ zip: {
967+ required: true
968+ },
969+ password: {
970+ required: true
971+ },
972+ password_two: {
973+ required: true,
974+ equalTo: '.password:first'
975+ }
976+ },
977+ messages: {
978+ firstname: '""", str(T("Enter your firstname")), """',
979+ email: {
980+ required: '""", str(T("Please enter a valid email address")), """',
981+ minlength: '""", str(T("Please enter a valid email address")), """'
982+ },
983+ password: {
984+ required: '""", str(T("Provide a password")), """'
985+ },
986+ password_two: {
987+ required: '""", str(T("Repeat your password")), """',
988+ equalTo: '""", str(T("Enter the same password as above")), """'
989+ }
990+ },
991+ errorPlacement: function(error, element) {
992+ error.appendTo( element.parent().next() );
993+ },
994+ submitHandler: function(form) {
995+ form.submit();
996+ }
997+});""" ))
998+ response.s3.jquery_ready.append( script )
999+
1000+ if session.s3.debug:
1001+ response.s3.scripts.append( "%s/jquery.validate.js" % s3_script_dir )
1002+ response.s3.scripts.append( "%s/jquery.pstrength.1.3.js" % s3_script_dir )
1003+ else:
1004+ response.s3.scripts.append( "%s/jquery.validate.min.js" % s3_script_dir )
1005+ response.s3.scripts.append( "%s/jquery.pstrength.1.3.min.js" % s3_script_dir )
1006+
1007+ response.s3.jquery_ready.append("$('.password:first').pstrength();\n")
1008+
1009+ response.title = T("Register")
1010+ response.s3.has_required = True
1011+
1012+ return dict(form=form)
1013+
1014+# -----------------------------------------------------------------------------
1015+def register_validation(form):
1016+ """ Validate the custom fields in registration form """
1017+ # Name
1018+ if "organisation" in request.post_vars and request.post_vars.organisation:
1019+ # Check not in use
1020+ table = db.org_organisation
1021+ query = (table.name == request.post_vars.organisation)
1022+ name = db(query).select(table.id, limitby=(0, 1)).first()
1023+ if name:
1024+ form.errors.organisation = T("Organisation Name is already in use")
1025+ else:
1026+ form.errors.organisation = T("Organisation Name is required")
1027+ # Phone
1028+ if "work_phone" in request.post_vars and request.post_vars.work_phone:
1029+ regex = re.compile(single_phone_number_pattern)
1030+ if not regex.match(request.post_vars.work_phone):
1031+ form.errors.work_phone = T("Invalid phone number")
1032+ if "mobile_phone" in request.post_vars and request.post_vars.mobile_phone:
1033+ regex = re.compile(single_phone_number_pattern)
1034+ if not regex.match(request.post_vars.mobile_phone):
1035+ form.errors.mobile_phone = T("Invalid phone number")
1036+ else:
1037+ form.errors.mobile_phone = T("Phone number is required")
1038+ # Address
1039+ if not request.post_vars.address1:
1040+ form.errors.address1 = T("Address is required")
1041+ if not request.post_vars.city:
1042+ form.errors.city = T("City is required")
1043+ if not request.post_vars.zip:
1044+ form.errors.zip = T("Zip is required")
1045+
1046+ return
1047+
1048+# -----------------------------------------------------------------------------
1049+def register_onaccept(form):
1050+ # Usual Registration Tasks
1051+ # (PR record, Authenticated Role, Contacts)
1052+ person = auth.s3_register(form)
1053+
1054+ # LA-specific
1055+ ptable = db.pr_person
1056+ query = (ptable.id == person)
1057+
1058+ db(query).update(middle_name=request.post_vars.middle_name)
1059+
1060+ pe = db(query).select(ptable.pe_id,
1061+ limitby=(0, 1)).first().pe_id
1062+
1063+ # Organisation
1064+ organisation = request.post_vars.organisation
1065+ if "vols" in request.post_vars and request.post_vars.vols == "on":
1066+ has_vols = True
1067+ else:
1068+ has_vols = False
1069+ if "has_items" in request.post_vars and request.post_vars.has_items == "on":
1070+ has_items = True
1071+ else:
1072+ has_items = False
1073+
1074+ # Phone
1075+ table = db.pr_contact
1076+ work_phone = request.post_vars.work_phone
1077+ mobile_phone = request.post_vars.mobile_phone
1078+ if mobile_phone:
1079+ # Don't auto-subscribe to SMS (priority 10)
1080+ table.insert(pe_id = pe,
1081+ contact_method = "SMS",
1082+ value = mobile_phone,
1083+ priority=10)
1084+ if work_phone:
1085+ # Don't auto-subscribe to SMS (priority 10)
1086+ table.insert(pe_id = pe,
1087+ contact_method = "WORK_PHONE",
1088+ value = work_phone,
1089+ priority=10)
1090+
1091+ # Address
1092+ address1 = request.post_vars.address1
1093+ address2 = request.post_vars.address2
1094+ #if address2:
1095+ # address = "%s\n%s" % (address1,
1096+ # address2)
1097+ #else:
1098+ # address = address1
1099+ city = request.post_vars.city
1100+ state = request.post_vars.location_id
1101+ zip = request.post_vars.zip
1102+ if request.post_vars.sector_other:
1103+ sector_other = "Sector: %s" % request.post_vars.sector_other
1104+ else:
1105+ sector_other = ""
1106+ otable = db.org_organisation
1107+ org = otable.insert(name = organisation,
1108+ has_vols = has_vols,
1109+ has_items = has_items,
1110+ #phone=phone,
1111+ address=address1,
1112+ address_2=address2,
1113+ L3=city,
1114+ L1=state,
1115+ postcode=zip,
1116+ sector_id=request.post_vars.sector_id,
1117+ comments=sector_other)
1118+ record = Storage(id=org)
1119+ s3mgr.model.update_super(otable, record)
1120+ auth.s3_set_record_owner(otable, org)
1121+ # For OrgDons which don't require approval
1122+ if auth.user:
1123+ # Update the session
1124+ auth.user.organisation_id = org
1125+ user_id = auth.user.id
1126+ else:
1127+ user_id = form.vars.id
1128+
1129+ # Create HRM
1130+ table = db.hrm_human_resource
1131+ hrm = table.insert(person_id = person,
1132+ organisation_id = org,
1133+ focal_point = True,
1134+ owned_by_user = user_id)
1135+ record = Storage(id=hrm)
1136+ s3mgr.model.update_super(table, record)
1137+ auth.s3_set_record_owner(table, hrm)
1138+
1139+ # Set the Roles
1140+ person = db(query).select(ptable.uuid,
1141+ limitby=(0, 1)).first()
1142+ if not person:
1143+ # Error
1144+ return
1145+ utable = db[auth.settings.table_user]
1146+ query = (utable.person_uuid == person.uuid)
1147+ db(query).update(organisation_id = org)
1148+ user = db(query).select(utable.id,
1149+ limitby=(0, 1)).first()
1150+ if not user:
1151+ # Error
1152+ return
1153+ mtable = db[auth.settings.table_membership]
1154+ gtable = db[auth.settings.table_group]
1155+ _org = db(otable.id == org).select(otable.owned_by_organisation,
1156+ limitby=(0, 1)).first()
1157+ if _org:
1158+ mtable.insert(user_id = user.id,
1159+ group_id = _org.owned_by_organisation)
1160+
1161+ if has_vols:
1162+ OrgVol = db(gtable.uuid == ORG_VOL).select(gtable.id,
1163+ limitby=(0, 1)).first()
1164+ if OrgVol:
1165+ mtable.insert(user_id = user.id,
1166+ group_id = OrgVol.id)
1167+ # Go to the Contacts page so that a secondary contact can be added
1168+ # Flag that we've come from registration for subsequent workflow
1169+ #redirect(URL(c="vol", f="organisation", args=[org, "human_resource"],
1170+ # vars={"register":1}))
1171+
1172+ if has_items:
1173+ OrgDon = db(gtable.uuid == ORG_DON).select(gtable.id,
1174+ limitby=(0, 1)).first()
1175+ if OrgDon:
1176+ mtable.insert(user_id = user.id,
1177+ group_id = OrgDon.id)
1178+ # Go to the Contacts page so that a secondary contact can be added
1179+ # Flag that we've come from registration for subsequent workflow
1180+ redirect(URL(c="don", f="organisation", args=[org, "human_resource"],
1181+ vars={"register":1}))
1182+
1183+# -----------------------------------------------------------------------------
1184+#auth.settings.registration_requires_approval = True
1185+auth.settings.register_onvalidation = register_validation
1186+auth.settings.register_onaccept = register_onaccept
1187
1188 # END =========================================================================
1189
1190=== added file 'controllers/don.py'
1191--- controllers/don.py 1970-01-01 00:00:00 +0000
1192+++ controllers/don.py 2011-09-14 11:00:06 +0000
1193@@ -0,0 +1,693 @@
1194+# -*- coding: utf-8 -*-
1195+
1196+"""
1197+ Donations (LA Specific)
1198+
1199+ @author: Michael Howden (michael@sahanafoundation.org)
1200+ @date-created: 2011-08-02
1201+"""
1202+
1203+
1204+module = request.controller
1205+resourcename = request.function
1206+
1207+if not deployment_settings.has_module(module):
1208+ raise HTTP(404, body="Module disabled: %s" % module)
1209+
1210+# Options Menu (available in all Functions)
1211+shn_menu(module)
1212+
1213+# Load Models
1214+s3mgr.load("don_collect")
1215+# -----------------------------------------------------------------------------
1216+def index():
1217+ """ Custom View """
1218+ if s3_has_role(STAFF):
1219+ redirect(URL(f="req"))
1220+ else:
1221+ response.menu_options = []
1222+ if session.s3.debug:
1223+ response.s3.scripts.append( "%s/jquery.hoverIntent.js" % s3_script_dir )
1224+ else:
1225+ response.s3.scripts.append( "%s/jquery.hoverIntent.minified.js" % s3_script_dir )
1226+
1227+ response.title = T("Donate")
1228+ response.s3.jquery_ready.append("""
1229+ $(".donate-popup").css("display", "none");
1230+ $(".organizations-list li a").hoverIntent(donateFadeIn, donateFadeOut);
1231+ """)
1232+ response.s3.js_global.append("""
1233+ function donateFadeIn(){$(this).next(".donate-popup").fadeIn();}
1234+ function donateFadeOut(){$(this).next(".donate-popup").fadeOut();}
1235+ """)
1236+
1237+ # Get Donation Drives List
1238+ table = db.don_collect
1239+ query = ( ( table.end_datetime > request.utcnow ) &
1240+ ( table.deleted == False )
1241+ )
1242+ rows = db(query).select( table.start_datetime,
1243+ table.end_datetime,
1244+ table.site_id,
1245+ orderby = table.start_datetime
1246+ )
1247+ if rows:
1248+ list = []
1249+ offset = IS_UTC_OFFSET.get_offset_value(session.s3.utc_offset)
1250+ for row in rows:
1251+ if offset:
1252+ start_datetime = row.start_datetime + datetime.timedelta(seconds=offset)
1253+ end_datetime = row.end_datetime + datetime.timedelta(seconds=offset)
1254+
1255+ start_date = start_datetime.strftime("%b %d")
1256+ start_time = start_datetime.strftime("%I:%M %p")
1257+ end_date = end_datetime.strftime("%b %d")
1258+ end_time = end_datetime.strftime("%I:%M %p")
1259+
1260+ site = shn_site_represent(row.site_id, address = True)
1261+
1262+ if start_date == end_date:
1263+ list.append( LI( SPAN( start_date, _class = "date"),
1264+ " %s - %s" % (start_time, end_time),
1265+ site,
1266+ BR()
1267+ )
1268+ )
1269+ else:
1270+ list.append( LI( SPAN( start_date, _class = "date" ),
1271+ " %s - " % start_time,
1272+ SPAN( end_date, _class = "date" ),
1273+ " %s" % end_time,
1274+ site,
1275+ BR()
1276+ )
1277+ )
1278+ donation_drives = TAG[""](*list)
1279+ else:
1280+ donation_drives = P(T("No Donation Drives Scheduled. Please check back later"))
1281+
1282+ return dict(donation_drives = donation_drives)
1283+
1284+# -----------------------------------------------------------------------------
1285+def match():
1286+ # Get Req Resource Details
1287+ tablename, req_id = request.vars.viewing.split(".")
1288+
1289+ # create fake resource for rheader
1290+ req = db(db.req_req.id == req_id).select(limitby = (0, 1)).first()
1291+ r = Storage()
1292+ r.record = req
1293+ r.representation = "html"
1294+ r.name = "don"
1295+ r.function = "match"
1296+ r.vars = request.vars
1297+ rheader = response.s3.don_rheader(r)
1298+
1299+ # Get Item Filter
1300+ req_item = db(db.req_req_item.req_id == req_id).select(db.req_req_item.item_id,
1301+ limitby = [0,1]
1302+ ).first()
1303+ if req_item:
1304+ item_id = req_item.item_id
1305+ else:
1306+ item_id = None
1307+ response.s3.filter = (db.inv_inv_item.item_id == item_id)
1308+
1309+ # Configure inv_item list
1310+ s3mgr.configure("inv_inv_item",
1311+ insertable = False,
1312+ list_fields = ["id",
1313+ "organisation_id",
1314+ "item_id",
1315+ "quantity",
1316+ "comments"]
1317+ )
1318+ response.s3.actions = [dict(url = URL(c="don", f="req",
1319+ args = [req_id, "commit"],
1320+ vars = dict(inv_item_id = "[id]")
1321+ ),
1322+ _class = "action-btn",
1323+ label = str(T("Select")),
1324+ )
1325+ ]
1326+ s3mgr.load("inv_inv_item")
1327+ output = response.s3.inv_item_controller() #s3_rest_controller("inv", "inv_item")
1328+
1329+ # Customize form
1330+ output["rheader"] = rheader
1331+ output["title"] = T("Request for Donations Details")
1332+ output["subtitle"] = T("Matching Donation Items")
1333+
1334+ return output
1335+
1336+# -----------------------------------------------------------------------------
1337+def req():
1338+ """
1339+ /req/req/default_type=1
1340+ """
1341+ s3mgr.configure("req_req",
1342+ list_fields = ["id",
1343+ #"priority",
1344+ "status",
1345+ (T("BOC Status"), "req_commit_status"),
1346+ "created_on",
1347+ "date_required",
1348+ (T("Item"), "item"),
1349+ "site_id",
1350+ ],
1351+ orderby = ["created_on"])
1352+
1353+ s3mgr.load("req_req")
1354+ request.vars["default_type"] = 1
1355+
1356+ #s3mgr.model.set_method("req", "req", method="match", action = match)
1357+
1358+ # Defined in the Model for use from Multiple Controllers for unified menus
1359+ output = response.s3.req_controller()
1360+ if "rheader" in output:
1361+ if len(request.args) >0:
1362+ args = request.args[0]
1363+ else:
1364+ args = None
1365+ reportButtons = DIV(
1366+ BUTTON(T("Print Donation Request Form"),
1367+ _class="accept-button",
1368+ _onClick="javascript: window.location='%s'" % \
1369+ URL(c=request.controller,
1370+ f="req_print",
1371+ args=args
1372+ )
1373+ ),
1374+ # Also in controllers/vol.py - DRY
1375+ A(IMG(_src="/%s/static/img/get_adobe_reader.png" % request.application,
1376+ _title="%s - %s" % (T("Get Adobe Reader"),
1377+ T("This link will open a new browser window.")),
1378+ _alt=T("Get Adobe Reader"),
1379+ _width=158,
1380+ _height=39,
1381+ _style="float:right;"),
1382+ _href="http://www.adobe.com/products/acrobat/readstep2.html",
1383+ _target="_blank"),
1384+ )
1385+ output["rheader"].append(reportButtons)
1386+
1387+ return output
1388+
1389+def req_print():
1390+ """ Print Donation Request Form """
1391+ s3mgr.load("req_req_item")
1392+ r = s3base.S3Request(s3mgr,
1393+ prefix="req",
1394+ name="req_item",
1395+ extension="pdf")
1396+ s3mgr.configure("req_req_item",
1397+ callback = response.s3.donationRequest,
1398+ formname = T("Request for Donations"),
1399+ footer = lambda x, y: None,
1400+ )
1401+ return r()
1402+
1403+# -----------------------------------------------------------------------------
1404+# @ToDo: move to inside a prep - only for inv_item components
1405+# Add inv_item directly to Org
1406+if deployment_settings.has_module("inv"):
1407+ s3mgr.load("inv_inv_item")
1408+
1409+s3mgr.configure("inv_inv_item",
1410+ list_fields = ["id",
1411+ "organisation_id",
1412+ "item_category_id",
1413+ "item_id",
1414+ "pack_value",
1415+ "quantity",
1416+ ]
1417+ )
1418+
1419+db.inv_inv_item.site_id.readable = db.inv_inv_item.site_id.writable = False
1420+db.inv_inv_item.expiry_date.readable = db.inv_inv_item.expiry_date.writable = False
1421+# inv_item CRUD strings
1422+# INV_ITEM = T(" Donation Item")
1423+
1424+ADD_INV_ITEM = T("Add Donation Item")
1425+LIST_INV_ITEMS = T("List Donation Items")
1426+s3.crud_strings["inv_inv_item"] = Storage(
1427+ title_create = ADD_INV_ITEM,
1428+ title_display = T("Donation Item Details"),
1429+ title_list = LIST_INV_ITEMS,
1430+ title_update = T("Edit Donation Item"),
1431+ title_search = T("Search Donation Items"),
1432+ subtitle_create = ADD_INV_ITEM,
1433+ subtitle_list = T("Donation Items"),
1434+ label_list_button = LIST_INV_ITEMS,
1435+ label_create_button = ADD_INV_ITEM,
1436+ label_delete_button = T("Remove Donation Item"),
1437+ msg_record_created = T("Donation Item Added"),
1438+ msg_record_modified = T("Donation Item updated"),
1439+ msg_record_deleted = T("Donation Item removed"),
1440+ msg_list_empty = T("No Donation Items for this Corporation"))
1441+
1442+ADD_GOODS = T("In the event of a declared disaster we MAY be able to donate the following Goods:")
1443+LIST_GOODS = T("List Donation Goods")
1444+s3.crud_strings["don_good"] = Storage(
1445+ title_create = ADD_GOODS,
1446+ title_display = T("Donation Good Details"),
1447+ title_list = LIST_GOODS,
1448+ title_update = T("Edit Donation Goods"),
1449+ title_search = T("Search Donation Goods"),
1450+ subtitle_create = ADD_GOODS,
1451+ subtitle_list = T("Donate Goods"),
1452+ label_list_button = LIST_GOODS,
1453+ label_create_button = ADD_GOODS,
1454+ label_delete_button = T("Remove Donation Goods"),
1455+ msg_record_created = T("Donation Goods Added"),
1456+ msg_record_modified = T("Donation Goods updated"),
1457+ msg_record_deleted = T("Donation Goods removed"),
1458+ msg_list_empty = T("No Donation Goods for this Corporation"))
1459+
1460+ADD_SERVICE = T("In the event of a declared disaster we MAY be able to donate the following Services:")
1461+LIST_SERVICES = T("List Donation Services")
1462+s3.crud_strings["don_service"] = Storage(
1463+ title_create = ADD_SERVICE,
1464+ title_display = T("Donation Service Details"),
1465+ title_list = LIST_SERVICES,
1466+ title_update = T("Edit Donation Service"),
1467+ title_search = T("Search Donation Services"),
1468+ subtitle_create = ADD_SERVICE,
1469+ subtitle_list = T("Donate Services"),
1470+ label_list_button = LIST_SERVICES,
1471+ label_create_button = ADD_SERVICE,
1472+ label_delete_button = T("Remove Donation Service"),
1473+ msg_record_created = T("Donation Service Added"),
1474+ msg_record_modified = T("Donation Service updated"),
1475+ msg_record_deleted = T("Donation Service removed"),
1476+ msg_list_empty = T("No Donation Services for this Corporation"))
1477+
1478+ADD_FACILITY = T("In the event of a declared disaster we MAY be able to donate the following Facilities:")
1479+LIST_FACILITYS = T("List Donation Facilities")
1480+s3.crud_strings["don_facility"] = Storage(
1481+ title_create = ADD_FACILITY,
1482+ title_display = T("Donation Facility Details"),
1483+ title_list = LIST_FACILITYS,
1484+ title_update = T("Edit Donation Facility"),
1485+ title_search = T("Search Donation Facilities"),
1486+ subtitle_create = ADD_FACILITY,
1487+ subtitle_list = T("Donate Facilities"),
1488+ label_list_button = LIST_FACILITYS,
1489+ label_create_button = ADD_FACILITY,
1490+ label_delete_button = T("Remove Donation Facility"),
1491+ msg_record_created = T("Donation Facility Added"),
1492+ msg_record_modified = T("Donation Facility updated"),
1493+ msg_record_deleted = T("Donation Facility removed"),
1494+ msg_list_empty = T("No Donation Facilities for this Corporation"))
1495+
1496+# -----------------------------------------------------------------------------
1497+def don_item_filter(inv_item_add_filter_func):
1498+ """
1499+ Filter donated 'items' by category
1500+ Services = category 'SERVICES'
1501+ Facilities = category 'FACILITY'
1502+ Goods = everything else
1503+ """
1504+ itable = db.inv_inv_item
1505+ ctable = db.supply_item_category
1506+
1507+ query = (ctable.code == "FACILITY")
1508+ facility_cat_id = db(query).select(ctable.id,
1509+ limitby = (0, 1),
1510+ cache = gis.cache).first().id
1511+ query = (ctable.code == "SERVICES")
1512+ service_cat_id = db(query).select(ctable.id,
1513+ limitby = (0, 1),
1514+ cache = gis.cache).first().id
1515+
1516+ itable.organisation_id.widget = None # Implement SearchACWidget for Organisations
1517+ itable.organisation_id.requires = IS_NULL_OR(IS_ONE_OF(db, "org_organisation.id",
1518+ organisation_represent,
1519+ orderby="org_organisation.name",
1520+ sort=True,
1521+ filterby = "has_items",
1522+ filter_opts = [True])
1523+ )
1524+ itable.item_id.widget = None
1525+
1526+ if not s3_has_role(STAFF):
1527+ # Only Staff can add Categories
1528+ itable.item_category_id.comment = ""
1529+ # Only Staff can add Item Types
1530+ # @ToDo: Allow adding Items to 'OTHER' category
1531+ itable.item_id.comment = ""
1532+ # Only Staff can add Item Units
1533+ # @ToDo: Should Units for Services/Facilities be hardcoded/hidden?
1534+ # (always day/ea)
1535+ comment = DIV(_class="tooltip",
1536+ _title="%s|%s" % (T("Item Units"),
1537+ T("The way in which an item is normally distributed")))
1538+ script = SCRIPT(
1539+"""
1540+S3FilterFieldChange({
1541+ 'FilterField': 'item_id',
1542+ 'Field': 'item_pack_id',
1543+ 'FieldResource':'item_pack',
1544+ 'FieldPrefix': 'supply',
1545+ 'msgNoRecords': S3.i18n.no_packs,
1546+ 'fncPrep': fncPrepItem,
1547+ 'fncRepresent': fncRepresentItem
1548+});""")
1549+ itable.item_pack_id.comment = TAG[""](comment, script)
1550+
1551+ item_type = request.vars.item
1552+ if item_type == "goods":
1553+ s3.crud_strings["inv_inv_item"] = s3.crud_strings["don_good"]
1554+
1555+ itable.item_category_id.requires = IS_NULL_OR(IS_ONE_OF(db,
1556+ "supply_item_category.id",
1557+ "%(name)s",
1558+ not_filterby = "id",
1559+ not_filter_opts = [facility_cat_id, service_cat_id],
1560+ sort=True))
1561+ response.s3.jquery_ready = [
1562+"""
1563+$(document).ready(function() {
1564+ S3FilterFieldChange({
1565+ 'FilterField': 'item_category_id',
1566+ 'Field': 'item_id',
1567+ 'FieldResource':'item',
1568+ 'FieldPrefix': 'supply',
1569+ //'url': S3.Ap.concat('/req/req_item_packs/'),
1570+ //'msgNoRecords': S3.i18n.no_packs,
1571+ //'fncPrep': fncPrepItem,
1572+ //'fncRepresent': fncRepresentItem
1573+ });
1574+});
1575+"""
1576+]
1577+ query = (itable.item_category_id != facility_cat_id) & \
1578+ (itable.item_category_id != service_cat_id)
1579+ inv_item_add_filter_func( query )
1580+ elif item_type in ["services", "facilities"]:
1581+ if item_type == "services":
1582+ s3.crud_strings["inv_inv_item"] = s3.crud_strings["don_service"]
1583+ item_category_filter = service_cat_id
1584+ itable.item_id.label = T("Service Type")
1585+ if s3_has_role(STAFF):
1586+ itable.item_id.comment = DIV(A(T("Add Service Type"),
1587+ _class="colorbox",
1588+ _href=URL(c="supply", f="item",
1589+ args="create",
1590+ vars=dict(format="popup")),
1591+ _target="top",
1592+ _title=T("Add Service Type")))
1593+ itable.quantity.label = T("Duration of Donated Services")
1594+ itable.item_pack_id.label = T("Unit of Time")
1595+ itable.pack_value.label = T("Estimated Value ($) per Unit of Time")
1596+
1597+ elif item_type == "facilities":
1598+ s3.crud_strings["inv_inv_item"] = s3.crud_strings["don_facility"]
1599+ item_category_filter = facility_cat_id
1600+ itable.item_id.label = T("Facility Type")
1601+ if s3_has_role(STAFF):
1602+ itable.item_id.comment = DIV(A(T("Add Facility Type"),
1603+ _class="colorbox",
1604+ _href=URL(c="supply", f="item",
1605+ args="create",
1606+ vars=dict(format="popup")),
1607+ _target="top",
1608+ _title=T("Add Facility Type")))
1609+ itable.comments.label = T("Comments (Sq. Ft, Bldg Type, Stories)")
1610+ itable.comments.comment = DIV(_class="tooltip",
1611+ _title="%s|%s|%s" % (T("Comments"),
1612+ T("Square Feet, Building Type, Stories."),
1613+ T("300 character limit.")))
1614+
1615+ itable.item_category_id.readable = itable.item_category_id.writable = False
1616+ itable.item_category_id.default = item_category_filter
1617+
1618+ itable.item_id.requires = IS_ONE_OF(db, "supply_item.id",
1619+ lambda id: response.s3.item_represent(id,
1620+ show_um = False,
1621+ show_link = False),
1622+ filterby = "item_category_id",
1623+ filter_opts = [item_category_filter],
1624+ sort=True)
1625+
1626+ query = (itable.item_category_id == item_category_filter)
1627+ inv_item_add_filter_func( query )
1628+# -----------------------------------------------------------------------------
1629+def organisation():
1630+ """
1631+ /org/organisation/ corporation
1632+ """
1633+
1634+ otable = db.org_organisation
1635+
1636+ otable.acronym.readable = False
1637+ otable.acronym.writable = False
1638+ otable.sector_id.readable = True
1639+ otable.sector_id.writable = True
1640+ org_has_items_field = otable.has_items
1641+ org_has_items_field.default = True
1642+ response.s3.filter = (org_has_items_field == True)
1643+
1644+ if not s3_has_role(STAFF):
1645+ # Tweak the breadcrumb
1646+ breadcrumbs[2] = (T("Organization Profile"), False,
1647+ URL(c=request.controller,
1648+ f=request.function,
1649+ args=request.args))
1650+
1651+ def corporation_rheader(r, tabs = []):
1652+ """ Corporation rheader """
1653+
1654+ if r.representation == "html":
1655+
1656+ if r.record is None:
1657+ # List or Create form: rheader makes no sense here
1658+ return None
1659+
1660+ tabs = [(T("Basic Details"), None),
1661+ (T("Contacts"), "human_resource"),
1662+ (T("Donate Goods"), "inv_item", dict(item="goods")),
1663+ (T("Donate Services "), "inv_item", dict(item="services")),
1664+ (T("Donate Facilities "), "inv_item", dict(item="facilities")),
1665+ ]
1666+ if "register" not in request.vars:
1667+ tabs.append( (T("Donations"), "commit") )
1668+ rheader_tabs = s3_rheader_tabs(r, tabs)
1669+
1670+ organisation = r.record
1671+ if organisation.sector_id:
1672+ _sectors = org_sector_represent(organisation.sector_id)
1673+ else:
1674+ _sectors = None
1675+
1676+ if deployment_settings.get_ui_cluster():
1677+ sector_label = T("Cluster(s)")
1678+ else:
1679+ sector_label = T("Sector(s)")
1680+
1681+ rheader = DIV(TABLE(
1682+ TR(
1683+ TH("%s: " % T("Corporation")),
1684+ organisation.name,
1685+ TH("%s: " % sector_label),
1686+ _sectors
1687+ )),
1688+ rheader_tabs
1689+ )
1690+ return rheader
1691+ return None
1692+
1693+ ADD_CORPORATION = T("Add Corporation / Organization")
1694+ LIST_CORPORATIONS = T("List Corporations & Organizations")
1695+ s3.crud_strings["org_organisation"] = Storage(
1696+ title_create = ADD_CORPORATION,
1697+ title_display = T("Corporation / Organization Details"),
1698+ title_list = LIST_CORPORATIONS,
1699+ title_update = T("Edit Corporation / Organization"),
1700+ title_search = T("Search Corporations & Organizations"),
1701+ subtitle_create = T("Add New Corporation / Organization"),
1702+ subtitle_list = T("Corporations & Organizations"),
1703+ label_list_button = LIST_CORPORATIONS,
1704+ label_create_button = ADD_CORPORATION,
1705+ label_delete_button = T("Delete Corporation / Organization"),
1706+ msg_record_created = T("Corporation / Organization added"),
1707+ msg_record_modified = T("Corporation / Organization updated"),
1708+ msg_record_deleted = T("Corporation / Organization deleted"),
1709+ msg_list_empty = T("No Corporations & Organizations currently registered"))
1710+
1711+ s3mgr.load("inv_inv_item")
1712+
1713+ def prep(r):
1714+ don_item_filter(lambda query:
1715+ r.resource.add_component_filter("inv_item", query))
1716+ if r.component and r.component.name == "human_resource":
1717+ hrtable = db.hrm_human_resource
1718+ hrtable.type.writable = hrtable.type.readable = False
1719+ hrtable.status.writable = hrtable.status.readable = False
1720+ hrtable.focal_point.writable = hrtable.focal_point.readable = False
1721+ response.s3.jquery_ready.append("$('#hrm_human_resource_person_id__row1').hide();")
1722+
1723+ s3.crud_strings["hrm_human_resource"] = Storage(
1724+ title_create = T("Add Contact"),
1725+ title_display = T("Contact Details"),
1726+ title_list = T("Contacts"),
1727+ title_update = T("Edit Contact"),
1728+ title_search = T("Search Contacts"),
1729+ subtitle_create = T("Additional Contacts (optional)"),
1730+ subtitle_list = T("Contacts"),
1731+ label_list_button = T("List Contacts"),
1732+ label_create_button = T("Add Contacts"),
1733+ label_delete_button = T("Delete Contact"),
1734+ msg_record_created = T("Contact added"),
1735+ msg_record_modified = T("Contact updated"),
1736+ msg_record_deleted = T("Contact deleted"),
1737+ msg_no_match = T("No Contacts Found"),
1738+ msg_list_empty = T("Currently there are no Contact registered"))
1739+
1740+ # Donation Organization Regisration Workflow
1741+ if "register" in request.vars:
1742+ # Only force the open on 1st run
1743+ response.s3.show_listadd = True
1744+ s3mgr.configure("hrm_human_resource",
1745+ create_next = URL(c="don", f="organisation",
1746+ args = [r.record.id, "inv_item"],
1747+ vars = dict(item="goods"))
1748+ )
1749+
1750+ # We don't want this enforced workflow as it doesn't allow adding multiple Goods, etc
1751+ #if r.component and r.component.name == "inv_item":
1752+ # next_item = dict(goods = "services",
1753+ # services = "facilities")
1754+ # if request.vars.item in next_item:
1755+ # response.s3.show_listadd = True
1756+ # url = URL(c="don", f="organisation",
1757+ # args = [r.record.id, "inv_item"],
1758+ # vars = dict(item=next_item[request.vars.item],
1759+ # register=1))
1760+ # else:
1761+ # url = URL(c="don", f="index", args = [], vars = {})
1762+ # s3mgr.configure("inv_inv_item",
1763+ # create_next = url
1764+ # )
1765+
1766+ # Add req to Corporations as Donations
1767+ s3mgr.configure("req_commit",
1768+ insertable = False,
1769+ editable = False,
1770+ deletable = False,
1771+ )
1772+
1773+ s3mgr.configure( "org_organisation",
1774+ list_fields = ["id",
1775+ "name",
1776+ #"type",
1777+ "sector_id",
1778+ #"country",
1779+ #"website"
1780+ ])
1781+
1782+ # req CRUD strings
1783+ REQ = T("Donation")
1784+ #ADD_REQ = T("Add Donation")
1785+ LIST_REQ = T("List Donations")
1786+ s3.crud_strings["req_req"] = Storage(
1787+ #title_create = ADD_REQ,
1788+ title_display = T("Donation Details"),
1789+ title_list = LIST_REQ,
1790+ #title_update = T("Edit Donation"),
1791+ title_search = T("Search Donations"),
1792+ #subtitle_create = ADD_REQ,
1793+ subtitle_list = T("Donations"),
1794+ label_list_button = LIST_REQ,
1795+ #label_create_button = ADD_REQ,
1796+ #label_delete_button = T("Remove Donations"),
1797+ #msg_record_created = T("Donation Added"),
1798+ #msg_record_modified = T("Donation updated"),
1799+ #msg_record_deleted = T("Donation removed"),
1800+ msg_list_empty = T("No Donations from this Corporation"))
1801+
1802+ if not s3_has_role(STAFF):
1803+ response.s3.donate_cash_link =True
1804+
1805+ return organisation_controller(organisation_rheader = corporation_rheader,
1806+ org_prep = prep)
1807+
1808+# -----------------------------------------------------------------------------
1809+def item():
1810+ """ REST Controller """
1811+
1812+ s3mgr.load("inv_inv_item")
1813+ def inv_item_add_filter_func(query):
1814+ response.s3.filter = query
1815+ don_item_filter(inv_item_add_filter_func)
1816+
1817+ # Tweak the breadcrumb
1818+ type = request.get_vars.item
1819+ if type == "goods":
1820+ label = T("Goods")
1821+ elif type == "services":
1822+ label = T("Services")
1823+ elif type == "facilities":
1824+ label = T("Facilities")
1825+ else:
1826+ # Error!
1827+ label = ""
1828+ breadcrumbs[2] = (label, False,
1829+ URL(c=request.controller,
1830+ f=request.function,
1831+ vars=request.vars))
1832+
1833+ return response.s3.inv_item_controller()
1834+
1835+# -----------------------------------------------------------------------------
1836+def collect():
1837+ """ REST Controller """
1838+ def prep(r):
1839+ s3mgr.configure("don_collect",
1840+ list_fields = ["id",
1841+ "start_datetime",
1842+ "end_datetime",
1843+ "site_id",
1844+ "organisation_id",
1845+ ])
1846+ return True
1847+
1848+ response.s3.prep = prep
1849+ output = s3_rest_controller(module, resourcename)
1850+ return output
1851+
1852+# -----------------------------------------------------------------------------
1853+def distribute():
1854+ """ REST Controller """
1855+ output = s3_rest_controller(module, resourcename)
1856+ return output
1857+
1858+# -----------------------------------------------------------------------------
1859+def profile():
1860+ """ Organisation Profile """
1861+ return organisation_profile()
1862+
1863+# -----------------------------------------------------------------------------
1864+################################################################################
1865+### Hook to call get the donation certificate
1866+################################################################################
1867+#def certprint():
1868+# """ Print Cert TEST """
1869+# s3mgr.load("don_distribute")
1870+# r = s3base.S3Request(s3mgr,
1871+# prefix="don",
1872+# name="distribute",
1873+# extension="pdf")
1874+# s3mgr.configure("don_distribute",
1875+# callback = response.s3.donationCertificate,
1876+# formname = "Donation Certificate",
1877+# header = response.s3.donCertBorder,
1878+# footer = lambda x, y: None,
1879+# )
1880+# return r()
1881+################################################################################
1882+###
1883+################################################################################
1884+
1885+
1886+# END =========================================================================
1887\ No newline at end of file
1888
1889=== modified file 'controllers/event.py'
1890--- controllers/event.py 2011-08-06 18:24:53 +0000
1891+++ controllers/event.py 2011-09-14 11:00:06 +0000
1892@@ -32,6 +32,16 @@
1893 redirect(URL(f="event", args="create"))
1894
1895 # =============================================================================
1896+# Incidents
1897+# =============================================================================
1898+def incident():
1899+ """ RESTful CRUD controller """
1900+
1901+ tablename = "event_incident"
1902+ s3mgr.load(tablename)
1903+ return s3_rest_controller(module, resourcename)
1904+
1905+# =============================================================================
1906 # Events
1907 # =============================================================================
1908 def event():
1909@@ -184,7 +194,7 @@
1910 tabs.append((T("Map Configuration"), "config"))
1911
1912 rheader = lambda r, tabs=tabs: event_rheader(r, tabs)
1913- output = s3_rest_controller("event", resourcename,
1914+ output = s3_rest_controller(module, resourcename,
1915 rheader=rheader)
1916 return output
1917
1918
1919=== modified file 'controllers/gis.py'
1920--- controllers/gis.py 2011-08-11 23:34:08 +0000
1921+++ controllers/gis.py 2011-09-14 11:00:06 +0000
1922@@ -63,7 +63,7 @@
1923 catalogue_toolbar = False
1924
1925 # @ToDo: Make these configurable
1926- search = True
1927+ search = False
1928 googleEarth = True
1929 googleStreetview = True
1930 catalogue_layers = True
1931@@ -92,7 +92,8 @@
1932 search=search,
1933 catalogue_layers=catalogue_layers,
1934 mouse_position = mouse_position,
1935- print_tool = print_tool
1936+ print_tool = print_tool,
1937+ collapsed = True
1938 )
1939
1940 return map
1941
1942=== modified file 'controllers/hrm.py'
1943--- controllers/hrm.py 2011-08-28 19:50:18 +0000
1944+++ controllers/hrm.py 2011-09-14 11:00:06 +0000
1945@@ -915,4 +915,197 @@
1946 redirect_vars = {fieldname: id},
1947 title_name = title)
1948
1949+# =============================================================================
1950+# Adding a human resource inline
1951+# =============================================================================
1952+
1953+def create_inline():
1954+ add_object_inline = local_import("s3.add_object_inline").add_object_inline
1955+ # Set up fields
1956+ pr_person = db.pr_person
1957+ hrm_human_resource = db.hrm_human_resource
1958+
1959+ field_dict = {
1960+ "first_name": pr_person.first_name,
1961+ "middle_name": pr_person.middle_name,
1962+ "last_name": pr_person.last_name,
1963+
1964+ "job_title": hrm_human_resource.job_title,
1965+ "organisation_id": hrm_human_resource.organisation_id,
1966+ }
1967+
1968+ emailRequired = (
1969+ request.controller == "hrm" and
1970+ deployment_settings.get_hrm_email_required()
1971+ )
1972+
1973+ if emailRequired:
1974+ email_validator = IS_EMAIL()
1975+ else:
1976+ email_validator = IS_NULL_OR(IS_EMAIL())
1977+
1978+ email_field_name = "email_address"
1979+ mobile_phone_field_name = "mobile_phone"
1980+ field_dict.update({
1981+ email_field_name: Field(
1982+ email_field_name,
1983+ notnull=emailRequired,
1984+ requires=email_validator,
1985+ label=T("Email Address")
1986+ ),
1987+ mobile_phone_field_name: Field(
1988+ mobile_phone_field_name,
1989+ label=T("Mobile Phone Number")
1990+ )
1991+ })
1992+
1993+
1994+ def validate_and_set_up_result(
1995+ update_result,
1996+ errors,
1997+ var,
1998+ check_request_var_matches_value_if_given,
1999+ existing
2000+ ):
2001+ pr_contact = db.pr_contact
2002+ pr_person = db.pr_person
2003+
2004+ existing_email_contact = None
2005+ existing_mobile_phone_contact = None
2006+ existing_person = None
2007+ existing_human_resource = None
2008+
2009+ def check_attributes_match(existing_object, attribute_names):
2010+ for attribute_name in attribute_names:
2011+ check_request_var_matches_value_if_given(
2012+ attribute_name,
2013+ field_dict[attribute_name].label,
2014+ getattr(existing_object, attribute_name)
2015+ )
2016+
2017+ # look for a person with the same email address
2018+ email_address = var(email_field_name)
2019+ if email_address:
2020+ existing_email_contact = existing(
2021+ (pr_contact.contact_method == "EMAIL") &
2022+ (pr_contact.value == email_address)
2023+ )
2024+ if existing_email_contact is not None:
2025+ # check every other contact detail matches, otherwise complain
2026+
2027+ # mobile phone
2028+ existing_mobile_phone_contact = existing(
2029+ (pr_contact.contact_method == "MOBILE PHONE") &
2030+ (pr_contact.pe_id == existing_email_contact.pe_id)
2031+ )
2032+ if existing_mobile_phone_contact is not None:
2033+ check_request_var_matches_value_if_given(
2034+ mobile_phone_field_name,
2035+ field_dict[mobile_phone_field_name].label,
2036+ existing_mobile_phone_contact.value
2037+ )
2038+
2039+ # person details
2040+ existing_person = existing(
2041+ pr_person.pe_id == existing_email_contact.pe_id
2042+ )
2043+ if existing_person is not None:
2044+ check_attributes_match(
2045+ existing_person,
2046+ (
2047+ "first_name",
2048+ "middle_name",
2049+ "last_name"
2050+ )
2051+ )
2052+
2053+ # human resource
2054+ existing_human_resource = existing(
2055+ hrm_human_resource.person_id == existing_person.id
2056+ )
2057+ if existing_human_resource is not None:
2058+ check_attributes_match(
2059+ existing_human_resource,
2060+ (
2061+ "job_title",
2062+ "organisation_id",
2063+ )
2064+ )
2065+
2066+ # an email address is assumed to uniquely identify a person
2067+ # however, there is no guarantee that a person has an email address
2068+ if existing_email_contact is None:
2069+ # there is no pr_pentity
2070+ pe_id = db.pr_pentity.insert().pe_id
2071+
2072+ db.pr_contact.insert(
2073+ pe_id = pe_id,
2074+ contact_method = "EMAIL",
2075+ value = email_address,
2076+ )
2077+ else:
2078+ contact_id = existing_email_contact.id
2079+ pe_id = existing_email_contact.pe_id
2080+
2081+ assert pe_id is not None
2082+
2083+ if existing_mobile_phone_contact is None:
2084+ if var(mobile_phone_field_name):
2085+ db.pr_contact.insert(
2086+ pe_id = pe_id,
2087+ contact_method = "MOBILE PHONE",
2088+ value = var(mobile_phone_field_name)
2089+ )
2090+
2091+ if existing_person is None:
2092+ person_id = db.pr_person.insert(
2093+ pe_id = pe_id,
2094+ first_name = var("first_name"),
2095+ middle_name = var("middle_name"),
2096+ last_name = var("last_name")
2097+ )
2098+ else:
2099+ person_id = existing_person.id
2100+ else:
2101+ errors[email_field_name] = T("Required")
2102+
2103+ if errors:
2104+ response.flash = T("form invalid")
2105+ else:
2106+ response.flash = T("form accepted")
2107+ if existing_human_resource:
2108+ result_human_resource = existing_human_resource
2109+ else:
2110+ result_human_resource = db.hrm_human_resource.insert(
2111+ person_id = person_id,
2112+ job_title = var("job_title"),
2113+ organisation_id = var("organisation_id")
2114+ )
2115+ db.commit()
2116+ update_result(
2117+ created_object_id = result_human_resource.id,
2118+ created_object_representation = hrm_human_resource_represent(
2119+ result_human_resource
2120+ )
2121+ )
2122+
2123+ return add_object_inline(
2124+ db = db,
2125+ formstyle = s3_formstyle,
2126+ fields = field_dict.values(),
2127+ table = db.hrm_human_resource,
2128+ table_name = "hrm_human_resource",
2129+ template = "inner_form.html",
2130+ validate_and_set_up_result = validate_and_set_up_result,
2131+ s3_mark_required = s3_mark_required,
2132+ TAG = TAG,
2133+ SPAN = SPAN,
2134+ response = response,
2135+ SQLFORM = SQLFORM,
2136+ T = T,
2137+ INPUT = INPUT,
2138+ XML = XML,
2139+ request = request,
2140+ )
2141+
2142 # END =========================================================================
2143
2144=== added file 'controllers/master.py'
2145--- controllers/master.py 1970-01-01 00:00:00 +0000
2146+++ controllers/master.py 2011-09-14 11:00:06 +0000
2147@@ -0,0 +1,22 @@
2148+# -*- coding: utf-8 -*-
2149+
2150+"""
2151+ Master Data - Controllers
2152+
2153+ @author: Fran
2154+"""
2155+
2156+module = request.controller
2157+
2158+if module not in deployment_settings.modules:
2159+ raise HTTP(404, body="Module disabled: %s" % module)
2160+
2161+# Options Menu (available in all Functions' Views)
2162+shn_menu(module)
2163+
2164+def index():
2165+ "Module's Home Page"
2166+
2167+ module_name = deployment_settings.modules[module].name_nice
2168+ response.title = module_name
2169+ return dict(module_name=module_name)
2170
2171=== modified file 'controllers/org.py'
2172--- controllers/org.py 2011-08-06 18:24:53 +0000
2173+++ controllers/org.py 2011-09-14 11:00:06 +0000
2174@@ -159,4 +159,87 @@
2175
2176 return output
2177
2178+
2179+def add_site_inline():
2180+ formstyle = s3_formstyle
2181+
2182+ org_office = db.org_office
2183+ field_names = [
2184+ "type",
2185+ "name",
2186+ "address",
2187+ "address_2",
2188+ "L3",
2189+ "L1",
2190+ "postcode",
2191+ "phone1",
2192+ "email",
2193+ ]
2194+ field_dict = {}
2195+ for field_name in field_names:
2196+ field_dict[field_name] = getattr(org_office, field_name)
2197+
2198+ def validate_and_set_up_result(
2199+ update_result, errors, var,
2200+ check_request_var_matches_value_if_given,
2201+ existing
2202+ ):
2203+ # look for an office with the same name
2204+ name = var("name")
2205+ if name:
2206+ existing_office = existing(
2207+ org_office.name == name
2208+ )
2209+ if existing_office is not None:
2210+ for field_name, field in field_dict.iteritems():
2211+ print field_name
2212+ check_request_var_matches_value_if_given(
2213+ field_name,
2214+ field.label,
2215+ getattr(existing_office, field_name)
2216+ )
2217+ else:
2218+ existing_office = None
2219+
2220+ if errors:
2221+ response.flash = T("form is invalid")
2222+ else:
2223+ response.flash = T("form accepted")
2224+ if existing_office:
2225+ result_object = existing_office
2226+ else:
2227+ result_object = db.org_office.insert(
2228+ **dict(
2229+ (field_name, var(field_name)) for field_name in field_names
2230+ )
2231+ )
2232+ db.commit()
2233+ update_result(
2234+ created_object_id = existing_office.id,
2235+ created_object_representation = existing_office.name
2236+ )
2237+
2238+
2239+ add_object_inline = local_import("s3.add_object_inline").add_object_inline
2240+
2241+ return add_object_inline(
2242+ db = db,
2243+ formstyle = s3_formstyle,
2244+ fields = field_dict.values(),
2245+ table = db.org_office,
2246+ table_name = "org_office",
2247+ template = "inner_form.html",
2248+ validate_and_set_up_result = validate_and_set_up_result,
2249+ s3_mark_required = s3_mark_required,
2250+ TAG = TAG,
2251+ SPAN = SPAN,
2252+ response = response,
2253+ SQLFORM = SQLFORM,
2254+ T = T,
2255+ INPUT = INPUT,
2256+ XML = XML,
2257+ request = request,
2258+ )
2259+
2260+
2261 # END =========================================================================
2262
2263=== modified file 'controllers/project.py'
2264--- controllers/project.py 2011-08-06 18:24:53 +0000
2265+++ controllers/project.py 2011-09-14 11:00:06 +0000
2266@@ -17,7 +17,6 @@
2267
2268 # =============================================================================
2269 def index():
2270-
2271 """ Module's Home Page """
2272
2273 module_name = deployment_settings.modules[module].name_nice
2274@@ -31,21 +30,18 @@
2275
2276 # =============================================================================
2277 def need():
2278-
2279 """ RESTful CRUD controller """
2280
2281 return s3_rest_controller(module, resourcename)
2282
2283 # -----------------------------------------------------------------------------
2284 def need_type():
2285-
2286 """ RESTful CRUD controller """
2287
2288 return s3_rest_controller(module, resourcename)
2289
2290 # =============================================================================
2291 def project():
2292-
2293 """ RESTful CRUD controller """
2294
2295 tablename = "%s_%s" % (module, resourcename)
2296@@ -71,65 +67,13 @@
2297
2298 # =============================================================================
2299 def activity():
2300-
2301 """ RESTful CRUD controller """
2302
2303- tablename = "%s_%s" % (module, resourcename)
2304- table = db[tablename]
2305-
2306- tabs = [
2307- (T("Details"), None),
2308- (T("Requests"), "req"),
2309- (T("Documents"), "document"),
2310- (T("Photos"), "image"),
2311- #(T("Shipments To"), "rms_req"),
2312- ]
2313- rheader = lambda r: activity_rheader(r, tabs)
2314-
2315- if "create" in request.args:
2316- # Default values (from gap_report) set for fields
2317- default_fieldnames = ["location_id", "need_type_id"]
2318- for fieldname in default_fieldnames:
2319- if fieldname in request.vars:
2320- table[fieldname].default = request.vars[fieldname]
2321- table[fieldname].writable = False
2322- table[fieldname].comment = None
2323-
2324- return s3_rest_controller(module, resourcename,
2325- rheader = rheader)
2326-
2327-# -----------------------------------------------------------------------------
2328-def activity_rheader(r, tabs=[]):
2329- """ Resource Header for Activities"""
2330-
2331- if r.representation == "html":
2332- record = r.record
2333- if record:
2334- rheader_tabs = s3_rheader_tabs(r, tabs)
2335- rheader = DIV( TABLE(
2336- TR( TH( "%s: " % T("Short Description")),
2337- record.name,
2338- ),
2339- TR( TH( "%s: " % T("Location")),
2340- gis_location_represent(record.location_id),
2341- TH( "%s: " % T("Duration")),
2342- "%s to %s" % (record.start_date,
2343- record.end_date),
2344- ),
2345- TR( TH( "%s: " % T("Organization")),
2346- organisation_represent(record.organisation_id),
2347- TH( "%s: " % SECTOR),
2348- shn_org_sector_represent(record.sector_id),
2349- ),
2350- ),
2351- rheader_tabs
2352- )
2353- return rheader
2354- return None
2355+ # Defined in the Model for use from Multiple Controllers for unified menus
2356+ return response.s3.project_activity_controller()
2357
2358 # =============================================================================
2359 def task():
2360-
2361 """ RESTful CRUD controller """
2362
2363 # Pre-process
2364@@ -211,7 +155,6 @@
2365
2366 # =============================================================================
2367 def gap_report():
2368-
2369 """ Provide a Report on Gaps between Activities & Needs Assessments """
2370
2371 # Get all assess_summary
2372
2373=== modified file 'controllers/req.py'
2374--- controllers/req.py 2011-08-07 08:48:01 +0000
2375+++ controllers/req.py 2011-09-14 11:00:06 +0000
2376@@ -37,9 +37,13 @@
2377 - custom View
2378 """
2379
2380- module_name = deployment_settings.modules[module].name_nice
2381- response.title = module_name
2382- return dict(module_name=module_name)
2383+ if s3_has_role("ADMIN"):
2384+ module_name = deployment_settings.modules[module].name_nice
2385+ response.title = module_name
2386+ return dict(module_name=module_name)
2387+
2388+ else:
2389+ redirect(URL(f="req_skill"))
2390
2391 # -----------------------------------------------------------------------------
2392 def is_affiliated():
2393@@ -65,7 +69,7 @@
2394 def create():
2395 """ Redirect to req/create """
2396 redirect(URL(f="req", args="create"))
2397-
2398+
2399 # -----------------------------------------------------------------------------
2400 def req():
2401 """ REST Controller """
2402@@ -135,10 +139,10 @@
2403
2404 output["title"] = T("Request Item from Available Inventory")
2405 output["req_btn"] = A( T("Return to Request"),
2406- _href = URL( c = "req",
2407- f = "req",
2408- args = [req_item.req_id, "req_item"]
2409- ),
2410+ _href = URL(c = "req",
2411+ f = "req",
2412+ args = [req_item.req_id, "req_item"]
2413+ ),
2414 _class = "action-btn"
2415 )
2416
2417@@ -301,7 +305,7 @@
2418 if r.representation == "html":
2419 record = r.record
2420 if record and r.name == "commit":
2421- tabs = [(T("Edit Details"), None)]
2422+ tabs = [(T("Details"), None)]
2423
2424 if record.type == 1:
2425 tabs.append((T("Items"), "commit_item"))
2426
2427=== modified file 'controllers/supply.py'
2428--- controllers/supply.py 2011-09-09 11:19:19 +0000
2429+++ controllers/supply.py 2011-09-14 11:00:06 +0000
2430@@ -30,6 +30,7 @@
2431 module_name = deployment_settings.modules[module].name_nice
2432 response.title = module_name
2433 return dict(module_name=module_name)
2434+
2435 # -----------------------------------------------------------------------------
2436 def catalog():
2437 """ RESTful CRUD controller """
2438
2439=== modified file 'controllers/vol.py' (properties changed: +x to -x)
2440--- controllers/vol.py 2011-08-06 18:24:53 +0000
2441+++ controllers/vol.py 2011-09-14 11:00:06 +0000
2442@@ -2,8 +2,6 @@
2443
2444 """
2445 Volunteer Module
2446- - A 'My Sahana' view of an individual Volunteer's Data, Tasks, etc
2447- (Ability for Anonymous users to sign-up as a Volunteer?)
2448 """
2449
2450 module = request.controller
2451@@ -19,8 +17,11 @@
2452 def index():
2453 """ Module's Home Page """
2454
2455- # Default to the Personal profile
2456- return person()
2457+ if s3_has_role(STAFF):
2458+ response.view = "vol/index.html"
2459+ return dict()
2460+ else:
2461+ redirect(URL(c="vol", f="req_skill"))
2462
2463 # -----------------------------------------------------------------------------
2464 # People
2465@@ -28,150 +29,41 @@
2466 def person():
2467 """
2468 Person Controller
2469- - allows a person to view/update the details held about them
2470+ - List of Volunteers
2471 """
2472
2473+ module = "pr"
2474 resourcename = "person"
2475+ tablename = "pr_person"
2476+ table = db[tablename]
2477
2478+ response.s3.filter = (table.volunteer == True)
2479+
2480 # Load Model
2481 s3mgr.load("pr_address")
2482- s3mgr.load("hrm_skill")
2483-
2484- s3.crud.submit_button = T("Next")
2485-
2486- s3mgr.model.add_component("hrm_certification",
2487- pr_person="person_id")
2488-
2489- s3mgr.model.add_component("hrm_competency",
2490- pr_person="person_id")
2491-
2492- s3mgr.model.add_component("hrm_credential",
2493- pr_person="person_id")
2494-
2495- s3mgr.model.add_component("hrm_training",
2496- pr_person="person_id")
2497-
2498- s3mgr.model.add_component("hrm_experience",
2499- pr_person="person_id")
2500-
2501- # Q: Are volunteers assigned to a specific organisation outside of a specific Event?
2502- # Yes, some are: CERT
2503- s3mgr.model.add_component("hrm_human_resource",
2504- pr_person="person_id")
2505-
2506- # Configure human resource table
2507- # - for Positions
2508- tablename = "hrm_human_resource"
2509- table = db[tablename]
2510- table.type.readable = True
2511- table.type.writable = False
2512- table.location_id.writable = False # Populated from pr_address
2513- table.location_id.readable = False
2514- s3mgr.configure(tablename,
2515- list_fields=["id",
2516- "organisation_id",
2517- "job_title",
2518- "status"])
2519-
2520- # Configure person table
2521- # - hide fields
2522- tablename = "pr_person"
2523- table = db[tablename]
2524- table.pe_label.readable = False
2525- table.pe_label.writable = False
2526- table.missing.readable = False
2527- table.missing.writable = False
2528- table.age_group.readable = False
2529- table.age_group.writable = False
2530- s3mgr.configure(tablename,
2531- # Wizard: Move to next Tab after Save
2532- update_next = URL(args=["[id]", "address"]),
2533- deletable=False)
2534-
2535- # Configure for personal mode
2536- s3.crud_strings[tablename].update(
2537- title_display = T("Personal Profile"),
2538- title_update = T("Personal Profile"))
2539- # People can view their own HR data, but not edit it
2540- db.hrm_human_resource.organisation_id.readable = True
2541- s3mgr.configure("hrm_human_resource",
2542- insertable = False,
2543- editable = False,
2544- deletable = False)
2545- s3mgr.configure("hrm_certification",
2546- insertable = True,
2547- editable = True,
2548- deletable = True)
2549- s3mgr.configure("hrm_credential",
2550- insertable = False,
2551- editable = False,
2552- deletable = False)
2553- s3mgr.configure("hrm_competency",
2554- insertable = True, # Can add unconfirmed
2555- editable = False,
2556- deletable = False)
2557- s3mgr.configure("hrm_training", # Can add but not provide grade
2558- insertable = True,
2559- editable = False,
2560- deletable = False)
2561- s3mgr.configure("hrm_experience",
2562- insertable = False,
2563- editable = False,
2564- deletable = False)
2565- s3mgr.configure("pr_group_membership",
2566- insertable = False,
2567- editable = False,
2568- deletable = False)
2569- tabs = [(T("Person Details"), None),
2570- (T("Addresses"), "address"),
2571- (T("Certificates"), "certification"),
2572- (T("Contact Information"), "contact"),
2573- #(T("Skills"), "competency"),
2574- #(T("Credentials"), "credential"),
2575- #(T("Trainings"), "training"),
2576- #(T("Mission Record"), "experience"),
2577- #(T("Positions"), "human_resource"),
2578- #(T("Teams"), "group_membership")
2579- ]
2580-
2581- # Prepare CRUD
2582- def prep(r):
2583- if r.interactive:
2584- resource = r.resource
2585- r.resource.build_query(id=s3_logged_in_person())
2586- if resource.count() == 1:
2587- resource.load()
2588- r.record = resource.records().first()
2589- if r.record:
2590- r.id = r.record.id
2591- if r.component:
2592- if r.component.name == "address":
2593- s3mgr.configure("pr_address",
2594- create_next = URL(args=[str(r.id), "certification"]))
2595- elif r.component.name == "certification":
2596- r.component.table.certificate_id.comment = None
2597- r.component.table.organisation_id.readable = False
2598- s3mgr.configure("hrm_certification",
2599- create_next = URL(args=[str(r.id), "contact"]))
2600- #elif r.component.name == "contact":
2601- # s3mgr.configure("pr_contact",
2602- # create_next = URL(# args=[str(r.id), ""]))
2603-
2604- return True
2605- else:
2606- # This controller is only for interactive use
2607- redirect(URL(f='default', c="index"))
2608- response.s3.prep = prep
2609-
2610- rheader = lambda r, tabs=tabs: vol_rheader(r, tabs)
2611-
2612- output = s3_rest_controller("pr", resourcename,
2613- native=False,
2614- rheader=rheader)
2615+ s3mgr.load("vol_skill")
2616+
2617+ s3mgr.model.add_component("vol_skill",
2618+ pr_person="person_id")
2619+
2620+ s3mgr.configure("vol_skill",
2621+ deletable = False)
2622+
2623+ tabs = [ (T("Basic Details"), None),
2624+ (T("Skills"), "skill"),
2625+ (T("Organizational Affiliations"), "organisation"),
2626+ (T("Address"), "address"),
2627+ (T("Contact Details"), "contact"),
2628+ ]
2629+
2630+ rheader = lambda r: vol_pr_rheader(r, tabs=tabs)
2631+
2632+ output = s3_rest_controller(module, resourcename, rheader=rheader)
2633+
2634 return output
2635
2636 # -----------------------------------------------------------------------------
2637-def vol_rheader(r, tabs=[]):
2638+def vol_pr_rheader(r, tabs=[]):
2639 """ Resource headers for component views """
2640
2641 rheader = None
2642@@ -190,46 +82,2328 @@
2643 TH(""),
2644 ""),
2645
2646- TR(TH("%s: " % T("Date of Birth")),
2647- "%s" % (s3_date_represent(person.date_of_birth) or T("unknown")),
2648- TH(""),
2649- ""),
2650-
2651 ), rheader_tabs)
2652
2653 return rheader
2654
2655-# -----------------------------------------------------------------------------
2656-# Tasks
2657-# -----------------------------------------------------------------------------
2658-def task():
2659-
2660- """ Allow a volunteer to View the details of their own tasks """
2661-
2662- tablename = "project_task"
2663- s3mgr.load(tablename)
2664- table = db[tablename]
2665-
2666- my_person_id = s3_logged_in_person()
2667-
2668- if not my_person_id:
2669- session.error = T("No person record found for current user.")
2670- redirect(URL(f="index"))
2671-
2672- table.person_id.default = my_person_id
2673-
2674- response.s3.filter = (table.person_id == my_person_id)
2675-
2676- s3.crud_strings[tablename].title_list = T("My Tasks")
2677- s3.crud_strings[tablename].subtitle_list = T("Task List")
2678- s3.crud_strings[tablename].msg_list_empty = T("No tasks currently assigned")
2679-
2680- s3mgr.configure(tablename,
2681- insertable = False,
2682- editable = False,
2683- deletable = False)
2684-
2685- return s3_rest_controller("project", "task")
2686+# =============================================================================
2687+def organisation():
2688+ """
2689+ Org Controller for OrgAdmins
2690+ """
2691+
2692+ table = db.org_organisation
2693+
2694+ table.acronym.readable = False
2695+ table.acronym.writable = False
2696+ org_has_vols_field = table.has_vols
2697+ org_has_vols_field.default = True
2698+ response.s3.filter = (org_has_vols_field == True)
2699+
2700+ if "register" in request.vars:
2701+ response.s3.show_listadd = True
2702+
2703+ return organisation_controller(organisation_rheader = organisation_rheader)
2704+
2705+# -----------------------------------------------------------------------------
2706+def organisation_rheader(r, tabs = []):
2707+ """ Organisation rheader """
2708+
2709+ if r.representation == "html":
2710+
2711+ if r.record is None:
2712+ # List or Create form: rheader makes no sense here
2713+ return None
2714+
2715+ tabs = [(T("Basic Details"), None),
2716+ (T("Contacts"), "human_resource"),
2717+ (T("Activities"), "activity")
2718+ ]
2719+ rheader_tabs = s3_rheader_tabs(r, tabs)
2720+
2721+ organisation = r.record
2722+
2723+ rheader = DIV(TABLE(
2724+ TR(
2725+ TH("%s: " % T("Organization")),
2726+ organisation.name,
2727+ ),
2728+ ),
2729+ rheader_tabs
2730+ )
2731+ return rheader
2732+ return None
2733+
2734+# =============================================================================
2735+def vol_sidebar():
2736+ """ Sidebar for people who aren't logged-in """
2737+
2738+ request.args = ["login"]
2739+ loginform = auth()
2740+ loginform.attributes["_id"] = "loginform"
2741+ request.args = []
2742+
2743+ if not response.menu_left:
2744+ response.menu_left = DIV()
2745+
2746+ if not auth.is_logged_in():
2747+ response.menu_left.append(
2748+ DIV(
2749+ P("%s:" % T("Already registered? Sign in here")),
2750+ #loginform,
2751+ FORM(
2752+ LABEL(T("Email"), _for="email"),
2753+ INPUT(_type="text", _name="email", _title=T("Email")),
2754+ LABEL(T("Password"), _for="password"),
2755+ INPUT(_type="password", _name="password", _title=T("Password")),
2756+ INPUT(_type="submit", _value=T("Sign In"), _title=T("Sign In"),
2757+ _class="sign-in-button"),
2758+ loginform.custom.end,
2759+ _action="", _enctype="multipart/form-data", _method="post"),
2760+ _id="sign-in-menu",
2761+ _class="sub-menu"))
2762+ response.menu_left.append(
2763+ DIV(
2764+ P("%s:" % T("Other Volunteering Opportunities"), _class="osm-title"),
2765+ UL(
2766+ LI(
2767+ A(
2768+ DIV("American Red Cross", _class="link-name"),
2769+ DIV(
2770+ H3("redcrossla.org"),
2771+ IMG(_src="/%s/static/img/la/logo_arc.png" % request.application, _width="87", _height="29", _alt="American Red Cross Logo"),
2772+ P("Rooted in over a century's tradition of service to the community, the American Red Cross is one of the world's most renowned humanitarian organizations. American Red Cross volunteers assist with community emergency preparedness, services to the armed forces, emergency and disaster response, blood services, health and safety education, international services and other support volunteer services."),
2773+ _class="other-popup"),
2774+ _href="http://redcrossla.org", _target="_blank",
2775+ _title="American Red Cross - Link will open to new Window")
2776+ ),
2777+ LI(
2778+ A(
2779+ DIV("CERT", _class="link-name"),
2780+ DIV(
2781+ H3("www.cert-la.com"),
2782+ IMG(_src="/%s/static/img/la/certlogo.png" % request.application, _width="51", _height="30", _alt="CERT Logo"),
2783+ P("The Los Angeles Fire Department Community Emergency Response Training Program (CERT), has been educating the citizens of Los Angeles in Disaster Preparedness since 1987. "),
2784+ _class="other-popup"),
2785+ _href="http://www.cert-la.com", _target="_blank",
2786+ _title="CERT - %s" % T("Link will open to new Window"))
2787+ ),
2788+ LI(
2789+ A(
2790+ DIV("LA County Disaster Healthcare Volunteers", _class="link-name"),
2791+ DIV(
2792+ H3("www.lacountydhv.org"),
2793+ IMG(_src="/%s/static/img/la/logo_dhv.png" % request.application, _width="88", _height="30", _alt="Disaster Healthcare Volunteers"),
2794+ P("LA County Disaster Healthcare Volunteers, comprised of 4 volunteer units, is led by the County of LA Departments of Health Services, Emergency Medical Services Agency and Public Health Emergency Preparedness and Response Program. It is specifically for licensed medical, health, mental health, and other volunteers who want to volunteer their professional skills for public health emergencies or other large scale disasters. Actively licensed health professionals are encouraged to register with one of the four units (MRC LA, LA County Surge Unit, Long Beach MRC, Beach Cities Health District MRC) on the State of California Disaster Healthcare Volunteers (DHV) site in advance of the next disaster."),
2795+ _class="other-popup"),
2796+ _href="http://www.lacountydhv.org", _target="_blank",
2797+ _title="Disaster Healthcare Volunteers - %s" % T("Link will open to new Window"))
2798+ ),
2799+ LI(
2800+ A(
2801+ DIV("LA Works", _class="link-name"),
2802+ DIV(
2803+ H3("www.laworks.com"),
2804+ IMG(_src="/%s/static/img/la/laworks.png" % request.application, _width="47", _height="40", _alt="LA Works Logo"),
2805+ P("L.A. Works is a 501(c)3 nonprofit, volunteer action center that creates and implements hands-on community service projects throughout the greater LA area."),
2806+ _class="other-popup"),
2807+ _href="http://www.laworks.com", _target="_blank",
2808+ _title="LA Works - %s" % T("Link will open to new Window"))
2809+ ),
2810+ LI(
2811+ A(
2812+ DIV("Public Health Emergency Volunteer Network", _class="link-name"),
2813+ DIV(
2814+ H3("publichealth.lacounty.gov"),
2815+ IMG(_src="/%s/static/img/la/logo_phev.png" % request.application, _width="121", _height="30", _alt="Public Health Emergency Volunteer (PHEV) Network"),
2816+ P("The purpose of the Public Health Emergency Volunteer (PHEV) Network is to increase the coordination and collaboration with established community volunteer units that are willing to assist the Department of Public Health in responding to public health emergencies by creating a system to engage, train, and deploy these groups."),
2817+ _class="other-popup"),
2818+ _href="http://publichealth.lacounty.gov/eprp/volview.htm", _target="_blank",
2819+ _title="Public Health Emergency Volunteer (PHEV) Network - %s" % T("Link will open to new Window"))
2820+ ),
2821+ LI(
2822+ A(
2823+ DIV("VCLA", _class="link-name"),
2824+ DIV(
2825+ H3("www.vcla.net"),
2826+ IMG(_src="/%s/static/img/la/vcla.png" % request.application, _width="40", _height="40", _alt="VCLA Logo"),
2827+ P("The Volunteer Center of Los Angeles (VCLA) is one of the largest civic action centers in the nation. We are the volunteer hub that connects people, communities, resources and businesses across Los Angeles. Our mission is to change lives and communities by connecting volunteers with community needs to make Los Angeles a safer, greener, healthier and more hopeful place to live and work."),
2828+ _class="other-popup"),
2829+ _href="http://www.vcla.net", _target="_blank",
2830+ _title="VCLA - %s" % T("Link will open to new Window"))
2831+ )
2832+ ),
2833+ _class="other-sub-menu")
2834+ )
2835+ if session.s3.debug:
2836+ response.s3.scripts.append( "%s/jquery.hoverIntent.js" % s3_script_dir )
2837+ else:
2838+ response.s3.scripts.append( "%s/jquery.hoverIntent.minified.js" % s3_script_dir )
2839+
2840+ response.s3.jquery_ready.append("""
2841+$('.other-popup').css('display', 'none');
2842+$('.tooltip .message').css('display', 'none');
2843+$('.other-sub-menu li a').hoverIntent(menuFadeIn, menuFadeOut);
2844+$('.tooltip a').hoverIntent(tooltipFadeIn, tooltipFadeOut);""")
2845+ response.s3.js_global.append("""
2846+// For Popup Panels for the Other Menu
2847+function menuFadeIn(){$(this).children('.other-popup').fadeIn();}
2848+function menuFadeOut(){$(this).children('.other-popup').fadeOut();}
2849+// For Tooltop Panels for the Other Menu
2850+function tooltipFadeIn(){$(this).next('.message').fadeIn();}
2851+function tooltipFadeOut(){$(this).next('.message').fadeOut();}""")
2852+
2853+# -----------------------------------------------------------------------------
2854+def register():
2855+ """ Custom Registration Form """
2856+
2857+ auth.messages.submit_button = T("I accept. Create my account.")
2858+ request.args = ["register"]
2859+ db[auth.settings.table_user].language.default = T.accepted_language
2860+ # @ToDo: Make use to the SQLFORM.createform function instead of inserting fields?
2861+ form = auth()
2862+ form.attributes["_id"] = "regform"
2863+ # Custom class for Submit Button
2864+ form[0][-1][0][0]["_class"] = "accept-button"
2865+
2866+ # Cancel button
2867+ form[0][-1][0].append(BR())
2868+ #form[0][-1][0].append(INPUT(_type="reset", _value=T("Cancel")))
2869+ form[0][-1][0].append(INPUT(_type="button",
2870+ _value=T("Cancel"),
2871+ _class="wide-grey-button",
2872+ _onClick="javascript: history.go(-1)"))
2873+
2874+ formstyle = s3.crud.formstyle
2875+
2876+ # Medical note
2877+ row = formstyle(id = "",
2878+ label = "",
2879+ widget = DIV("%s:" % T("If you are a Medical or Health Professional and wish to volunteer in a professional capacity, please register at"),
2880+ BR(),
2881+ A("LA County Disaster Healthcare Volunteers",
2882+ _href="http://www.lacountydhv.org",
2883+ _target="_blank"),
2884+ BR(),
2885+ BR(),
2886+ "%s:" % T("Individuals or Volunteer Units/Organizations interested in assisting during public health emergencies (i.e. Mass vaccine/medication dispensing sites or PODs) please register at"),
2887+ BR(),
2888+ A("LA County Department of Public Health Emergency Preparedness and Response Program",
2889+ _href="http://publichealth.lacounty.gov/eprp/volview.htm",
2890+ _target="_blank"),
2891+ _style="width: 400px;",
2892+ ),
2893+ comment = "")
2894+ form[0].insert(0, row)
2895+
2896+ # Middle Name
2897+ row = formstyle(id = "middle_name",
2898+ label = LABEL("%s:" % T("Middle Name"),
2899+ _for="middle_name"),
2900+ widget = INPUT(_name="middle_name",
2901+ _id="middle_name",
2902+ _class="string"),
2903+ comment = "")
2904+ form[0][2].append(row)
2905+
2906+ # Phone
2907+ phone_opts = {
2908+ "SMS": current.deployment_settings.get_ui_label_mobile_phone(),
2909+ "HOME_PHONE": T("Home phone"),
2910+ "WORK_PHONE": T("Work phone")
2911+ }
2912+ if form.errors.phone:
2913+ phone_error = DIV(form.errors.phone,
2914+ _id="phone__error",
2915+ _class="error",
2916+ _style="display: block;")
2917+ else:
2918+ phone_error = ""
2919+ phone_widget = (INPUT(_name="phone",
2920+ _id="",
2921+ _class="string"),
2922+ SELECT(OPTION(current.deployment_settings.get_ui_label_mobile_phone(), _value="SMS"),
2923+ OPTION(T("Home phone"), _value="HOME_PHONE"),
2924+ OPTION(T("Work phone"), _value="WORK_PHONE"),
2925+ requires=IS_IN_SET(phone_opts),
2926+ value="SMS",
2927+ _name="phone_type",
2928+ _id="phone_type"),
2929+ phone_error
2930+ )
2931+ phone_help = DIV(_class="tooltip",
2932+ _title="%s|%s" % (T("Phone"),
2933+ T("if you provide your Cell phone then you can choose to subscribe to SMS notifications by selecting 'My Profile' once you have completed registration.")))
2934+ row = formstyle(id = "phone",
2935+ label = LABEL("%s:" % T("Phone"),
2936+ SPAN(" *", _class="req"),
2937+ _for="phone"),
2938+ widget = phone_widget,
2939+ comment = phone_help)
2940+ form[0][11].append(row)
2941+
2942+ # Address
2943+ if form.errors.address1:
2944+ address1_error = DIV(form.errors.address1,
2945+ _id="address1__error",
2946+ _class="error",
2947+ _style="display: block;")
2948+ else:
2949+ address1_error = ""
2950+ row = formstyle(id = "address1",
2951+ label = LABEL("%s:" % T("Address 1"),
2952+ SPAN(" *", _class="req"),
2953+ _for="address1"),
2954+ widget = (INPUT(_name="address1",
2955+ _id="address1",
2956+ _class="string"),
2957+ address1_error),
2958+ comment = "")
2959+ form[0][11].append(row)
2960+ row = formstyle(id = "address2",
2961+ label = LABEL("%s:" % T("Address 2"),
2962+ _for="address2"),
2963+ widget = INPUT(_name="address2",
2964+ _id="address2",
2965+ _class="string"),
2966+ comment = "")
2967+ form[0][11].append(row)
2968+ row = formstyle(id = "city",
2969+ label = LABEL("%s:" % T("City"),
2970+ SPAN(" *", _class="req"),
2971+ _for="city"),
2972+ widget = INPUT(_name="city",
2973+ _id = "city",
2974+ _class = "string"),
2975+ comment = "")
2976+ form[0][11].append(row)
2977+ states = S3LocationDropdownWidget(level="L1",
2978+ default="California",
2979+ empty=False)
2980+ widget = states(db.pr_address.location_id, None)
2981+ row = formstyle(id = "state",
2982+ label = LABEL("%s:" % T("State"),
2983+ SPAN(" *", _class="req"),
2984+ _for="location_id"),
2985+ widget = widget,
2986+ comment = "")
2987+ form[0][11].append(row)
2988+ if form.errors.zip:
2989+ zip_error = DIV(form.errors.zip,
2990+ _id="zip__error",
2991+ _class="error",
2992+ _style="display: block;")
2993+ else:
2994+ zip_error = ""
2995+ row = formstyle(id = "zip",
2996+ label = LABEL("%s:" % T("Zip"),
2997+ SPAN(" *", _class="req"),
2998+ _for="zip"
2999+ ),
3000+ widget = ( INPUT(_name="zip",
3001+ _id="zip",
3002+ _class="string"),
3003+ zip_error
3004+ ),
3005+ comment = "")
3006+ form[0][11].append(row)
3007+
3008+
3009+ #form[0][-2].append(TR(TD(LABEL(T("Terms of Service:"),
3010+ # _id="terms_of_service__label"),
3011+ # _class="w2p_fl"),
3012+ # TD(LABEL(TEXTAREA(deployment_settings.get_terms_of_service(),
3013+ # _onfocus="this.rows=10",
3014+ # _readonly="readonly",
3015+ # _style="width:100%;text-align:",
3016+ # _cols="80", _rows="10"),
3017+ # _id="terms_of_service"),
3018+ # _class="w2p_fw",
3019+ # _colspan="2"),
3020+ # _id="terms_of_service__row"))
3021+
3022+ # Eighteen
3023+ if form.errors.eighteen:
3024+ eighteen_error = DIV(form.errors.eighteen,
3025+ _id="eighteen__error",
3026+ _class="error",
3027+ _style="display: block;")
3028+ else:
3029+ eighteen_error = ""
3030+ label = LABEL(#"%s:" % T("I am 18 or over"),
3031+ T("I am 18 or over"),
3032+ SPAN(" *", _class="req"),
3033+ _for="eighteen",
3034+ _style="display: inline;",
3035+ _id="eighteen__label")
3036+ widget = INPUT(_type="checkbox",
3037+ _value="on",
3038+ _name="eighteen",
3039+ _id="eighteen",
3040+ _class="boolean")
3041+ #row = formstyle(id = "eighteen",
3042+ # label = label,
3043+ # widget = (widget,
3044+ # eighteen_error),
3045+ # comment = "")
3046+ row = TR(TD(widget, label, eighteen_error),
3047+ TD(),
3048+ _id="eighteen")
3049+ form[0][-2].append(row)
3050+
3051+ # US Citizen
3052+ if form.errors.citizen:
3053+ citizen_error = DIV(form.errors.citizen,
3054+ _id="citizen__error",
3055+ _class="error",
3056+ _style="display: block;")
3057+ else:
3058+ citizen_error = ""
3059+ label = LABEL(#"%s:" % T("I am a U.S. Citizen"),
3060+ T("I am a U.S. Citizen"),
3061+ SPAN(" *", _class="req"),
3062+ _for="citizen",
3063+ _style="display: inline;",
3064+ _id="citizen__label")
3065+ widget = INPUT(_type="checkbox",
3066+ _value="on",
3067+ _name="citizen",
3068+ _id="citizen",
3069+ _class="boolean")
3070+ #row = formstyle(id = "citizen",
3071+ # label = label,
3072+ # widget = (widget,
3073+ # citizen_error),
3074+ # comment = "")
3075+ row = TR(TD(widget, label, citizen_error),
3076+ TD(),
3077+ _id="citizen")
3078+ form[0][-2].append(row)
3079+
3080+ # Privacy Policy
3081+ row = formstyle(id = "",
3082+ label = "",
3083+ widget = DIV(XML(T("By clicking on 'I accept' below you are agreeing to the %(privacy_policy)s.") % \
3084+ dict(privacy_policy = A(T("Privacy Policy"),
3085+ _href = URL(c="default",
3086+ f="disclaimer#privacy"),
3087+ _target = "_blank"
3088+ )
3089+ )
3090+ ),
3091+ _style="width: 300px;",
3092+ ),
3093+ comment = "")
3094+ form[0][-2].append(row)
3095+
3096+ # Add client-side validation
3097+ # simplified copy of s3_register_validation()
3098+ script = "".join(( """
3099+$('#regform').validate({
3100+ errorClass: 'req',
3101+ rules: {
3102+ first_name: {
3103+ required: true
3104+ },
3105+ last_name: {
3106+ required: true
3107+ },
3108+ phone: {
3109+ required: true
3110+ },
3111+ email: {
3112+ required: true,
3113+ email: true
3114+ },
3115+ address1: {
3116+ required: true
3117+ },
3118+ city: {
3119+ required: true
3120+ },
3121+ zip: {
3122+ required: true
3123+ },
3124+ password: {
3125+ required: true
3126+ },
3127+ password_two: {
3128+ required: true,
3129+ equalTo: '.password:first'
3130+ },
3131+ eighteen: {
3132+ required: true
3133+ },
3134+ citizen: {
3135+ required: true
3136+ }
3137+ },
3138+ messages: {
3139+ firstname: '""", str(T("Enter your firstname")), """',
3140+ email: {
3141+ required: '""", str(T("Please enter a valid email address")), """',
3142+ minlength: '""", str(T("Please enter a valid email address")), """'
3143+ },
3144+ password: {
3145+ required: '""", str(T("Provide a password")), """'
3146+ },
3147+ password_two: {
3148+ required: '""", str(T("Repeat your password")), """',
3149+ equalTo: '""", str(T("Enter the same password as above")), """'
3150+ },
3151+ eighteen: '""", str(T("If you are under the age 18, you may not register to apply to be a volunteer. Thank you for your interest in our volunteer opportunities.")), """',
3152+ citizen: '""", str(T("If you are not a US citizen, you may not register to apply to be a volunteer. Thank you for your interest in our volunteer opportunities.")), """',
3153+ },
3154+ errorPlacement: function(error, element) {
3155+ error.appendTo( element.parent().next() );
3156+ },
3157+ submitHandler: function(form) {
3158+ form.submit();
3159+ }
3160+});""" ))
3161+ response.s3.jquery_ready.append( script )
3162+
3163+ if session.s3.debug:
3164+ response.s3.scripts.append( "%s/jquery.validate.js" % s3_script_dir )
3165+ response.s3.scripts.append( "%s/jquery.pstrength.1.3.js" % s3_script_dir )
3166+ else:
3167+ response.s3.scripts.append( "%s/jquery.validate.min.js" % s3_script_dir )
3168+ response.s3.scripts.append( "%s/jquery.pstrength.1.3.min.js" % s3_script_dir )
3169+
3170+ response.s3.jquery_ready.append("$('.password:first').pstrength();\n")
3171+
3172+ # Add sidebar for login box & other volunteering opportunities
3173+ vol_sidebar()
3174+
3175+ response.s3.donate_cash_link = True
3176+
3177+ response.title = T("Register")
3178+ response.s3.has_required = True
3179+
3180+ return dict(form=form)
3181+
3182+# -----------------------------------------------------------------------------
3183+def register_validation(form):
3184+ """ Validate the custom fields in registration form """
3185+ # Terms of Service
3186+ if "eighteen" not in request.post_vars or request.post_vars.eighteen != "on":
3187+ form.errors.eighteen = T("If you are under the age 18, you may not register to apply to be a volunteer. Thank you for your interest in our volunteer opportunities.")
3188+ if "citizen" not in request.post_vars or request.post_vars.citizen != "on":
3189+ form.errors.citizen = T("If you are not a U.S. citizen, you may not register to apply to be a volunteer. Thank you for your interest in our volunteer opportunities.")
3190+ # Phone
3191+ if "phone" in request.post_vars and request.post_vars.phone:
3192+ regex = re.compile(single_phone_number_pattern)
3193+ if not regex.match(request.post_vars.phone):
3194+ form.errors.phone = T("Invalid phone number")
3195+ else:
3196+ form.errors.phone = T("Phone number is required")
3197+ # Address
3198+ if not request.post_vars.address1:
3199+ form.errors.address1 = T("Address is required")
3200+ if not request.post_vars.city:
3201+ form.errors.city = T("City is required")
3202+ if not request.post_vars.zip:
3203+ form.errors.zip = T("Zip is required")
3204+
3205+ return
3206+
3207+# -----------------------------------------------------------------------------
3208+def register_onaccept(form):
3209+ # Usual Registration Tasks
3210+ # (PR record, Authenticated Role, Contacts)
3211+ person = auth.s3_register(form)
3212+
3213+ # LA-specific
3214+ table = db.pr_person
3215+ query = (table.id == person)
3216+
3217+ db(query).update(volunteer=True,
3218+ middle_name=request.post_vars.middle_name)
3219+
3220+ pe = db(query).select(table.pe_id,
3221+ limitby=(0, 1)).first().pe_id
3222+
3223+ # Phone
3224+ table = db.pr_contact
3225+ phone = request.post_vars.phone
3226+ phone_type = request.post_vars.phone_type
3227+ # Don't auto-subscribe to SMS (priority 10)
3228+ table.insert(pe_id = pe,
3229+ contact_method = phone_type,
3230+ value = phone,
3231+ priority=10)
3232+
3233+ # Address
3234+ table = db.gis_location
3235+ address1 = request.post_vars.address1
3236+ address2 = request.post_vars.address2
3237+ if address2:
3238+ address = "%s\n%s" % (address1,
3239+ address2)
3240+ else:
3241+ address = address1
3242+ city = request.post_vars.city
3243+ location = request.post_vars.location_id
3244+ state = db(table.id == location).select(table.id,
3245+ table.name,
3246+ cache=gis.cache,
3247+ limitby=(0, 1)).first()
3248+ if state:
3249+ statename = state.name
3250+ else:
3251+ # Duff data - not USA
3252+ statename = "unknown"
3253+ zip = request.post_vars.zip
3254+ county = ""
3255+ if city:
3256+ query = (table.name == city) & \
3257+ (table.level == "L3") & \
3258+ (table.path.like("%%/%s/%%" % state))
3259+ _city = db(query).select(table.id,
3260+ table.parent,
3261+ limitby=(0, 1)).first()
3262+ if _city:
3263+ if _city.parent != state:
3264+ query = (table.id == _city.parent)
3265+ county = db(query).select(table.name,
3266+ limitby=(0, 1)).first().name
3267+ elif state:
3268+ _city = table.insert(name=city, level="L3", parent=state.id)
3269+ gis.update_location_tree(_city, state.id)
3270+ else:
3271+ usa = db(table.code == "US").select(table.id,
3272+ limitby=(0, 1)).first().id
3273+ _city = table.insert(name=city, level="L3", parent=usa)
3274+ gis.update_location_tree(_city, usa)
3275+ else:
3276+ _city = None
3277+ location = table.insert(addr_street=address, addr_postcode=zip, parent=_city)
3278+ gis.update_location_tree(location, _city)
3279+ s3mgr.load("pr_address")
3280+ table = db.pr_address
3281+ table.insert(pe_id = pe,
3282+ location_id = location,
3283+ address=address,
3284+ postcode=zip,
3285+ L3=city,
3286+ L2=county,
3287+ L1=statename,
3288+ L0="United States")
3289+
3290+# -----------------------------------------------------------------------------
3291+auth.settings.register_onvalidation = register_validation
3292+auth.settings.register_onaccept = register_onaccept
3293+auth.settings.register_next = URL(c="vol", f="skill", args=["create"])
3294+
3295+# Organisation widget for use in Registration Screen
3296+# NB User Profile is only editable by Admin - using User Management
3297+org_widget = IS_ONE_OF(db, "org_organisation.id",
3298+ organisation_represent,
3299+ orderby="org_organisation.name",
3300+ sort=True)
3301+if deployment_settings.get_auth_registration_organisation_mandatory():
3302+ _table_user.organisation_id.requires = org_widget
3303+else:
3304+ _table_user.organisation_id.requires = IS_NULL_OR(org_widget)
3305+
3306+# =============================================================================
3307+def profile():
3308+ """ Custom Profile Screen """
3309+
3310+ if not auth.is_logged_in():
3311+ redirect(URL(f="user", args="login"))
3312+ elif s3_has_role(ORG_VOL):
3313+ #return organisation_profile()
3314+ redirect(URL(f="organisation", args=[auth.user.organisation_id]))
3315+
3316+ # Only show admin-set fields if there is data
3317+ table = db.auth_user
3318+ if not auth.user.organisation_id:
3319+ # Not relevant to Volunteers
3320+ table.organisation_id.readable = False
3321+ if not auth.user.site_id:
3322+ # Only relevant to Field Staff
3323+ table.site_id.readable = False
3324+
3325+ # Validate the custom fields
3326+ auth.settings.profile_onvalidation = profile_validation
3327+
3328+ # Process the custom fields
3329+ auth.settings.profile_onaccept = profile_onaccept
3330+
3331+ auth.messages.profile_save_button = "Save"
3332+
3333+ request.args = ["profile"]
3334+ form = auth()
3335+
3336+ # Custom class for Submit Button
3337+ form[0][-1][1][0]["_class"] = "submit-button"
3338+
3339+ # Lookup the Person
3340+ id = auth.s3_logged_in_person()
3341+ table = db.pr_person
3342+ person = db(table.id == id).select(table.pe_id,
3343+ table.middle_name,
3344+ table.volunteer,
3345+ limitby=(0, 1)).first()
3346+
3347+ row = TR(TD(LABEL("%s:" % table.middle_name.label, _for="middle_name")),
3348+ TD(INPUT(_name="middle_name",
3349+ _id="middle_name",
3350+ _class="string",
3351+ _value=person.middle_name)))
3352+ form[0][0].append(row)
3353+
3354+ # Lookup the Contacts
3355+ table = db.pr_contact
3356+ query = (table.pe_id == person.pe_id) & \
3357+ (table.deleted == False)
3358+ contacts = db(query).select(table.contact_method,
3359+ table.value,
3360+ table.priority)
3361+ cell = ""
3362+ home = ""
3363+ work = ""
3364+ sms_enabled = False
3365+ email_enabled = True
3366+ for contact in contacts:
3367+ if contact.contact_method == "SMS":
3368+ cell = contact.value
3369+ if contact.priority == 10:
3370+ sms_enabled = False
3371+ else:
3372+ sms_enabled = True
3373+ elif contact.contact_method == "EMAIL":
3374+ if contact.priority == 10:
3375+ email_enabled = False
3376+ else:
3377+ email_enabled = True
3378+ elif contact.contact_method == "HOME_PHONE":
3379+ home = contact.value
3380+ elif contact.contact_method == "WORK_PHONE":
3381+ work = contact.value
3382+
3383+ # Cell phone
3384+ if form.errors.mobile:
3385+ cell_error = DIV(form.errors.mobile,
3386+ _id="mobile__error",
3387+ _class="error",
3388+ _style="display: block;")
3389+ else:
3390+ cell_error = ""
3391+ row = TR(TD(LABEL("%s:" % msg.CONTACT_OPTS["SMS"], _for="mobile")),
3392+ TD(INPUT(_name="mobile",
3393+ _id="mobile",
3394+ _class="string",
3395+ _value=cell),
3396+ cell_error))
3397+ form[0][2].append(row)
3398+ # @ToDo: JS validation
3399+
3400+ if person.volunteer:
3401+ # Home phone
3402+ if form.errors.home_phone:
3403+ home_phone_error = DIV(form.errors.home_phone,
3404+ _id="home_phone__error",
3405+ _class="error",
3406+ _style="display: block;")
3407+ else:
3408+ home_phone_error = ""
3409+ row = TR(TD(LABEL("%s:" % msg.CONTACT_OPTS["HOME_PHONE"], _for="home_phone")),
3410+ TD(INPUT(_name="home_phone",
3411+ _id="home_phone",
3412+ _class="string",
3413+ _value=home),
3414+ home_phone_error))
3415+ form[0][2].append(row)
3416+ # @ToDo: JS validation
3417+
3418+ # Work phone
3419+ if form.errors.work_phone:
3420+ work_phone_error = DIV(form.errors.work_phone,
3421+ _id="work_phone__error",
3422+ _class="error",
3423+ _style="display: block;")
3424+ else:
3425+ work_phone_error = ""
3426+ row = TR(TD(LABEL("%s:" % msg.CONTACT_OPTS["WORK_PHONE"], _for="work_phone")),
3427+ TD(INPUT(_name="work_phone",
3428+ _id="work_phone",
3429+ _class="string",
3430+ _value=work),
3431+ work_phone_error))
3432+ form[0][2].append(row)
3433+ # @ToDo: JS validation
3434+
3435+ # Lookup the Address
3436+ table = db.pr_address
3437+ query = (table.pe_id == person.pe_id) & \
3438+ (table.deleted == False)
3439+ address = db(query).select(table.location_id,
3440+ limitby=(0, 1)).first()
3441+ address1 = ""
3442+ address2 = ""
3443+ city = ""
3444+ state = ""
3445+ zip = ""
3446+ if address:
3447+ table = db.gis_location
3448+ query = (table.id == address.location_id)
3449+ location = db(query).select(table.id,
3450+ table.path,
3451+ table.parent,
3452+ table.addr_street,
3453+ table.addr_postcode,
3454+ limitby=(0, 1)).first()
3455+ if location:
3456+ try:
3457+ address1, address2 = location.addr_street.split("\n", 1)
3458+ except ValueError:
3459+ address1 = location.addr_street
3460+ zip = location.addr_postcode
3461+ results = gis.get_parent_per_level(None, location.id, location)
3462+ try:
3463+ city = results["L3"].name
3464+ #except KeyError, AttributeError:
3465+ except:
3466+ pass
3467+ try:
3468+ state = results["L1"].name
3469+ #except KeyError, AttributeError:
3470+ except:
3471+ pass
3472+
3473+ if form.errors.address1:
3474+ address1_error = DIV(form.errors.address1,
3475+ _id="address1__error",
3476+ _class="error",
3477+ _style="display: block;")
3478+ else:
3479+ address1_error = ""
3480+ row = TR(TD(LABEL("%s: " % T("Address 1"),
3481+ SPAN("*", _class="req"),
3482+ _for="Address 1")),
3483+ TD(INPUT(_name="address1",
3484+ _id="address1",
3485+ _class="string",
3486+ _value=address1),
3487+ address1_error))
3488+ form[0][2].append(row)
3489+ row = TR(TD(LABEL("%s:" % T("Address 2"), _for="Address 2")),
3490+ TD(INPUT(_name="address2",
3491+ _id="address2",
3492+ _class="string",
3493+ _value=address2)))
3494+ form[0][2].append(row)
3495+ row = TR(TD(LABEL("%s: " % T("City"),
3496+ SPAN("*", _class="req"),
3497+ _for="city")),
3498+ TD(INPUT(_name="city",
3499+ _id="city",
3500+ _class="string",
3501+ _value=city)))
3502+ form[0][2].append(row)
3503+ states = S3LocationDropdownWidget(level="L1",
3504+ default=state or "California",
3505+ empty=False)
3506+ widget = states(db.pr_address.location_id, None)
3507+ row = TR(TD(LABEL("%s:" % T("State"),
3508+ SPAN("*", _class="req"),
3509+ _for="location_id")),
3510+ TD(widget))
3511+ form[0][2].append(row)
3512+ if form.errors.zip:
3513+ zip_error = DIV(form.errors.zip,
3514+ _id="zip__error",
3515+ _class="error",
3516+ _style="display: block;")
3517+ else:
3518+ zip_error = ""
3519+ row = TR(TD(LABEL("%s:" % T("Zip"),
3520+ SPAN("*", _class="req"),
3521+ _for="zip")),
3522+ TD(INPUT(_name="zip",
3523+ _id="zip",
3524+ _class="string",
3525+ _value=zip),
3526+ zip_error))
3527+ form[0][2].append(row)
3528+
3529+ # Notifications
3530+ div = DIV(TR(TD(LABEL("%s:" % T("Notifications"))),
3531+ TD()
3532+ ),
3533+ TR(TD(T("Receive Notifications via Email?")),
3534+ TD(INPUT(_type="checkbox",
3535+ _value="on",
3536+ value="on" if email_enabled else "",
3537+ _name="sub_email",
3538+ _id="sub_email",
3539+ _alt=T("Receive Notifications via Email?"),
3540+ _class="boolean"))
3541+ ),
3542+ TR(TD(T("Receive Notifications via SMS?")),
3543+ TD(INPUT(_type="checkbox",
3544+ _value="on",
3545+ value="on" if sms_enabled else "",
3546+ _name="sub_sms",
3547+ _id="sub_sms",
3548+ _alt=T("Receive Notifications via SMS?"),
3549+ _class="boolean")),
3550+ TD(DIV(_class="tooltip",
3551+ _title="%s|%s" % (T("Receive Notifications via SMS"),
3552+ T("Note that this will may incur costs from your carrier."))))
3553+ )
3554+ )
3555+ form[0][-2].append(div)
3556+
3557+ # Affiliated Orgs
3558+ s3mgr.load("vol_organisation")
3559+ table = db.vol_organisation
3560+ orgs = db(table.pe_id == person.pe_id).select(table.organisations_id,
3561+ limitby=(0, 1)).first()
3562+ if orgs:
3563+ orgs = orgs.organisations_id
3564+ table = db.org_organisation
3565+ if orgs:
3566+ _orgs = db(table.id.belongs(orgs)).select(table.name)
3567+ else:
3568+ _orgs = []
3569+ orgs = []
3570+ for org in _orgs:
3571+ orgs.append(org.name)
3572+ else:
3573+ orgs = []
3574+
3575+ div = DIV(TR(TD(),
3576+ TD(B(T("Affiliation with other volunteer organizations")))),
3577+ TR(TD(T("Are you affiliated with other volunteer organizations?"),
3578+ " (", DIV(T("Check all that apply"),
3579+ _style="display: inline;",
3580+ _class="red"),
3581+ ")",
3582+ _colspan=2)),
3583+ TR(TD("American Red Cross"),
3584+ TD(INPUT(_type="checkbox",
3585+ _value="on",
3586+ value="on" if "American Red Cross of Greater Los Angeles" in orgs else "",
3587+ _name="arc",
3588+ _id="arc",
3589+ _alt=T("American Red Cross"),
3590+ _class="boolean"))),
3591+ TR(TD("CERT"),
3592+ TD(INPUT(_type="checkbox",
3593+ _value="on",
3594+ value="on" if "CERT Los Angeles" in orgs else "",
3595+ _name="cert",
3596+ _id="cert",
3597+ _alt=T("CERT"),
3598+ _class="boolean"))),
3599+ TR(TD("Disaster Healthcare Volunteers"),
3600+ TD(INPUT(_type="checkbox",
3601+ _value="on",
3602+ value="on" if "Disaster Healthcare Volunteers" in orgs else "",
3603+ _name="dhv",
3604+ _id="dhv",
3605+ _alt=T("Disaster Healthcare Volunteers"),
3606+ _class="boolean"))),
3607+ TR(TD("LA Works"),
3608+ TD(INPUT(_type="checkbox",
3609+ _value="on",
3610+ value="on" if "LA Works" in orgs else "",
3611+ _name="laworks",
3612+ _id="laworks",
3613+ _alt=T("LA Works"),
3614+ _class="boolean"))),
3615+ TR(TD("Volunteer Center of Los Angeles"),
3616+ TD(INPUT(_type="checkbox",
3617+ _value="on",
3618+ value="on" if "Volunteer Center of Los Angeles" in orgs else "",
3619+ _name="vcla",
3620+ _id="vcla",
3621+ _alt=T("Volunteer Center of Los Angeles"),
3622+ _class="boolean"))),
3623+ )
3624+ form[0][-2].append(div)
3625+
3626+ # Skills
3627+ # - separate screen
3628+
3629+ response.title = T("Profile")
3630+ response.s3.has_required = True
3631+
3632+ return dict(form=form)
3633+
3634+# -----------------------------------------------------------------------------
3635+def profile_validation(form):
3636+ """ Validate the custom fields in profile form """
3637+
3638+ if s3_has_role(STAFF):
3639+ volunteer = False
3640+ else:
3641+ volunteer = True
3642+
3643+ # Mobile Phone
3644+ if "mobile" in request.post_vars and request.post_vars.mobile:
3645+ regex = re.compile(single_phone_number_pattern)
3646+ if not regex.match(request.post_vars.mobile):
3647+ form.errors.mobile = T("Invalid phone number")
3648+ elif "sub_sms" in request.post_vars and request.post_vars.sub_sms == "on":
3649+ form.errors.mobile = T("Cell phone number is required for SMS notifications")
3650+ # Home Phone
3651+ if "home_phone" in request.post_vars and request.post_vars.home_phone:
3652+ regex = re.compile(single_phone_number_pattern)
3653+ if not regex.match(request.post_vars.home_phone):
3654+ form.errors.home_phone = T("Invalid phone number")
3655+ # Work Phone
3656+ if "work_phone" in request.post_vars and request.post_vars.work_phone:
3657+ regex = re.compile(single_phone_number_pattern)
3658+ if not regex.match(request.post_vars.work_phone):
3659+ form.errors.work_phone = T("Invalid phone number")
3660+ # Address
3661+ if volunteer and not request.post_vars.address1:
3662+ form.errors.address1 = T("Address is required")
3663+ if volunteer and not request.post_vars.zip:
3664+ form.errors.zip = T("Zip is required")
3665+
3666+ return
3667+
3668+# -----------------------------------------------------------------------------
3669+def profile_onaccept(form):
3670+ """ Process the custom fields """
3671+
3672+ if s3_has_role(STAFF):
3673+ volunteer = False
3674+ else:
3675+ volunteer = True
3676+
3677+ table = db.pr_person
3678+ query = (table.id == auth.s3_logged_in_person())
3679+ db(query).update(middle_name=request.post_vars.middle_name)
3680+ pe = db(query).select(table.pe_id,
3681+ limitby=(0, 1)).first().pe_id
3682+
3683+ if volunteer:
3684+ # Contacts / Notifications
3685+ if "sub_email" in request.post_vars and \
3686+ request.post_vars.sub_email == "on":
3687+ email_enabled = True
3688+ else:
3689+ email_enabled = False
3690+ if "sub_sms" in request.post_vars and \
3691+ request.post_vars.sub_sms == "on":
3692+ sms_enabled = True
3693+ else:
3694+ sms_enabled = False
3695+ home_phone = request.post_vars.home_phone
3696+ work_phone = request.post_vars.work_phone
3697+ email = request.post_vars.email
3698+ cell = request.post_vars.mobile
3699+ table = db.pr_contact
3700+ if email:
3701+ utable = db.auth_user
3702+ query = (utable.id == auth.user.id)
3703+ _email = db(query).select(utable.email,
3704+ limitby=(0, 1)).first().email
3705+ if not _email == email:
3706+ # Email has changed
3707+ # Check that the new email doesn't clash
3708+ query2 = (utable.email == email)
3709+ test = db(query2).select(utable.id,
3710+ limitby=(0, 1)).first()
3711+ if not test:
3712+ # update auth_user
3713+ db(query).update(email=email)
3714+ # update pr_contact
3715+ query = (table.value == _email)
3716+ if volunteer:
3717+ priority = 1 if email_enabled else 10
3718+ db(query).update(value=email, priority=priority)
3719+ else:
3720+ db(query).update(value=email)
3721+ else:
3722+ # Existing .requires validation prevents the user from reusing
3723+ # an existing email
3724+ pass
3725+ elif volunteer:
3726+ # Just update the notifications
3727+ query = (table.value == email)
3728+ priority = 1 if email_enabled else 10
3729+ db(query).update(priority=priority)
3730+
3731+ _cell = ""
3732+ _home_phone = ""
3733+ _work_phone = ""
3734+ query = (table.pe_id == pe) & \
3735+ (table.deleted == False)
3736+ contacts = db(query).select(table.contact_method,
3737+ table.value)
3738+ for contact in contacts:
3739+ if contact.contact_method == "SMS":
3740+ _cell = contact.value
3741+ elif contact.contact_method == "HOME_PHONE":
3742+ _home_phone = contact.value
3743+ elif contact.contact_method == "WORK_PHONE":
3744+ _work_phone = contact.value
3745+ table = db.pr_contact
3746+ base_query = (table.pe_id == pe)
3747+ if volunteer:
3748+ priority = 1 if sms_enabled else 10
3749+ if _cell:
3750+ # Update existing record
3751+ query = base_query & (table.value == _cell)
3752+ db(query).update(value=cell, priority=priority)
3753+ elif cell:
3754+ # Insert new record
3755+ table.insert(pe_id=pe, contact_method="SMS", value=cell,
3756+ priority=priority)
3757+ if _home_phone:
3758+ # Update existing record
3759+ query = base_query & (table.value == _home_phone)
3760+ db(query).update(value=home_phone)
3761+ elif home_phone:
3762+ # Insert new record
3763+ table.insert(pe_id=pe, contact_method="HOME_PHONE",
3764+ value=home_phone)
3765+
3766+ if _work_phone:
3767+ # Update existing record
3768+ query = base_query & (table.value == _work_phone)
3769+ db(query).update(value=work_phone)
3770+ elif work_phone:
3771+ # Insert new record
3772+ table.insert(pe_id=pe, contact_method="WORK_PHONE",
3773+ value=work_phone)
3774+
3775+ # Address
3776+ address1 = request.post_vars.address1
3777+ address2 = request.post_vars.address2
3778+ if address2:
3779+ address = "%s\n%s" % (address1, address2)
3780+ else:
3781+ address = address1
3782+ zip = request.post_vars.zip
3783+ city = request.post_vars.city
3784+ state = request.post_vars.location_id
3785+ table = db.gis_location
3786+ statename = db(table.id == state).select(table.name,
3787+ cache=gis.cache,
3788+ limitby=(0, 1)).first().name
3789+ county = ""
3790+ if city:
3791+ query = (table.name == city) & \
3792+ (table.level == "L3") & \
3793+ (table.path.like("%%/%s/%%" % state))
3794+ _city = db(query).select(table.id,
3795+ table.parent,
3796+ limitby=(0, 1)).first()
3797+ if _city:
3798+ if _city.parent != state:
3799+ query = (table.id == _city.parent)
3800+ county = db(query).select(table.name,
3801+ limitby=(0, 1)).first().name
3802+ else:
3803+ _city = table.insert(name=city, level="L3", parent=state)
3804+ gis.update_location_tree(_city, state)
3805+ else:
3806+ _city = None
3807+ table = db.pr_address
3808+ query = (table.pe_id == pe) & \
3809+ (table.deleted == False)
3810+ test = db(query).select(table.location_id,
3811+ limitby=(0, 1)).first()
3812+ if test:
3813+ db(query).update(address=address,
3814+ postcode=zip,
3815+ L3=city,
3816+ L2=county,
3817+ L1=statename,
3818+ L0="United States")
3819+ table = db.gis_location
3820+ location = test.location_id
3821+ query = (table.id == location)
3822+ db(query).update(addr_street=address,
3823+ addr_postcode=zip,
3824+ parent=_city)
3825+ gis.update_location_tree(location, _city)
3826+
3827+ # Affiliated Organisations
3828+ # Also in models/vol.py
3829+ vol_orgs = ["American Red Cross of Greater Los Angeles",
3830+ "CERT Los Angeles",
3831+ "Disaster Healthcare Volunteers",
3832+ "LA Works",
3833+ "Volunteer Center of Los Angeles"]
3834+ table = db.org_organisation
3835+ query = (table.name.belongs(vol_orgs))
3836+ orgs = db(query).select(table.id,
3837+ table.name,
3838+ cache=(cache.ram, 60))
3839+ for org in orgs:
3840+ if org.name == "American Red Cross of Greater Los Angeles":
3841+ ARC = org.id
3842+ elif org.name == "CERT Los Angeles":
3843+ CERT = org.id
3844+ elif org.name == "Disaster Healthcare Volunteers":
3845+ DHV = org.id
3846+ elif org.name == "LA Works":
3847+ LAW = org.id
3848+ elif org.name == "Volunteer Center of Los Angeles":
3849+ VCLA = org.id
3850+ orgs = []
3851+ if "arc" in request.post_vars and request.post_vars.arc == "on":
3852+ orgs.append(ARC)
3853+ if "cert" in request.post_vars and request.post_vars.cert == "on":
3854+ orgs.append(CERT)
3855+ if "dhv" in request.post_vars and request.post_vars.dhv == "on":
3856+ orgs.append(DHV)
3857+ if "laworks" in request.post_vars and \
3858+ request.post_vars.laworks == "on":
3859+ orgs.append(LAW)
3860+ if "vcla" in request.post_vars and request.post_vars.vcla == "on":
3861+ orgs.append(VCLA)
3862+ s3mgr.load("vol_organisation")
3863+ table = db.vol_organisation
3864+ query = (table.pe_id == pe)
3865+ test = db(query).select(table.id,
3866+ limitby=(0, 1)).first()
3867+ if test:
3868+ db(query).update(organisations_id = orgs)
3869+ else:
3870+ table.insert(pe_id = pe,
3871+ organisations_id = orgs,
3872+ owned_by_user = auth.user.id)
3873+
3874+ else:
3875+ # Not a volunteer: EOC staff
3876+ if _cell:
3877+ # Update existing record
3878+ query = base_query & (table.value == _cell)
3879+ db(query).update(value=cell)
3880+ elif cell:
3881+ # Insert new record
3882+ table.insert(pe_id=pe, contact_method="SMS", value=cell)
3883+
3884+# -----------------------------------------------------------------------------
3885+# Skills
3886+# -----------------------------------------------------------------------------
3887+def skill():
3888+ """
3889+ Collect a Volunteer's Skills
3890+ """
3891+
3892+ tablename = "vol_skill"
3893+ s3mgr.load(tablename)
3894+ table = db[tablename]
3895+
3896+ person = auth.s3_logged_in_person()
3897+ table.person_id.default = person
3898+ table.person_id.writable = table.person_id.readable = False
3899+
3900+ s3mgr.configure(tablename,
3901+ # Workflow: Prompt for creating Emergency Contacts after adding Skills
3902+ create_next = URL(f="contact", args=["my"]),
3903+ update_next = URL(f="skill", args=["my"]),
3904+ deletable = False,
3905+ listadd = False)
3906+
3907+ # Parse the request
3908+ if "my" in request.args:
3909+ query = (table.person_id == person)
3910+ record = db(query).select(table.id,
3911+ limitby=(0, 1),
3912+ cache = gis.cache).first()
3913+ if record:
3914+ r = s3mgr.parse_request(args=[str(record.id)])
3915+ else:
3916+ r = s3mgr.parse_request(args=["create"])
3917+ else:
3918+ r = s3mgr.parse_request()
3919+
3920+ # Execute the request
3921+ output = r()
3922+
3923+ response.title = T("My Skills")
3924+ return output
3925+
3926+# -----------------------------------------------------------------------------
3927+# Contacts
3928+# -----------------------------------------------------------------------------
3929+def contact():
3930+ """
3931+ Emergency Contacts for the volunteer
3932+ """
3933+
3934+ tablename = "vol_contact"
3935+ s3mgr.load(tablename)
3936+ table = db[tablename]
3937+
3938+ person = auth.s3_logged_in_person()
3939+ table.person_id.default = person
3940+ table.person_id.writable = table.person_id.readable = False
3941+
3942+ s3mgr.configure(tablename,
3943+ create_next = URL(f="req_skill"),
3944+ update_next = URL(f="contact", args=["my"]),
3945+ deletable = False,
3946+ listadd = False)
3947+
3948+ # Parse the request
3949+ if "my" in request.args:
3950+ query = (table.person_id == person)
3951+ record = db(query).select(table.id,
3952+ limitby=(0, 1),
3953+ cache = gis.cache).first()
3954+ if record:
3955+ r = s3mgr.parse_request(args=[str(record.id)])
3956+ else:
3957+ r = s3mgr.parse_request(args=["create"])
3958+ else:
3959+ r = s3mgr.parse_request()
3960+
3961+ # Execute the request
3962+ output = r()
3963+
3964+ response.title = T("Emergency Contact")
3965+ return output
3966+
3967+# -----------------------------------------------------------------------------
3968+# Assignments
3969+# - My Assignments menu
3970+# -----------------------------------------------------------------------------
3971+def assignment():
3972+
3973+ """ Allow a Volunteer/Org to View the details of their own assignments """
3974+
3975+ tablename = "vol_assignment"
3976+ s3mgr.load(tablename)
3977+ table = db[tablename]
3978+
3979+ if auth.user.organisation_id:
3980+ response.s3.filter = (table.organisation_id == auth.user.organisation_id)
3981+ # Control List View
3982+ table.person_id.readable = False
3983+ table.number.readable = True
3984+ table.number.label = T("Number of Volunteers Committed")
3985+ else:
3986+ my_person_id = s3_logged_in_person()
3987+ # Control List View
3988+ table.person_id.readable = False
3989+
3990+ if not my_person_id:
3991+ session.error = T("No person record found for current user.")
3992+ redirect(URL(f="index"))
3993+
3994+ response.s3.filter = (table.person_id == my_person_id)
3995+
3996+ # Filter for current/past assignments
3997+ sel = ((table.checkout == None) |
3998+ (table.checkout >= request.utcnow) |
3999+ (table.task_evaluation == None))
4000+ s3.crud_strings[tablename].update(
4001+ subtitle_list = T("Current Assignments"))
4002+ if "show" in request.get_vars:
4003+ show = request.get_vars["show"]
4004+ if show == "past":
4005+ sel = ((table.checkout < request.utcnow) &
4006+ (table.task_evaluation != None))
4007+ s3.crud_strings[tablename].update(
4008+ subtitle_list = T("Past Assignments"),
4009+ msg_no_match = T("No past assigments"))
4010+ response.s3.filter &= sel
4011+
4012+ # Post-process
4013+ def postp(r, output):
4014+ if r.interactive:
4015+ response.s3.actions = [dict(
4016+ url = URL(c = "vol",
4017+ f = "req",
4018+ args = ["assignment", "[id]"]),
4019+ _class = "action-btn",
4020+ label = str(T("Details"))
4021+ )]
4022+ if "show" in request.get_vars and request.get_vars["show"] == "past":
4023+ response.s3.actions += [dict(
4024+ url = URL(c=request.controller,
4025+ f="req",
4026+ args=["assignment",
4027+ "[id]",
4028+ "print"]),
4029+ _class = "action-btn",
4030+ label = str(T("Certificate"))
4031+ )]
4032+
4033+ return output
4034+ response.s3.postp = postp
4035+
4036+ output = s3_rest_controller("vol", "assignment")
4037+ return output
4038+
4039+# -----------------------------------------------------------------------------
4040+# Requests
4041+# -----------------------------------------------------------------------------
4042+def vol_req_rheader(r):
4043+ """
4044+ Resource Header for Requests
4045+ - Volunteer view
4046+ - inc Volunteer Roster
4047+ """
4048+
4049+ if r.representation == "html":
4050+ if r.name == "req":
4051+ record = r.record
4052+ if record:
4053+ if s3_has_role(STAFF):
4054+ # Show Tabs on Roster
4055+ tabs = [(T("Details"), None)]
4056+ if record.type == 3 and deployment_settings.has_module("hrm"):
4057+ tabs.append((T("Requested Volunteers"), "req_skill"))
4058+ if deployment_settings.has_module("doc"):
4059+ tabs.append((T("Documents"), "document"))
4060+ #if deployment_settings.get_req_use_commit():
4061+ # tabs.append((T("Commitments"), "commit"))
4062+ tabs.append((T("Roster"), "assignment"))
4063+ try:
4064+ # Remove the default_type from URLs as set within respective controller
4065+ r.vars.pop("default_type")
4066+ except KeyError:
4067+ pass
4068+ rheader_tabs = s3_rheader_tabs(r, tabs)
4069+ elif not r.component:
4070+ # Volunteers should only ever access Requests via the Components
4071+ return
4072+
4073+ table = db.req_req_skill
4074+ query = (table.req_id == record.id)
4075+ srecord = db(query).select(table.skill_id,
4076+ table.quantity,
4077+ table.quantity_commit,
4078+ limitby=(0, 1)).first()
4079+ if srecord:
4080+ skills = response.s3.hrm_multi_skill_represent(srecord.skill_id)
4081+ requested = srecord.quantity
4082+ required = srecord.quantity - srecord.quantity_commit
4083+ else:
4084+ skills = requested = required = ""
4085+
4086+ address = shn_site_represent(record.site_id, address=True)
4087+ #table = db.org_office
4088+ #query = (table.id == record.site_id)
4089+ #srecord = db(query).select(table.name,
4090+ # table.address,
4091+ # table.postcode,
4092+ # table.L3,
4093+ # table.L1,
4094+ # limitby=(0, 1)).first()
4095+ #if srecord:
4096+ # location = T("%(site)s in %(location)s") % dict(site = srecord.name,
4097+ # location = record.location)
4098+ # ltable = db.gis_location
4099+ # query = (ltable.name == srecord.L1)
4100+ # state = db(query).select(ltable.code,
4101+ # limitby=(0, 1),
4102+ # cache=gis.cache).first()
4103+ # if state:
4104+ # statecode = state.code
4105+ # else:
4106+ # # Not valid US data (e.g. prepopulate)
4107+ # statecode = None
4108+ # address = "%s, %s %s %s" % (srecord.address,
4109+ # srecord.L3,
4110+ # statecode,
4111+ # srecord.postcode)
4112+ #else:
4113+ # location = address = ""
4114+
4115+ table = db.hrm_human_resource
4116+ query = (table.id == record.request_for_id)
4117+ person = db(query).select(table.person_id,
4118+ limitby=(0, 1)).first()
4119+ phone = email = report_to = ""
4120+ if person:
4121+ report_to = person_represent(person.person_id)
4122+
4123+ # @ToDo: Investigate if these db calls can be done together with a join?
4124+ table = db.pr_person
4125+ query = (table.id == person.person_id)
4126+ pe = db(query).select(table.pe_id,
4127+ limitby=(0, 1)).first()
4128+ if pe:
4129+ table = db.pr_contact
4130+ query = (table.pe_id == pe.pe_id) & \
4131+ (table.contact_method.belongs(("SMS", "EMAIL")))
4132+ contacts = db(query).select(table.contact_method,
4133+ table.value)
4134+ for contact in contacts:
4135+ if contact.contact_method == "SMS":
4136+ phone = contact.value
4137+ elif contact.contact_method == "EMAIL":
4138+ email = contact.value
4139+
4140+ if s3_has_role(STAFF):
4141+ subtitle = T("Request Details")
4142+ buttons = DIV(
4143+ BUTTON(T("PRINT ROSTER"),
4144+ _class="wide-grey-button",
4145+ _onClick="javascript: window.location='%s'" % \
4146+ URL(c=request.controller,
4147+ f="req",
4148+ args=[record.id,
4149+ "assignment",
4150+ "print"])),
4151+ BUTTON(T("CANCEL REQUEST"),
4152+ _class="wide-grey-button",
4153+ _onClick="javascript: window.location='%s'" % \
4154+ URL(c=request.controller,
4155+ f="req",
4156+ args=[record.id,
4157+ "cancel"]))
4158+ )
4159+ textbox = ""
4160+ else:
4161+ subtitle = T("Volunteer Assignment Details")
4162+ buttons = ""
4163+ if r.component.name == "application":
4164+ textbox = P(T("When you volunteer with Give2LA, you are an extension of the good will of the City of Los Angeles. For many survivors, volunteers are their first point of contact. Your respect, courtesy and sensitivity during these difficult times are vital. In addition, following directions, procedures, and processes given to you by your supervisor are essential; failure to do so may exclude you from future volunteer assignments."))
4165+ else:
4166+ give2la_forms = A( T("Give2LA Registration Forms"),
4167+ _href = URL(c="static",
4168+ f="Volunteer_Ethics_and_Conduct.pdf")
4169+ )
4170+ textbox = DIV(P(T("Thank you for Volunteering. Please print out your Volunteer Assignment Form as it contains important information about who, when, and where to report to for your Volunteer Assignment.")),
4171+ P(XML(T("As a volunteer working to assist disaster survivors and in disaster recovery efforts, you will be required to fill out and sign the %(give2la_forms)s") %
4172+ dict(give2la_forms = give2la_forms) ),
4173+ "."),
4174+ P(T("Please let us know in advance if you cannot report for your Volunteer Assignment by calling the Point of Contact listed above.")),
4175+ )
4176+
4177+ if record.date_required:
4178+ time_req = s3_time_represent(record.date_required, utc=True)
4179+ else:
4180+ time_req = ""
4181+ if record.date_required_until:
4182+ time_until = s3_time_represent(record.date_required_until, utc=True)
4183+ else:
4184+ time_until = ""
4185+
4186+ class DL(DIV):
4187+ tag = 'dl'
4188+
4189+ class DT(DIV):
4190+ tag = 'dt'
4191+
4192+ class DD(DIV):
4193+ tag = 'dd'
4194+
4195+ rheader = DIV(
4196+ textbox,
4197+ H2(subtitle, _class="paper-strip"),
4198+ buttons,
4199+ DIV(
4200+ TABLE(
4201+ TR(
4202+ TH(T("Request Number")),
4203+ TH(T("Task")),
4204+ TH(T("Date+Time")),
4205+ TH(T("Required Skills")),
4206+ TH(T("Request Priority")),
4207+ TH(T("Location")),
4208+ TH(T("Number of Volunteers"),BR(),DIV("(", T("Still Required"), "/", T("Total Requested"), ")")), # (assigned/required)
4209+ _class="odd"
4210+ ),
4211+ TR(
4212+ TD(record.request_number),
4213+ TD(record.purpose),
4214+ TD(B(s3_date_represent_utc(record.date_required)),
4215+ BR(),
4216+ "%s - %s" % (time_req,
4217+ time_until),
4218+ ),
4219+ TD(skills),
4220+ TD(response.s3.req_priority_represent(record.priority) ),
4221+ TD(record.location or ""),
4222+ TD(B(required),
4223+ " / %s" % requested,
4224+ _class="red"),
4225+ _class="even"
4226+ ),
4227+ _class="datatable display"
4228+ ),
4229+ _id="table-container"
4230+ ),
4231+ DL(
4232+ DT(T("Point of Contact")),
4233+ DD(report_to,
4234+ BR(),
4235+ "%s: %s" % (T("Phone"),
4236+ phone),
4237+ BR(),
4238+ "%s: %s" % (T("Email"),
4239+ email),
4240+ ),
4241+ DT(T("Volunteering Location")),
4242+ DD(#location,
4243+ #BR(),
4244+ address),
4245+ DT(T("Comments")),
4246+ DD(record.comments),
4247+ _class="assignment-details"
4248+ ),
4249+ )
4250+ if r.component and r.component.name == "assignment":
4251+ if s3_has_role(STAFF):
4252+ # Volunteer Roster
4253+ response.s3.rfooter = DIV(
4254+ BUTTON(T("CHECK-IN ALL"),
4255+ _class="wide-grey-button",
4256+ _onClick="javascript: window.location='%s'" % \
4257+ URL(c=request.controller,
4258+ f="req",
4259+ args=[record.id,
4260+ "assignment",
4261+ "checkin"])),
4262+ BUTTON(T("CHECK-OUT ALL"),
4263+ _class="wide-grey-button",
4264+ _onClick="javascript: window.location='%s'" % \
4265+ URL(c=request.controller,
4266+ f="req",
4267+ args=[record.id,
4268+ "assignment",
4269+ "checkout"]))
4270+ )
4271+ else:
4272+ # Volunteer Assignment
4273+ assignment = r.component_id
4274+ table = db.vol_assignment
4275+ query = table.id == r.component_id
4276+ record = db(query).select(limitby=(0, 1)).first()
4277+ if not record:
4278+ # Probably a Volunteer trying to use a URL for STAFF
4279+ session.error = auth.permission.INSUFFICIENT_PRIVILEGES
4280+ redirect(URL(c="vol", f="req_skill"))
4281+ req_id = record.req_id
4282+ if record.checkout != None and record.task_evaluation != None:
4283+ printBtn = T("PRINT CERTIFICATE")
4284+ else:
4285+ printBtn = T("PRINT VOLUNTEER ASSIGNMENT FORM")
4286+ rheader.append(
4287+ DIV(
4288+ BUTTON(printBtn,
4289+ _class="big-red-arrow",
4290+ _onClick="javascript: window.location='%s'" % \
4291+ URL(c=request.controller,
4292+ f="req",
4293+ args=["assignment",
4294+ assignment,
4295+ "print"])),
4296+ # Also in controllers/don.py - DRY
4297+ A(IMG(_src="/%s/static/img/get_adobe_reader.png" % request.application,
4298+ _title="%s - %s" % (T("Get Adobe Reader"),
4299+ T("This link will open a new browser window.")),
4300+ _alt=T("Get Adobe Reader"),
4301+ _width=158,
4302+ _height=39,
4303+ _style="float:right;"),
4304+ _href="http://www.adobe.com/products/acrobat/readstep2.html",
4305+ _target="_blank"),
4306+ BR(),
4307+ BUTTON(T("CANCEL ASSIGNMENT"),
4308+ _class="wide-grey-button",
4309+ _onClick="javascript: window.location='%s'" % \
4310+ URL(c=request.controller,
4311+ f="req",
4312+ args=[record.id,
4313+ "assignment",
4314+ assignment,
4315+ "delete"]))
4316+ )
4317+ )
4318+ if s3_has_role(STAFF):
4319+ rheader.append(rheader_tabs)
4320+
4321+ return rheader
4322+ return None
4323+
4324+# -----------------------------------------------------------------------------
4325+def req():
4326+ """ REST Controller """
4327+
4328+ s3mgr.load("req_req")
4329+ default_type = 3
4330+ request.vars["default_type"] = 3
4331+
4332+ if "skill_id" in request.get_vars:
4333+ # Look up the Request from the Skill component
4334+ # (since that is what is available to us within the dataTables row)
4335+ table = db.req_req_skill
4336+ query = (table.id == request.vars.skill_id)
4337+ req = db(query).select(table.req_id,
4338+ limitby=(0, 1)).first()
4339+ if not req:
4340+ session.error = T("Invalid Request!")
4341+ redirect(URL(f="req_skill", args=[], vars={}))
4342+ # @ToDo: A nicer way to do this (without a 2nd request)
4343+ # simply amending request.args doesn't work
4344+ # Need to rewrite to not use prep/postp but rather process r() directly
4345+ redirect(URL(args = [req.req_id, "application", "create"],
4346+ vars={}))
4347+
4348+ if "document" in request.args:
4349+ s3mgr.load("doc_document")
4350+
4351+ req_table = db.req_req
4352+ s3mgr.configure(req_table,
4353+ deletable=False)
4354+
4355+ type_field = req_table.type
4356+ type_field.default = int(default_type)
4357+ type_field.writable = False
4358+
4359+ if s3_has_role(STAFF):
4360+ if "assignment" in request.args or \
4361+ "checkin" in request.args or \
4362+ "checkout" in request.args:
4363+ s3mgr.load("vol_assignment")
4364+
4365+ elif s3_has_role(ORG_VOL):
4366+ s3mgr.load("vol_application")
4367+ # Volunteer Orgs can see Public Requests & those published for them
4368+ org = auth.user.organisation_id
4369+ response.s3.filter = (req_table.public == True) | \
4370+ (req_table.organisations_id.belongs((org,)))
4371+ s3mgr.configure(req_table,
4372+ editable=False), # We need the Controller/Table ACL to be editable to be able to add components
4373+ if "application" in request.args:
4374+ s3.crud.submit_button = T("COMMIT")
4375+ table = db.vol_application
4376+ table.organisation_id.default = org
4377+ table.organisation_id.readable = table.organisation_id.writable = False
4378+ table.number.readable = table.number.writable = True
4379+ table.person_id.readable = table.person_id.writable = False
4380+ field = table.team_leader_id
4381+ field.readable = field.writable = True
4382+ field.requires = IS_NOT_EMPTY()
4383+ #table.person_id.label = T("Team Leader")
4384+ #table.person_id.comment = None
4385+ #table.person_id.widget = S3AddPersonWidget(
4386+ # #select_existing=False
4387+ # )
4388+ #table.person_id.requires = IS_ADD_PERSON_WIDGET()
4389+ #field = table.team_leader
4390+ #field.readable = field.writable = True
4391+ #field.requires = IS_NOT_EMPTY()
4392+ #field = table.team_leader_contact
4393+ #field.readable = field.writable = True
4394+ #field.requires = IS_NOT_EMPTY()
4395+ table.emergency_contact_name.readable = table.emergency_contact_name.writable = False
4396+ table.emergency_contact_name.requires = []
4397+ table.emergency_contact_relationship.readable = table.emergency_contact_relationship.writable = False
4398+ table.emergency_contact_relationship.requires = []
4399+ table.emergency_contact_phone.readable = table.emergency_contact_phone.writable = False
4400+ table.emergency_contact_phone.requires = []
4401+ s3.crud_strings["vol_application"].subtitle_create = ""
4402+
4403+ elif "assignment" in request.args:
4404+ table = db.vol_assignment
4405+ table.organisation_id.default = org
4406+ table.organisation_id.readable = table.organisation_id.writable = False
4407+ table.person_id.readable = table.person_id.writable = False
4408+ # Make the component multiple=False
4409+ # @ToDo: Currently multiple=False will just return the .first() record even if inaccessible!
4410+ # That would be fixed by a filter, however then UI would break.
4411+ s3mgr.model.add_component("vol_assignment",
4412+ req_req=dict(joinby="req_id",
4413+ multiple=False))
4414+ else:
4415+ s3mgr.load("vol_application")
4416+ # Volunteers can only see Public Requests
4417+ response.s3.filter = (req_table.public == True)
4418+ s3mgr.configure(req_table,
4419+ editable=False), # We need the Controller/Table ACL to be editable to be able to add components
4420+ person = s3_logged_in_person()
4421+ if "application" in request.args:
4422+ # Check for required Skills
4423+ table = db.req_req_skill
4424+ query = (table.req_id == request.args[0])
4425+ req = db(query).select(table.skill_id,
4426+ limitby=(0, 1)).first()
4427+ if req:
4428+ # Read the applicant's skills
4429+ table = db.vol_skill
4430+ query = (table.person_id == auth.s3_logged_in_person())
4431+ skillset = db(query).select(table.skill_id,
4432+ limitby=(0, 1)).first()
4433+ gap = False
4434+ if not skillset:
4435+ gap = True
4436+ else:
4437+ for skill in req.skill_id:
4438+ if not skill in skillset.skill_id:
4439+ gap = True
4440+ break
4441+ if gap:
4442+ response.warning = T("Please check that you have the required skills for this assignment as you've not specified that you have all the required skills.")
4443+
4444+ s3.crud.submit_button = T("COMMIT")
4445+ table = db.vol_application
4446+ table.person_id.default = person
4447+ table.person_id.readable = table.person_id.writable = False
4448+ s3.crud_strings["vol_application"].subtitle_create = ""
4449+ # Read in current Emergency Contacts
4450+ ctable = db.vol_contact
4451+ query = (ctable.person_id == person)
4452+ contacts = db(query).select(limitby=(0, 1)).first()
4453+ if contacts:
4454+ table.emergency_contact_name.default = contacts.emergency_contact_name
4455+ table.emergency_contact_relationship.default = contacts.emergency_contact_relationship
4456+ table.emergency_contact_phone.default = contacts.emergency_contact_phone
4457+
4458+ elif "assignment" in request.args:
4459+ table = db.vol_assignment
4460+ table.person_id.default = person
4461+ table.person_id.readable = table.person_id.writable = False
4462+ # Make the component multiple=False
4463+ # @ToDo: Currently multiple=False will just return the .first() record even if inaccessible!
4464+ # That would be fixed by a filter, however then UI would break.
4465+ #s3mgr.model.add_component("vol_assignment",
4466+ # req_req=dict(joinby="req_id",
4467+ # multiple=False))
4468+ s3mgr.configure("vol_assignment",
4469+ enforce_single_record=True)
4470+
4471+ def prep(r):
4472+ db.req_req.status.readable = db.req_req.status.writable = False
4473+
4474+ # Remove type from list_fields
4475+ list_fields = s3mgr.model.get_config("req_req",
4476+ "list_fields")
4477+ try:
4478+ list_fields.remove("type")
4479+ except:
4480+ # It has already been removed.
4481+ # This can happen if the req controller is called
4482+ # for a second time, such as when printing reports
4483+ # see vol.print_assignment()
4484+ pass
4485+ s3mgr.configure(tablename, list_fields=list_fields)
4486+
4487+ type = ( r.record and r.record.type ) or \
4488+ ( request.vars and request.vars.default_type )
4489+ if type:
4490+ type = int(type)
4491+ req_table.type.default = int(type)
4492+
4493+ # Filter the query based on type
4494+ if response.s3.filter:
4495+ response.s3.filter = response.s3.filter & \
4496+ (db.req_req.type == type)
4497+ else:
4498+ response.s3.filter = (db.req_req.type == type)
4499+
4500+ #if type == 3:
4501+ s3mgr.configure("req_req",
4502+ list_fields = ["id",
4503+ "priority",
4504+ "event_id",
4505+ "purpose",
4506+ "site_id",
4507+ "date_required",
4508+ ])
4509+
4510+ if r.interactive:
4511+ # Set Fields and Labels depending on type
4512+ if type:
4513+ # This prevents the type from being edited AFTER it is set
4514+ req_table.type.readable = False
4515+ req_table.type.writable = False
4516+
4517+ crud_strings = deployment_settings.get_req_req_crud_strings(type)
4518+ if crud_strings:
4519+ s3.crud_strings["req_req"] = crud_strings
4520+
4521+ if "application" in request.args:
4522+ s3.crud_strings["req_req"].title_display = T("Volunteer Application")
4523+ elif "assignment" in request.args:
4524+ if s3_has_role(STAFF) and not r.component_id:
4525+ s3.crud_strings["req_req"].title_display = T("Volunteer Roster")
4526+ else:
4527+ s3.crud_strings["req_req"].title_display = T("Volunteer Assignment")
4528+ atable = db.vol_assignment
4529+ # @ToDo: Hide if vol hasn't yet checked-out or if time not > time_until
4530+ #atable.task_evaluation.readable = False
4531+
4532+
4533+ if r.method != "create":
4534+ # Disable Edit for fields where WebEOC is Master
4535+ req_table.event_id.writable = False
4536+ req_table.incident_id.writable = False
4537+ #req_table.incident_id.writable = True
4538+ req_table.request_number.writable = False
4539+ req_table.priority.writable = False
4540+ req_table.site_id.writable = False
4541+ req_table.request_for_id.writable = False
4542+ req_table.date_required.writable = False
4543+ req_table.date_required_until.writable = False
4544+ req_table.purpose.writable = False
4545+ req_table.date.writable = False
4546+ req_table.requester_id.writable = False
4547+ req_table.approved_by_id.writable = False
4548+ req_table.comments.writable = False
4549+ req_table.cancel.writable = False
4550+
4551+ if r.record and r.record.req_req_skill.count():#r.component and r.component.name == "req_skill":
4552+ # Is this component being updated (not created)
4553+ req_skill_table = db.req_req_skill
4554+ req_skill_table.skill_id.writable = False
4555+ req_skill_table.quantity.writable = False
4556+
4557+ # @ToDo: apply these changes via JS for the create form where type is editable
4558+ #if type == 3: # Person
4559+ req_table.date_required.requires = req_table.date_required.requires.other
4560+ req_table.date_required_until.requires = req_table.date_required_until.requires.other
4561+ req_table.location.readable = True
4562+ req_table.public.readable = True
4563+ req_table.organisations_id.readable = True
4564+ req_table.emailed.readable = True
4565+
4566+ if r.method == "create":
4567+ # Only enable Edit for fields where Sahana (not WebEOC) is Master
4568+ req_table.location.writable = True
4569+ req_table.public.writable = True
4570+ req_table.organisations_id.writable = True
4571+
4572+ req_table.purpose.label = T("Task Details")
4573+ req_table.site_id.label = T("Report To Facility")
4574+ req_table.request_for_id.label = T("Point of Contact")
4575+
4576+ if r.method != "update" and r.method != "read":
4577+ if not r.component:
4578+ # Hide fields which don't make sense in a Create form
4579+ # - includes one embedded in list_create
4580+ # - list_fields over-rides, so still visible within list itself
4581+ response.s3.req_create_form_mods()
4582+
4583+ # Get the default Facility for this user
4584+ # @ToDo: Use site_id in User Profile (like current organisation_id)
4585+ if deployment_settings.has_module("hrm"):
4586+ query = (db.hrm_human_resource.person_id == s3_logged_in_person())
4587+ site = db(query).select(db.org_site.id,
4588+ limitby=(0, 1)).first()
4589+ if site:
4590+ r.table.site_id.default = site.id
4591+
4592+ elif r.component.name == "document":
4593+ s3.crud.submit_button = T("Add")
4594+ table = r.component.table
4595+ # @ToDo: Fix for Link Table
4596+ #table.date.default = r.record.date
4597+ #if r.record.site_id:
4598+ # stable = db.org_site
4599+ # query = (stable.id == r.record.site_id)
4600+ # site = db(query).select(stable.location_id,
4601+ # stable.organisation_id,
4602+ # limitby=(0, 1)).first()
4603+ # if site:
4604+ # table.location_id.default = site.location_id
4605+ # table.organisation_id.default = site.organisation_id
4606+
4607+ elif r.component.name == "req_skill":
4608+ #req_hide_quantities(r.component.table)
4609+ table = r.component.table
4610+ table.quantity_commit.writable = table.quantity_commit.readable = False
4611+ table.quantity_fulfil.writable = table.quantity_fulfil.readable = False
4612+
4613+ if r.component:
4614+ if r.component.name == "document" or \
4615+ r.component.name == "req_skill":
4616+ # Limit site_id to facilities the user has permissions for
4617+ # @ToDo: Non-Item requests shouldn't be bound to a Facility?
4618+ auth.permission.permitted_facilities(table=r.table,
4619+ error_msg=T("You do not have permission for any facility to make a request."))
4620+
4621+ elif r.component.name == "assignment":
4622+ # Tweak the breadcrumb
4623+ if r.component.count() > 1:
4624+ label = T("Roster")
4625+ else:
4626+ label = T("Assignment")
4627+ breadcrumbs[2] = (label, False,
4628+ URL(c=request.controller,
4629+ f=request.function,
4630+ args=request.args))
4631+ table = db.vol_assignment
4632+ if s3_has_role(STAFF):
4633+ table.report_to_id.default = r.record.request_for_id
4634+ else:
4635+ # Volunteer can evaluate task if vol has checked-in and (checked-out or time > time_until)
4636+ query = (table.id == r.component_id)
4637+ assign = db(query).select(table.checkin,
4638+ table.checkout,
4639+ limitby=(0, 1)).first()
4640+ checkin = None
4641+ if assign:
4642+ checkin = assign.checkin
4643+ checkout = assign.checkout
4644+ query = (req_table.id == r.id)
4645+ req = db(query).select(req_table.date_required_until,
4646+ limitby=(0, 1)).first()
4647+ if req:
4648+ required_until = req.date_required_until
4649+ else:
4650+ required_until = datetime.datetime(3000, 1, 1)
4651+
4652+ if checkin and (checkout or \
4653+ request.utcnow > required_until):
4654+ table.task_evaluation.writable = True
4655+ table.task_eval_comments.writable = True
4656+ else:
4657+ table.task_evaluation.readable = False
4658+ table.task_eval_comments.readable = False
4659+ if s3_has_role(ORG_VOL):
4660+ # Allow to edit Who/How Many until Evaluation time
4661+ s3.crud_strings.vol_assignment.title_update = T("Application Details")
4662+ table.team_leader_id.readable = table.team_leader_id.writable = True
4663+ table.number.readable = table.number.writable = True
4664+ table.number.label = T("Number of Volunteers Committed")
4665+
4666+ elif r.id and \
4667+ r.component.name == "application" and \
4668+ r.method == "create":
4669+
4670+ if not auth.user.organisation_id:
4671+ # Display an error message if the user has already got an
4672+ # assignment during this time period, this can
4673+ # alternatively be done onvalidation of the application,
4674+ # but doing it here is better UX and saves a query:
4675+ req_id = r.id
4676+ req = r.record
4677+ # @todo: make min_lag deployment setting:
4678+ min_lag = datetime.timedelta(hours=1)
4679+ # Earliest that another Request can start
4680+ earliest = req.date_required_until + min_lag
4681+ # Latest that another Request can end
4682+ latest = req.date_required - min_lag
4683+ person_id = s3_logged_in_person()
4684+ if person_id:
4685+ rtable = db.req_req
4686+ atable = db.vol_assignment
4687+ query = (atable.person_id == person_id) & \
4688+ (rtable.id == atable.req_id) & \
4689+ ((rtable.date_required >= earliest) | \
4690+ (rtable.date_required_until >= latest))
4691+ conflict = db(query).select(rtable.id,
4692+ limitby=(0, 1)).first()
4693+ if conflict:
4694+ session.error = T("You are assigned to another request during this time period")
4695+ redirect(URL(c="vol", f="req_skill"))
4696+ # Could be made less strong:
4697+ #response.error = T("You are assigned to another request during this time period")
4698+ else:
4699+ # Limit site_id to facilities the user has permissions for
4700+ # @ToDo: Non-Item requests shouldn't be bound to a Facility?
4701+ auth.permission.permitted_facilities(table=r.table,
4702+ error_msg=T("You do not have permission for any facility to make a request."))
4703+
4704+ return True
4705+ response.s3.prep = prep
4706+
4707+ # Post-process
4708+ def postp(r, output):
4709+
4710+ if r.interactive:
4711+ if r.http == "POST":
4712+ form = output.get("form", None)
4713+ if not form.errors:
4714+ # Form submitted Succesfully
4715+ # interactive-only onaccept routines can go here
4716+ if r.component and \
4717+ r.component.name == "application":
4718+ # Check whether Request Full:
4719+ # Create Assignment, Update Commit status
4720+ response.s3.application_onaccept_interactive(form)
4721+
4722+ # GET & POST
4723+ s3_action_buttons(r)
4724+ if not r.component:
4725+ # This is only appropriate for item requests
4726+ pass
4727+ elif r.component.name == "req_skill":
4728+ pass
4729+ elif r.component.name == "application":
4730+ try:
4731+ form = output["form"]
4732+ if form.errors.dsw:
4733+ dsw_error = DIV(form.errors.dsw,
4734+ _id="dsw__error",
4735+ _class="error",
4736+ _style="display: block;")
4737+ else:
4738+ dsw_error = ""
4739+ give2la_forms = A( T("Give2LA Registration Forms"),
4740+ _href = URL(c="static",
4741+ f="Volunteer_Ethics_and_Conduct.pdf")
4742+ )
4743+ vol_appl_conditions = SPAN( XML(T("As a volunteer working to assist disaster survivors and in disaster recovery efforts, you will be required to fill out and sign the %(give2la_forms)s. Please print these forms and bring them to the Volunteering Location. The forms are in English only.") % \
4744+ dict(give2la_forms = give2la_forms)
4745+ ),
4746+ #T("As a volunteer working to assist disaster survivors and in disaster recovery efforts, you will be required to sign the following forms: "),
4747+ #A( T("Personal Statement of Understanding"),
4748+ # _href = URL(c="static",
4749+ # f="Volunteer_Personal_Statement.pdf"),
4750+ # ),
4751+ #", ",
4752+ #A( T("City of Los Angeles Code of Volunteer Ethics and Conduct"),
4753+ # _href = URL(c="static",
4754+ # f="Volunteer_Ethics_and_Conduct.pdf"),
4755+ # ),
4756+ #T("and "),
4757+ #A( T("DSW Volunteer Registration Form "),
4758+ # _href = URL(c="static",
4759+ # f="DSW_Volunteer_Registration_Form.pdf"),
4760+ # ),
4761+ #T("or "),
4762+ #A( T("Waiver/Release of Liability"),
4763+ # _href = URL(c="static",
4764+ # f="Volunteer_Personal_Statement.pdf"),
4765+ # ),
4766+ #T(". All forms will be in English."),
4767+ BR(),
4768+ T("Check here to agree to these conditions:")
4769+ )
4770+ form[0][-2].append(TR(TD(LABEL(vol_appl_conditions,
4771+ _for="dsw",
4772+ _style="display: inline;",
4773+ _id="dsw__label"),
4774+ SPAN("*", _class="req"),
4775+ INPUT(_type="checkbox",
4776+ _value="on",
4777+ _name="dsw",
4778+ _id="dsw",
4779+ _class="boolean"),
4780+ dsw_error,
4781+ _class="w2p_fl"),
4782+ _id="dsw__row"))
4783+ form[0][-1][0][0]["_class"] = "accept-button"
4784+ form[0][-1][0].append(BR())
4785+ form[0][-1][0].append(INPUT(_type="button",
4786+ _value=T("DECLINE"),
4787+ _class="wide-grey-button",
4788+ _onClick="javascript: window.location='%s'" % \
4789+ URL(c=request.controller,
4790+ f="req_skill")))
4791+ except KeyError:
4792+ # update 'form'
4793+ pass
4794+ if "subtitle" in output and not output["subtitle"]:
4795+ # Remove null subtitle from Application form to avoid CSS markup
4796+ output.pop("subtitle")
4797+
4798+ elif r.component.name == "assignment":
4799+ ctable = r.component.table
4800+ # This is always appropriate
4801+ response.s3.actions = [
4802+ dict(url = URL(f = "req",
4803+ args = [r.id,
4804+ r.component_name,
4805+ "[id]"]),
4806+ _class = "action-btn",
4807+ label = str(T("Details"))
4808+ )]
4809+ # This is only appropriate if vol not yet checked-in
4810+ query = (ctable.checkin == None)
4811+ rows = db(query).select(ctable.id)
4812+ restrict = [str(row.id) for row in rows]
4813+ response.s3.actions.append(
4814+ dict(url = URL(f = "req",
4815+ args = [r.id,
4816+ r.component_name,
4817+ "[id]",
4818+ "checkin"]),
4819+ _class = "action-btn",
4820+ label = str(T("Check-In NOW")),
4821+ restrict = restrict
4822+ )
4823+ )
4824+ # This is only appropriate if vol checked-in but not out
4825+ query = (ctable.checkin != None) & \
4826+ (ctable.checkout == None)
4827+ rows = db(query).select(ctable.id)
4828+ restrict = [str(row.id) for row in rows]
4829+ response.s3.actions.append(
4830+ dict(url = URL(f = "req",
4831+ args = [r.id,
4832+ r.component_name,
4833+ "[id]",
4834+ "checkout"]),
4835+ _class = "action-btn",
4836+ label = str(T("Check-Out NOW")),
4837+ restrict = restrict
4838+ )
4839+ )
4840+ else:
4841+ # We don't yet have other components
4842+ pass
4843+
4844+ return output
4845+ response.s3.postp = postp
4846+
4847+ rheader = vol_req_rheader
4848+
4849+ output = s3_rest_controller("req", "req", rheader=rheader)
4850+
4851+ return output
4852+
4853+# -----------------------------------------------------------------------------
4854+def req_skill():
4855+ """ REST Controller """
4856+
4857+ if s3_has_role(ORG_DON) and not s3_has_role(ORG_VOL):
4858+ session.warning = T("As a Corporation/Organization we suggest you donate staff as Services. If you wish to volunteer personally, then please sign out and then login with another account.")
4859+ redirect(URL(c="don", f="organisation", args=[auth.user.organisation_id, "inv_item"], vars={"item":"services"}))
4860+
4861+ if s3_has_role(STAFF):
4862+ # NB No link leads them here
4863+ # Tweak the breadcrumbs
4864+ breadcrumbs[2] = (T("Requests for Volunteers"), False,
4865+ URL(c=request.controller,
4866+ f=request.function))
4867+
4868+ tablename = "req_req_skill"
4869+ s3mgr.load(tablename)
4870+ table = db[tablename]
4871+
4872+ s3mgr.configure(tablename,
4873+ insertable=False)
4874+
4875+ if not s3_has_role(STAFF):
4876+ req_table = db.req_req
4877+ s3mgr.load("vol_assignment")
4878+ vtable = db.vol_assignment
4879+
4880+ #if auth.is_logged_in():
4881+ # person = auth.s3_logged_in_person()
4882+ #else:
4883+ vol_sidebar()
4884+
4885+ # Filter out:
4886+ # Reqs which haven't been published to the public site
4887+ # Reqs which are complete (status defined in models/req.py)
4888+ # @ToDo: if s3_has_role(ORG) then also allow reqs for that Org
4889+ response.s3.filter = \
4890+ ((req_table.id == table.req_id) & (req_table.public == True)) & \
4891+ ((req_table.id == table.req_id) & (req_table.commit_status != 2))
4892+
4893+ #if auth.s3_is_logged_in():
4894+ # # Filter Out:
4895+ # # Reqs which the Vol has already got an Assignment for
4896+ # response.s3.filter = response.s3.filter & \
4897+ # ~((vtable.req_id == table.req_id) & (vtable.person_id == person))
4898+
4899+ s3mgr.configure(tablename,
4900+ list_fields=["id",
4901+ (T("Task"), "task"),
4902+ (T("Date + Time"), "date"),
4903+ "skill_id",
4904+ # @ToDo: If-needed, VirtualField
4905+ #"priority",
4906+ (T("Location"), "location"),
4907+ #(T("Number of Volunteers Needed (Still Needed/Total Needed)"), "needed"),
4908+ (T("Number of Volunteers Still Needed"), "needed"),
4909+ #"quantity",
4910+ "comments"
4911+ ])
4912+ actions = [
4913+ dict(url = URL(c = "vol",
4914+ f = "req",
4915+ args = ["application", "create"],
4916+ vars = {"skill_id" : "[id]"}),
4917+ _class = "apply-button",
4918+ label = str(T("Apply"))
4919+ )
4920+ ]
4921+ else:
4922+ actions = [
4923+ dict(url = URL(c = "vol",
4924+ f = "req",
4925+ args = ["req_skill", "[id]"]),
4926+ _class = "action-btn",
4927+ label = str(READ)
4928+ )
4929+ ]
4930+
4931+ def prep(r):
4932+ if r.interactive:
4933+ if r.method != "update" and r.method != "read":
4934+ # Hide fields which don't make sense in a Create form
4935+ # - includes one embedded in list_create
4936+ # - list_fields over-rides, so still visible within list itself
4937+ response.s3.req_hide_quantities(r.table)
4938+
4939+ return True
4940+ response.s3.prep = prep
4941+
4942+ # Post-process
4943+ def postp(r, output):
4944+ if r.interactive:
4945+ response.s3.actions = actions
4946+ if not s3_has_role(STAFF) and r.method == None:
4947+ if auth.is_logged_in():
4948+ line = XML(T("If you have the required skills, click to %(apply_button)s") %
4949+ dict( apply_button = SPAN(T("Apply"),
4950+ _class="apply-button",
4951+ _style ="display:inline-block;text-align:center")
4952+ )
4953+ )
4954+ else:
4955+ line = TAG[""]( XML(T("Please %(register)s to volunteer with %(give2la)s and receive updates of new Requests for Volunteers.") %
4956+ dict( register = A( T("register"),
4957+ _href = URL(c = "vol",
4958+ f = "register")
4959+ ),
4960+ give2la = B("Give2", I("LA"))
4961+ )
4962+ ),
4963+ #BR(),
4964+ #XML( T("If you have already registered please %(sign_in)s.") %
4965+ # dict( sign_in = A( T("Sign In"),
4966+ # _href = URL(c = "default",
4967+ # f = "user",
4968+ # args = ["login"],
4969+ # vars = dict(_next = "/la/vol/req_skill")
4970+ # )
4971+ # )
4972+ # )
4973+ #)
4974+ )
4975+ output["rheader"] = DIV( P( B(T("The City of Los Angeles has the following Requests for Volunteers.") ),
4976+ BR(),
4977+ line
4978+ )
4979+ #T(" or "),
4980+ #A( T("Sign In"), _href = URL(c = "default",
4981+ # f = "user",
4982+ # args = ["login"],
4983+ # vars = dict(_next = "/la/vol/req_skill")
4984+ # )
4985+ )
4986+ #T("The City of Los Angeles requests your participation in the following volunteering opportunities. When you see a request that appeals to you click apply. If you have not registered to volunteer yet, please "),
4987+ #A(T("sign up"),
4988+ # _href="/%s/vol/register" % request.application,
4989+ # _class="signup"),
4990+ #T(" first. You can narrow down the list by for example searching for a specific task or your neighborhood."))
4991+ return output
4992+ response.s3.postp = postp
4993+
4994+ output = s3_rest_controller("req", "req_skill")
4995+
4996+ return output
4997+
4998+# -----------------------------------------------------------------------------
4999+def activity():
5000+ """ REST Controller """
The diff has been truncated for viewing.