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

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

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

Description of the change

Add site (office) widget.

Implemented requested changes to the Add Human resource widget:

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

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

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

Add xyz widget refactoring and tests

2595. By Mike Amy

merged from la

2596. By Mike Amy

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

2597. By Mike Amy

merged from LA

Unmerged revisions

2597. By Mike Amy

merged from LA

2596. By Mike Amy

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

2595. By Mike Amy

merged from la

2594. By Mike Amy

Add xyz widget refactoring and tests

2593. By Mike Amy

fixed a problem stopping offices being created from the inner forms

2592. By Mike Amy

merged from trunk

2591. By Mike Amy

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

2590. By Mike Amy

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

2589. By Mike Amy

stopped link showing for autocompleted sites (offices)

2588. By Mike Amy

merged from trunk

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file '.bzrignore'
--- .bzrignore 2011-08-10 21:33:23 +0000
+++ .bzrignore 2011-09-14 11:00:06 +0000
@@ -14,6 +14,8 @@
14!static/fonts/setfonts.py14!static/fonts/setfonts.py
15!static/fonts/generatefontmapping.py15!static/fonts/generatefontmapping.py
16*.ttf16*.ttf
17!static/fonts/la/
18!static/fonts/la/*.ttf
17static/scripts/ext/.docs/*19static/scripts/ext/.docs/*
18static/scripts/ext/.examples/*20static/scripts/ext/.examples/*
19static/scripts/ext/.test/*21static/scripts/ext/.test/*
2022
=== modified file 'VERSION'
--- VERSION 2011-09-14 07:02:18 +0000
+++ VERSION 2011-09-14 11:00:06 +0000
@@ -1,1 +1,1 @@
1r2745 (2011-09-14 08:02:17)
2\ No newline at end of file1\ No newline at end of file
2r2778 (2011-09-14 08:44:36)
3\ No newline at end of file3\ No newline at end of file
44
=== added file 'clean_la.cmd'
--- clean_la.cmd 1970-01-01 00:00:00 +0000
+++ clean_la.cmd 2011-09-14 11:00:06 +0000
@@ -0,0 +1,11 @@
1@echo off
2rem CHOICE Do you want to delete and reinitialise the Sahana Eden Database
3rem if ERRORLEVEL N goto end
4
5rd /Q /S compiled
6
7del databases\*.* /Q
8del errors\*.* /Q
9del sessions\*.* /Q
10python ..\..\web2py.py -S la -M -R applications\la\static\scripts\tools\noop.py
11PAUSE
0\ No newline at end of file12\ No newline at end of file
113
=== modified file 'controllers/admin.py'
--- controllers/admin.py 2011-09-13 14:56:01 +0000
+++ controllers/admin.py 2011-09-14 11:00:06 +0000
@@ -73,6 +73,37 @@
7373
7474
75# -----------------------------------------------------------------------------75# -----------------------------------------------------------------------------
76def register_onaccept(form):
77 """
78 LA-specific:
79 Ensure manually-created users get added to 'Staff' role &
80 have an HRM record created.
81 """
82 # Usual Registration Tasks
83 # (PR record, Authenticated Role, Contacts)
84 person = auth.s3_register(form)
85
86 # LA-specific
87 # Add to 'Staff' role
88 table = db.auth_group
89 STAFF = db(table.uuid == "STAFF").select(table.id,
90 limitby=(0, 1)).first().id
91 table = db.pr_person
92 person_uuid = db(table.id == person).select(table.uuid,
93 limitby=(0, 1)).first().uuid
94 table = db.auth_user
95 query = (table.person_uuid == person_uuid)
96 user = db(query).select(table.id,
97 limitby=(0, 1)).first().id
98 table = db.auth_membership
99 table.insert(user_id = user,
100 group_id = STAFF)
101
102 # Create an HRM record so that the user appears in human_resource_id() lookups
103 table = db.hrm_human_resource
104 table.insert(person_id=person)
105
106# -----------------------------------------------------------------------------
76@auth.s3_requires_membership(1)107@auth.s3_requires_membership(1)
77def user():108def user():
78 """ RESTful CRUD controller """109 """ RESTful CRUD controller """
@@ -84,7 +115,10 @@
84 s3mgr.configure(tablename,115 s3mgr.configure(tablename,
85 main="first_name",116 main="first_name",
86 # Add users to Person Registry & 'Authenticated' role:117 # Add users to Person Registry & 'Authenticated' role:
87 create_onaccept = auth.s3_register)118 create_onaccept = register_onaccept)
119
120 # Staff don't need a Mobile Phone
121 deployment_settings.auth.registration_requests_mobile_phone = False
88122
89 def disable_user(r):123 def disable_user(r):
90 if not r.id:124 if not r.id:
@@ -591,10 +625,13 @@
591@auth.s3_requires_membership(1)625@auth.s3_requires_membership(1)
592def portable():626def portable():
593 """ Portable app creator"""627 """ Portable app creator"""
628
629
594 from gluon.admin import apath630 from gluon.admin import apath
595 import os631 import os
596 from operator import itemgetter, attrgetter632 from operator import itemgetter, attrgetter
597633
634
598 app = request.application635 app = request.application
599 uploadfolder=os.path.join(apath("%s" % app,r=request),'cache')636 uploadfolder=os.path.join(apath("%s" % app,r=request),'cache')
600 web2py_source = None637 web2py_source = None
@@ -715,6 +752,20 @@
715 return response.stream(portable_app)752 return response.stream(portable_app)
716753
717# =============================================================================754# =============================================================================
755# LA Code
756# =============================================================================
757@auth.s3_requires_membership(1)
758def rostermail():
759 """
760 Email Addresses for the Volunteer Roster to be sent to
761 """
762
763 return s3_rest_controller("vol", resourcename)
764
765
766
767
768# =============================================================================
718# Deprecated Code below here769# Deprecated Code below here
719# =============================================================================770# =============================================================================
720@auth.s3_requires_membership(1)771@auth.s3_requires_membership(1)
721772
=== modified file 'controllers/default.py'
--- controllers/default.py 2011-09-12 13:46:38 +0000
+++ controllers/default.py 2011-09-14 11:00:06 +0000
@@ -26,350 +26,14 @@
26 return response.download(request, db)26 return response.download(request, db)
2727
28# -----------------------------------------------------------------------------28# -----------------------------------------------------------------------------
29# Check the validity of entered Mobile Phone data
30def register_validation(form):
31 """ Validate the fields in registration form """
32 # Mobile Phone
33 if "mobile" in form.vars and form.vars.mobile:
34 regex = re.compile(single_phone_number_pattern)
35 if not regex.match(form.vars.mobile):
36 form.errors.mobile = T("Invalid phone number")
37 elif deployment_settings.get_auth_registration_mobile_phone_mandatory():
38 form.errors.mobile = T("Phone number is required")
39 return
40
41auth.settings.register_onvalidation = register_validation
42# Add newly-registered users to Person Registry, add 'Authenticated' role
43# If Organisation is provided, then: add HRM record & add to 'Org_X_Access' role
44auth.settings.register_onaccept = auth.s3_register
45
46_table_user = auth.settings.table_user
47_table_user.first_name.label = T("First Name")
48_table_user.first_name.comment = SPAN("*", _class="req")
49_table_user.last_name.label = T("Last Name")
50if deployment_settings.get_L10n_mandatory_lastname():
51 _table_user.last_name.comment = SPAN("*", _class="req")
52_table_user.email.label = T("E-mail")
53_table_user.email.comment = SPAN("*", _class="req")
54_table_user.password.comment = SPAN("*", _class="req")
55_table_user.language.label = T("Language")
56_table_user.language.comment = DIV(_class="tooltip",
57 _title="%s|%s" % (T("Language"),
58 T("The language you wish the site to be displayed in.")))
59_table_user.language.represent = lambda opt: s3_languages.get(opt, UNKNOWN_OPT)
60
61# Organisation widget for use in Registration Screen
62# NB User Profile is only editable by Admin - using User Management
63org_widget = IS_ONE_OF(db, "org_organisation.id",
64 organisation_represent,
65 orderby="org_organisation.name",
66 sort=True)
67if deployment_settings.get_auth_registration_organisation_mandatory():
68 _table_user.organisation_id.requires = org_widget
69else:
70 _table_user.organisation_id.requires = IS_NULL_OR(org_widget)
71
72# For the User Profile:
73_table_user.organisation_id.represent = organisation_represent
74_table_user.organisation_id.comment = DIV(_class="tooltip",
75 _title="%s|%s|%s" % (T("Organization"),
76 T("The default Organization for whom you are acting."),
77 T("This setting can only be controlled by the Administrator.")))
78
79_table_user.site_id.represent = shn_site_represent
80_table_user.site_id.comment = DIV(_class="tooltip",
81 _title="%s|%s|%s" % (T("Facility"),
82 T("The default Facility for which you are acting."),
83 T("This setting can only be controlled by the Administrator.")))
84
85# -----------------------------------------------------------------------------
86def index():29def index():
87 """ Main Home Page """30 """ Main Home Page """
8831
89 title = deployment_settings.get_system_name()32 title = deployment_settings.get_system_name()
90 response.title = title33 response.title = title
9134
92 if deployment_settings.has_module("cr"):35 return dict(title = title)
93 s3mgr.load("cr_shelter")36
94 SHELTERS = s3.crud_strings["cr_shelter"].subtitle_list
95 else:
96 SHELTERS = ""
97
98 # Menu Boxes
99 menu_btns = [#div, label, app, function
100 ["facility", SHELTERS, "cr", "shelter"],
101 ["facility", T("Warehouses"), "inv", "warehouse"],
102 ["facility", T("Hospitals"), "hms", "hospital"],
103 ["facility", T("Offices"), "org", "office"],
104 ["sit", T("Incidents"), "irs", "ireport"],
105 ["sit", T("Assessments"), "assess", "assess"],
106 ["sit", T("Assets"), "asset", "asset"],
107 ["sit", T("Inventory Items"), "inv", "inv_item"],
108 ["dec", T("Gap Map"), "project", "gap_map"],
109 ["dec", T("Gap Report"), "project", "gap_report"],
110 ["dec", T("Requests"), "req", "req"],
111 ["res", T("Projects"), "project", "project"],
112 ["res", T("Activities"), "project", "assess"],
113 ["res", T("Commitments"), "req", "commit"],
114 ["res", T("Sent Shipments"), "inv", "send"],
115 ["res", T("Received Shipments"), "inv", "recv"],
116 ]
117
118 # Change to (Mitigation)/Preparedness/Response/Recovery?
119 menu_divs = {"facility": DIV( H3(T("Facilities")),
120 _id = "facility_box", _class = "menu_box"),
121 "sit": DIV( H3(T("Situation")),
122 _id = "menu_div_sit", _class = "menu_div"),
123 "dec": DIV( H3(T("Decision")),
124 _id = "menu_div_dec", _class = "menu_div"),
125 "res": DIV( H3(T("Response")),
126 _id = "menu_div_res", _class = "menu_div"),
127 }
128
129 for div, label, app, function in menu_btns:
130 if deployment_settings.has_module(app):
131 # @ToDo: Also check permissions (e.g. for anonymous users)
132 menu_divs[div].append(A( DIV(label,
133 _class = "menu-btn-r"),
134 _class = "menu-btn-l",
135 _href = URL(app,function)
136 )
137 )
138
139 div_arrow = DIV(IMG(_src = "/%s/static/img/arrow_blue_right.png" % \
140 request.application),
141 _class = "div_arrow")
142 sit_dec_res_box = DIV(menu_divs["sit"],
143 div_arrow,
144 menu_divs["dec"],
145 div_arrow,
146 menu_divs["res"],
147 _id = "sit_dec_res_box",
148 _class = "menu_box fleft swidth"
149 #div_additional,
150 )
151 facility_box = menu_divs["facility"]
152 facility_box.append( A( IMG(_src = "/%s/static/img/map_icon_128.png" % \
153 request.application),
154 _href = URL(c="gis", f="index"),
155 _title = T("Map")
156 )
157 )
158
159 datatable_ajax_source = ""
160 # Check logged in AND permissions
161 if AUTHENTICATED in session.s3.roles and \
162 auth.s3_has_permission("read", db.org_organisation):
163 org_items = organisation()
164 datatable_ajax_source = "/%s/default/organisation.aaData" % \
165 request.application
166 response.s3.actions = None
167 response.view = "default/index.html"
168 auth.permission.controller = "org"
169 auth.permission.function = "site"
170 permitted_facilities = auth.permission.permitted_facilities(redirect_on_error=False)
171 manage_facility_box = ""
172 if permitted_facilities:
173 facility_list = s3_represent_facilities(db, permitted_facilities,
174 link=False)
175 facility_opts = [OPTION(opt[1], _value = opt[0])
176 for opt in facility_list]
177 if facility_list:
178 manage_facility_box = DIV(H3(T("Manage Your Facilities")),
179 SELECT(_id = "manage_facility_select",
180 _style = "max-width:400px;",
181 *facility_opts
182 ),
183 A(T("Go"),
184 _href = URL(c="default", f="site",
185 args=[facility_list[0][0]]),
186 #_disabled = "disabled",
187 _id = "manage_facility_btn",
188 _class = "action-btn"
189 ),
190 _id = "manage_facility_box",
191 _class = "menu_box fleft")
192 response.s3.jquery_ready.append( """
193$('#manage_facility_select').change(function() {
194 $('#manage_facility_btn').attr('href', S3.Ap.concat('/default/site/', $('#manage_facility_select').val()));
195})""" )
196 else:
197 manage_facility_box = DIV()
198
199 org_box = DIV( H3(T("Organizations")),
200 A(T("Add Organization"),
201 _href = URL(c="org", f="organisation",
202 args=["create"]),
203 _id = "add-btn",
204 _class = "action-btn",
205 _style = "margin-right: 10px;"),
206 org_items["items"],
207 _id = "org_box",
208 _class = "menu_box fleft"
209 )
210 else:
211 manage_facility_box = ""
212 org_box = ""
213
214 # @ToDo: Replace this with an easily-customisable section on the homepage
215 #settings = db(db.s3_setting.id == 1).select(limitby=(0, 1)).first()
216 #if settings:
217 # admin_name = settings.admin_name
218 # admin_email = settings.admin_email
219 # admin_tel = settings.admin_tel
220 #else:
221 # # db empty and prepopulate is false
222 # admin_name = T("Sahana Administrator").xml(),
223 # admin_email = "support@Not Set",
224 # admin_tel = T("Not Set").xml(),
225
226 # Login/Registration forms
227 self_registration = deployment_settings.get_security_self_registration()
228 registered = False
229 login_form = None
230 login_div = None
231 register_form = None
232 register_div = None
233 if AUTHENTICATED not in session.s3.roles:
234 # This user isn't yet logged-in
235 if request.cookies.has_key("registered"):
236 # This browser has logged-in before
237 registered = True
238
239 # Provide a login box on front page
240 request.args = ["login"]
241 auth.messages.submit_button = T("Login")
242 login_form = auth()
243 login_div = DIV(H3(T("Login")),
244 P(XML("%s <b>%s</b> %s" % (T("Registered users can"),
245 T("login"),
246 T("to access the system")))))
247
248 if self_registration:
249 # Provide a Registration box on front page
250 request.args = ["register"]
251 if deployment_settings.get_terms_of_service():
252 auth.messages.submit_button = T("I accept. Create my account.")
253 else:
254 auth.messages.submit_button = T("Register")
255 register_form = auth()
256 register_div = DIV(H3(T("Register")),
257 P(XML("%s <b>%s</b>" % (T("If you would like to help, then please"),
258 T("sign-up now")))))
259
260 # Add client-side validation
261 s3_register_validation()
262
263 if session.s3.debug:
264 response.s3.scripts.append( "%s/jquery.validate.js" % s3_script_dir )
265 else:
266 response.s3.scripts.append( "%s/jquery.validate.min.js" % s3_script_dir )
267 if request.env.request_method == "POST":
268 post_script = """// Unhide register form
269 $('#register_form').removeClass('hide');
270 // Hide login form
271 $('#login_form').addClass('hide');"""
272 else:
273 post_script = ""
274 register_script = """
275 // Change register/login links to avoid page reload, make back button work.
276 $('#register-btn').attr('href', '#register');
277 $('#login-btn').attr('href', '#login');
278 %s
279 // Redirect Register Button to unhide
280 $('#register-btn').click(function() {
281 // Unhide register form
282 $('#register_form').removeClass('hide');
283 // Hide login form
284 $('#login_form').addClass('hide');
285 });
286
287 // Redirect Login Button to unhide
288 $('#login-btn').click(function() {
289 // Hide register form
290 $('#register_form').addClass('hide');
291 // Unhide login form
292 $('#login_form').removeClass('hide');
293 });""" % post_script
294 response.s3.jquery_ready.append(register_script)
295
296 if deployment_settings.frontpage.rss:
297 response.s3.external_stylesheets.append( "http://www.google.com/uds/solutions/dynamicfeed/gfdynamicfeedcontrol.css" )
298 response.s3.scripts.append( "http://www.google.com/jsapi?key=notsupplied-wizard" )
299 response.s3.scripts.append( "http://www.google.com/uds/solutions/dynamicfeed/gfdynamicfeedcontrol.js" )
300 counter = 0
301 feeds = ""
302 for feed in deployment_settings.frontpage.rss:
303 counter += 1
304 feeds = "".join((feeds,
305 "{title: '%s',\n" % feed["title"],
306 "url: '%s'}" % feed["url"]))
307 # Don't add a trailing comma for old IEs
308 if counter != len(deployment_settings.frontpage.rss):
309 feeds += ",\n"
310 feed_control = "".join(("""
311function LoadDynamicFeedControl() {
312 var feeds = [
313 """, feeds, """
314 ];
315 var options = {
316 // milliseconds before feed is reloaded (5 minutes)
317 feedCycleTime : 300000,
318 numResults : 5,
319 stacked : true,
320 horizontal : false,
321 title : '""", str(T("News")), """'
322 };
323 new GFdynamicFeedControl(feeds, 'feed-control', options);
324}
325// Load the feeds API and set the onload callback.
326google.load('feeds', '1');
327google.setOnLoadCallback(LoadDynamicFeedControl);"""))
328 response.s3.js_global.append( feed_control )
329
330 return dict(title = title,
331
332 sit_dec_res_box = sit_dec_res_box,
333 facility_box = facility_box,
334 manage_facility_box = manage_facility_box,
335 org_box = org_box,
336
337 r = None, # Required for dataTable to work
338 datatable_ajax_source = datatable_ajax_source,
339 #admin_name=admin_name,
340 #admin_email=admin_email,
341 #admin_tel=admin_tel,
342 self_registration=self_registration,
343 registered=registered,
344 login_form=login_form,
345 login_div=login_div,
346 register_form=register_form,
347 register_div=register_div
348 )
349
350# -----------------------------------------------------------------------------
351def organisation():
352 """
353 Function to handle pagination for the org list on the homepage
354 """
355
356 table = db.org_organisation
357 table.id.label = T("Organization")
358 table.id.represent = organisation_represent
359
360 response.s3.dataTable_sPaginationType = "two_button"
361 response.s3.dataTable_sDom = "rtip" #"frtip" - filter broken
362 response.s3.dataTable_iDisplayLength = 25
363
364 s3mgr.configure("org_organisation",
365 listadd = False,
366 addbtn = True,
367 super_entity = db.pr_pentity,
368 linkto = "/%s/org/organisation/%s" % (request.application,
369 "%s"),
370 list_fields = ["id",])
371
372 return s3_rest_controller("org", "organisation")
373# -----------------------------------------------------------------------------37# -----------------------------------------------------------------------------
374def site():38def site():
375 """39 """
@@ -388,7 +52,7 @@
388 args = [id]))52 args = [id]))
389 else:53 else:
390 raise HTTP(404)54 raise HTTP(404)
39155
392# -----------------------------------------------------------------------------56# -----------------------------------------------------------------------------
393def message():57def message():
394 #if "verify_email_sent" in request.args:58 #if "verify_email_sent" in request.args:
@@ -425,12 +89,13 @@
425 _table_user = auth.settings.table_user89 _table_user = auth.settings.table_user
426 if request.args and request.args(0) == "profile":90 if request.args and request.args(0) == "profile":
427 #_table_user.organisation.writable = False91 #_table_user.organisation.writable = False
428 _table_user.utc_offset.readable = True92 #_table_user.utc_offset.readable = True
429 _table_user.utc_offset.writable = True93 #_table_user.utc_offset.writable = True
94 pass
43095
431 login_form = register_form = None96 login_form = register_form = None
432 if request.args and request.args(0) == "login":97 if request.args and request.args(0) == "login":
433 auth.messages.submit_button = T("Login")98 auth.messages.submit_button = T("Sign In")
434 form = auth()99 form = auth()
435 login_form = form100 login_form = form
436 if s3.crud.submit_style:101 if s3.crud.submit_style:
@@ -555,46 +220,640 @@
555 xlwt_version=xlwt_version220 xlwt_version=xlwt_version
556 )221 )
557222
223# =============================================================================
224# LA Custom Views
225# =============================================================================
226def why():
227 """ Custom View """
228 response.title = T("Why?")
229 return dict()
230
231# -----------------------------------------------------------------------------
232def contact():
233 """ Custom View """
234 response.title = T("Contact us")
235 return dict()
236
558# -----------------------------------------------------------------------------237# -----------------------------------------------------------------------------
559def help():238def help():
560 """ Custom View """239 """ Custom View """
561 response.title = T("Help")240 response.title = T("Help")
562 return dict()241 entries = []
563242 question = T("How do I register as a volunteer?")
564# -----------------------------------------------------------------------------243 reply = T("Follow these steps, to register:")
565def contact():244 list = []
566 """245 list.append(T("On the Home Page, click the ‘Volunteer’ link. Review the ‘List of Requests for Volunteers’. To select a %(request)s (Volunteer Task), click on %(apply)s; OR You can register by clicking the ‘Register’ link located on the top of the web page.") % dict(request="<i>%s</i>" % T("Request for Volunteers"),
567 Give the user options to contact the site admins.246 apply = "<strong>%s</strong>" % T("‘APPLY’")))
568 Either:247 list.append(T("Enter your information into the Required Fields. After you have entered your information, click %(accept)s to register.") % dict(accept="<strong>%s</strong" % T("‘I ACCEPT, CREATE MY ACCOUNT’")))
569 An internal Support Requests database248 entry = (question, reply, list)
570 or:249 entries.append(entry)
571 Custom View250 question = T("How do I update my Profile, Skills and Emergency Details?")
572 """251 reply = T("After you are signed-in, please follow these steps to edit your profile: ")
573 if auth.is_logged_in() and deployment_settings.get_options_support_requests():252 list = []
574 # Provide an internal Support Requests ticketing system.253 list.append(T("On the top menu, click %(volunteer)s.") % dict(volunteer="<strong>%s</strong>" % T("VOLUNTEER")))
575 prefix = "support"254 list.append(T("On the left menu, click %(profile)s .") % dict(profile="<strong>%s</strong>" % T("'My Profile'")))
576 resourcename = "req"255 list.append(T("On this page, you can update your profile."))
577 tablename = "%s_%s" % (prefix, resourcename)256 list.append(T("Additional information like %(notifications)s and other volunteer agency affiliations also can be updated in this page.") % dict(notifications="<strong>%s</strong>" % T("Notifications")))
578 table = db[tablename]257 list.append(T("Then click %(save)s") % dict(save="<strong>%s</strong>" % T("Save")))
579258 list.append(T("On the left menu, click %(skills)s.") % dict(skills="<strong>%s</strong>" % T("Skills")))
580 # Pre-processor259 list.append(T("Click the %s sign next to the skill(s) that best describes how you can support the City.") % "<strong>'+'</strong>")
581 def prep(r):260 list.append(T("You may also search for a skill by providing the first few characters in the search box. (Example: When searching for ‘Driving’ skills, enter the first letters of the word)."))
582 # Only Admins should be able to update ticket status261 list.append(T("If your skill(s) is not listed, you may enter it in the %(extraSkills)s field below.") % dict(extraSkills="<strong>%s</strong>" % T("Skills")))
583 if not auth.s3_has_role(ADMIN):262 list.append(T("Then click %(save)s") % dict(save="<strong>%s</strong>") % T("Save"))
584 table.status.writable = False263 list.append(T("You may also update your Emergency Contacts information by clicking on 'Emergency Contacts' located on the left menu."))
585 table.actions.writable = False264 list.append(T("Then click %(save)s") % dict(save="<strong>%s</strong>") % T("Save"))
586 if r.interactive and r.method == "create":265 entry = (question, reply, list)
587 table.status.readable = False266 entries.append(entry)
588 table.actions.readable = False267 question = T("How do I apply for a Volunteer Request after I have registered with Give2LA?")
589 return True268 reply = T("You can apply by clicking the %(signin)s link located on the top of the web page.") % dict(signin="<strong>%s</strong>" % T("‘Sign-In’"))
590 response.s3.prep = prep269 list = []
591270 list.append(T("Enter your E-Mail and Password information."))
592 output = s3_rest_controller(prefix, resourcename)271 list.append(T("Review the ‘List of Requests for Volunteers’."))
593 return output272 list.append(T("To select a Volunteer Task, click on %(apply)s.") % dict(apply="<strong>%s</strong>") % T("‘APPLY’"))
594 else:273 list.append(T("Review the ‘Volunteer Assignment Details’. Enter your Emergency Contact information."))
595 # Default: Simple Custom View274 list.append(T("Download the Give2LA Volunteer Registration Forms, complete the forms, and take them with you to your Volunteer Assignment."))
596 response.title = T("Contact us")275 list.append(T("Then click %(commit)s.") % dict(commit="<strong>%s</strong>") % T("Commit"))
597 return dict()276 list.append(T("Review the Volunteer Application details."))
598277 list.append(T("Then click %(form)s and take them with you to your Volunteer Assignment.") % dict(form="<strong>%s</strong>" % T("'Print Volunteer Assignment Form'")))
278 entry = (question, reply, list)
279 entries.append(entry)
280 question = T("Can I provide feedback or evaluation for an assignment after volunteering?")
281 reply = T("Yes. After you have signed-in, follow these steps:")
282 list = []
283 list.append(T("On the top menu, click %(volunteer)s.") % dict(volunteer="<strong>%s</strong>") % T("VOLUNTEER"))
284 list.append(T("On the left menu, click %(assignments)s.") % dict(assignments="<strong>%s</strong>") % T("‘My Assignments’"))
285 list.append(T("Click on the ‘Details’ button of the Volunteer Task you have completed."))
286 list.append(T("Scroll down to the bottom of the screen to get to ‘Evaluation of Event’."))
287 list.append(T("Enter the evaluation details; and click %(save)s") % dict(save="<strong>%s</strong>") % T("Save"))
288 entry = (question, reply, list)
289 entries.append(entry)
290 question = T("I would like to register my corporation for donations, what is the process?")
291 reply = T("Follow these steps, to register")
292 list = []
293 list.append(T("Locate the %(register)s for Corporations and Organizations on the Home Page and click on it.") % dict(register="<strong>%s</strong>" % T("‘CLICK HERE TO REGISTER’")))
294 list.append(T("Enter the Mandatory details and click on the button %(accept)s at the bottom of the screen to register") % dict(accept="<strong>%s</strong" % T("‘I ACCEPT, CREATE MY ACCOUNT’")))
295 entry = (question, reply, list)
296 entries.append(entry)
297
298 return dict(entries=entries)
299
300# -----------------------------------------------------------------------------
301def faq():
302 """ Custom View """
303 donateLink = A(T("Donate Page"), _href=URL(c="don", f="index"))
304
305 response.title = T("Frequently Asked Questions")
306 entries = []
307 question = T("Is there a minimum age limit to volunteer?")
308 reply = T("Yes, you have to be at least 18 years of age to Volunteer.")
309 entry = (question, reply)
310 entries.append(entry)
311 question = T("What is the Privacy policy of Give2LA?")
312 reply = T("Please refer to the %(privacy)s") % dict(privacy=A(T("Privacy Policy"),
313 _href=URL(c="default", f="disclaimer")))
314 entry = (question, reply)
315 entries.append(entry)
316 question = T("Can I bring a friend to volunteer?")
317 reply = T("Yes, but your friend must also %(register)s and apply to the same Volunteer Assignment.") % dict(register=A(T("Register"),
318 _href=URL(c="vol", f="register")))
319 entry = (question, reply)
320 entries.append(entry)
321 question = T("Will I get any food or reimbursements for expenses while volunteering?")
322 reply = T("No, unless it is otherwise stated on the Volunteer Assignment.")
323 entry = (question, reply)
324 entries.append(entry)
325 question = T("Can I donate cash to support the City of Los Angeles?")
326 reply = T("Yes, you can donate cash to the %(laepf)s who support the City of Los Angeles or other partner organizations listed on the %(donate)s. These links will take you directly to the organization’s website to complete the cash donation transaction.") % dict(laepf="<a href=http://laemergencypreparednessfoundation.org/>LAEPF</a>", donate=donateLink)
327 entry = (question, reply)
328 entries.append(entry)
329 question = T("What is the process for donating in-kind Items?")
330 reply = T("The City of Los Angeles prefers cash donations. In-kind items can be donated directly to the organization(s) listed under Donate Items or through any Upcoming Donation Drive on the %(donate)s") % dict(donate=donateLink)
331 entry = (question, reply)
332 entries.append(entry)
333 question = T("Do I have to be a U.S. citizen or legal U.S. resident to volunteer?")
334 reply = T("Yes. You must be a U.S. citizen, legal U.S. resident, or have gained legal entry into the United States.")
335 entry = (question, reply)
336 entries.append(entry)
337 question = T("Do I have to live in the City of Los Angeles to volunteer?")
338 reply = T("No, anyone may register to support the City of Los Angeles’ volunteer efforts.")
339 entry = (question, reply)
340 entries.append(entry)
341 question = T("Is my donation tax deductible?")
342 reply = T("Yes. All donations are tax deductible. The partner organization that receives your donation will provide a receipt for your donation. Please make sure you obtain your tax receipt from the organization and keep it in a safe place for tax purposes. The City of Los Angeles will not issue receipts for your donation.")
343 entry = (question, reply)
344 entries.append(entry)
345
346 return dict(entries=entries)
347
348# -----------------------------------------------------------------------------
349def sitemap():
350 """ Custom View """
351 response.title = T("Site Map")
352 return dict()
353
354# -----------------------------------------------------------------------------
355def disclaimer():
356 """ Custom View """
357 response.title = T("Disclaimer")
358 return dict()
359
360# -----------------------------------------------------------------------------
361def register():
362 """
363 Registration for Organisations
364 - custom form
365 """
366
367 # Which type of organisation are we registering?
368 don = False
369 vol = False
370 if "type" in request.vars:
371 if request.vars.type == "don":
372 don = True
373 elif request.vars.type == "vol":
374 vol = True
375 auth.settings.registration_requires_approval = True
376
377 auth.messages.submit_button = T("I accept. Create my account.")
378 request.args = ["register"]
379 _table_user.language.default = T.accepted_language
380 _table_user.language.readable = False
381 _table_user.language.writable = False
382 form = auth()
383 form.attributes["_id"] = "regform"
384 # Custom class for Submit Button
385 form[0][-1][0][0]["_class"] = "accept-button"
386
387 # Cancel button
388 form[0][-1][0].append(BR())
389 #form[0][-1][1].append(INPUT(_type="reset", _value=T("Cancel")))
390 form[0][-1][0].append(INPUT(_type="button",
391 _value=T("Cancel"),
392 _class="wide-grey-button",
393 _onClick="javascript: history.go(-1)"))
394
395 formstyle = s3.crud.formstyle
396
397 # Organisation
398 if form.errors.organisation:
399 organisation_error = DIV(form.errors.organisation,
400 _id="organisation__error",
401 _class="error",
402 _style="display: block;")
403 else:
404 organisation_error = ""
405 if don:
406 label = T("Corporation/Organization Name")
407 else:
408 label = T("Organization Name")
409 row = formstyle(id = "organisation",
410 label = LABEL("%s:" % label,
411 SPAN(" *", _class="req")),
412 widget = DIV(INPUT(_name="organisation",
413 _id="organisation",
414 _class="string"),
415 organisation_error),
416 comment = "")
417 form[0].insert(0, row)
418
419 # Industry Sector
420 if vol:
421 hidden = True
422 widget = INPUT(_name="sector_id",
423 _id="sector_id",
424 _class="string")
425 else:
426 from gluon.sqlhtml import OptionsWidget
427 hidden = False
428 widget = OptionsWidget.widget(db.org_organisation.sector_id,
429 value="")
430 # dropdown
431 row = formstyle(id = "sector_id",
432 label = LABEL("%s:" % T("Industry Sector")),
433 widget = widget,
434 comment = "",
435 hidden = hidden)
436 form[0].insert(1, row)
437 # freetext box for not listed
438 row = formstyle(id = "sector_other",
439 label = LABEL("%s:" % T("Other Sector not listed")),
440 widget = INPUT(_name="sector_other",
441 _id="sector_other",
442 _class="string"),
443 comment = "",
444 hidden = hidden)
445 form[0].insert(2, row)
446
447
448 # Primary Contact Person section
449 row = TR(TD(LABEL(T("Primary Contact")),
450 _colspan="3",
451 _class="subheading"))
452 form[0][2].append(row)
453 row = formstyle(id = "middle_name",
454 label = LABEL("%s:" % T("Middle Name")),
455 widget = INPUT(_name="middle_name",
456 _id="middle_name",
457 _class="string"),
458 comment = "")
459 form[0][4].append(row)
460
461 row = formstyle(id = "secondary_email",
462 label = LABEL("%s:" % T("Secondary Email")),
463 widget = INPUT(_name="secondary_email",
464 _id="secondary_email",
465 _class="string"),
466 comment = "")
467 form[0][12].append(row)
468
469 # What are you offering?
470 if don or vol:
471 hidden = True
472 else:
473 hidden = False
474 row = formstyle(id = "offer",
475 hidden = hidden,
476 label = LABEL("%s:" % T("We can offer"),
477 SPAN(" *", _class="req")),
478 widget = (T("Items"),
479 INPUT(_type="checkbox",
480 _value="on",
481 value="on" if don else "",
482 _name="has_items",
483 _id="has_items",
484 _class="boolean"),
485 T("Volunteers"),
486 INPUT(_type="checkbox",
487 _value="on",
488 value="on" if vol else "",
489 _name="vols",
490 _id="vols",
491 _class="boolean"),
492 ),
493 comment = "")
494 form[0][12].append(row)
495
496 # Phone
497 if form.errors.mobile_phone:
498 mobile_phone_error = DIV(form.errors.mobile_phone,
499 _id="mobile_phone__error",
500 _class="error",
501 _style="display: block;")
502 else:
503 mobile_phone_error = ""
504 if form.errors.work_phone:
505 work_phone_error = DIV(form.errors.work_phone,
506 _id="work_phone__error",
507 _class="error",
508 _style="display: block;")
509 else:
510 work_phone_error = ""
511 row = formstyle(id = "phone",
512 label = LABEL("%s:" % T("Work Phone")),
513 widget = DIV(INPUT(_name="work_phone",
514 _id="",
515 _class="string"),
516 work_phone_error),
517 comment = "")
518 form[0][12].append(row)
519 row = formstyle(id = "phone",
520 label = LABEL("%s:" % current.deployment_settings.get_ui_label_mobile_phone(),
521 SPAN(" *", _class="req")),
522 widget = DIV(INPUT(_name="mobile_phone",
523 _id="",
524 _class="string"),
525 mobile_phone_error),
526 comment = "")
527 form[0][12].append(row)
528
529 # Address
530 row = TR(TD(LABEL(T("Corporation/Organization Address")),
531 _colspan="3",
532 _class="subheading"))
533 form[0][12].append(row)
534 if form.errors.address1:
535 address1_error = DIV(form.errors.address1,
536 _id="address1__error",
537 _class="error",
538 _style="display: block;")
539 else:
540 address1_error = ""
541 row = formstyle(id = "address1",
542 label = LABEL("%s:" % T("Address 1"),
543 SPAN(" *", _class="req")
544 ),
545 widget = (INPUT(_name="address1",
546 _id="address1",
547 _class="string"),
548 address1_error),
549 comment = "")
550 form[0][12].append(row)
551 row = formstyle(id = "address2",
552 label = LABEL("%s:" % T("Address 2")),
553 widget = INPUT(_name="address2",
554 _id="address2",
555 _class="string"),
556 comment = "")
557 form[0][12].append(row)
558 row = formstyle(id = "city",
559 label = LABEL("%s:" % T("City"),
560 SPAN(" *", _class="req")),
561 widget = INPUT(_name="city",
562 _id = "city",
563 _class = "string"),
564 comment = "")
565 form[0][12].append(row)
566 states = S3LocationDropdownWidget(level="L1",
567 default="California",
568 empty=False)
569 widget = states(db.pr_address.location_id, None)
570 row = formstyle(id = "state",
571 label = LABEL("%s:" % T("State"),
572 SPAN(" *", _class="req")),
573 widget = widget,
574 comment = "")
575 form[0][12].append(row)
576 if form.errors.zip:
577 zip_error = DIV(form.errors.zip,
578 _id="zip__error",
579 _class="error",
580 _style="display: block;")
581 else:
582 zip_error = ""
583 row = formstyle(id = "zip",
584 label = LABEL("%s:" % T("Zip"),
585 SPAN(" *", _class="req")
586 ),
587 widget = ( INPUT(_name="zip",
588 _id="zip",
589 _class="string"),
590 zip_error
591 ),
592 comment = "")
593 form[0][12].append(row)
594
595
596 #form[0][-2].append(TR(TD(LABEL(T("Terms of Service:"),
597 # _id="terms_of_service__label"),
598 # _class="w2p_fl"),
599 # TD(LABEL(TEXTAREA(deployment_settings.get_terms_of_service(),
600 # _onfocus="this.rows=10",
601 # _readonly="readonly",
602 # _style="width:100%;text-align:",
603 # _cols="80", _rows="10"),
604 # _id="terms_of_service"),
605 # _class="w2p_fw",
606 # _colspan="2"),
607 # _id="terms_of_service__row"))
608
609 # Add client-side validation
610 # simplified copy of s3_register_validation()
611 script = "".join(( """
612$('#regform').validate({
613 errorClass: 'req',
614 rules: {
615 first_name: {
616 required: true
617 },
618 last_name: {
619 required: true
620 },
621 email: {
622 required: true,
623 email: true
624 },
625 organisation: {
626 required: true
627 },
628 mobile_phone: {
629 required: true
630 },
631 address1: {
632 required: true
633 },
634 city: {
635 required: true
636 },
637 zip: {
638 required: true
639 },
640 password: {
641 required: true
642 },
643 password_two: {
644 required: true,
645 equalTo: '.password:first'
646 }
647 },
648 messages: {
649 firstname: '""", str(T("Enter your firstname")), """',
650 email: {
651 required: '""", str(T("Please enter a valid email address")), """',
652 minlength: '""", str(T("Please enter a valid email address")), """'
653 },
654 password: {
655 required: '""", str(T("Provide a password")), """'
656 },
657 password_two: {
658 required: '""", str(T("Repeat your password")), """',
659 equalTo: '""", str(T("Enter the same password as above")), """'
660 }
661 },
662 errorPlacement: function(error, element) {
663 error.appendTo( element.parent().next() );
664 },
665 submitHandler: function(form) {
666 form.submit();
667 }
668});""" ))
669 response.s3.jquery_ready.append( script )
670
671 if session.s3.debug:
672 response.s3.scripts.append( "%s/jquery.validate.js" % s3_script_dir )
673 response.s3.scripts.append( "%s/jquery.pstrength.1.3.js" % s3_script_dir )
674 else:
675 response.s3.scripts.append( "%s/jquery.validate.min.js" % s3_script_dir )
676 response.s3.scripts.append( "%s/jquery.pstrength.1.3.min.js" % s3_script_dir )
677
678 response.s3.jquery_ready.append("$('.password:first').pstrength();\n")
679
680 response.title = T("Register")
681 response.s3.has_required = True
682
683 return dict(form=form)
684
685# -----------------------------------------------------------------------------
686def register_validation(form):
687 """ Validate the custom fields in registration form """
688 # Name
689 if "organisation" in request.post_vars and request.post_vars.organisation:
690 # Check not in use
691 table = db.org_organisation
692 query = (table.name == request.post_vars.organisation)
693 name = db(query).select(table.id, limitby=(0, 1)).first()
694 if name:
695 form.errors.organisation = T("Organisation Name is already in use")
696 else:
697 form.errors.organisation = T("Organisation Name is required")
698 # Phone
699 if "work_phone" in request.post_vars and request.post_vars.work_phone:
700 regex = re.compile(single_phone_number_pattern)
701 if not regex.match(request.post_vars.work_phone):
702 form.errors.work_phone = T("Invalid phone number")
703 if "mobile_phone" in request.post_vars and request.post_vars.mobile_phone:
704 regex = re.compile(single_phone_number_pattern)
705 if not regex.match(request.post_vars.mobile_phone):
706 form.errors.mobile_phone = T("Invalid phone number")
707 else:
708 form.errors.mobile_phone = T("Phone number is required")
709 # Address
710 if not request.post_vars.address1:
711 form.errors.address1 = T("Address is required")
712 if not request.post_vars.city:
713 form.errors.city = T("City is required")
714 if not request.post_vars.zip:
715 form.errors.zip = T("Zip is required")
716
717 return
718
719# -----------------------------------------------------------------------------
720def register_onaccept(form):
721 # Usual Registration Tasks
722 # (PR record, Authenticated Role, Contacts)
723 person = auth.s3_register(form)
724
725 # LA-specific
726 ptable = db.pr_person
727 query = (ptable.id == person)
728
729 db(query).update(middle_name=request.post_vars.middle_name)
730
731 pe = db(query).select(ptable.pe_id,
732 limitby=(0, 1)).first().pe_id
733
734 # Organisation
735 organisation = request.post_vars.organisation
736 if "vols" in request.post_vars and request.post_vars.vols == "on":
737 has_vols = True
738 else:
739 has_vols = False
740 if "has_items" in request.post_vars and request.post_vars.has_items == "on":
741 has_items = True
742 else:
743 has_items = False
744
745 # Phone
746 table = db.pr_contact
747 work_phone = request.post_vars.work_phone
748 mobile_phone = request.post_vars.mobile_phone
749 if mobile_phone:
750 # Don't auto-subscribe to SMS (priority 10)
751 table.insert(pe_id = pe,
752 contact_method = "SMS",
753 value = mobile_phone,
754 priority=10)
755 if work_phone:
756 # Don't auto-subscribe to SMS (priority 10)
757 table.insert(pe_id = pe,
758 contact_method = "WORK_PHONE",
759 value = work_phone,
760 priority=10)
761
762 # Address
763 address1 = request.post_vars.address1
764 address2 = request.post_vars.address2
765 #if address2:
766 # address = "%s\n%s" % (address1,
767 # address2)
768 #else:
769 # address = address1
770 city = request.post_vars.city
771 state = request.post_vars.location_id
772 zip = request.post_vars.zip
773 if request.post_vars.sector_other:
774 sector_other = "Sector: %s" % request.post_vars.sector_other
775 else:
776 sector_other = ""
777 otable = db.org_organisation
778 org = otable.insert(name = organisation,
779 has_vols = has_vols,
780 has_items = has_items,
781 #phone=phone,
782 address=address1,
783 address_2=address2,
784 L3=city,
785 L1=state,
786 postcode=zip,
787 sector_id=request.post_vars.sector_id,
788 comments=sector_other)
789 record = Storage(id=org)
790 s3mgr.model.update_super(otable, record)
791 auth.s3_set_record_owner(otable, org)
792 # For OrgDons which don't require approval
793 if auth.user:
794 # Update the session
795 auth.user.organisation_id = org
796 user_id = auth.user.id
797 else:
798 user_id = form.vars.id
799
800 # Create HRM
801 table = db.hrm_human_resource
802 hrm = table.insert(person_id = person,
803 organisation_id = org,
804 focal_point = True,
805 owned_by_user = user_id)
806 record = Storage(id=hrm)
807 s3mgr.model.update_super(table, record)
808 auth.s3_set_record_owner(table, hrm)
809
810 # Set the Roles
811 person = db(query).select(ptable.uuid,
812 limitby=(0, 1)).first()
813 if not person:
814 # Error
815 return
816 utable = db[auth.settings.table_user]
817 query = (utable.person_uuid == person.uuid)
818 db(query).update(organisation_id = org)
819 user = db(query).select(utable.id,
820 limitby=(0, 1)).first()
821 if not user:
822 # Error
823 return
824 mtable = db[auth.settings.table_membership]
825 gtable = db[auth.settings.table_group]
826 _org = db(otable.id == org).select(otable.owned_by_organisation,
827 limitby=(0, 1)).first()
828 if _org:
829 mtable.insert(user_id = user.id,
830 group_id = _org.owned_by_organisation)
831
832 if has_vols:
833 OrgVol = db(gtable.uuid == ORG_VOL).select(gtable.id,
834 limitby=(0, 1)).first()
835 if OrgVol:
836 mtable.insert(user_id = user.id,
837 group_id = OrgVol.id)
838 # Go to the Contacts page so that a secondary contact can be added
839 # Flag that we've come from registration for subsequent workflow
840 #redirect(URL(c="vol", f="organisation", args=[org, "human_resource"],
841 # vars={"register":1}))
842
843 if has_items:
844 OrgDon = db(gtable.uuid == ORG_DON).select(gtable.id,
845 limitby=(0, 1)).first()
846 if OrgDon:
847 mtable.insert(user_id = user.id,
848 group_id = OrgDon.id)
849 # Go to the Contacts page so that a secondary contact can be added
850 # Flag that we've come from registration for subsequent workflow
851 redirect(URL(c="don", f="organisation", args=[org, "human_resource"],
852 vars={"register":1}))
853
854# -----------------------------------------------------------------------------
855#auth.settings.registration_requires_approval = True
856auth.settings.register_onvalidation = register_validation
857auth.settings.register_onaccept = register_onaccept
599858
600# END =========================================================================859# END =========================================================================
601860
=== added file 'controllers/don.py'
--- controllers/don.py 1970-01-01 00:00:00 +0000
+++ controllers/don.py 2011-09-14 11:00:06 +0000
@@ -0,0 +1,693 @@
1# -*- coding: utf-8 -*-
2
3"""
4 Donations (LA Specific)
5
6 @author: Michael Howden (michael@sahanafoundation.org)
7 @date-created: 2011-08-02
8"""
9
10
11module = request.controller
12resourcename = request.function
13
14if not deployment_settings.has_module(module):
15 raise HTTP(404, body="Module disabled: %s" % module)
16
17# Options Menu (available in all Functions)
18shn_menu(module)
19
20# Load Models
21s3mgr.load("don_collect")
22# -----------------------------------------------------------------------------
23def index():
24 """ Custom View """
25 if s3_has_role(STAFF):
26 redirect(URL(f="req"))
27 else:
28 response.menu_options = []
29 if session.s3.debug:
30 response.s3.scripts.append( "%s/jquery.hoverIntent.js" % s3_script_dir )
31 else:
32 response.s3.scripts.append( "%s/jquery.hoverIntent.minified.js" % s3_script_dir )
33
34 response.title = T("Donate")
35 response.s3.jquery_ready.append("""
36 $(".donate-popup").css("display", "none");
37 $(".organizations-list li a").hoverIntent(donateFadeIn, donateFadeOut);
38 """)
39 response.s3.js_global.append("""
40 function donateFadeIn(){$(this).next(".donate-popup").fadeIn();}
41 function donateFadeOut(){$(this).next(".donate-popup").fadeOut();}
42 """)
43
44 # Get Donation Drives List
45 table = db.don_collect
46 query = ( ( table.end_datetime > request.utcnow ) &
47 ( table.deleted == False )
48 )
49 rows = db(query).select( table.start_datetime,
50 table.end_datetime,
51 table.site_id,
52 orderby = table.start_datetime
53 )
54 if rows:
55 list = []
56 offset = IS_UTC_OFFSET.get_offset_value(session.s3.utc_offset)
57 for row in rows:
58 if offset:
59 start_datetime = row.start_datetime + datetime.timedelta(seconds=offset)
60 end_datetime = row.end_datetime + datetime.timedelta(seconds=offset)
61
62 start_date = start_datetime.strftime("%b %d")
63 start_time = start_datetime.strftime("%I:%M %p")
64 end_date = end_datetime.strftime("%b %d")
65 end_time = end_datetime.strftime("%I:%M %p")
66
67 site = shn_site_represent(row.site_id, address = True)
68
69 if start_date == end_date:
70 list.append( LI( SPAN( start_date, _class = "date"),
71 " %s - %s" % (start_time, end_time),
72 site,
73 BR()
74 )
75 )
76 else:
77 list.append( LI( SPAN( start_date, _class = "date" ),
78 " %s - " % start_time,
79 SPAN( end_date, _class = "date" ),
80 " %s" % end_time,
81 site,
82 BR()
83 )
84 )
85 donation_drives = TAG[""](*list)
86 else:
87 donation_drives = P(T("No Donation Drives Scheduled. Please check back later"))
88
89 return dict(donation_drives = donation_drives)
90
91# -----------------------------------------------------------------------------
92def match():
93 # Get Req Resource Details
94 tablename, req_id = request.vars.viewing.split(".")
95
96 # create fake resource for rheader
97 req = db(db.req_req.id == req_id).select(limitby = (0, 1)).first()
98 r = Storage()
99 r.record = req
100 r.representation = "html"
101 r.name = "don"
102 r.function = "match"
103 r.vars = request.vars
104 rheader = response.s3.don_rheader(r)
105
106 # Get Item Filter
107 req_item = db(db.req_req_item.req_id == req_id).select(db.req_req_item.item_id,
108 limitby = [0,1]
109 ).first()
110 if req_item:
111 item_id = req_item.item_id
112 else:
113 item_id = None
114 response.s3.filter = (db.inv_inv_item.item_id == item_id)
115
116 # Configure inv_item list
117 s3mgr.configure("inv_inv_item",
118 insertable = False,
119 list_fields = ["id",
120 "organisation_id",
121 "item_id",
122 "quantity",
123 "comments"]
124 )
125 response.s3.actions = [dict(url = URL(c="don", f="req",
126 args = [req_id, "commit"],
127 vars = dict(inv_item_id = "[id]")
128 ),
129 _class = "action-btn",
130 label = str(T("Select")),
131 )
132 ]
133 s3mgr.load("inv_inv_item")
134 output = response.s3.inv_item_controller() #s3_rest_controller("inv", "inv_item")
135
136 # Customize form
137 output["rheader"] = rheader
138 output["title"] = T("Request for Donations Details")
139 output["subtitle"] = T("Matching Donation Items")
140
141 return output
142
143# -----------------------------------------------------------------------------
144def req():
145 """
146 /req/req/default_type=1
147 """
148 s3mgr.configure("req_req",
149 list_fields = ["id",
150 #"priority",
151 "status",
152 (T("BOC Status"), "req_commit_status"),
153 "created_on",
154 "date_required",
155 (T("Item"), "item"),
156 "site_id",
157 ],
158 orderby = ["created_on"])
159
160 s3mgr.load("req_req")
161 request.vars["default_type"] = 1
162
163 #s3mgr.model.set_method("req", "req", method="match", action = match)
164
165 # Defined in the Model for use from Multiple Controllers for unified menus
166 output = response.s3.req_controller()
167 if "rheader" in output:
168 if len(request.args) >0:
169 args = request.args[0]
170 else:
171 args = None
172 reportButtons = DIV(
173 BUTTON(T("Print Donation Request Form"),
174 _class="accept-button",
175 _onClick="javascript: window.location='%s'" % \
176 URL(c=request.controller,
177 f="req_print",
178 args=args
179 )
180 ),
181 # Also in controllers/vol.py - DRY
182 A(IMG(_src="/%s/static/img/get_adobe_reader.png" % request.application,
183 _title="%s - %s" % (T("Get Adobe Reader"),
184 T("This link will open a new browser window.")),
185 _alt=T("Get Adobe Reader"),
186 _width=158,
187 _height=39,
188 _style="float:right;"),
189 _href="http://www.adobe.com/products/acrobat/readstep2.html",
190 _target="_blank"),
191 )
192 output["rheader"].append(reportButtons)
193
194 return output
195
196def req_print():
197 """ Print Donation Request Form """
198 s3mgr.load("req_req_item")
199 r = s3base.S3Request(s3mgr,
200 prefix="req",
201 name="req_item",
202 extension="pdf")
203 s3mgr.configure("req_req_item",
204 callback = response.s3.donationRequest,
205 formname = T("Request for Donations"),
206 footer = lambda x, y: None,
207 )
208 return r()
209
210# -----------------------------------------------------------------------------
211# @ToDo: move to inside a prep - only for inv_item components
212# Add inv_item directly to Org
213if deployment_settings.has_module("inv"):
214 s3mgr.load("inv_inv_item")
215
216s3mgr.configure("inv_inv_item",
217 list_fields = ["id",
218 "organisation_id",
219 "item_category_id",
220 "item_id",
221 "pack_value",
222 "quantity",
223 ]
224 )
225
226db.inv_inv_item.site_id.readable = db.inv_inv_item.site_id.writable = False
227db.inv_inv_item.expiry_date.readable = db.inv_inv_item.expiry_date.writable = False
228# inv_item CRUD strings
229# INV_ITEM = T(" Donation Item")
230
231ADD_INV_ITEM = T("Add Donation Item")
232LIST_INV_ITEMS = T("List Donation Items")
233s3.crud_strings["inv_inv_item"] = Storage(
234 title_create = ADD_INV_ITEM,
235 title_display = T("Donation Item Details"),
236 title_list = LIST_INV_ITEMS,
237 title_update = T("Edit Donation Item"),
238 title_search = T("Search Donation Items"),
239 subtitle_create = ADD_INV_ITEM,
240 subtitle_list = T("Donation Items"),
241 label_list_button = LIST_INV_ITEMS,
242 label_create_button = ADD_INV_ITEM,
243 label_delete_button = T("Remove Donation Item"),
244 msg_record_created = T("Donation Item Added"),
245 msg_record_modified = T("Donation Item updated"),
246 msg_record_deleted = T("Donation Item removed"),
247 msg_list_empty = T("No Donation Items for this Corporation"))
248
249ADD_GOODS = T("In the event of a declared disaster we MAY be able to donate the following Goods:")
250LIST_GOODS = T("List Donation Goods")
251s3.crud_strings["don_good"] = Storage(
252 title_create = ADD_GOODS,
253 title_display = T("Donation Good Details"),
254 title_list = LIST_GOODS,
255 title_update = T("Edit Donation Goods"),
256 title_search = T("Search Donation Goods"),
257 subtitle_create = ADD_GOODS,
258 subtitle_list = T("Donate Goods"),
259 label_list_button = LIST_GOODS,
260 label_create_button = ADD_GOODS,
261 label_delete_button = T("Remove Donation Goods"),
262 msg_record_created = T("Donation Goods Added"),
263 msg_record_modified = T("Donation Goods updated"),
264 msg_record_deleted = T("Donation Goods removed"),
265 msg_list_empty = T("No Donation Goods for this Corporation"))
266
267ADD_SERVICE = T("In the event of a declared disaster we MAY be able to donate the following Services:")
268LIST_SERVICES = T("List Donation Services")
269s3.crud_strings["don_service"] = Storage(
270 title_create = ADD_SERVICE,
271 title_display = T("Donation Service Details"),
272 title_list = LIST_SERVICES,
273 title_update = T("Edit Donation Service"),
274 title_search = T("Search Donation Services"),
275 subtitle_create = ADD_SERVICE,
276 subtitle_list = T("Donate Services"),
277 label_list_button = LIST_SERVICES,
278 label_create_button = ADD_SERVICE,
279 label_delete_button = T("Remove Donation Service"),
280 msg_record_created = T("Donation Service Added"),
281 msg_record_modified = T("Donation Service updated"),
282 msg_record_deleted = T("Donation Service removed"),
283 msg_list_empty = T("No Donation Services for this Corporation"))
284
285ADD_FACILITY = T("In the event of a declared disaster we MAY be able to donate the following Facilities:")
286LIST_FACILITYS = T("List Donation Facilities")
287s3.crud_strings["don_facility"] = Storage(
288 title_create = ADD_FACILITY,
289 title_display = T("Donation Facility Details"),
290 title_list = LIST_FACILITYS,
291 title_update = T("Edit Donation Facility"),
292 title_search = T("Search Donation Facilities"),
293 subtitle_create = ADD_FACILITY,
294 subtitle_list = T("Donate Facilities"),
295 label_list_button = LIST_FACILITYS,
296 label_create_button = ADD_FACILITY,
297 label_delete_button = T("Remove Donation Facility"),
298 msg_record_created = T("Donation Facility Added"),
299 msg_record_modified = T("Donation Facility updated"),
300 msg_record_deleted = T("Donation Facility removed"),
301 msg_list_empty = T("No Donation Facilities for this Corporation"))
302
303# -----------------------------------------------------------------------------
304def don_item_filter(inv_item_add_filter_func):
305 """
306 Filter donated 'items' by category
307 Services = category 'SERVICES'
308 Facilities = category 'FACILITY'
309 Goods = everything else
310 """
311 itable = db.inv_inv_item
312 ctable = db.supply_item_category
313
314 query = (ctable.code == "FACILITY")
315 facility_cat_id = db(query).select(ctable.id,
316 limitby = (0, 1),
317 cache = gis.cache).first().id
318 query = (ctable.code == "SERVICES")
319 service_cat_id = db(query).select(ctable.id,
320 limitby = (0, 1),
321 cache = gis.cache).first().id
322
323 itable.organisation_id.widget = None # Implement SearchACWidget for Organisations
324 itable.organisation_id.requires = IS_NULL_OR(IS_ONE_OF(db, "org_organisation.id",
325 organisation_represent,
326 orderby="org_organisation.name",
327 sort=True,
328 filterby = "has_items",
329 filter_opts = [True])
330 )
331 itable.item_id.widget = None
332
333 if not s3_has_role(STAFF):
334 # Only Staff can add Categories
335 itable.item_category_id.comment = ""
336 # Only Staff can add Item Types
337 # @ToDo: Allow adding Items to 'OTHER' category
338 itable.item_id.comment = ""
339 # Only Staff can add Item Units
340 # @ToDo: Should Units for Services/Facilities be hardcoded/hidden?
341 # (always day/ea)
342 comment = DIV(_class="tooltip",
343 _title="%s|%s" % (T("Item Units"),
344 T("The way in which an item is normally distributed")))
345 script = SCRIPT(
346"""
347S3FilterFieldChange({
348 'FilterField': 'item_id',
349 'Field': 'item_pack_id',
350 'FieldResource':'item_pack',
351 'FieldPrefix': 'supply',
352 'msgNoRecords': S3.i18n.no_packs,
353 'fncPrep': fncPrepItem,
354 'fncRepresent': fncRepresentItem
355});""")
356 itable.item_pack_id.comment = TAG[""](comment, script)
357
358 item_type = request.vars.item
359 if item_type == "goods":
360 s3.crud_strings["inv_inv_item"] = s3.crud_strings["don_good"]
361
362 itable.item_category_id.requires = IS_NULL_OR(IS_ONE_OF(db,
363 "supply_item_category.id",
364 "%(name)s",
365 not_filterby = "id",
366 not_filter_opts = [facility_cat_id, service_cat_id],
367 sort=True))
368 response.s3.jquery_ready = [
369"""
370$(document).ready(function() {
371 S3FilterFieldChange({
372 'FilterField': 'item_category_id',
373 'Field': 'item_id',
374 'FieldResource':'item',
375 'FieldPrefix': 'supply',
376 //'url': S3.Ap.concat('/req/req_item_packs/'),
377 //'msgNoRecords': S3.i18n.no_packs,
378 //'fncPrep': fncPrepItem,
379 //'fncRepresent': fncRepresentItem
380 });
381});
382"""
383]
384 query = (itable.item_category_id != facility_cat_id) & \
385 (itable.item_category_id != service_cat_id)
386 inv_item_add_filter_func( query )
387 elif item_type in ["services", "facilities"]:
388 if item_type == "services":
389 s3.crud_strings["inv_inv_item"] = s3.crud_strings["don_service"]
390 item_category_filter = service_cat_id
391 itable.item_id.label = T("Service Type")
392 if s3_has_role(STAFF):
393 itable.item_id.comment = DIV(A(T("Add Service Type"),
394 _class="colorbox",
395 _href=URL(c="supply", f="item",
396 args="create",
397 vars=dict(format="popup")),
398 _target="top",
399 _title=T("Add Service Type")))
400 itable.quantity.label = T("Duration of Donated Services")
401 itable.item_pack_id.label = T("Unit of Time")
402 itable.pack_value.label = T("Estimated Value ($) per Unit of Time")
403
404 elif item_type == "facilities":
405 s3.crud_strings["inv_inv_item"] = s3.crud_strings["don_facility"]
406 item_category_filter = facility_cat_id
407 itable.item_id.label = T("Facility Type")
408 if s3_has_role(STAFF):
409 itable.item_id.comment = DIV(A(T("Add Facility Type"),
410 _class="colorbox",
411 _href=URL(c="supply", f="item",
412 args="create",
413 vars=dict(format="popup")),
414 _target="top",
415 _title=T("Add Facility Type")))
416 itable.comments.label = T("Comments (Sq. Ft, Bldg Type, Stories)")
417 itable.comments.comment = DIV(_class="tooltip",
418 _title="%s|%s|%s" % (T("Comments"),
419 T("Square Feet, Building Type, Stories."),
420 T("300 character limit.")))
421
422 itable.item_category_id.readable = itable.item_category_id.writable = False
423 itable.item_category_id.default = item_category_filter
424
425 itable.item_id.requires = IS_ONE_OF(db, "supply_item.id",
426 lambda id: response.s3.item_represent(id,
427 show_um = False,
428 show_link = False),
429 filterby = "item_category_id",
430 filter_opts = [item_category_filter],
431 sort=True)
432
433 query = (itable.item_category_id == item_category_filter)
434 inv_item_add_filter_func( query )
435# -----------------------------------------------------------------------------
436def organisation():
437 """
438 /org/organisation/ corporation
439 """
440
441 otable = db.org_organisation
442
443 otable.acronym.readable = False
444 otable.acronym.writable = False
445 otable.sector_id.readable = True
446 otable.sector_id.writable = True
447 org_has_items_field = otable.has_items
448 org_has_items_field.default = True
449 response.s3.filter = (org_has_items_field == True)
450
451 if not s3_has_role(STAFF):
452 # Tweak the breadcrumb
453 breadcrumbs[2] = (T("Organization Profile"), False,
454 URL(c=request.controller,
455 f=request.function,
456 args=request.args))
457
458 def corporation_rheader(r, tabs = []):
459 """ Corporation rheader """
460
461 if r.representation == "html":
462
463 if r.record is None:
464 # List or Create form: rheader makes no sense here
465 return None
466
467 tabs = [(T("Basic Details"), None),
468 (T("Contacts"), "human_resource"),
469 (T("Donate Goods"), "inv_item", dict(item="goods")),
470 (T("Donate Services "), "inv_item", dict(item="services")),
471 (T("Donate Facilities "), "inv_item", dict(item="facilities")),
472 ]
473 if "register" not in request.vars:
474 tabs.append( (T("Donations"), "commit") )
475 rheader_tabs = s3_rheader_tabs(r, tabs)
476
477 organisation = r.record
478 if organisation.sector_id:
479 _sectors = org_sector_represent(organisation.sector_id)
480 else:
481 _sectors = None
482
483 if deployment_settings.get_ui_cluster():
484 sector_label = T("Cluster(s)")
485 else:
486 sector_label = T("Sector(s)")
487
488 rheader = DIV(TABLE(
489 TR(
490 TH("%s: " % T("Corporation")),
491 organisation.name,
492 TH("%s: " % sector_label),
493 _sectors
494 )),
495 rheader_tabs
496 )
497 return rheader
498 return None
499
500 ADD_CORPORATION = T("Add Corporation / Organization")
501 LIST_CORPORATIONS = T("List Corporations & Organizations")
502 s3.crud_strings["org_organisation"] = Storage(
503 title_create = ADD_CORPORATION,
504 title_display = T("Corporation / Organization Details"),
505 title_list = LIST_CORPORATIONS,
506 title_update = T("Edit Corporation / Organization"),
507 title_search = T("Search Corporations & Organizations"),
508 subtitle_create = T("Add New Corporation / Organization"),
509 subtitle_list = T("Corporations & Organizations"),
510 label_list_button = LIST_CORPORATIONS,
511 label_create_button = ADD_CORPORATION,
512 label_delete_button = T("Delete Corporation / Organization"),
513 msg_record_created = T("Corporation / Organization added"),
514 msg_record_modified = T("Corporation / Organization updated"),
515 msg_record_deleted = T("Corporation / Organization deleted"),
516 msg_list_empty = T("No Corporations & Organizations currently registered"))
517
518 s3mgr.load("inv_inv_item")
519
520 def prep(r):
521 don_item_filter(lambda query:
522 r.resource.add_component_filter("inv_item", query))
523 if r.component and r.component.name == "human_resource":
524 hrtable = db.hrm_human_resource
525 hrtable.type.writable = hrtable.type.readable = False
526 hrtable.status.writable = hrtable.status.readable = False
527 hrtable.focal_point.writable = hrtable.focal_point.readable = False
528 response.s3.jquery_ready.append("$('#hrm_human_resource_person_id__row1').hide();")
529
530 s3.crud_strings["hrm_human_resource"] = Storage(
531 title_create = T("Add Contact"),
532 title_display = T("Contact Details"),
533 title_list = T("Contacts"),
534 title_update = T("Edit Contact"),
535 title_search = T("Search Contacts"),
536 subtitle_create = T("Additional Contacts (optional)"),
537 subtitle_list = T("Contacts"),
538 label_list_button = T("List Contacts"),
539 label_create_button = T("Add Contacts"),
540 label_delete_button = T("Delete Contact"),
541 msg_record_created = T("Contact added"),
542 msg_record_modified = T("Contact updated"),
543 msg_record_deleted = T("Contact deleted"),
544 msg_no_match = T("No Contacts Found"),
545 msg_list_empty = T("Currently there are no Contact registered"))
546
547 # Donation Organization Regisration Workflow
548 if "register" in request.vars:
549 # Only force the open on 1st run
550 response.s3.show_listadd = True
551 s3mgr.configure("hrm_human_resource",
552 create_next = URL(c="don", f="organisation",
553 args = [r.record.id, "inv_item"],
554 vars = dict(item="goods"))
555 )
556
557 # We don't want this enforced workflow as it doesn't allow adding multiple Goods, etc
558 #if r.component and r.component.name == "inv_item":
559 # next_item = dict(goods = "services",
560 # services = "facilities")
561 # if request.vars.item in next_item:
562 # response.s3.show_listadd = True
563 # url = URL(c="don", f="organisation",
564 # args = [r.record.id, "inv_item"],
565 # vars = dict(item=next_item[request.vars.item],
566 # register=1))
567 # else:
568 # url = URL(c="don", f="index", args = [], vars = {})
569 # s3mgr.configure("inv_inv_item",
570 # create_next = url
571 # )
572
573 # Add req to Corporations as Donations
574 s3mgr.configure("req_commit",
575 insertable = False,
576 editable = False,
577 deletable = False,
578 )
579
580 s3mgr.configure( "org_organisation",
581 list_fields = ["id",
582 "name",
583 #"type",
584 "sector_id",
585 #"country",
586 #"website"
587 ])
588
589 # req CRUD strings
590 REQ = T("Donation")
591 #ADD_REQ = T("Add Donation")
592 LIST_REQ = T("List Donations")
593 s3.crud_strings["req_req"] = Storage(
594 #title_create = ADD_REQ,
595 title_display = T("Donation Details"),
596 title_list = LIST_REQ,
597 #title_update = T("Edit Donation"),
598 title_search = T("Search Donations"),
599 #subtitle_create = ADD_REQ,
600 subtitle_list = T("Donations"),
601 label_list_button = LIST_REQ,
602 #label_create_button = ADD_REQ,
603 #label_delete_button = T("Remove Donations"),
604 #msg_record_created = T("Donation Added"),
605 #msg_record_modified = T("Donation updated"),
606 #msg_record_deleted = T("Donation removed"),
607 msg_list_empty = T("No Donations from this Corporation"))
608
609 if not s3_has_role(STAFF):
610 response.s3.donate_cash_link =True
611
612 return organisation_controller(organisation_rheader = corporation_rheader,
613 org_prep = prep)
614
615# -----------------------------------------------------------------------------
616def item():
617 """ REST Controller """
618
619 s3mgr.load("inv_inv_item")
620 def inv_item_add_filter_func(query):
621 response.s3.filter = query
622 don_item_filter(inv_item_add_filter_func)
623
624 # Tweak the breadcrumb
625 type = request.get_vars.item
626 if type == "goods":
627 label = T("Goods")
628 elif type == "services":
629 label = T("Services")
630 elif type == "facilities":
631 label = T("Facilities")
632 else:
633 # Error!
634 label = ""
635 breadcrumbs[2] = (label, False,
636 URL(c=request.controller,
637 f=request.function,
638 vars=request.vars))
639
640 return response.s3.inv_item_controller()
641
642# -----------------------------------------------------------------------------
643def collect():
644 """ REST Controller """
645 def prep(r):
646 s3mgr.configure("don_collect",
647 list_fields = ["id",
648 "start_datetime",
649 "end_datetime",
650 "site_id",
651 "organisation_id",
652 ])
653 return True
654
655 response.s3.prep = prep
656 output = s3_rest_controller(module, resourcename)
657 return output
658
659# -----------------------------------------------------------------------------
660def distribute():
661 """ REST Controller """
662 output = s3_rest_controller(module, resourcename)
663 return output
664
665# -----------------------------------------------------------------------------
666def profile():
667 """ Organisation Profile """
668 return organisation_profile()
669
670# -----------------------------------------------------------------------------
671################################################################################
672### Hook to call get the donation certificate
673################################################################################
674#def certprint():
675# """ Print Cert TEST """
676# s3mgr.load("don_distribute")
677# r = s3base.S3Request(s3mgr,
678# prefix="don",
679# name="distribute",
680# extension="pdf")
681# s3mgr.configure("don_distribute",
682# callback = response.s3.donationCertificate,
683# formname = "Donation Certificate",
684# header = response.s3.donCertBorder,
685# footer = lambda x, y: None,
686# )
687# return r()
688################################################################################
689###
690################################################################################
691
692
693# END =========================================================================
0\ No newline at end of file694\ No newline at end of file
1695
=== modified file 'controllers/event.py'
--- controllers/event.py 2011-08-06 18:24:53 +0000
+++ controllers/event.py 2011-09-14 11:00:06 +0000
@@ -32,6 +32,16 @@
32 redirect(URL(f="event", args="create"))32 redirect(URL(f="event", args="create"))
3333
34# =============================================================================34# =============================================================================
35# Incidents
36# =============================================================================
37def incident():
38 """ RESTful CRUD controller """
39
40 tablename = "event_incident"
41 s3mgr.load(tablename)
42 return s3_rest_controller(module, resourcename)
43
44# =============================================================================
35# Events45# Events
36# =============================================================================46# =============================================================================
37def event():47def event():
@@ -184,7 +194,7 @@
184 tabs.append((T("Map Configuration"), "config"))194 tabs.append((T("Map Configuration"), "config"))
185195
186 rheader = lambda r, tabs=tabs: event_rheader(r, tabs)196 rheader = lambda r, tabs=tabs: event_rheader(r, tabs)
187 output = s3_rest_controller("event", resourcename,197 output = s3_rest_controller(module, resourcename,
188 rheader=rheader)198 rheader=rheader)
189 return output199 return output
190200
191201
=== modified file 'controllers/gis.py'
--- controllers/gis.py 2011-08-11 23:34:08 +0000
+++ controllers/gis.py 2011-09-14 11:00:06 +0000
@@ -63,7 +63,7 @@
63 catalogue_toolbar = False63 catalogue_toolbar = False
6464
65 # @ToDo: Make these configurable65 # @ToDo: Make these configurable
66 search = True66 search = False
67 googleEarth = True67 googleEarth = True
68 googleStreetview = True68 googleStreetview = True
69 catalogue_layers = True69 catalogue_layers = True
@@ -92,7 +92,8 @@
92 search=search,92 search=search,
93 catalogue_layers=catalogue_layers,93 catalogue_layers=catalogue_layers,
94 mouse_position = mouse_position,94 mouse_position = mouse_position,
95 print_tool = print_tool95 print_tool = print_tool,
96 collapsed = True
96 )97 )
9798
98 return map99 return map
99100
=== modified file 'controllers/hrm.py'
--- controllers/hrm.py 2011-08-28 19:50:18 +0000
+++ controllers/hrm.py 2011-09-14 11:00:06 +0000
@@ -915,4 +915,197 @@
915 redirect_vars = {fieldname: id},915 redirect_vars = {fieldname: id},
916 title_name = title)916 title_name = title)
917917
918# =============================================================================
919# Adding a human resource inline
920# =============================================================================
921
922def create_inline():
923 add_object_inline = local_import("s3.add_object_inline").add_object_inline
924 # Set up fields
925 pr_person = db.pr_person
926 hrm_human_resource = db.hrm_human_resource
927
928 field_dict = {
929 "first_name": pr_person.first_name,
930 "middle_name": pr_person.middle_name,
931 "last_name": pr_person.last_name,
932
933 "job_title": hrm_human_resource.job_title,
934 "organisation_id": hrm_human_resource.organisation_id,
935 }
936
937 emailRequired = (
938 request.controller == "hrm" and
939 deployment_settings.get_hrm_email_required()
940 )
941
942 if emailRequired:
943 email_validator = IS_EMAIL()
944 else:
945 email_validator = IS_NULL_OR(IS_EMAIL())
946
947 email_field_name = "email_address"
948 mobile_phone_field_name = "mobile_phone"
949 field_dict.update({
950 email_field_name: Field(
951 email_field_name,
952 notnull=emailRequired,
953 requires=email_validator,
954 label=T("Email Address")
955 ),
956 mobile_phone_field_name: Field(
957 mobile_phone_field_name,
958 label=T("Mobile Phone Number")
959 )
960 })
961
962
963 def validate_and_set_up_result(
964 update_result,
965 errors,
966 var,
967 check_request_var_matches_value_if_given,
968 existing
969 ):
970 pr_contact = db.pr_contact
971 pr_person = db.pr_person
972
973 existing_email_contact = None
974 existing_mobile_phone_contact = None
975 existing_person = None
976 existing_human_resource = None
977
978 def check_attributes_match(existing_object, attribute_names):
979 for attribute_name in attribute_names:
980 check_request_var_matches_value_if_given(
981 attribute_name,
982 field_dict[attribute_name].label,
983 getattr(existing_object, attribute_name)
984 )
985
986 # look for a person with the same email address
987 email_address = var(email_field_name)
988 if email_address:
989 existing_email_contact = existing(
990 (pr_contact.contact_method == "EMAIL") &
991 (pr_contact.value == email_address)
992 )
993 if existing_email_contact is not None:
994 # check every other contact detail matches, otherwise complain
995
996 # mobile phone
997 existing_mobile_phone_contact = existing(
998 (pr_contact.contact_method == "MOBILE PHONE") &
999 (pr_contact.pe_id == existing_email_contact.pe_id)
1000 )
1001 if existing_mobile_phone_contact is not None:
1002 check_request_var_matches_value_if_given(
1003 mobile_phone_field_name,
1004 field_dict[mobile_phone_field_name].label,
1005 existing_mobile_phone_contact.value
1006 )
1007
1008 # person details
1009 existing_person = existing(
1010 pr_person.pe_id == existing_email_contact.pe_id
1011 )
1012 if existing_person is not None:
1013 check_attributes_match(
1014 existing_person,
1015 (
1016 "first_name",
1017 "middle_name",
1018 "last_name"
1019 )
1020 )
1021
1022 # human resource
1023 existing_human_resource = existing(
1024 hrm_human_resource.person_id == existing_person.id
1025 )
1026 if existing_human_resource is not None:
1027 check_attributes_match(
1028 existing_human_resource,
1029 (
1030 "job_title",
1031 "organisation_id",
1032 )
1033 )
1034
1035 # an email address is assumed to uniquely identify a person
1036 # however, there is no guarantee that a person has an email address
1037 if existing_email_contact is None:
1038 # there is no pr_pentity
1039 pe_id = db.pr_pentity.insert().pe_id
1040
1041 db.pr_contact.insert(
1042 pe_id = pe_id,
1043 contact_method = "EMAIL",
1044 value = email_address,
1045 )
1046 else:
1047 contact_id = existing_email_contact.id
1048 pe_id = existing_email_contact.pe_id
1049
1050 assert pe_id is not None
1051
1052 if existing_mobile_phone_contact is None:
1053 if var(mobile_phone_field_name):
1054 db.pr_contact.insert(
1055 pe_id = pe_id,
1056 contact_method = "MOBILE PHONE",
1057 value = var(mobile_phone_field_name)
1058 )
1059
1060 if existing_person is None:
1061 person_id = db.pr_person.insert(
1062 pe_id = pe_id,
1063 first_name = var("first_name"),
1064 middle_name = var("middle_name"),
1065 last_name = var("last_name")
1066 )
1067 else:
1068 person_id = existing_person.id
1069 else:
1070 errors[email_field_name] = T("Required")
1071
1072 if errors:
1073 response.flash = T("form invalid")
1074 else:
1075 response.flash = T("form accepted")
1076 if existing_human_resource:
1077 result_human_resource = existing_human_resource
1078 else:
1079 result_human_resource = db.hrm_human_resource.insert(
1080 person_id = person_id,
1081 job_title = var("job_title"),
1082 organisation_id = var("organisation_id")
1083 )
1084 db.commit()
1085 update_result(
1086 created_object_id = result_human_resource.id,
1087 created_object_representation = hrm_human_resource_represent(
1088 result_human_resource
1089 )
1090 )
1091
1092 return add_object_inline(
1093 db = db,
1094 formstyle = s3_formstyle,
1095 fields = field_dict.values(),
1096 table = db.hrm_human_resource,
1097 table_name = "hrm_human_resource",
1098 template = "inner_form.html",
1099 validate_and_set_up_result = validate_and_set_up_result,
1100 s3_mark_required = s3_mark_required,
1101 TAG = TAG,
1102 SPAN = SPAN,
1103 response = response,
1104 SQLFORM = SQLFORM,
1105 T = T,
1106 INPUT = INPUT,
1107 XML = XML,
1108 request = request,
1109 )
1110
918# END =========================================================================1111# END =========================================================================
9191112
=== added file 'controllers/master.py'
--- controllers/master.py 1970-01-01 00:00:00 +0000
+++ controllers/master.py 2011-09-14 11:00:06 +0000
@@ -0,0 +1,22 @@
1# -*- coding: utf-8 -*-
2
3"""
4 Master Data - Controllers
5
6 @author: Fran
7"""
8
9module = request.controller
10
11if module not in deployment_settings.modules:
12 raise HTTP(404, body="Module disabled: %s" % module)
13
14# Options Menu (available in all Functions' Views)
15shn_menu(module)
16
17def index():
18 "Module's Home Page"
19
20 module_name = deployment_settings.modules[module].name_nice
21 response.title = module_name
22 return dict(module_name=module_name)
023
=== modified file 'controllers/org.py'
--- controllers/org.py 2011-08-06 18:24:53 +0000
+++ controllers/org.py 2011-09-14 11:00:06 +0000
@@ -159,4 +159,87 @@
159159
160 return output160 return output
161161
162
163def add_site_inline():
164 formstyle = s3_formstyle
165
166 org_office = db.org_office
167 field_names = [
168 "type",
169 "name",
170 "address",
171 "address_2",
172 "L3",
173 "L1",
174 "postcode",
175 "phone1",
176 "email",
177 ]
178 field_dict = {}
179 for field_name in field_names:
180 field_dict[field_name] = getattr(org_office, field_name)
181
182 def validate_and_set_up_result(
183 update_result, errors, var,
184 check_request_var_matches_value_if_given,
185 existing
186 ):
187 # look for an office with the same name
188 name = var("name")
189 if name:
190 existing_office = existing(
191 org_office.name == name
192 )
193 if existing_office is not None:
194 for field_name, field in field_dict.iteritems():
195 print field_name
196 check_request_var_matches_value_if_given(
197 field_name,
198 field.label,
199 getattr(existing_office, field_name)
200 )
201 else:
202 existing_office = None
203
204 if errors:
205 response.flash = T("form is invalid")
206 else:
207 response.flash = T("form accepted")
208 if existing_office:
209 result_object = existing_office
210 else:
211 result_object = db.org_office.insert(
212 **dict(
213 (field_name, var(field_name)) for field_name in field_names
214 )
215 )
216 db.commit()
217 update_result(
218 created_object_id = existing_office.id,
219 created_object_representation = existing_office.name
220 )
221
222
223 add_object_inline = local_import("s3.add_object_inline").add_object_inline
224
225 return add_object_inline(
226 db = db,
227 formstyle = s3_formstyle,
228 fields = field_dict.values(),
229 table = db.org_office,
230 table_name = "org_office",
231 template = "inner_form.html",
232 validate_and_set_up_result = validate_and_set_up_result,
233 s3_mark_required = s3_mark_required,
234 TAG = TAG,
235 SPAN = SPAN,
236 response = response,
237 SQLFORM = SQLFORM,
238 T = T,
239 INPUT = INPUT,
240 XML = XML,
241 request = request,
242 )
243
244
162# END =========================================================================245# END =========================================================================
163246
=== modified file 'controllers/project.py'
--- controllers/project.py 2011-08-06 18:24:53 +0000
+++ controllers/project.py 2011-09-14 11:00:06 +0000
@@ -17,7 +17,6 @@
1717
18# =============================================================================18# =============================================================================
19def index():19def index():
20
21 """ Module's Home Page """20 """ Module's Home Page """
2221
23 module_name = deployment_settings.modules[module].name_nice22 module_name = deployment_settings.modules[module].name_nice
@@ -31,21 +30,18 @@
3130
32# =============================================================================31# =============================================================================
33def need():32def need():
34
35 """ RESTful CRUD controller """33 """ RESTful CRUD controller """
3634
37 return s3_rest_controller(module, resourcename)35 return s3_rest_controller(module, resourcename)
3836
39# -----------------------------------------------------------------------------37# -----------------------------------------------------------------------------
40def need_type():38def need_type():
41
42 """ RESTful CRUD controller """39 """ RESTful CRUD controller """
4340
44 return s3_rest_controller(module, resourcename)41 return s3_rest_controller(module, resourcename)
4542
46# =============================================================================43# =============================================================================
47def project():44def project():
48
49 """ RESTful CRUD controller """45 """ RESTful CRUD controller """
5046
51 tablename = "%s_%s" % (module, resourcename)47 tablename = "%s_%s" % (module, resourcename)
@@ -71,65 +67,13 @@
7167
72# =============================================================================68# =============================================================================
73def activity():69def activity():
74
75 """ RESTful CRUD controller """70 """ RESTful CRUD controller """
7671
77 tablename = "%s_%s" % (module, resourcename)72 # Defined in the Model for use from Multiple Controllers for unified menus
78 table = db[tablename]73 return response.s3.project_activity_controller()
79
80 tabs = [
81 (T("Details"), None),
82 (T("Requests"), "req"),
83 (T("Documents"), "document"),
84 (T("Photos"), "image"),
85 #(T("Shipments To"), "rms_req"),
86 ]
87 rheader = lambda r: activity_rheader(r, tabs)
88
89 if "create" in request.args:
90 # Default values (from gap_report) set for fields
91 default_fieldnames = ["location_id", "need_type_id"]
92 for fieldname in default_fieldnames:
93 if fieldname in request.vars:
94 table[fieldname].default = request.vars[fieldname]
95 table[fieldname].writable = False
96 table[fieldname].comment = None
97
98 return s3_rest_controller(module, resourcename,
99 rheader = rheader)
100
101# -----------------------------------------------------------------------------
102def activity_rheader(r, tabs=[]):
103 """ Resource Header for Activities"""
104
105 if r.representation == "html":
106 record = r.record
107 if record:
108 rheader_tabs = s3_rheader_tabs(r, tabs)
109 rheader = DIV( TABLE(
110 TR( TH( "%s: " % T("Short Description")),
111 record.name,
112 ),
113 TR( TH( "%s: " % T("Location")),
114 gis_location_represent(record.location_id),
115 TH( "%s: " % T("Duration")),
116 "%s to %s" % (record.start_date,
117 record.end_date),
118 ),
119 TR( TH( "%s: " % T("Organization")),
120 organisation_represent(record.organisation_id),
121 TH( "%s: " % SECTOR),
122 shn_org_sector_represent(record.sector_id),
123 ),
124 ),
125 rheader_tabs
126 )
127 return rheader
128 return None
12974
130# =============================================================================75# =============================================================================
131def task():76def task():
132
133 """ RESTful CRUD controller """77 """ RESTful CRUD controller """
13478
135 # Pre-process79 # Pre-process
@@ -211,7 +155,6 @@
211155
212# =============================================================================156# =============================================================================
213def gap_report():157def gap_report():
214
215 """ Provide a Report on Gaps between Activities & Needs Assessments """158 """ Provide a Report on Gaps between Activities & Needs Assessments """
216159
217 # Get all assess_summary160 # Get all assess_summary
218161
=== modified file 'controllers/req.py'
--- controllers/req.py 2011-08-07 08:48:01 +0000
+++ controllers/req.py 2011-09-14 11:00:06 +0000
@@ -37,9 +37,13 @@
37 - custom View37 - custom View
38 """38 """
3939
40 module_name = deployment_settings.modules[module].name_nice40 if s3_has_role("ADMIN"):
41 response.title = module_name41 module_name = deployment_settings.modules[module].name_nice
42 return dict(module_name=module_name)42 response.title = module_name
43 return dict(module_name=module_name)
44
45 else:
46 redirect(URL(f="req_skill"))
4347
44# -----------------------------------------------------------------------------48# -----------------------------------------------------------------------------
45def is_affiliated():49def is_affiliated():
@@ -65,7 +69,7 @@
65def create():69def create():
66 """ Redirect to req/create """70 """ Redirect to req/create """
67 redirect(URL(f="req", args="create"))71 redirect(URL(f="req", args="create"))
6872
69# -----------------------------------------------------------------------------73# -----------------------------------------------------------------------------
70def req():74def req():
71 """ REST Controller """75 """ REST Controller """
@@ -135,10 +139,10 @@
135139
136 output["title"] = T("Request Item from Available Inventory")140 output["title"] = T("Request Item from Available Inventory")
137 output["req_btn"] = A( T("Return to Request"),141 output["req_btn"] = A( T("Return to Request"),
138 _href = URL( c = "req",142 _href = URL(c = "req",
139 f = "req",143 f = "req",
140 args = [req_item.req_id, "req_item"]144 args = [req_item.req_id, "req_item"]
141 ),145 ),
142 _class = "action-btn"146 _class = "action-btn"
143 )147 )
144148
@@ -301,7 +305,7 @@
301 if r.representation == "html":305 if r.representation == "html":
302 record = r.record306 record = r.record
303 if record and r.name == "commit":307 if record and r.name == "commit":
304 tabs = [(T("Edit Details"), None)]308 tabs = [(T("Details"), None)]
305 309
306 if record.type == 1:310 if record.type == 1:
307 tabs.append((T("Items"), "commit_item"))311 tabs.append((T("Items"), "commit_item"))
308312
=== modified file 'controllers/supply.py'
--- controllers/supply.py 2011-09-09 11:19:19 +0000
+++ controllers/supply.py 2011-09-14 11:00:06 +0000
@@ -30,6 +30,7 @@
30 module_name = deployment_settings.modules[module].name_nice30 module_name = deployment_settings.modules[module].name_nice
31 response.title = module_name31 response.title = module_name
32 return dict(module_name=module_name)32 return dict(module_name=module_name)
33
33# -----------------------------------------------------------------------------34# -----------------------------------------------------------------------------
34def catalog():35def catalog():
35 """ RESTful CRUD controller """36 """ RESTful CRUD controller """
3637
=== modified file 'controllers/vol.py' (properties changed: +x to -x)
--- controllers/vol.py 2011-08-06 18:24:53 +0000
+++ controllers/vol.py 2011-09-14 11:00:06 +0000
@@ -2,8 +2,6 @@
22
3"""3"""
4 Volunteer Module4 Volunteer Module
5 - A 'My Sahana' view of an individual Volunteer's Data, Tasks, etc
6 (Ability for Anonymous users to sign-up as a Volunteer?)
7"""5"""
86
9module = request.controller7module = request.controller
@@ -19,8 +17,11 @@
19def index():17def index():
20 """ Module's Home Page """18 """ Module's Home Page """
2119
22 # Default to the Personal profile20 if s3_has_role(STAFF):
23 return person()21 response.view = "vol/index.html"
22 return dict()
23 else:
24 redirect(URL(c="vol", f="req_skill"))
2425
25# -----------------------------------------------------------------------------26# -----------------------------------------------------------------------------
26# People27# People
@@ -28,150 +29,41 @@
28def person():29def person():
29 """30 """
30 Person Controller31 Person Controller
31 - allows a person to view/update the details held about them32 - List of Volunteers
32 """33 """
3334
35 module = "pr"
34 resourcename = "person"36 resourcename = "person"
37 tablename = "pr_person"
38 table = db[tablename]
3539
40 response.s3.filter = (table.volunteer == True)
41
36 # Load Model42 # Load Model
37 s3mgr.load("pr_address")43 s3mgr.load("pr_address")
38 s3mgr.load("hrm_skill")44 s3mgr.load("vol_skill")
3945
40 s3.crud.submit_button = T("Next")46 s3mgr.model.add_component("vol_skill",
4147 pr_person="person_id")
42 s3mgr.model.add_component("hrm_certification",48
43 pr_person="person_id")49 s3mgr.configure("vol_skill",
4450 deletable = False)
45 s3mgr.model.add_component("hrm_competency",51
46 pr_person="person_id")52 tabs = [ (T("Basic Details"), None),
4753 (T("Skills"), "skill"),
48 s3mgr.model.add_component("hrm_credential",54 (T("Organizational Affiliations"), "organisation"),
49 pr_person="person_id")55 (T("Address"), "address"),
5056 (T("Contact Details"), "contact"),
51 s3mgr.model.add_component("hrm_training",57 ]
52 pr_person="person_id")58
5359 rheader = lambda r: vol_pr_rheader(r, tabs=tabs)
54 s3mgr.model.add_component("hrm_experience",60
55 pr_person="person_id")61 output = s3_rest_controller(module, resourcename, rheader=rheader)
5662
57 # Q: Are volunteers assigned to a specific organisation outside of a specific Event?
58 # Yes, some are: CERT
59 s3mgr.model.add_component("hrm_human_resource",
60 pr_person="person_id")
61
62 # Configure human resource table
63 # - for Positions
64 tablename = "hrm_human_resource"
65 table = db[tablename]
66 table.type.readable = True
67 table.type.writable = False
68 table.location_id.writable = False # Populated from pr_address
69 table.location_id.readable = False
70 s3mgr.configure(tablename,
71 list_fields=["id",
72 "organisation_id",
73 "job_title",
74 "status"])
75
76 # Configure person table
77 # - hide fields
78 tablename = "pr_person"
79 table = db[tablename]
80 table.pe_label.readable = False
81 table.pe_label.writable = False
82 table.missing.readable = False
83 table.missing.writable = False
84 table.age_group.readable = False
85 table.age_group.writable = False
86 s3mgr.configure(tablename,
87 # Wizard: Move to next Tab after Save
88 update_next = URL(args=["[id]", "address"]),
89 deletable=False)
90
91 # Configure for personal mode
92 s3.crud_strings[tablename].update(
93 title_display = T("Personal Profile"),
94 title_update = T("Personal Profile"))
95 # People can view their own HR data, but not edit it
96 db.hrm_human_resource.organisation_id.readable = True
97 s3mgr.configure("hrm_human_resource",
98 insertable = False,
99 editable = False,
100 deletable = False)
101 s3mgr.configure("hrm_certification",
102 insertable = True,
103 editable = True,
104 deletable = True)
105 s3mgr.configure("hrm_credential",
106 insertable = False,
107 editable = False,
108 deletable = False)
109 s3mgr.configure("hrm_competency",
110 insertable = True, # Can add unconfirmed
111 editable = False,
112 deletable = False)
113 s3mgr.configure("hrm_training", # Can add but not provide grade
114 insertable = True,
115 editable = False,
116 deletable = False)
117 s3mgr.configure("hrm_experience",
118 insertable = False,
119 editable = False,
120 deletable = False)
121 s3mgr.configure("pr_group_membership",
122 insertable = False,
123 editable = False,
124 deletable = False)
125 tabs = [(T("Person Details"), None),
126 (T("Addresses"), "address"),
127 (T("Certificates"), "certification"),
128 (T("Contact Information"), "contact"),
129 #(T("Skills"), "competency"),
130 #(T("Credentials"), "credential"),
131 #(T("Trainings"), "training"),
132 #(T("Mission Record"), "experience"),
133 #(T("Positions"), "human_resource"),
134 #(T("Teams"), "group_membership")
135 ]
136
137 # Prepare CRUD
138 def prep(r):
139 if r.interactive:
140 resource = r.resource
141 r.resource.build_query(id=s3_logged_in_person())
142 if resource.count() == 1:
143 resource.load()
144 r.record = resource.records().first()
145 if r.record:
146 r.id = r.record.id
147 if r.component:
148 if r.component.name == "address":
149 s3mgr.configure("pr_address",
150 create_next = URL(args=[str(r.id), "certification"]))
151 elif r.component.name == "certification":
152 r.component.table.certificate_id.comment = None
153 r.component.table.organisation_id.readable = False
154 s3mgr.configure("hrm_certification",
155 create_next = URL(args=[str(r.id), "contact"]))
156 #elif r.component.name == "contact":
157 # s3mgr.configure("pr_contact",
158 # create_next = URL(# args=[str(r.id), ""]))
159
160 return True
161 else:
162 # This controller is only for interactive use
163 redirect(URL(f='default', c="index"))
164 response.s3.prep = prep
165
166 rheader = lambda r, tabs=tabs: vol_rheader(r, tabs)
167
168 output = s3_rest_controller("pr", resourcename,
169 native=False,
170 rheader=rheader)
171 return output63 return output
17264
173# -----------------------------------------------------------------------------65# -----------------------------------------------------------------------------
174def vol_rheader(r, tabs=[]):66def vol_pr_rheader(r, tabs=[]):
175 """ Resource headers for component views """67 """ Resource headers for component views """
17668
177 rheader = None69 rheader = None
@@ -190,46 +82,2328 @@
190 TH(""),82 TH(""),
191 ""),83 ""),
19284
193 TR(TH("%s: " % T("Date of Birth")),
194 "%s" % (s3_date_represent(person.date_of_birth) or T("unknown")),
195 TH(""),
196 ""),
197
198 ), rheader_tabs)85 ), rheader_tabs)
19986
200 return rheader87 return rheader
20188
202# -----------------------------------------------------------------------------89# =============================================================================
203# Tasks90def organisation():
204# -----------------------------------------------------------------------------91 """
205def task():92 Org Controller for OrgAdmins
20693 """
207 """ Allow a volunteer to View the details of their own tasks """94
20895 table = db.org_organisation
209 tablename = "project_task"96
210 s3mgr.load(tablename)97 table.acronym.readable = False
211 table = db[tablename]98 table.acronym.writable = False
21299 org_has_vols_field = table.has_vols
213 my_person_id = s3_logged_in_person()100 org_has_vols_field.default = True
214101 response.s3.filter = (org_has_vols_field == True)
215 if not my_person_id:102
216 session.error = T("No person record found for current user.")103 if "register" in request.vars:
217 redirect(URL(f="index"))104 response.s3.show_listadd = True
218105
219 table.person_id.default = my_person_id106 return organisation_controller(organisation_rheader = organisation_rheader)
220107
221 response.s3.filter = (table.person_id == my_person_id)108# -----------------------------------------------------------------------------
222109def organisation_rheader(r, tabs = []):
223 s3.crud_strings[tablename].title_list = T("My Tasks")110 """ Organisation rheader """
224 s3.crud_strings[tablename].subtitle_list = T("Task List")111
225 s3.crud_strings[tablename].msg_list_empty = T("No tasks currently assigned")112 if r.representation == "html":
226113
227 s3mgr.configure(tablename,114 if r.record is None:
228 insertable = False,115 # List or Create form: rheader makes no sense here
229 editable = False,116 return None
230 deletable = False)117
231118 tabs = [(T("Basic Details"), None),
232 return s3_rest_controller("project", "task")119 (T("Contacts"), "human_resource"),
120 (T("Activities"), "activity")
121 ]
122 rheader_tabs = s3_rheader_tabs(r, tabs)
123
124 organisation = r.record
125
126 rheader = DIV(TABLE(
127 TR(
128 TH("%s: " % T("Organization")),
129 organisation.name,
130 ),
131 ),
132 rheader_tabs
133 )
134 return rheader
135 return None
136
137# =============================================================================
138def vol_sidebar():
139 """ Sidebar for people who aren't logged-in """
140
141 request.args = ["login"]
142 loginform = auth()
143 loginform.attributes["_id"] = "loginform"
144 request.args = []
145
146 if not response.menu_left:
147 response.menu_left = DIV()
148
149 if not auth.is_logged_in():
150 response.menu_left.append(
151 DIV(
152 P("%s:" % T("Already registered? Sign in here")),
153 #loginform,
154 FORM(
155 LABEL(T("Email"), _for="email"),
156 INPUT(_type="text", _name="email", _title=T("Email")),
157 LABEL(T("Password"), _for="password"),
158 INPUT(_type="password", _name="password", _title=T("Password")),
159 INPUT(_type="submit", _value=T("Sign In"), _title=T("Sign In"),
160 _class="sign-in-button"),
161 loginform.custom.end,
162 _action="", _enctype="multipart/form-data", _method="post"),
163 _id="sign-in-menu",
164 _class="sub-menu"))
165 response.menu_left.append(
166 DIV(
167 P("%s:" % T("Other Volunteering Opportunities"), _class="osm-title"),
168 UL(
169 LI(
170 A(
171 DIV("American Red Cross", _class="link-name"),
172 DIV(
173 H3("redcrossla.org"),
174 IMG(_src="/%s/static/img/la/logo_arc.png" % request.application, _width="87", _height="29", _alt="American Red Cross Logo"),
175 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."),
176 _class="other-popup"),
177 _href="http://redcrossla.org", _target="_blank",
178 _title="American Red Cross - Link will open to new Window")
179 ),
180 LI(
181 A(
182 DIV("CERT", _class="link-name"),
183 DIV(
184 H3("www.cert-la.com"),
185 IMG(_src="/%s/static/img/la/certlogo.png" % request.application, _width="51", _height="30", _alt="CERT Logo"),
186 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. "),
187 _class="other-popup"),
188 _href="http://www.cert-la.com", _target="_blank",
189 _title="CERT - %s" % T("Link will open to new Window"))
190 ),
191 LI(
192 A(
193 DIV("LA County Disaster Healthcare Volunteers", _class="link-name"),
194 DIV(
195 H3("www.lacountydhv.org"),
196 IMG(_src="/%s/static/img/la/logo_dhv.png" % request.application, _width="88", _height="30", _alt="Disaster Healthcare Volunteers"),
197 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."),
198 _class="other-popup"),
199 _href="http://www.lacountydhv.org", _target="_blank",
200 _title="Disaster Healthcare Volunteers - %s" % T("Link will open to new Window"))
201 ),
202 LI(
203 A(
204 DIV("LA Works", _class="link-name"),
205 DIV(
206 H3("www.laworks.com"),
207 IMG(_src="/%s/static/img/la/laworks.png" % request.application, _width="47", _height="40", _alt="LA Works Logo"),
208 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."),
209 _class="other-popup"),
210 _href="http://www.laworks.com", _target="_blank",
211 _title="LA Works - %s" % T("Link will open to new Window"))
212 ),
213 LI(
214 A(
215 DIV("Public Health Emergency Volunteer Network", _class="link-name"),
216 DIV(
217 H3("publichealth.lacounty.gov"),
218 IMG(_src="/%s/static/img/la/logo_phev.png" % request.application, _width="121", _height="30", _alt="Public Health Emergency Volunteer (PHEV) Network"),
219 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."),
220 _class="other-popup"),
221 _href="http://publichealth.lacounty.gov/eprp/volview.htm", _target="_blank",
222 _title="Public Health Emergency Volunteer (PHEV) Network - %s" % T("Link will open to new Window"))
223 ),
224 LI(
225 A(
226 DIV("VCLA", _class="link-name"),
227 DIV(
228 H3("www.vcla.net"),
229 IMG(_src="/%s/static/img/la/vcla.png" % request.application, _width="40", _height="40", _alt="VCLA Logo"),
230 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."),
231 _class="other-popup"),
232 _href="http://www.vcla.net", _target="_blank",
233 _title="VCLA - %s" % T("Link will open to new Window"))
234 )
235 ),
236 _class="other-sub-menu")
237 )
238 if session.s3.debug:
239 response.s3.scripts.append( "%s/jquery.hoverIntent.js" % s3_script_dir )
240 else:
241 response.s3.scripts.append( "%s/jquery.hoverIntent.minified.js" % s3_script_dir )
242
243 response.s3.jquery_ready.append("""
244$('.other-popup').css('display', 'none');
245$('.tooltip .message').css('display', 'none');
246$('.other-sub-menu li a').hoverIntent(menuFadeIn, menuFadeOut);
247$('.tooltip a').hoverIntent(tooltipFadeIn, tooltipFadeOut);""")
248 response.s3.js_global.append("""
249// For Popup Panels for the Other Menu
250function menuFadeIn(){$(this).children('.other-popup').fadeIn();}
251function menuFadeOut(){$(this).children('.other-popup').fadeOut();}
252// For Tooltop Panels for the Other Menu
253function tooltipFadeIn(){$(this).next('.message').fadeIn();}
254function tooltipFadeOut(){$(this).next('.message').fadeOut();}""")
255
256# -----------------------------------------------------------------------------
257def register():
258 """ Custom Registration Form """
259
260 auth.messages.submit_button = T("I accept. Create my account.")
261 request.args = ["register"]
262 db[auth.settings.table_user].language.default = T.accepted_language
263 # @ToDo: Make use to the SQLFORM.createform function instead of inserting fields?
264 form = auth()
265 form.attributes["_id"] = "regform"
266 # Custom class for Submit Button
267 form[0][-1][0][0]["_class"] = "accept-button"
268
269 # Cancel button
270 form[0][-1][0].append(BR())
271 #form[0][-1][0].append(INPUT(_type="reset", _value=T("Cancel")))
272 form[0][-1][0].append(INPUT(_type="button",
273 _value=T("Cancel"),
274 _class="wide-grey-button",
275 _onClick="javascript: history.go(-1)"))
276
277 formstyle = s3.crud.formstyle
278
279 # Medical note
280 row = formstyle(id = "",
281 label = "",
282 widget = DIV("%s:" % T("If you are a Medical or Health Professional and wish to volunteer in a professional capacity, please register at"),
283 BR(),
284 A("LA County Disaster Healthcare Volunteers",
285 _href="http://www.lacountydhv.org",
286 _target="_blank"),
287 BR(),
288 BR(),
289 "%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"),
290 BR(),
291 A("LA County Department of Public Health Emergency Preparedness and Response Program",
292 _href="http://publichealth.lacounty.gov/eprp/volview.htm",
293 _target="_blank"),
294 _style="width: 400px;",
295 ),
296 comment = "")
297 form[0].insert(0, row)
298
299 # Middle Name
300 row = formstyle(id = "middle_name",
301 label = LABEL("%s:" % T("Middle Name"),
302 _for="middle_name"),
303 widget = INPUT(_name="middle_name",
304 _id="middle_name",
305 _class="string"),
306 comment = "")
307 form[0][2].append(row)
308
309 # Phone
310 phone_opts = {
311 "SMS": current.deployment_settings.get_ui_label_mobile_phone(),
312 "HOME_PHONE": T("Home phone"),
313 "WORK_PHONE": T("Work phone")
314 }
315 if form.errors.phone:
316 phone_error = DIV(form.errors.phone,
317 _id="phone__error",
318 _class="error",
319 _style="display: block;")
320 else:
321 phone_error = ""
322 phone_widget = (INPUT(_name="phone",
323 _id="",
324 _class="string"),
325 SELECT(OPTION(current.deployment_settings.get_ui_label_mobile_phone(), _value="SMS"),
326 OPTION(T("Home phone"), _value="HOME_PHONE"),
327 OPTION(T("Work phone"), _value="WORK_PHONE"),
328 requires=IS_IN_SET(phone_opts),
329 value="SMS",
330 _name="phone_type",
331 _id="phone_type"),
332 phone_error
333 )
334 phone_help = DIV(_class="tooltip",
335 _title="%s|%s" % (T("Phone"),
336 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.")))
337 row = formstyle(id = "phone",
338 label = LABEL("%s:" % T("Phone"),
339 SPAN(" *", _class="req"),
340 _for="phone"),
341 widget = phone_widget,
342 comment = phone_help)
343 form[0][11].append(row)
344
345 # Address
346 if form.errors.address1:
347 address1_error = DIV(form.errors.address1,
348 _id="address1__error",
349 _class="error",
350 _style="display: block;")
351 else:
352 address1_error = ""
353 row = formstyle(id = "address1",
354 label = LABEL("%s:" % T("Address 1"),
355 SPAN(" *", _class="req"),
356 _for="address1"),
357 widget = (INPUT(_name="address1",
358 _id="address1",
359 _class="string"),
360 address1_error),
361 comment = "")
362 form[0][11].append(row)
363 row = formstyle(id = "address2",
364 label = LABEL("%s:" % T("Address 2"),
365 _for="address2"),
366 widget = INPUT(_name="address2",
367 _id="address2",
368 _class="string"),
369 comment = "")
370 form[0][11].append(row)
371 row = formstyle(id = "city",
372 label = LABEL("%s:" % T("City"),
373 SPAN(" *", _class="req"),
374 _for="city"),
375 widget = INPUT(_name="city",
376 _id = "city",
377 _class = "string"),
378 comment = "")
379 form[0][11].append(row)
380 states = S3LocationDropdownWidget(level="L1",
381 default="California",
382 empty=False)
383 widget = states(db.pr_address.location_id, None)
384 row = formstyle(id = "state",
385 label = LABEL("%s:" % T("State"),
386 SPAN(" *", _class="req"),
387 _for="location_id"),
388 widget = widget,
389 comment = "")
390 form[0][11].append(row)
391 if form.errors.zip:
392 zip_error = DIV(form.errors.zip,
393 _id="zip__error",
394 _class="error",
395 _style="display: block;")
396 else:
397 zip_error = ""
398 row = formstyle(id = "zip",
399 label = LABEL("%s:" % T("Zip"),
400 SPAN(" *", _class="req"),
401 _for="zip"
402 ),
403 widget = ( INPUT(_name="zip",
404 _id="zip",
405 _class="string"),
406 zip_error
407 ),
408 comment = "")
409 form[0][11].append(row)
410
411
412 #form[0][-2].append(TR(TD(LABEL(T("Terms of Service:"),
413 # _id="terms_of_service__label"),
414 # _class="w2p_fl"),
415 # TD(LABEL(TEXTAREA(deployment_settings.get_terms_of_service(),
416 # _onfocus="this.rows=10",
417 # _readonly="readonly",
418 # _style="width:100%;text-align:",
419 # _cols="80", _rows="10"),
420 # _id="terms_of_service"),
421 # _class="w2p_fw",
422 # _colspan="2"),
423 # _id="terms_of_service__row"))
424
425 # Eighteen
426 if form.errors.eighteen:
427 eighteen_error = DIV(form.errors.eighteen,
428 _id="eighteen__error",
429 _class="error",
430 _style="display: block;")
431 else:
432 eighteen_error = ""
433 label = LABEL(#"%s:" % T("I am 18 or over"),
434 T("I am 18 or over"),
435 SPAN(" *", _class="req"),
436 _for="eighteen",
437 _style="display: inline;",
438 _id="eighteen__label")
439 widget = INPUT(_type="checkbox",
440 _value="on",
441 _name="eighteen",
442 _id="eighteen",
443 _class="boolean")
444 #row = formstyle(id = "eighteen",
445 # label = label,
446 # widget = (widget,
447 # eighteen_error),
448 # comment = "")
449 row = TR(TD(widget, label, eighteen_error),
450 TD(),
451 _id="eighteen")
452 form[0][-2].append(row)
453
454 # US Citizen
455 if form.errors.citizen:
456 citizen_error = DIV(form.errors.citizen,
457 _id="citizen__error",
458 _class="error",
459 _style="display: block;")
460 else:
461 citizen_error = ""
462 label = LABEL(#"%s:" % T("I am a U.S. Citizen"),
463 T("I am a U.S. Citizen"),
464 SPAN(" *", _class="req"),
465 _for="citizen",
466 _style="display: inline;",
467 _id="citizen__label")
468 widget = INPUT(_type="checkbox",
469 _value="on",
470 _name="citizen",
471 _id="citizen",
472 _class="boolean")
473 #row = formstyle(id = "citizen",
474 # label = label,
475 # widget = (widget,
476 # citizen_error),
477 # comment = "")
478 row = TR(TD(widget, label, citizen_error),
479 TD(),
480 _id="citizen")
481 form[0][-2].append(row)
482
483 # Privacy Policy
484 row = formstyle(id = "",
485 label = "",
486 widget = DIV(XML(T("By clicking on 'I accept' below you are agreeing to the %(privacy_policy)s.") % \
487 dict(privacy_policy = A(T("Privacy Policy"),
488 _href = URL(c="default",
489 f="disclaimer#privacy"),
490 _target = "_blank"
491 )
492 )
493 ),
494 _style="width: 300px;",
495 ),
496 comment = "")
497 form[0][-2].append(row)
498
499 # Add client-side validation
500 # simplified copy of s3_register_validation()
501 script = "".join(( """
502$('#regform').validate({
503 errorClass: 'req',
504 rules: {
505 first_name: {
506 required: true
507 },
508 last_name: {
509 required: true
510 },
511 phone: {
512 required: true
513 },
514 email: {
515 required: true,
516 email: true
517 },
518 address1: {
519 required: true
520 },
521 city: {
522 required: true
523 },
524 zip: {
525 required: true
526 },
527 password: {
528 required: true
529 },
530 password_two: {
531 required: true,
532 equalTo: '.password:first'
533 },
534 eighteen: {
535 required: true
536 },
537 citizen: {
538 required: true
539 }
540 },
541 messages: {
542 firstname: '""", str(T("Enter your firstname")), """',
543 email: {
544 required: '""", str(T("Please enter a valid email address")), """',
545 minlength: '""", str(T("Please enter a valid email address")), """'
546 },
547 password: {
548 required: '""", str(T("Provide a password")), """'
549 },
550 password_two: {
551 required: '""", str(T("Repeat your password")), """',
552 equalTo: '""", str(T("Enter the same password as above")), """'
553 },
554 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.")), """',
555 citizen: '""", str(T("If you are not a US citizen, you may not register to apply to be a volunteer. Thank you for your interest in our volunteer opportunities.")), """',
556 },
557 errorPlacement: function(error, element) {
558 error.appendTo( element.parent().next() );
559 },
560 submitHandler: function(form) {
561 form.submit();
562 }
563});""" ))
564 response.s3.jquery_ready.append( script )
565
566 if session.s3.debug:
567 response.s3.scripts.append( "%s/jquery.validate.js" % s3_script_dir )
568 response.s3.scripts.append( "%s/jquery.pstrength.1.3.js" % s3_script_dir )
569 else:
570 response.s3.scripts.append( "%s/jquery.validate.min.js" % s3_script_dir )
571 response.s3.scripts.append( "%s/jquery.pstrength.1.3.min.js" % s3_script_dir )
572
573 response.s3.jquery_ready.append("$('.password:first').pstrength();\n")
574
575 # Add sidebar for login box & other volunteering opportunities
576 vol_sidebar()
577
578 response.s3.donate_cash_link = True
579
580 response.title = T("Register")
581 response.s3.has_required = True
582
583 return dict(form=form)
584
585# -----------------------------------------------------------------------------
586def register_validation(form):
587 """ Validate the custom fields in registration form """
588 # Terms of Service
589 if "eighteen" not in request.post_vars or request.post_vars.eighteen != "on":
590 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.")
591 if "citizen" not in request.post_vars or request.post_vars.citizen != "on":
592 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.")
593 # Phone
594 if "phone" in request.post_vars and request.post_vars.phone:
595 regex = re.compile(single_phone_number_pattern)
596 if not regex.match(request.post_vars.phone):
597 form.errors.phone = T("Invalid phone number")
598 else:
599 form.errors.phone = T("Phone number is required")
600 # Address
601 if not request.post_vars.address1:
602 form.errors.address1 = T("Address is required")
603 if not request.post_vars.city:
604 form.errors.city = T("City is required")
605 if not request.post_vars.zip:
606 form.errors.zip = T("Zip is required")
607
608 return
609
610# -----------------------------------------------------------------------------
611def register_onaccept(form):
612 # Usual Registration Tasks
613 # (PR record, Authenticated Role, Contacts)
614 person = auth.s3_register(form)
615
616 # LA-specific
617 table = db.pr_person
618 query = (table.id == person)
619
620 db(query).update(volunteer=True,
621 middle_name=request.post_vars.middle_name)
622
623 pe = db(query).select(table.pe_id,
624 limitby=(0, 1)).first().pe_id
625
626 # Phone
627 table = db.pr_contact
628 phone = request.post_vars.phone
629 phone_type = request.post_vars.phone_type
630 # Don't auto-subscribe to SMS (priority 10)
631 table.insert(pe_id = pe,
632 contact_method = phone_type,
633 value = phone,
634 priority=10)
635
636 # Address
637 table = db.gis_location
638 address1 = request.post_vars.address1
639 address2 = request.post_vars.address2
640 if address2:
641 address = "%s\n%s" % (address1,
642 address2)
643 else:
644 address = address1
645 city = request.post_vars.city
646 location = request.post_vars.location_id
647 state = db(table.id == location).select(table.id,
648 table.name,
649 cache=gis.cache,
650 limitby=(0, 1)).first()
651 if state:
652 statename = state.name
653 else:
654 # Duff data - not USA
655 statename = "unknown"
656 zip = request.post_vars.zip
657 county = ""
658 if city:
659 query = (table.name == city) & \
660 (table.level == "L3") & \
661 (table.path.like("%%/%s/%%" % state))
662 _city = db(query).select(table.id,
663 table.parent,
664 limitby=(0, 1)).first()
665 if _city:
666 if _city.parent != state:
667 query = (table.id == _city.parent)
668 county = db(query).select(table.name,
669 limitby=(0, 1)).first().name
670 elif state:
671 _city = table.insert(name=city, level="L3", parent=state.id)
672 gis.update_location_tree(_city, state.id)
673 else:
674 usa = db(table.code == "US").select(table.id,
675 limitby=(0, 1)).first().id
676 _city = table.insert(name=city, level="L3", parent=usa)
677 gis.update_location_tree(_city, usa)
678 else:
679 _city = None
680 location = table.insert(addr_street=address, addr_postcode=zip, parent=_city)
681 gis.update_location_tree(location, _city)
682 s3mgr.load("pr_address")
683 table = db.pr_address
684 table.insert(pe_id = pe,
685 location_id = location,
686 address=address,
687 postcode=zip,
688 L3=city,
689 L2=county,
690 L1=statename,
691 L0="United States")
692
693# -----------------------------------------------------------------------------
694auth.settings.register_onvalidation = register_validation
695auth.settings.register_onaccept = register_onaccept
696auth.settings.register_next = URL(c="vol", f="skill", args=["create"])
697
698# Organisation widget for use in Registration Screen
699# NB User Profile is only editable by Admin - using User Management
700org_widget = IS_ONE_OF(db, "org_organisation.id",
701 organisation_represent,
702 orderby="org_organisation.name",
703 sort=True)
704if deployment_settings.get_auth_registration_organisation_mandatory():
705 _table_user.organisation_id.requires = org_widget
706else:
707 _table_user.organisation_id.requires = IS_NULL_OR(org_widget)
708
709# =============================================================================
710def profile():
711 """ Custom Profile Screen """
712
713 if not auth.is_logged_in():
714 redirect(URL(f="user", args="login"))
715 elif s3_has_role(ORG_VOL):
716 #return organisation_profile()
717 redirect(URL(f="organisation", args=[auth.user.organisation_id]))
718
719 # Only show admin-set fields if there is data
720 table = db.auth_user
721 if not auth.user.organisation_id:
722 # Not relevant to Volunteers
723 table.organisation_id.readable = False
724 if not auth.user.site_id:
725 # Only relevant to Field Staff
726 table.site_id.readable = False
727
728 # Validate the custom fields
729 auth.settings.profile_onvalidation = profile_validation
730
731 # Process the custom fields
732 auth.settings.profile_onaccept = profile_onaccept
733
734 auth.messages.profile_save_button = "Save"
735
736 request.args = ["profile"]
737 form = auth()
738
739 # Custom class for Submit Button
740 form[0][-1][1][0]["_class"] = "submit-button"
741
742 # Lookup the Person
743 id = auth.s3_logged_in_person()
744 table = db.pr_person
745 person = db(table.id == id).select(table.pe_id,
746 table.middle_name,
747 table.volunteer,
748 limitby=(0, 1)).first()
749
750 row = TR(TD(LABEL("%s:" % table.middle_name.label, _for="middle_name")),
751 TD(INPUT(_name="middle_name",
752 _id="middle_name",
753 _class="string",
754 _value=person.middle_name)))
755 form[0][0].append(row)
756
757 # Lookup the Contacts
758 table = db.pr_contact
759 query = (table.pe_id == person.pe_id) & \
760 (table.deleted == False)
761 contacts = db(query).select(table.contact_method,
762 table.value,
763 table.priority)
764 cell = ""
765 home = ""
766 work = ""
767 sms_enabled = False
768 email_enabled = True
769 for contact in contacts:
770 if contact.contact_method == "SMS":
771 cell = contact.value
772 if contact.priority == 10:
773 sms_enabled = False
774 else:
775 sms_enabled = True
776 elif contact.contact_method == "EMAIL":
777 if contact.priority == 10:
778 email_enabled = False
779 else:
780 email_enabled = True
781 elif contact.contact_method == "HOME_PHONE":
782 home = contact.value
783 elif contact.contact_method == "WORK_PHONE":
784 work = contact.value
785
786 # Cell phone
787 if form.errors.mobile:
788 cell_error = DIV(form.errors.mobile,
789 _id="mobile__error",
790 _class="error",
791 _style="display: block;")
792 else:
793 cell_error = ""
794 row = TR(TD(LABEL("%s:" % msg.CONTACT_OPTS["SMS"], _for="mobile")),
795 TD(INPUT(_name="mobile",
796 _id="mobile",
797 _class="string",
798 _value=cell),
799 cell_error))
800 form[0][2].append(row)
801 # @ToDo: JS validation
802
803 if person.volunteer:
804 # Home phone
805 if form.errors.home_phone:
806 home_phone_error = DIV(form.errors.home_phone,
807 _id="home_phone__error",
808 _class="error",
809 _style="display: block;")
810 else:
811 home_phone_error = ""
812 row = TR(TD(LABEL("%s:" % msg.CONTACT_OPTS["HOME_PHONE"], _for="home_phone")),
813 TD(INPUT(_name="home_phone",
814 _id="home_phone",
815 _class="string",
816 _value=home),
817 home_phone_error))
818 form[0][2].append(row)
819 # @ToDo: JS validation
820
821 # Work phone
822 if form.errors.work_phone:
823 work_phone_error = DIV(form.errors.work_phone,
824 _id="work_phone__error",
825 _class="error",
826 _style="display: block;")
827 else:
828 work_phone_error = ""
829 row = TR(TD(LABEL("%s:" % msg.CONTACT_OPTS["WORK_PHONE"], _for="work_phone")),
830 TD(INPUT(_name="work_phone",
831 _id="work_phone",
832 _class="string",
833 _value=work),
834 work_phone_error))
835 form[0][2].append(row)
836 # @ToDo: JS validation
837
838 # Lookup the Address
839 table = db.pr_address
840 query = (table.pe_id == person.pe_id) & \
841 (table.deleted == False)
842 address = db(query).select(table.location_id,
843 limitby=(0, 1)).first()
844 address1 = ""
845 address2 = ""
846 city = ""
847 state = ""
848 zip = ""
849 if address:
850 table = db.gis_location
851 query = (table.id == address.location_id)
852 location = db(query).select(table.id,
853 table.path,
854 table.parent,
855 table.addr_street,
856 table.addr_postcode,
857 limitby=(0, 1)).first()
858 if location:
859 try:
860 address1, address2 = location.addr_street.split("\n", 1)
861 except ValueError:
862 address1 = location.addr_street
863 zip = location.addr_postcode
864 results = gis.get_parent_per_level(None, location.id, location)
865 try:
866 city = results["L3"].name
867 #except KeyError, AttributeError:
868 except:
869 pass
870 try:
871 state = results["L1"].name
872 #except KeyError, AttributeError:
873 except:
874 pass
875
876 if form.errors.address1:
877 address1_error = DIV(form.errors.address1,
878 _id="address1__error",
879 _class="error",
880 _style="display: block;")
881 else:
882 address1_error = ""
883 row = TR(TD(LABEL("%s: " % T("Address 1"),
884 SPAN("*", _class="req"),
885 _for="Address 1")),
886 TD(INPUT(_name="address1",
887 _id="address1",
888 _class="string",
889 _value=address1),
890 address1_error))
891 form[0][2].append(row)
892 row = TR(TD(LABEL("%s:" % T("Address 2"), _for="Address 2")),
893 TD(INPUT(_name="address2",
894 _id="address2",
895 _class="string",
896 _value=address2)))
897 form[0][2].append(row)
898 row = TR(TD(LABEL("%s: " % T("City"),
899 SPAN("*", _class="req"),
900 _for="city")),
901 TD(INPUT(_name="city",
902 _id="city",
903 _class="string",
904 _value=city)))
905 form[0][2].append(row)
906 states = S3LocationDropdownWidget(level="L1",
907 default=state or "California",
908 empty=False)
909 widget = states(db.pr_address.location_id, None)
910 row = TR(TD(LABEL("%s:" % T("State"),
911 SPAN("*", _class="req"),
912 _for="location_id")),
913 TD(widget))
914 form[0][2].append(row)
915 if form.errors.zip:
916 zip_error = DIV(form.errors.zip,
917 _id="zip__error",
918 _class="error",
919 _style="display: block;")
920 else:
921 zip_error = ""
922 row = TR(TD(LABEL("%s:" % T("Zip"),
923 SPAN("*", _class="req"),
924 _for="zip")),
925 TD(INPUT(_name="zip",
926 _id="zip",
927 _class="string",
928 _value=zip),
929 zip_error))
930 form[0][2].append(row)
931
932 # Notifications
933 div = DIV(TR(TD(LABEL("%s:" % T("Notifications"))),
934 TD()
935 ),
936 TR(TD(T("Receive Notifications via Email?")),
937 TD(INPUT(_type="checkbox",
938 _value="on",
939 value="on" if email_enabled else "",
940 _name="sub_email",
941 _id="sub_email",
942 _alt=T("Receive Notifications via Email?"),
943 _class="boolean"))
944 ),
945 TR(TD(T("Receive Notifications via SMS?")),
946 TD(INPUT(_type="checkbox",
947 _value="on",
948 value="on" if sms_enabled else "",
949 _name="sub_sms",
950 _id="sub_sms",
951 _alt=T("Receive Notifications via SMS?"),
952 _class="boolean")),
953 TD(DIV(_class="tooltip",
954 _title="%s|%s" % (T("Receive Notifications via SMS"),
955 T("Note that this will may incur costs from your carrier."))))
956 )
957 )
958 form[0][-2].append(div)
959
960 # Affiliated Orgs
961 s3mgr.load("vol_organisation")
962 table = db.vol_organisation
963 orgs = db(table.pe_id == person.pe_id).select(table.organisations_id,
964 limitby=(0, 1)).first()
965 if orgs:
966 orgs = orgs.organisations_id
967 table = db.org_organisation
968 if orgs:
969 _orgs = db(table.id.belongs(orgs)).select(table.name)
970 else:
971 _orgs = []
972 orgs = []
973 for org in _orgs:
974 orgs.append(org.name)
975 else:
976 orgs = []
977
978 div = DIV(TR(TD(),
979 TD(B(T("Affiliation with other volunteer organizations")))),
980 TR(TD(T("Are you affiliated with other volunteer organizations?"),
981 " (", DIV(T("Check all that apply"),
982 _style="display: inline;",
983 _class="red"),
984 ")",
985 _colspan=2)),
986 TR(TD("American Red Cross"),
987 TD(INPUT(_type="checkbox",
988 _value="on",
989 value="on" if "American Red Cross of Greater Los Angeles" in orgs else "",
990 _name="arc",
991 _id="arc",
992 _alt=T("American Red Cross"),
993 _class="boolean"))),
994 TR(TD("CERT"),
995 TD(INPUT(_type="checkbox",
996 _value="on",
997 value="on" if "CERT Los Angeles" in orgs else "",
998 _name="cert",
999 _id="cert",
1000 _alt=T("CERT"),
1001 _class="boolean"))),
1002 TR(TD("Disaster Healthcare Volunteers"),
1003 TD(INPUT(_type="checkbox",
1004 _value="on",
1005 value="on" if "Disaster Healthcare Volunteers" in orgs else "",
1006 _name="dhv",
1007 _id="dhv",
1008 _alt=T("Disaster Healthcare Volunteers"),
1009 _class="boolean"))),
1010 TR(TD("LA Works"),
1011 TD(INPUT(_type="checkbox",
1012 _value="on",
1013 value="on" if "LA Works" in orgs else "",
1014 _name="laworks",
1015 _id="laworks",
1016 _alt=T("LA Works"),
1017 _class="boolean"))),
1018 TR(TD("Volunteer Center of Los Angeles"),
1019 TD(INPUT(_type="checkbox",
1020 _value="on",
1021 value="on" if "Volunteer Center of Los Angeles" in orgs else "",
1022 _name="vcla",
1023 _id="vcla",
1024 _alt=T("Volunteer Center of Los Angeles"),
1025 _class="boolean"))),
1026 )
1027 form[0][-2].append(div)
1028
1029 # Skills
1030 # - separate screen
1031
1032 response.title = T("Profile")
1033 response.s3.has_required = True
1034
1035 return dict(form=form)
1036
1037# -----------------------------------------------------------------------------
1038def profile_validation(form):
1039 """ Validate the custom fields in profile form """
1040
1041 if s3_has_role(STAFF):
1042 volunteer = False
1043 else:
1044 volunteer = True
1045
1046 # Mobile Phone
1047 if "mobile" in request.post_vars and request.post_vars.mobile:
1048 regex = re.compile(single_phone_number_pattern)
1049 if not regex.match(request.post_vars.mobile):
1050 form.errors.mobile = T("Invalid phone number")
1051 elif "sub_sms" in request.post_vars and request.post_vars.sub_sms == "on":
1052 form.errors.mobile = T("Cell phone number is required for SMS notifications")
1053 # Home Phone
1054 if "home_phone" in request.post_vars and request.post_vars.home_phone:
1055 regex = re.compile(single_phone_number_pattern)
1056 if not regex.match(request.post_vars.home_phone):
1057 form.errors.home_phone = T("Invalid phone number")
1058 # Work Phone
1059 if "work_phone" in request.post_vars and request.post_vars.work_phone:
1060 regex = re.compile(single_phone_number_pattern)
1061 if not regex.match(request.post_vars.work_phone):
1062 form.errors.work_phone = T("Invalid phone number")
1063 # Address
1064 if volunteer and not request.post_vars.address1:
1065 form.errors.address1 = T("Address is required")
1066 if volunteer and not request.post_vars.zip:
1067 form.errors.zip = T("Zip is required")
1068
1069 return
1070
1071# -----------------------------------------------------------------------------
1072def profile_onaccept(form):
1073 """ Process the custom fields """
1074
1075 if s3_has_role(STAFF):
1076 volunteer = False
1077 else:
1078 volunteer = True
1079
1080 table = db.pr_person
1081 query = (table.id == auth.s3_logged_in_person())
1082 db(query).update(middle_name=request.post_vars.middle_name)
1083 pe = db(query).select(table.pe_id,
1084 limitby=(0, 1)).first().pe_id
1085
1086 if volunteer:
1087 # Contacts / Notifications
1088 if "sub_email" in request.post_vars and \
1089 request.post_vars.sub_email == "on":
1090 email_enabled = True
1091 else:
1092 email_enabled = False
1093 if "sub_sms" in request.post_vars and \
1094 request.post_vars.sub_sms == "on":
1095 sms_enabled = True
1096 else:
1097 sms_enabled = False
1098 home_phone = request.post_vars.home_phone
1099 work_phone = request.post_vars.work_phone
1100 email = request.post_vars.email
1101 cell = request.post_vars.mobile
1102 table = db.pr_contact
1103 if email:
1104 utable = db.auth_user
1105 query = (utable.id == auth.user.id)
1106 _email = db(query).select(utable.email,
1107 limitby=(0, 1)).first().email
1108 if not _email == email:
1109 # Email has changed
1110 # Check that the new email doesn't clash
1111 query2 = (utable.email == email)
1112 test = db(query2).select(utable.id,
1113 limitby=(0, 1)).first()
1114 if not test:
1115 # update auth_user
1116 db(query).update(email=email)
1117 # update pr_contact
1118 query = (table.value == _email)
1119 if volunteer:
1120 priority = 1 if email_enabled else 10
1121 db(query).update(value=email, priority=priority)
1122 else:
1123 db(query).update(value=email)
1124 else:
1125 # Existing .requires validation prevents the user from reusing
1126 # an existing email
1127 pass
1128 elif volunteer:
1129 # Just update the notifications
1130 query = (table.value == email)
1131 priority = 1 if email_enabled else 10
1132 db(query).update(priority=priority)
1133
1134 _cell = ""
1135 _home_phone = ""
1136 _work_phone = ""
1137 query = (table.pe_id == pe) & \
1138 (table.deleted == False)
1139 contacts = db(query).select(table.contact_method,
1140 table.value)
1141 for contact in contacts:
1142 if contact.contact_method == "SMS":
1143 _cell = contact.value
1144 elif contact.contact_method == "HOME_PHONE":
1145 _home_phone = contact.value
1146 elif contact.contact_method == "WORK_PHONE":
1147 _work_phone = contact.value
1148 table = db.pr_contact
1149 base_query = (table.pe_id == pe)
1150 if volunteer:
1151 priority = 1 if sms_enabled else 10
1152 if _cell:
1153 # Update existing record
1154 query = base_query & (table.value == _cell)
1155 db(query).update(value=cell, priority=priority)
1156 elif cell:
1157 # Insert new record
1158 table.insert(pe_id=pe, contact_method="SMS", value=cell,
1159 priority=priority)
1160 if _home_phone:
1161 # Update existing record
1162 query = base_query & (table.value == _home_phone)
1163 db(query).update(value=home_phone)
1164 elif home_phone:
1165 # Insert new record
1166 table.insert(pe_id=pe, contact_method="HOME_PHONE",
1167 value=home_phone)
1168
1169 if _work_phone:
1170 # Update existing record
1171 query = base_query & (table.value == _work_phone)
1172 db(query).update(value=work_phone)
1173 elif work_phone:
1174 # Insert new record
1175 table.insert(pe_id=pe, contact_method="WORK_PHONE",
1176 value=work_phone)
1177
1178 # Address
1179 address1 = request.post_vars.address1
1180 address2 = request.post_vars.address2
1181 if address2:
1182 address = "%s\n%s" % (address1, address2)
1183 else:
1184 address = address1
1185 zip = request.post_vars.zip
1186 city = request.post_vars.city
1187 state = request.post_vars.location_id
1188 table = db.gis_location
1189 statename = db(table.id == state).select(table.name,
1190 cache=gis.cache,
1191 limitby=(0, 1)).first().name
1192 county = ""
1193 if city:
1194 query = (table.name == city) & \
1195 (table.level == "L3") & \
1196 (table.path.like("%%/%s/%%" % state))
1197 _city = db(query).select(table.id,
1198 table.parent,
1199 limitby=(0, 1)).first()
1200 if _city:
1201 if _city.parent != state:
1202 query = (table.id == _city.parent)
1203 county = db(query).select(table.name,
1204 limitby=(0, 1)).first().name
1205 else:
1206 _city = table.insert(name=city, level="L3", parent=state)
1207 gis.update_location_tree(_city, state)
1208 else:
1209 _city = None
1210 table = db.pr_address
1211 query = (table.pe_id == pe) & \
1212 (table.deleted == False)
1213 test = db(query).select(table.location_id,
1214 limitby=(0, 1)).first()
1215 if test:
1216 db(query).update(address=address,
1217 postcode=zip,
1218 L3=city,
1219 L2=county,
1220 L1=statename,
1221 L0="United States")
1222 table = db.gis_location
1223 location = test.location_id
1224 query = (table.id == location)
1225 db(query).update(addr_street=address,
1226 addr_postcode=zip,
1227 parent=_city)
1228 gis.update_location_tree(location, _city)
1229
1230 # Affiliated Organisations
1231 # Also in models/vol.py
1232 vol_orgs = ["American Red Cross of Greater Los Angeles",
1233 "CERT Los Angeles",
1234 "Disaster Healthcare Volunteers",
1235 "LA Works",
1236 "Volunteer Center of Los Angeles"]
1237 table = db.org_organisation
1238 query = (table.name.belongs(vol_orgs))
1239 orgs = db(query).select(table.id,
1240 table.name,
1241 cache=(cache.ram, 60))
1242 for org in orgs:
1243 if org.name == "American Red Cross of Greater Los Angeles":
1244 ARC = org.id
1245 elif org.name == "CERT Los Angeles":
1246 CERT = org.id
1247 elif org.name == "Disaster Healthcare Volunteers":
1248 DHV = org.id
1249 elif org.name == "LA Works":
1250 LAW = org.id
1251 elif org.name == "Volunteer Center of Los Angeles":
1252 VCLA = org.id
1253 orgs = []
1254 if "arc" in request.post_vars and request.post_vars.arc == "on":
1255 orgs.append(ARC)
1256 if "cert" in request.post_vars and request.post_vars.cert == "on":
1257 orgs.append(CERT)
1258 if "dhv" in request.post_vars and request.post_vars.dhv == "on":
1259 orgs.append(DHV)
1260 if "laworks" in request.post_vars and \
1261 request.post_vars.laworks == "on":
1262 orgs.append(LAW)
1263 if "vcla" in request.post_vars and request.post_vars.vcla == "on":
1264 orgs.append(VCLA)
1265 s3mgr.load("vol_organisation")
1266 table = db.vol_organisation
1267 query = (table.pe_id == pe)
1268 test = db(query).select(table.id,
1269 limitby=(0, 1)).first()
1270 if test:
1271 db(query).update(organisations_id = orgs)
1272 else:
1273 table.insert(pe_id = pe,
1274 organisations_id = orgs,
1275 owned_by_user = auth.user.id)
1276
1277 else:
1278 # Not a volunteer: EOC staff
1279 if _cell:
1280 # Update existing record
1281 query = base_query & (table.value == _cell)
1282 db(query).update(value=cell)
1283 elif cell:
1284 # Insert new record
1285 table.insert(pe_id=pe, contact_method="SMS", value=cell)
1286
1287# -----------------------------------------------------------------------------
1288# Skills
1289# -----------------------------------------------------------------------------
1290def skill():
1291 """
1292 Collect a Volunteer's Skills
1293 """
1294
1295 tablename = "vol_skill"
1296 s3mgr.load(tablename)
1297 table = db[tablename]
1298
1299 person = auth.s3_logged_in_person()
1300 table.person_id.default = person
1301 table.person_id.writable = table.person_id.readable = False
1302
1303 s3mgr.configure(tablename,
1304 # Workflow: Prompt for creating Emergency Contacts after adding Skills
1305 create_next = URL(f="contact", args=["my"]),
1306 update_next = URL(f="skill", args=["my"]),
1307 deletable = False,
1308 listadd = False)
1309
1310 # Parse the request
1311 if "my" in request.args:
1312 query = (table.person_id == person)
1313 record = db(query).select(table.id,
1314 limitby=(0, 1),
1315 cache = gis.cache).first()
1316 if record:
1317 r = s3mgr.parse_request(args=[str(record.id)])
1318 else:
1319 r = s3mgr.parse_request(args=["create"])
1320 else:
1321 r = s3mgr.parse_request()
1322
1323 # Execute the request
1324 output = r()
1325
1326 response.title = T("My Skills")
1327 return output
1328
1329# -----------------------------------------------------------------------------
1330# Contacts
1331# -----------------------------------------------------------------------------
1332def contact():
1333 """
1334 Emergency Contacts for the volunteer
1335 """
1336
1337 tablename = "vol_contact"
1338 s3mgr.load(tablename)
1339 table = db[tablename]
1340
1341 person = auth.s3_logged_in_person()
1342 table.person_id.default = person
1343 table.person_id.writable = table.person_id.readable = False
1344
1345 s3mgr.configure(tablename,
1346 create_next = URL(f="req_skill"),
1347 update_next = URL(f="contact", args=["my"]),
1348 deletable = False,
1349 listadd = False)
1350
1351 # Parse the request
1352 if "my" in request.args:
1353 query = (table.person_id == person)
1354 record = db(query).select(table.id,
1355 limitby=(0, 1),
1356 cache = gis.cache).first()
1357 if record:
1358 r = s3mgr.parse_request(args=[str(record.id)])
1359 else:
1360 r = s3mgr.parse_request(args=["create"])
1361 else:
1362 r = s3mgr.parse_request()
1363
1364 # Execute the request
1365 output = r()
1366
1367 response.title = T("Emergency Contact")
1368 return output
1369
1370# -----------------------------------------------------------------------------
1371# Assignments
1372# - My Assignments menu
1373# -----------------------------------------------------------------------------
1374def assignment():
1375
1376 """ Allow a Volunteer/Org to View the details of their own assignments """
1377
1378 tablename = "vol_assignment"
1379 s3mgr.load(tablename)
1380 table = db[tablename]
1381
1382 if auth.user.organisation_id:
1383 response.s3.filter = (table.organisation_id == auth.user.organisation_id)
1384 # Control List View
1385 table.person_id.readable = False
1386 table.number.readable = True
1387 table.number.label = T("Number of Volunteers Committed")
1388 else:
1389 my_person_id = s3_logged_in_person()
1390 # Control List View
1391 table.person_id.readable = False
1392
1393 if not my_person_id:
1394 session.error = T("No person record found for current user.")
1395 redirect(URL(f="index"))
1396
1397 response.s3.filter = (table.person_id == my_person_id)
1398
1399 # Filter for current/past assignments
1400 sel = ((table.checkout == None) |
1401 (table.checkout >= request.utcnow) |
1402 (table.task_evaluation == None))
1403 s3.crud_strings[tablename].update(
1404 subtitle_list = T("Current Assignments"))
1405 if "show" in request.get_vars:
1406 show = request.get_vars["show"]
1407 if show == "past":
1408 sel = ((table.checkout < request.utcnow) &
1409 (table.task_evaluation != None))
1410 s3.crud_strings[tablename].update(
1411 subtitle_list = T("Past Assignments"),
1412 msg_no_match = T("No past assigments"))
1413 response.s3.filter &= sel
1414
1415 # Post-process
1416 def postp(r, output):
1417 if r.interactive:
1418 response.s3.actions = [dict(
1419 url = URL(c = "vol",
1420 f = "req",
1421 args = ["assignment", "[id]"]),
1422 _class = "action-btn",
1423 label = str(T("Details"))
1424 )]
1425 if "show" in request.get_vars and request.get_vars["show"] == "past":
1426 response.s3.actions += [dict(
1427 url = URL(c=request.controller,
1428 f="req",
1429 args=["assignment",
1430 "[id]",
1431 "print"]),
1432 _class = "action-btn",
1433 label = str(T("Certificate"))
1434 )]
1435
1436 return output
1437 response.s3.postp = postp
1438
1439 output = s3_rest_controller("vol", "assignment")
1440 return output
1441
1442# -----------------------------------------------------------------------------
1443# Requests
1444# -----------------------------------------------------------------------------
1445def vol_req_rheader(r):
1446 """
1447 Resource Header for Requests
1448 - Volunteer view
1449 - inc Volunteer Roster
1450 """
1451
1452 if r.representation == "html":
1453 if r.name == "req":
1454 record = r.record
1455 if record:
1456 if s3_has_role(STAFF):
1457 # Show Tabs on Roster
1458 tabs = [(T("Details"), None)]
1459 if record.type == 3 and deployment_settings.has_module("hrm"):
1460 tabs.append((T("Requested Volunteers"), "req_skill"))
1461 if deployment_settings.has_module("doc"):
1462 tabs.append((T("Documents"), "document"))
1463 #if deployment_settings.get_req_use_commit():
1464 # tabs.append((T("Commitments"), "commit"))
1465 tabs.append((T("Roster"), "assignment"))
1466 try:
1467 # Remove the default_type from URLs as set within respective controller
1468 r.vars.pop("default_type")
1469 except KeyError:
1470 pass
1471 rheader_tabs = s3_rheader_tabs(r, tabs)
1472 elif not r.component:
1473 # Volunteers should only ever access Requests via the Components
1474 return
1475
1476 table = db.req_req_skill
1477 query = (table.req_id == record.id)
1478 srecord = db(query).select(table.skill_id,
1479 table.quantity,
1480 table.quantity_commit,
1481 limitby=(0, 1)).first()
1482 if srecord:
1483 skills = response.s3.hrm_multi_skill_represent(srecord.skill_id)
1484 requested = srecord.quantity
1485 required = srecord.quantity - srecord.quantity_commit
1486 else:
1487 skills = requested = required = ""
1488
1489 address = shn_site_represent(record.site_id, address=True)
1490 #table = db.org_office
1491 #query = (table.id == record.site_id)
1492 #srecord = db(query).select(table.name,
1493 # table.address,
1494 # table.postcode,
1495 # table.L3,
1496 # table.L1,
1497 # limitby=(0, 1)).first()
1498 #if srecord:
1499 # location = T("%(site)s in %(location)s") % dict(site = srecord.name,
1500 # location = record.location)
1501 # ltable = db.gis_location
1502 # query = (ltable.name == srecord.L1)
1503 # state = db(query).select(ltable.code,
1504 # limitby=(0, 1),
1505 # cache=gis.cache).first()
1506 # if state:
1507 # statecode = state.code
1508 # else:
1509 # # Not valid US data (e.g. prepopulate)
1510 # statecode = None
1511 # address = "%s, %s %s %s" % (srecord.address,
1512 # srecord.L3,
1513 # statecode,
1514 # srecord.postcode)
1515 #else:
1516 # location = address = ""
1517
1518 table = db.hrm_human_resource
1519 query = (table.id == record.request_for_id)
1520 person = db(query).select(table.person_id,
1521 limitby=(0, 1)).first()
1522 phone = email = report_to = ""
1523 if person:
1524 report_to = person_represent(person.person_id)
1525
1526 # @ToDo: Investigate if these db calls can be done together with a join?
1527 table = db.pr_person
1528 query = (table.id == person.person_id)
1529 pe = db(query).select(table.pe_id,
1530 limitby=(0, 1)).first()
1531 if pe:
1532 table = db.pr_contact
1533 query = (table.pe_id == pe.pe_id) & \
1534 (table.contact_method.belongs(("SMS", "EMAIL")))
1535 contacts = db(query).select(table.contact_method,
1536 table.value)
1537 for contact in contacts:
1538 if contact.contact_method == "SMS":
1539 phone = contact.value
1540 elif contact.contact_method == "EMAIL":
1541 email = contact.value
1542
1543 if s3_has_role(STAFF):
1544 subtitle = T("Request Details")
1545 buttons = DIV(
1546 BUTTON(T("PRINT ROSTER"),
1547 _class="wide-grey-button",
1548 _onClick="javascript: window.location='%s'" % \
1549 URL(c=request.controller,
1550 f="req",
1551 args=[record.id,
1552 "assignment",
1553 "print"])),
1554 BUTTON(T("CANCEL REQUEST"),
1555 _class="wide-grey-button",
1556 _onClick="javascript: window.location='%s'" % \
1557 URL(c=request.controller,
1558 f="req",
1559 args=[record.id,
1560 "cancel"]))
1561 )
1562 textbox = ""
1563 else:
1564 subtitle = T("Volunteer Assignment Details")
1565 buttons = ""
1566 if r.component.name == "application":
1567 textbox = P(T("When you volunteer with Give2LA, you are an extension of the good will of the City of Los Angeles. For many survivors, volunteers are their first point of contact. Your respect, courtesy and sensitivity during these difficult times are vital. In addition, following directions, procedures, and processes given to you by your supervisor are essential; failure to do so may exclude you from future volunteer assignments."))
1568 else:
1569 give2la_forms = A( T("Give2LA Registration Forms"),
1570 _href = URL(c="static",
1571 f="Volunteer_Ethics_and_Conduct.pdf")
1572 )
1573 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.")),
1574 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") %
1575 dict(give2la_forms = give2la_forms) ),
1576 "."),
1577 P(T("Please let us know in advance if you cannot report for your Volunteer Assignment by calling the Point of Contact listed above.")),
1578 )
1579
1580 if record.date_required:
1581 time_req = s3_time_represent(record.date_required, utc=True)
1582 else:
1583 time_req = ""
1584 if record.date_required_until:
1585 time_until = s3_time_represent(record.date_required_until, utc=True)
1586 else:
1587 time_until = ""
1588
1589 class DL(DIV):
1590 tag = 'dl'
1591
1592 class DT(DIV):
1593 tag = 'dt'
1594
1595 class DD(DIV):
1596 tag = 'dd'
1597
1598 rheader = DIV(
1599 textbox,
1600 H2(subtitle, _class="paper-strip"),
1601 buttons,
1602 DIV(
1603 TABLE(
1604 TR(
1605 TH(T("Request Number")),
1606 TH(T("Task")),
1607 TH(T("Date+Time")),
1608 TH(T("Required Skills")),
1609 TH(T("Request Priority")),
1610 TH(T("Location")),
1611 TH(T("Number of Volunteers"),BR(),DIV("(", T("Still Required"), "/", T("Total Requested"), ")")), # (assigned/required)
1612 _class="odd"
1613 ),
1614 TR(
1615 TD(record.request_number),
1616 TD(record.purpose),
1617 TD(B(s3_date_represent_utc(record.date_required)),
1618 BR(),
1619 "%s - %s" % (time_req,
1620 time_until),
1621 ),
1622 TD(skills),
1623 TD(response.s3.req_priority_represent(record.priority) ),
1624 TD(record.location or ""),
1625 TD(B(required),
1626 " / %s" % requested,
1627 _class="red"),
1628 _class="even"
1629 ),
1630 _class="datatable display"
1631 ),
1632 _id="table-container"
1633 ),
1634 DL(
1635 DT(T("Point of Contact")),
1636 DD(report_to,
1637 BR(),
1638 "%s: %s" % (T("Phone"),
1639 phone),
1640 BR(),
1641 "%s: %s" % (T("Email"),
1642 email),
1643 ),
1644 DT(T("Volunteering Location")),
1645 DD(#location,
1646 #BR(),
1647 address),
1648 DT(T("Comments")),
1649 DD(record.comments),
1650 _class="assignment-details"
1651 ),
1652 )
1653 if r.component and r.component.name == "assignment":
1654 if s3_has_role(STAFF):
1655 # Volunteer Roster
1656 response.s3.rfooter = DIV(
1657 BUTTON(T("CHECK-IN ALL"),
1658 _class="wide-grey-button",
1659 _onClick="javascript: window.location='%s'" % \
1660 URL(c=request.controller,
1661 f="req",
1662 args=[record.id,
1663 "assignment",
1664 "checkin"])),
1665 BUTTON(T("CHECK-OUT ALL"),
1666 _class="wide-grey-button",
1667 _onClick="javascript: window.location='%s'" % \
1668 URL(c=request.controller,
1669 f="req",
1670 args=[record.id,
1671 "assignment",
1672 "checkout"]))
1673 )
1674 else:
1675 # Volunteer Assignment
1676 assignment = r.component_id
1677 table = db.vol_assignment
1678 query = table.id == r.component_id
1679 record = db(query).select(limitby=(0, 1)).first()
1680 if not record:
1681 # Probably a Volunteer trying to use a URL for STAFF
1682 session.error = auth.permission.INSUFFICIENT_PRIVILEGES
1683 redirect(URL(c="vol", f="req_skill"))
1684 req_id = record.req_id
1685 if record.checkout != None and record.task_evaluation != None:
1686 printBtn = T("PRINT CERTIFICATE")
1687 else:
1688 printBtn = T("PRINT VOLUNTEER ASSIGNMENT FORM")
1689 rheader.append(
1690 DIV(
1691 BUTTON(printBtn,
1692 _class="big-red-arrow",
1693 _onClick="javascript: window.location='%s'" % \
1694 URL(c=request.controller,
1695 f="req",
1696 args=["assignment",
1697 assignment,
1698 "print"])),
1699 # Also in controllers/don.py - DRY
1700 A(IMG(_src="/%s/static/img/get_adobe_reader.png" % request.application,
1701 _title="%s - %s" % (T("Get Adobe Reader"),
1702 T("This link will open a new browser window.")),
1703 _alt=T("Get Adobe Reader"),
1704 _width=158,
1705 _height=39,
1706 _style="float:right;"),
1707 _href="http://www.adobe.com/products/acrobat/readstep2.html",
1708 _target="_blank"),
1709 BR(),
1710 BUTTON(T("CANCEL ASSIGNMENT"),
1711 _class="wide-grey-button",
1712 _onClick="javascript: window.location='%s'" % \
1713 URL(c=request.controller,
1714 f="req",
1715 args=[record.id,
1716 "assignment",
1717 assignment,
1718 "delete"]))
1719 )
1720 )
1721 if s3_has_role(STAFF):
1722 rheader.append(rheader_tabs)
1723
1724 return rheader
1725 return None
1726
1727# -----------------------------------------------------------------------------
1728def req():
1729 """ REST Controller """
1730
1731 s3mgr.load("req_req")
1732 default_type = 3
1733 request.vars["default_type"] = 3
1734
1735 if "skill_id" in request.get_vars:
1736 # Look up the Request from the Skill component
1737 # (since that is what is available to us within the dataTables row)
1738 table = db.req_req_skill
1739 query = (table.id == request.vars.skill_id)
1740 req = db(query).select(table.req_id,
1741 limitby=(0, 1)).first()
1742 if not req:
1743 session.error = T("Invalid Request!")
1744 redirect(URL(f="req_skill", args=[], vars={}))
1745 # @ToDo: A nicer way to do this (without a 2nd request)
1746 # simply amending request.args doesn't work
1747 # Need to rewrite to not use prep/postp but rather process r() directly
1748 redirect(URL(args = [req.req_id, "application", "create"],
1749 vars={}))
1750
1751 if "document" in request.args:
1752 s3mgr.load("doc_document")
1753
1754 req_table = db.req_req
1755 s3mgr.configure(req_table,
1756 deletable=False)
1757
1758 type_field = req_table.type
1759 type_field.default = int(default_type)
1760 type_field.writable = False
1761
1762 if s3_has_role(STAFF):
1763 if "assignment" in request.args or \
1764 "checkin" in request.args or \
1765 "checkout" in request.args:
1766 s3mgr.load("vol_assignment")
1767
1768 elif s3_has_role(ORG_VOL):
1769 s3mgr.load("vol_application")
1770 # Volunteer Orgs can see Public Requests & those published for them
1771 org = auth.user.organisation_id
1772 response.s3.filter = (req_table.public == True) | \
1773 (req_table.organisations_id.belongs((org,)))
1774 s3mgr.configure(req_table,
1775 editable=False), # We need the Controller/Table ACL to be editable to be able to add components
1776 if "application" in request.args:
1777 s3.crud.submit_button = T("COMMIT")
1778 table = db.vol_application
1779 table.organisation_id.default = org
1780 table.organisation_id.readable = table.organisation_id.writable = False
1781 table.number.readable = table.number.writable = True
1782 table.person_id.readable = table.person_id.writable = False
1783 field = table.team_leader_id
1784 field.readable = field.writable = True
1785 field.requires = IS_NOT_EMPTY()
1786 #table.person_id.label = T("Team Leader")
1787 #table.person_id.comment = None
1788 #table.person_id.widget = S3AddPersonWidget(
1789 # #select_existing=False
1790 # )
1791 #table.person_id.requires = IS_ADD_PERSON_WIDGET()
1792 #field = table.team_leader
1793 #field.readable = field.writable = True
1794 #field.requires = IS_NOT_EMPTY()
1795 #field = table.team_leader_contact
1796 #field.readable = field.writable = True
1797 #field.requires = IS_NOT_EMPTY()
1798 table.emergency_contact_name.readable = table.emergency_contact_name.writable = False
1799 table.emergency_contact_name.requires = []
1800 table.emergency_contact_relationship.readable = table.emergency_contact_relationship.writable = False
1801 table.emergency_contact_relationship.requires = []
1802 table.emergency_contact_phone.readable = table.emergency_contact_phone.writable = False
1803 table.emergency_contact_phone.requires = []
1804 s3.crud_strings["vol_application"].subtitle_create = ""
1805
1806 elif "assignment" in request.args:
1807 table = db.vol_assignment
1808 table.organisation_id.default = org
1809 table.organisation_id.readable = table.organisation_id.writable = False
1810 table.person_id.readable = table.person_id.writable = False
1811 # Make the component multiple=False
1812 # @ToDo: Currently multiple=False will just return the .first() record even if inaccessible!
1813 # That would be fixed by a filter, however then UI would break.
1814 s3mgr.model.add_component("vol_assignment",
1815 req_req=dict(joinby="req_id",
1816 multiple=False))
1817 else:
1818 s3mgr.load("vol_application")
1819 # Volunteers can only see Public Requests
1820 response.s3.filter = (req_table.public == True)
1821 s3mgr.configure(req_table,
1822 editable=False), # We need the Controller/Table ACL to be editable to be able to add components
1823 person = s3_logged_in_person()
1824 if "application" in request.args:
1825 # Check for required Skills
1826 table = db.req_req_skill
1827 query = (table.req_id == request.args[0])
1828 req = db(query).select(table.skill_id,
1829 limitby=(0, 1)).first()
1830 if req:
1831 # Read the applicant's skills
1832 table = db.vol_skill
1833 query = (table.person_id == auth.s3_logged_in_person())
1834 skillset = db(query).select(table.skill_id,
1835 limitby=(0, 1)).first()
1836 gap = False
1837 if not skillset:
1838 gap = True
1839 else:
1840 for skill in req.skill_id:
1841 if not skill in skillset.skill_id:
1842 gap = True
1843 break
1844 if gap:
1845 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.")
1846
1847 s3.crud.submit_button = T("COMMIT")
1848 table = db.vol_application
1849 table.person_id.default = person
1850 table.person_id.readable = table.person_id.writable = False
1851 s3.crud_strings["vol_application"].subtitle_create = ""
1852 # Read in current Emergency Contacts
1853 ctable = db.vol_contact
1854 query = (ctable.person_id == person)
1855 contacts = db(query).select(limitby=(0, 1)).first()
1856 if contacts:
1857 table.emergency_contact_name.default = contacts.emergency_contact_name
1858 table.emergency_contact_relationship.default = contacts.emergency_contact_relationship
1859 table.emergency_contact_phone.default = contacts.emergency_contact_phone
1860
1861 elif "assignment" in request.args:
1862 table = db.vol_assignment
1863 table.person_id.default = person
1864 table.person_id.readable = table.person_id.writable = False
1865 # Make the component multiple=False
1866 # @ToDo: Currently multiple=False will just return the .first() record even if inaccessible!
1867 # That would be fixed by a filter, however then UI would break.
1868 #s3mgr.model.add_component("vol_assignment",
1869 # req_req=dict(joinby="req_id",
1870 # multiple=False))
1871 s3mgr.configure("vol_assignment",
1872 enforce_single_record=True)
1873
1874 def prep(r):
1875 db.req_req.status.readable = db.req_req.status.writable = False
1876
1877 # Remove type from list_fields
1878 list_fields = s3mgr.model.get_config("req_req",
1879 "list_fields")
1880 try:
1881 list_fields.remove("type")
1882 except:
1883 # It has already been removed.
1884 # This can happen if the req controller is called
1885 # for a second time, such as when printing reports
1886 # see vol.print_assignment()
1887 pass
1888 s3mgr.configure(tablename, list_fields=list_fields)
1889
1890 type = ( r.record and r.record.type ) or \
1891 ( request.vars and request.vars.default_type )
1892 if type:
1893 type = int(type)
1894 req_table.type.default = int(type)
1895
1896 # Filter the query based on type
1897 if response.s3.filter:
1898 response.s3.filter = response.s3.filter & \
1899 (db.req_req.type == type)
1900 else:
1901 response.s3.filter = (db.req_req.type == type)
1902
1903 #if type == 3:
1904 s3mgr.configure("req_req",
1905 list_fields = ["id",
1906 "priority",
1907 "event_id",
1908 "purpose",
1909 "site_id",
1910 "date_required",
1911 ])
1912
1913 if r.interactive:
1914 # Set Fields and Labels depending on type
1915 if type:
1916 # This prevents the type from being edited AFTER it is set
1917 req_table.type.readable = False
1918 req_table.type.writable = False
1919
1920 crud_strings = deployment_settings.get_req_req_crud_strings(type)
1921 if crud_strings:
1922 s3.crud_strings["req_req"] = crud_strings
1923
1924 if "application" in request.args:
1925 s3.crud_strings["req_req"].title_display = T("Volunteer Application")
1926 elif "assignment" in request.args:
1927 if s3_has_role(STAFF) and not r.component_id:
1928 s3.crud_strings["req_req"].title_display = T("Volunteer Roster")
1929 else:
1930 s3.crud_strings["req_req"].title_display = T("Volunteer Assignment")
1931 atable = db.vol_assignment
1932 # @ToDo: Hide if vol hasn't yet checked-out or if time not > time_until
1933 #atable.task_evaluation.readable = False
1934
1935
1936 if r.method != "create":
1937 # Disable Edit for fields where WebEOC is Master
1938 req_table.event_id.writable = False
1939 req_table.incident_id.writable = False
1940 #req_table.incident_id.writable = True
1941 req_table.request_number.writable = False
1942 req_table.priority.writable = False
1943 req_table.site_id.writable = False
1944 req_table.request_for_id.writable = False
1945 req_table.date_required.writable = False
1946 req_table.date_required_until.writable = False
1947 req_table.purpose.writable = False
1948 req_table.date.writable = False
1949 req_table.requester_id.writable = False
1950 req_table.approved_by_id.writable = False
1951 req_table.comments.writable = False
1952 req_table.cancel.writable = False
1953
1954 if r.record and r.record.req_req_skill.count():#r.component and r.component.name == "req_skill":
1955 # Is this component being updated (not created)
1956 req_skill_table = db.req_req_skill
1957 req_skill_table.skill_id.writable = False
1958 req_skill_table.quantity.writable = False
1959
1960 # @ToDo: apply these changes via JS for the create form where type is editable
1961 #if type == 3: # Person
1962 req_table.date_required.requires = req_table.date_required.requires.other
1963 req_table.date_required_until.requires = req_table.date_required_until.requires.other
1964 req_table.location.readable = True
1965 req_table.public.readable = True
1966 req_table.organisations_id.readable = True
1967 req_table.emailed.readable = True
1968
1969 if r.method == "create":
1970 # Only enable Edit for fields where Sahana (not WebEOC) is Master
1971 req_table.location.writable = True
1972 req_table.public.writable = True
1973 req_table.organisations_id.writable = True
1974
1975 req_table.purpose.label = T("Task Details")
1976 req_table.site_id.label = T("Report To Facility")
1977 req_table.request_for_id.label = T("Point of Contact")
1978
1979 if r.method != "update" and r.method != "read":
1980 if not r.component:
1981 # Hide fields which don't make sense in a Create form
1982 # - includes one embedded in list_create
1983 # - list_fields over-rides, so still visible within list itself
1984 response.s3.req_create_form_mods()
1985
1986 # Get the default Facility for this user
1987 # @ToDo: Use site_id in User Profile (like current organisation_id)
1988 if deployment_settings.has_module("hrm"):
1989 query = (db.hrm_human_resource.person_id == s3_logged_in_person())
1990 site = db(query).select(db.org_site.id,
1991 limitby=(0, 1)).first()
1992 if site:
1993 r.table.site_id.default = site.id
1994
1995 elif r.component.name == "document":
1996 s3.crud.submit_button = T("Add")
1997 table = r.component.table
1998 # @ToDo: Fix for Link Table
1999 #table.date.default = r.record.date
2000 #if r.record.site_id:
2001 # stable = db.org_site
2002 # query = (stable.id == r.record.site_id)
2003 # site = db(query).select(stable.location_id,
2004 # stable.organisation_id,
2005 # limitby=(0, 1)).first()
2006 # if site:
2007 # table.location_id.default = site.location_id
2008 # table.organisation_id.default = site.organisation_id
2009
2010 elif r.component.name == "req_skill":
2011 #req_hide_quantities(r.component.table)
2012 table = r.component.table
2013 table.quantity_commit.writable = table.quantity_commit.readable = False
2014 table.quantity_fulfil.writable = table.quantity_fulfil.readable = False
2015
2016 if r.component:
2017 if r.component.name == "document" or \
2018 r.component.name == "req_skill":
2019 # Limit site_id to facilities the user has permissions for
2020 # @ToDo: Non-Item requests shouldn't be bound to a Facility?
2021 auth.permission.permitted_facilities(table=r.table,
2022 error_msg=T("You do not have permission for any facility to make a request."))
2023
2024 elif r.component.name == "assignment":
2025 # Tweak the breadcrumb
2026 if r.component.count() > 1:
2027 label = T("Roster")
2028 else:
2029 label = T("Assignment")
2030 breadcrumbs[2] = (label, False,
2031 URL(c=request.controller,
2032 f=request.function,
2033 args=request.args))
2034 table = db.vol_assignment
2035 if s3_has_role(STAFF):
2036 table.report_to_id.default = r.record.request_for_id
2037 else:
2038 # Volunteer can evaluate task if vol has checked-in and (checked-out or time > time_until)
2039 query = (table.id == r.component_id)
2040 assign = db(query).select(table.checkin,
2041 table.checkout,
2042 limitby=(0, 1)).first()
2043 checkin = None
2044 if assign:
2045 checkin = assign.checkin
2046 checkout = assign.checkout
2047 query = (req_table.id == r.id)
2048 req = db(query).select(req_table.date_required_until,
2049 limitby=(0, 1)).first()
2050 if req:
2051 required_until = req.date_required_until
2052 else:
2053 required_until = datetime.datetime(3000, 1, 1)
2054
2055 if checkin and (checkout or \
2056 request.utcnow > required_until):
2057 table.task_evaluation.writable = True
2058 table.task_eval_comments.writable = True
2059 else:
2060 table.task_evaluation.readable = False
2061 table.task_eval_comments.readable = False
2062 if s3_has_role(ORG_VOL):
2063 # Allow to edit Who/How Many until Evaluation time
2064 s3.crud_strings.vol_assignment.title_update = T("Application Details")
2065 table.team_leader_id.readable = table.team_leader_id.writable = True
2066 table.number.readable = table.number.writable = True
2067 table.number.label = T("Number of Volunteers Committed")
2068
2069 elif r.id and \
2070 r.component.name == "application" and \
2071 r.method == "create":
2072
2073 if not auth.user.organisation_id:
2074 # Display an error message if the user has already got an
2075 # assignment during this time period, this can
2076 # alternatively be done onvalidation of the application,
2077 # but doing it here is better UX and saves a query:
2078 req_id = r.id
2079 req = r.record
2080 # @todo: make min_lag deployment setting:
2081 min_lag = datetime.timedelta(hours=1)
2082 # Earliest that another Request can start
2083 earliest = req.date_required_until + min_lag
2084 # Latest that another Request can end
2085 latest = req.date_required - min_lag
2086 person_id = s3_logged_in_person()
2087 if person_id:
2088 rtable = db.req_req
2089 atable = db.vol_assignment
2090 query = (atable.person_id == person_id) & \
2091 (rtable.id == atable.req_id) & \
2092 ((rtable.date_required >= earliest) | \
2093 (rtable.date_required_until >= latest))
2094 conflict = db(query).select(rtable.id,
2095 limitby=(0, 1)).first()
2096 if conflict:
2097 session.error = T("You are assigned to another request during this time period")
2098 redirect(URL(c="vol", f="req_skill"))
2099 # Could be made less strong:
2100 #response.error = T("You are assigned to another request during this time period")
2101 else:
2102 # Limit site_id to facilities the user has permissions for
2103 # @ToDo: Non-Item requests shouldn't be bound to a Facility?
2104 auth.permission.permitted_facilities(table=r.table,
2105 error_msg=T("You do not have permission for any facility to make a request."))
2106
2107 return True
2108 response.s3.prep = prep
2109
2110 # Post-process
2111 def postp(r, output):
2112
2113 if r.interactive:
2114 if r.http == "POST":
2115 form = output.get("form", None)
2116 if not form.errors:
2117 # Form submitted Succesfully
2118 # interactive-only onaccept routines can go here
2119 if r.component and \
2120 r.component.name == "application":
2121 # Check whether Request Full:
2122 # Create Assignment, Update Commit status
2123 response.s3.application_onaccept_interactive(form)
2124
2125 # GET & POST
2126 s3_action_buttons(r)
2127 if not r.component:
2128 # This is only appropriate for item requests
2129 pass
2130 elif r.component.name == "req_skill":
2131 pass
2132 elif r.component.name == "application":
2133 try:
2134 form = output["form"]
2135 if form.errors.dsw:
2136 dsw_error = DIV(form.errors.dsw,
2137 _id="dsw__error",
2138 _class="error",
2139 _style="display: block;")
2140 else:
2141 dsw_error = ""
2142 give2la_forms = A( T("Give2LA Registration Forms"),
2143 _href = URL(c="static",
2144 f="Volunteer_Ethics_and_Conduct.pdf")
2145 )
2146 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.") % \
2147 dict(give2la_forms = give2la_forms)
2148 ),
2149 #T("As a volunteer working to assist disaster survivors and in disaster recovery efforts, you will be required to sign the following forms: "),
2150 #A( T("Personal Statement of Understanding"),
2151 # _href = URL(c="static",
2152 # f="Volunteer_Personal_Statement.pdf"),
2153 # ),
2154 #", ",
2155 #A( T("City of Los Angeles Code of Volunteer Ethics and Conduct"),
2156 # _href = URL(c="static",
2157 # f="Volunteer_Ethics_and_Conduct.pdf"),
2158 # ),
2159 #T("and "),
2160 #A( T("DSW Volunteer Registration Form "),
2161 # _href = URL(c="static",
2162 # f="DSW_Volunteer_Registration_Form.pdf"),
2163 # ),
2164 #T("or "),
2165 #A( T("Waiver/Release of Liability"),
2166 # _href = URL(c="static",
2167 # f="Volunteer_Personal_Statement.pdf"),
2168 # ),
2169 #T(". All forms will be in English."),
2170 BR(),
2171 T("Check here to agree to these conditions:")
2172 )
2173 form[0][-2].append(TR(TD(LABEL(vol_appl_conditions,
2174 _for="dsw",
2175 _style="display: inline;",
2176 _id="dsw__label"),
2177 SPAN("*", _class="req"),
2178 INPUT(_type="checkbox",
2179 _value="on",
2180 _name="dsw",
2181 _id="dsw",
2182 _class="boolean"),
2183 dsw_error,
2184 _class="w2p_fl"),
2185 _id="dsw__row"))
2186 form[0][-1][0][0]["_class"] = "accept-button"
2187 form[0][-1][0].append(BR())
2188 form[0][-1][0].append(INPUT(_type="button",
2189 _value=T("DECLINE"),
2190 _class="wide-grey-button",
2191 _onClick="javascript: window.location='%s'" % \
2192 URL(c=request.controller,
2193 f="req_skill")))
2194 except KeyError:
2195 # update 'form'
2196 pass
2197 if "subtitle" in output and not output["subtitle"]:
2198 # Remove null subtitle from Application form to avoid CSS markup
2199 output.pop("subtitle")
2200
2201 elif r.component.name == "assignment":
2202 ctable = r.component.table
2203 # This is always appropriate
2204 response.s3.actions = [
2205 dict(url = URL(f = "req",
2206 args = [r.id,
2207 r.component_name,
2208 "[id]"]),
2209 _class = "action-btn",
2210 label = str(T("Details"))
2211 )]
2212 # This is only appropriate if vol not yet checked-in
2213 query = (ctable.checkin == None)
2214 rows = db(query).select(ctable.id)
2215 restrict = [str(row.id) for row in rows]
2216 response.s3.actions.append(
2217 dict(url = URL(f = "req",
2218 args = [r.id,
2219 r.component_name,
2220 "[id]",
2221 "checkin"]),
2222 _class = "action-btn",
2223 label = str(T("Check-In NOW")),
2224 restrict = restrict
2225 )
2226 )
2227 # This is only appropriate if vol checked-in but not out
2228 query = (ctable.checkin != None) & \
2229 (ctable.checkout == None)
2230 rows = db(query).select(ctable.id)
2231 restrict = [str(row.id) for row in rows]
2232 response.s3.actions.append(
2233 dict(url = URL(f = "req",
2234 args = [r.id,
2235 r.component_name,
2236 "[id]",
2237 "checkout"]),
2238 _class = "action-btn",
2239 label = str(T("Check-Out NOW")),
2240 restrict = restrict
2241 )
2242 )
2243 else:
2244 # We don't yet have other components
2245 pass
2246
2247 return output
2248 response.s3.postp = postp
2249
2250 rheader = vol_req_rheader
2251
2252 output = s3_rest_controller("req", "req", rheader=rheader)
2253
2254 return output
2255
2256# -----------------------------------------------------------------------------
2257def req_skill():
2258 """ REST Controller """
2259
2260 if s3_has_role(ORG_DON) and not s3_has_role(ORG_VOL):
2261 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.")
2262 redirect(URL(c="don", f="organisation", args=[auth.user.organisation_id, "inv_item"], vars={"item":"services"}))
2263
2264 if s3_has_role(STAFF):
2265 # NB No link leads them here
2266 # Tweak the breadcrumbs
2267 breadcrumbs[2] = (T("Requests for Volunteers"), False,
2268 URL(c=request.controller,
2269 f=request.function))
2270
2271 tablename = "req_req_skill"
2272 s3mgr.load(tablename)
2273 table = db[tablename]
2274
2275 s3mgr.configure(tablename,
2276 insertable=False)
2277
2278 if not s3_has_role(STAFF):
2279 req_table = db.req_req
2280 s3mgr.load("vol_assignment")
2281 vtable = db.vol_assignment
2282
2283 #if auth.is_logged_in():
2284 # person = auth.s3_logged_in_person()
2285 #else:
2286 vol_sidebar()
2287
2288 # Filter out:
2289 # Reqs which haven't been published to the public site
2290 # Reqs which are complete (status defined in models/req.py)
2291 # @ToDo: if s3_has_role(ORG) then also allow reqs for that Org
2292 response.s3.filter = \
2293 ((req_table.id == table.req_id) & (req_table.public == True)) & \
2294 ((req_table.id == table.req_id) & (req_table.commit_status != 2))
2295
2296 #if auth.s3_is_logged_in():
2297 # # Filter Out:
2298 # # Reqs which the Vol has already got an Assignment for
2299 # response.s3.filter = response.s3.filter & \
2300 # ~((vtable.req_id == table.req_id) & (vtable.person_id == person))
2301
2302 s3mgr.configure(tablename,
2303 list_fields=["id",
2304 (T("Task"), "task"),
2305 (T("Date + Time"), "date"),
2306 "skill_id",
2307 # @ToDo: If-needed, VirtualField
2308 #"priority",
2309 (T("Location"), "location"),
2310 #(T("Number of Volunteers Needed (Still Needed/Total Needed)"), "needed"),
2311 (T("Number of Volunteers Still Needed"), "needed"),
2312 #"quantity",
2313 "comments"
2314 ])
2315 actions = [
2316 dict(url = URL(c = "vol",
2317 f = "req",
2318 args = ["application", "create"],
2319 vars = {"skill_id" : "[id]"}),
2320 _class = "apply-button",
2321 label = str(T("Apply"))
2322 )
2323 ]
2324 else:
2325 actions = [
2326 dict(url = URL(c = "vol",
2327 f = "req",
2328 args = ["req_skill", "[id]"]),
2329 _class = "action-btn",
2330 label = str(READ)
2331 )
2332 ]
2333
2334 def prep(r):
2335 if r.interactive:
2336 if r.method != "update" and r.method != "read":
2337 # Hide fields which don't make sense in a Create form
2338 # - includes one embedded in list_create
2339 # - list_fields over-rides, so still visible within list itself
2340 response.s3.req_hide_quantities(r.table)
2341
2342 return True
2343 response.s3.prep = prep
2344
2345 # Post-process
2346 def postp(r, output):
2347 if r.interactive:
2348 response.s3.actions = actions
2349 if not s3_has_role(STAFF) and r.method == None:
2350 if auth.is_logged_in():
2351 line = XML(T("If you have the required skills, click to %(apply_button)s") %
2352 dict( apply_button = SPAN(T("Apply"),
2353 _class="apply-button",
2354 _style ="display:inline-block;text-align:center")
2355 )
2356 )
2357 else:
2358 line = TAG[""]( XML(T("Please %(register)s to volunteer with %(give2la)s and receive updates of new Requests for Volunteers.") %
2359 dict( register = A( T("register"),
2360 _href = URL(c = "vol",
2361 f = "register")
2362 ),
2363 give2la = B("Give2", I("LA"))
2364 )
2365 ),
2366 #BR(),
2367 #XML( T("If you have already registered please %(sign_in)s.") %
2368 # dict( sign_in = A( T("Sign In"),
2369 # _href = URL(c = "default",
2370 # f = "user",
2371 # args = ["login"],
2372 # vars = dict(_next = "/la/vol/req_skill")
2373 # )
2374 # )
2375 # )
2376 #)
2377 )
2378 output["rheader"] = DIV( P( B(T("The City of Los Angeles has the following Requests for Volunteers.") ),
2379 BR(),
2380 line
2381 )
2382 #T(" or "),
2383 #A( T("Sign In"), _href = URL(c = "default",
2384 # f = "user",
2385 # args = ["login"],
2386 # vars = dict(_next = "/la/vol/req_skill")
2387 # )
2388 )
2389 #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 "),
2390 #A(T("sign up"),
2391 # _href="/%s/vol/register" % request.application,
2392 # _class="signup"),
2393 #T(" first. You can narrow down the list by for example searching for a specific task or your neighborhood."))
2394 return output
2395 response.s3.postp = postp
2396
2397 output = s3_rest_controller("req", "req_skill")
2398
2399 return output
2400
2401# -----------------------------------------------------------------------------
2402def activity():
2403 """ REST Controller """
The diff has been truncated for viewing.