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

Proposed by Mike Amy
Status: Superseded
Proposed branch: lp:~mike-amy/sahana-eden/LA
Merge into: lp:sahana-eden
Diff against target: 33883 lines (+15007/-12652)
193 files modified
.bzrignore (+2/-0)
VERSION (+1/-1)
clean_la.cmd (+11/-0)
controllers/admin.py (+52/-1)
controllers/default.py (+527/-375)
controllers/don.py (+619/-0)
controllers/event.py (+11/-1)
controllers/gis.py (+3/-2)
controllers/hrm.py (+233/-0)
controllers/master.py (+22/-0)
controllers/org.py (+129/-0)
controllers/project.py (+2/-59)
controllers/req.py (+13/-9)
controllers/vol.py (+2249/-176)
deployment-templates/models/000_config.py (+222/-205)
languages/es.py (+917/-6599)
models/00_db.py (+0/-1)
models/00_settings.py (+63/-19)
models/00_tables.py (+41/-61)
models/00_utils.py (+11/-5)
models/01_menu.py (+505/-339)
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 (+543/-95)
models/06_hrm.py (+93/-29)
models/06_supply.py (+20/-17)
models/08_inv.py (+7/-1)
models/09_project.py (+108/-11)
models/don.py (+611/-0)
models/event.py (+98/-2)
models/req.py (+1475/-415)
models/vol.py (+1685/-342)
models/zzz_1st_roles.py (+145/-86)
models/zzz_1st_run.py (+86/-185)
modules/s3/s3aaa.py (+37/-26)
modules/s3/s3cfg.py (+4/-0)
modules/s3/s3crud.py (+2/-0)
modules/s3/s3export.py (+4/-4)
modules/s3/s3msg.py (+18/-2)
modules/s3/s3pdf.py (+65/-14)
modules/s3/s3rest.py (+3/-3)
modules/s3/s3search.py (+3/-1)
modules/s3/s3tools.py (+124/-256)
modules/s3/s3widgets.py (+521/-380)
modules/test_utils/setup_request_environment.py (+23/-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 (+16/-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 (+40/-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 (+61/-0)
static/formats/s3csv/vol/req.xsl (+77/-0)
static/scripts/S3/S3.js (+0/-1)
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 (+6/-0)
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 (+729/-834)
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/models/01_menu.py (+125/-0)
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 (+60/-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/default/about.html (+1/-1)
views/default/contact.html (+7/-17)
views/default/disclaimer.html (+13/-0)
views/default/help.html (+97/-2)
views/default/index.html (+25/-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 (+13/-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 (+61/-89)
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/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 (+12/-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+74647@code.launchpad.net

This proposal has been superseded by 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
2593. By Mike Amy

fixed a problem stopping offices being created from the inner forms

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