Bring Sahana config up-to-date

This commit is contained in:
Disassembler 2019-12-20 17:53:39 +01:00
parent 18698ef5d4
commit 1801fcdc3d
No known key found for this signature in database
GPG Key ID: 524BD33A0EE29499
12 changed files with 483 additions and 210 deletions

View File

@ -68,8 +68,8 @@ settings.base.debug = True
# Uncomment this to prevent automated test runs from remote
# settings.base.allow_testing = False
# Configure the log level ("DEBUG", "INFO", "WARNING", "ERROR" or "CRITICAL"), None = turn off logging
#settings.log.level = "WARNING"
# Configure the log level ("DEBUG", "INFO", "WARNING", "ERROR" or "CRITICAL"), None = turn off logging (default)
#settings.log.level = "ERROR" # DEBUG set automatically when base.debug is True
# Uncomment to prevent writing log messages to the console (sys.stderr)
#settings.log.console = False
# Configure a log file (file name)
@ -87,6 +87,9 @@ settings.base.debug = True
# - should happen automatically if installing using supported scripts
settings.auth.hmac_key = "${SAHANADEMO_HMAC}"
# If using Masterkey Authentication, then set this to a deployment-specific 32 char string:
#settings.auth.masterkey_app_key = "randomstringrandomstringrandomstring"
# Minimum Password Length
#settings.auth.password_min_length = 8
@ -103,7 +106,7 @@ settings.mail.server = "host:25"
settings.mail.sender = "admin@example.com"
# Default email address to which requests to approve new user accounts gets sent
# This can be overridden for specific domains/organisations via the auth_domain table
#settings.mail.approver = "useradmin@example.org"
settings.mail.approver = "admin@example.com"
# Daily Limit on Sending of emails
#settings.mail.limit = 1000
@ -215,10 +218,8 @@ settings.base.session_db = True
# See http://alerting.worldweather.org/ for oid
# Country root oid. The oid for the organisation includes this base
#settings.cap.identifier_oid = "2.49.0.0.608.0"
# Change this for the offset period in days that the alert will be effective for
# Expire Date = Effective Date + expire_offset
# Default is 2 days
#settings.cap.expire_offset = 2
# Set the period (in days) after which alert info segments expire (default=2)
#settings.cap.info_effective_period = 2
# =============================================================================
# Import the settings from the Template
@ -238,6 +239,7 @@ settings.import_template()
#settings.L10n.default_language = "en"
#settings.security.policy = 7 # Organisation-ACLs
# Enable Additional Module(s)
#from gluon.storage import Storage
#settings.modules["delphi"] = Storage(
# name_nice = T("Delphi Decision Maker"),
# restricted = False,

View File

@ -68,8 +68,8 @@ settings.base.debug = True
# Uncomment this to prevent automated test runs from remote
# settings.base.allow_testing = False
# Configure the log level ("DEBUG", "INFO", "WARNING", "ERROR" or "CRITICAL"), None = turn off logging
#settings.log.level = "WARNING"
# Configure the log level ("DEBUG", "INFO", "WARNING", "ERROR" or "CRITICAL"), None = turn off logging (default)
#settings.log.level = "ERROR" # DEBUG set automatically when base.debug is True
# Uncomment to prevent writing log messages to the console (sys.stderr)
#settings.log.console = False
# Configure a log file (file name)
@ -87,6 +87,9 @@ settings.base.debug = True
# - should happen automatically if installing using supported scripts
settings.auth.hmac_key = "${SAHANA_HMAC}"
# If using Masterkey Authentication, then set this to a deployment-specific 32 char string:
#settings.auth.masterkey_app_key = "randomstringrandomstringrandomstring"
# Minimum Password Length
#settings.auth.password_min_length = 8
@ -219,10 +222,8 @@ settings.base.session_db = True
# See http://alerting.worldweather.org/ for oid
# Country root oid. The oid for the organisation includes this base
#settings.cap.identifier_oid = "2.49.0.0.608.0"
# Change this for the offset period in days that the alert will be effective for
# Expire Date = Effective Date + expire_offset
# Default is 2 days
#settings.cap.expire_offset = 2
# Set the period (in days) after which alert info segments expire (default=2)
#settings.cap.info_effective_period = 2
# =============================================================================
# Import the settings from the Template
@ -242,6 +243,7 @@ settings.import_template()
#settings.L10n.default_language = "en"
#settings.security.policy = 7 # Organisation-ACLs
# Enable Additional Module(s)
#from gluon.storage import Storage
#settings.modules["delphi"] = Storage(
# name_nice = T("Delphi Decision Maker"),
# restricted = False,

View File

@ -88,6 +88,7 @@ s3.stylesheets = []
s3.external_stylesheets = []
# To get included at the end of <BODY>
s3.scripts = []
s3.scripts_modules = []
s3.js_global = []
s3.jquery_ready = []
@ -194,20 +195,23 @@ _settings.allow_basic_login = True
_settings.logout_onlogout = s3_auth_on_logout
_settings.login_onaccept = s3_auth_on_login
_settings.login_next = settings.get_auth_login_next()
# Now read in auth.login() to avoid setting unneccesarily in every request
#_settings.login_next = settings.get_auth_login_next()
if settings.has_module("vol") and \
settings.get_auth_registration_volunteer():
_settings.register_next = URL(c="vol", f="person")
# Languages available in User Profiles
if len(s3.l10n_languages) > 1:
_settings.table_user.language.requires = IS_IN_SET(s3.l10n_languages,
zero=None)
else:
field = _settings.table_user.language
field.default = s3.l10n_languages.keys()[0]
field.readable = False
field.writable = False
#if len(s3.l10n_languages) > 1:
# _settings.table_user.language.requires = s3base.IS_ISO639_2_LANGUAGE_CODE(sort = True,
# translate = True,
# zero = None,
# )
#else:
# field = _settings.table_user.language
# field.default = s3.l10n_languages.keys()[0]
# field.readable = False
# field.writable = False
_settings.lock_keys = True
@ -241,14 +245,14 @@ session.information = []
session.warning = []
# Shortcuts for system role IDs, see modules/s3aaa.py/AuthS3
system_roles = auth.get_system_roles()
ADMIN = system_roles.ADMIN
AUTHENTICATED = system_roles.AUTHENTICATED
ANONYMOUS = system_roles.ANONYMOUS
EDITOR = system_roles.EDITOR
MAP_ADMIN = system_roles.MAP_ADMIN
ORG_ADMIN = system_roles.ORG_ADMIN
ORG_GROUP_ADMIN = system_roles.ORG_GROUP_ADMIN
#system_roles = auth.get_system_roles()
#ADMIN = system_roles.ADMIN
#AUTHENTICATED = system_roles.AUTHENTICATED
#ANONYMOUS = system_roles.ANONYMOUS
#EDITOR = system_roles.EDITOR
#MAP_ADMIN = system_roles.MAP_ADMIN
#ORG_ADMIN = system_roles.ORG_ADMIN
#ORG_GROUP_ADMIN = system_roles.ORG_GROUP_ADMIN
if s3.debug:
# Add the developer toolbar from modules/s3/s3utils.py
@ -261,15 +265,15 @@ s3_formstyle = settings.get_ui_formstyle()
s3_formstyle_read = settings.get_ui_formstyle_read()
s3_formstyle_mobile = s3_formstyle
submit_button = T("Save")
_crud = s3.crud
_crud.formstyle = s3_formstyle
_crud.formstyle_read = s3_formstyle_read
_crud.submit_button = submit_button
s3_crud = s3.crud
s3_crud.formstyle = s3_formstyle
s3_crud.formstyle_read = s3_formstyle_read
s3_crud.submit_button = submit_button
# Optional class for Submit buttons
#_crud.submit_style = "submit-button"
_crud.confirm_delete = T("Do you really want to delete these records?")
_crud.archive_not_delete = settings.get_security_archive_not_delete()
_crud.navigate_away_confirm = settings.get_ui_navigate_away_confirm()
#s3_crud.submit_style = "submit-button"
s3_crud.confirm_delete = T("Do you really want to delete these records?")
s3_crud.archive_not_delete = settings.get_security_archive_not_delete()
s3_crud.navigate_away_confirm = settings.get_ui_navigate_away_confirm()
# Content Type Headers, default is application/xml for XML formats
# and text/x-json for JSON formats, other content types must be

View File

@ -20,6 +20,9 @@ def config(settings):
# In Production, prepopulate = 0 (to save 1x DAL hit every page)
settings.base.prepopulate.append("Spotter")
# Uncomment this to prefer scalability-optimized strategies globally
#settings.base.bigtable = True
# Theme (folder to use for views/layout.html)
#settings.base.theme = "default"
@ -35,6 +38,8 @@ def config(settings):
#settings.auth.registration_requires_verification = True
# Do new users need to be approved by an administrator prior to being able to login?
#settings.auth.registration_requires_approval = True
# Disable welcome-emails to newly registered users
#settings.auth.registration_welcome_email = False
# Allow a new user to be linked to a record (and a new record will be created if it doesn't already exist)
settings.auth.registration_link_user_to = {"staff":T("Staff"),
@ -72,8 +77,6 @@ def config(settings):
#settings.auth.registration_requests_site = True
# Uncomment this to allow Admin to see Organisations in User Admin even if the Registration doesn't request this
#settings.auth.admin_sees_organisation = True
# Uncomment to hide the UTC Offset in Registration/Profile
#settings.auth.show_utc_offset = False
# Uncomment to set the default role UUIDs assigned to newly-registered users
# This is a dictionary of lists, where the key is the realm that the list of roles applies to
# The key 0 implies not realm restricted
@ -104,14 +107,20 @@ def config(settings):
# https://termsfeed.com/terms-conditions/generator/
# uses <template>/views/tos.html
settings.auth.terms_of_service = True
# Enable options for tracking user consent
settings.auth.consent_tracking = True
# Uncomment this to allow users to Login using Gmail's SMTP
#settings.auth.gmail_domains = ["gmail.com"]
# Uncomment this to allow users to Login using Office365's SMTP
#settings.auth.office365_domains = ["microsoft.com"]
# Uncomment this to allow users to Login using OpenID
#settings.auth.openid = True
# Uncomment this to allow users to login using master key
#settings.auth.masterkey = True
# Uncomment this to block password changes since managed externally (OpenID / SMTP / LDAP)
#settings.auth.password_changes = True
#settings.auth.password_changes = False
# Uncomment this to disable password retrieval (e.g. if impractical or unsafe)
#settings.auth.password_retrieval = False
# Uncomment this to enable presence records on login based on HTML5 geolocations
#settings.auth.set_presence_on_login = True
# Uncomment this and specify a list of location levels to be ignored by presence records
@ -120,7 +129,7 @@ def config(settings):
#settings.auth.create_unknown_locations = True
# L10n settings
# Languages used in the deployment (used for Language Toolbar & GIS Locations)
# Languages used in the deployment (used for Language Toolbar, GIS Locations, etc)
# http://www.loc.gov/standards/iso639-2/php/code_list.php
languages = OrderedDict([
("cs", "Czech"),
@ -133,7 +142,7 @@ def config(settings):
# Uncomment to Hide the language toolbar
#settings.L10n.display_toolbar = False
# Default timezone for users
#settings.L10n.utc_offset = "+0000"
settings.L10n.timezone = "Europe/Prague"
# Uncomment these to use US-style dates in English
#settings.L10n.date_format = "%m-%d-%Y"
#settings.L10n.time_format = "%H:%M:%S"
@ -185,10 +194,14 @@ def config(settings):
#settings.fin.currency_writable = False # False currently breaks things
# PDF settings
# Default page size for reports (defaults to A4)
#settings.base.paper_size = T("Letter")
# Default page size (defaults to A4)
#settings.base.pdf_size = "Letter"
# Default page orientation (defaults to "Auto" to auto-adapt for wide tables)
#settings.base.pdf_orientation = "Landscape"
# Location of Logo used in pdfs headers
#settings.ui.pdf_logo = "static/img/mylogo.png"
# Maximum number of records in PDF exports (None for unlimited)
#settings.base.pdf_max_rows = 1000
#Uncomment to add a title row to XLS exports
#settings.base.xls_title_row = True
@ -263,6 +276,8 @@ def config(settings):
#settings.gis.layer_tree_radio = True
# Uncomment to display the Map Legend as a floating DIV
settings.gis.legend = "float"
# Uncomment to use scalability-optimized options lookups in location filters
#settings.gis.location_filter_bigtable_lookups = True
# Uncomment to prevent showing LatLon in Location Represents
#settings.gis.location_represent_address_only = True
# Mouse Position: 'normal', 'mgrs' or None
@ -414,10 +429,18 @@ def config(settings):
settings.ui.default_cancel_button = True
# Uncomment to disable responsive behavior of datatables
#settings.ui.datatables_responsive = False
# Uncomment to enable double scroll bars on non-responsive datatables
#settings.ui.datatables_double_scroll = True
# Uncomment to modify the label of the Permalink
#settings.ui.label_permalink = "Permalink"
# Uncomment to modify the main menu logo
#settings.ui.menu_logo = URL(c="static", f="img", args=["S3menulogo.png"])
# Configure business hours to indicate in organizer (can be a list)
#settings.ui.organizer_business_hours = {"dow": [1,2,3,4,5], "start": "08:00", "end": "18:00"}
# Configure a time format for organizer events to override locale default
#settings.ui.organizer_time_format = "H:mm"
# Configure the snap raster width in organizer (hh:mm:ss)
#settings.ui.organizer_snap_duration = "00:15:00"
# -------------------------------------------------------------------------
# Sync
@ -429,6 +452,115 @@ def config(settings):
# Uncomment to have a specific asset type for Telephones
settings.asset.telephones = True
# -------------------------------------------------------------------------
# Beneficiary Registry
# --- Terminology ---
# Terminology to use when referring to cases (Beneficiary|Client|Case)
#settings.br.case_terminology = "Beneficiary"
# Terminology to use when referring to measures of assistance (Counseling|Assistance)
#settings.br.assistance_terminology = "Counseling"
# --- Need Categories ---
# Use hierarchical need categories
#settings.br.needs_hierarchical = True
# Let all orgs use a common set of need categories
#settings.br.needs_org_specific = False
# --- Basic Case Options ---
# Show the case organisation even if only one option
#settings.br.case_hide_default_org = False
# Disable assignment of cases to staff
#settings.br.case_manager = False
# Expose fields to track home address in case file
#settings.br.case_address = True
# Disable documentation of language details in case file
#settings.br.case_language_details = False
# Control household size tracking in case files: False, True or "auto" (=default)
#settings.br.household_size = "auto"
# Layout class for beneficiary ID cards
#settings.br.id_card_layout = IDCardLayout
# User roles with permission to export beneficiary ID cards
#settings.br.id_card_export_roles = ["ORG_ADMIN", "CASE_MANAGEMENT"]
# --- Case File Tabs ---
# Hide the contact info tab in case files
#settings.br.case_contacts_tab = False
# Show the ID-tab in case files
#settings.br.case_id_tab = True
# Hide the family members tab in case files
#settings.br.case_family_tab = False
# Enable case file tab to track service contacts
#settings.br.service_contacts = True
# Show tab with notes journal
#settings.br.case_notes_tab = True
# Show the photos-tab in case files
#settings.br.case_photos_tab = True
# Hide the documents-tab in case files
#settings.br.case_documents_tab = False
# --- Attachments ---
# Hide activity attachments from case documents-tab
#settings.br.case_include_activity_docs = False
# Hide case group attachments from case documents-tab
#settings.br.case_include_group_docs = False
# --- Case Activities ---
# Disable tracking of case activities
#settings.br.case_activities = False
# Disable assignment of case activities to staff
#settings.br.case_activity_manager = False
# Expose "urgent" priority for case activities (=emergencies)
#settings.br.case_activity_urgent_option = True
# Disable need categories in case activities
#settings.br.case_activity_need = False
# Use a free-text subject line in case activities
#settings.br.case_activity_subject = True
# Use a free-text field to document need details in case activities
#settings.br.case_activity_need_details = True
# Disable status and end-date for case activities
#settings.br.case_activity_status = False
# Show end-date of case activites (True=show, "writable"=allow manual edit)
#settings.br.case_activity_end_date = True
# Enable inline-updates of case activities
#settings.br.case_activity_updates = True
# Disable fields for outcome documentation
#settings.br.case_activity_outcome = False
# Allow documents to be attached to case activities
#settings.br.case_activity_documents = True
# --- Assistance Measures ---
# Disable tracking of individual assistance measures
#settings.br.manage_assistance = False
# Use separate tab to track assistance measures
#settings.br.assistance_tab = True
# Hide inline assistance measures on case activity tab
#settings.br.assistance_inline = False
# Document date+time (rather than only date) for assistance measures
#settings.br.assistance_measures_use_time = True
# Set default status of assistance measures to closed
#settings.br.assistance_measure_default_closed = True
# Disable assignment of assistance measures to staff
#settings.br.assistance_manager = False
# Disable types of assistance (e.g. if there is only one type)
#settings.br.assistance_types = False
# --- Assistance Themes ---
# Enable assistance themes
#settings.br.assistance_themes = True
# Use a common set of assistance themes rather than org-specific
#settings.br.assistance_themes_org_specific = False
# Organize assistance themes by org sector
#settings.br.assistance_themes_sectors = True
# Organize assistance themes by need type
#settings.br.assistance_themes_needs = True
# Document assistance details per theme
#settings.br.assistance_details_per_theme = True
# Enable auto-linking of assistance measure details to case activities
#settings.br.assistance_activity_autolink = True
# Disable tracking of effort (=hours spent) for assistance measures
#settings.br.assistance_track_effort = False
# -------------------------------------------------------------------------
# CMS
# Uncomment this to hide CMS from module index pages
@ -478,9 +610,6 @@ def config(settings):
# Uncomment to use the term Beneficiary instead of Case
#settings.dvr.label = "Beneficiary"
# Uncomment this to allow cases to belong to multiple case groups ("households")
#settings.dvr.multiple_case_groups = True
# Uncomment this to enable tracking of transfer origin/destination sites
#settings.dvr.track_transfer_sites = True
# Uncomment this to enable features to manage transferability of cases
@ -511,6 +640,13 @@ def config(settings):
#settings.dvr.activity_types_hierarchical = True
# Uncomment this to use status field in case activities
#settings.dvr.case_activity_use_status = True
# Uncomment this to disable follow-up fields in case activities
#settings.dvr.case_activity_follow_up = False
# Uncomment this to include case activity docs on beneficiary documents-tab
#settings.dvr.case_include_activity_docs = True
# Uncomment this to include case group docs on beneficiary documents-tab
#settings.dvr.case_include_group_docs = True
# Uncomment this if Case activities use multiple Needs
#settings.dvr.case_activity_needs_multiple = True
@ -523,6 +659,24 @@ def config(settings):
# Uncomment this to manage individual response actions in case activities
#settings.dvr.manage_response_actions = True
# Uncomment this to not use response action types
#settings.dvr.response_types = False
# Uncomment this to use response themes
#settings.dvr.response_themes = True
# Uncomment this to not use org-specific response themes
#settings.dvr.response_themes_org_specific = False
# Uncomment this to link response themes to org sectors
#settings.dvr.response_themes_sectors = True
# Uncomment this to link response themes to needs
#settings.dvr.response_themes_needs = True
# Uncomment this to automatically link responses to case activities
#settings.dvr.response_activity_autolink = True
# Uncomment this to activate features for response planning
#settings.dvr.response_planning = True
# Uncomment this to use a separate due-date for responses
#settings.dvr.response_due_date = True
# Uncomment this to use date+time for responses (instead of just date)
#settings.dvr.response_use_time = True
# Configure a regular expression pattern for ID Codes (QR Codes)
#settings.dvr.id_code_pattern = "(?P<label>[^,]*),(?P<first_name>[^,]*),(?P<last_name>[^,]*),(?P<date_of_birth>[^,]*)"
@ -594,6 +748,8 @@ def config(settings):
#settings.pr.use_address = False
# Show separate Public and Private Contacts Tabs
settings.pr.contacts_tabs = ("public", "private")
# Uncomment this to allow persons to belong to multiple case groups ("households")
#settings.pr.multiple_case_groups = True
# -------------------------------------------------------------------------
# Organisations
@ -644,8 +800,6 @@ def config(settings):
#settings.org.site_inv_req_tabs = False
# Uncomment to allow Sites to be staffed by Volunteers
settings.org.site_volunteers = True
# Uncomment to add summary fields for Organisations/Offices for # National/International staff
#settings.org.summary = True
# Enable certain fields just for specific Organisations
# Requires a call to settings.set_org_dependent_field(field)
# empty list => disabled for all (including Admin)
@ -675,7 +829,7 @@ def config(settings):
#settings.hrm.email_required = False
# Uncomment to allow Staff & Volunteers to be registered without an Organisation
settings.hrm.org_required = False
# Uncomment to if their are only Staff & Volunteers from a single Organisation with no Branches
# Uncomment if their are only Staff & Volunteers from a single Organisation with no Branches
#settings.hrm.multiple_orgs = False
# Uncomment to disable the 'Send Message' action button
#settings.hrm.compose_button = False
@ -687,6 +841,8 @@ def config(settings):
settings.hrm.multiple_job_titles = True
# Uncomment to have each root Org use a different Job Title Catalog
settings.hrm.org_dependent_job_titles = True
# Uncomment to display & search by National ID
#settings.hrm.use_national_id = True
# Uncomment to hide the Staff resource
#settings.hrm.show_staff = False
# Uncomment to have Staff use their Home Address as fallback if they have no Site defined
@ -939,8 +1095,6 @@ def config(settings):
settings.project.programmes = True
# Uncomment this to enable Budgets in Programmes
settings.project.programme_budget = True
# Uncomment this to use Tags in Tasks
settings.project.task_tag = True
# Uncomment this to enable Themes in 3W projects
settings.project.themes = True
# Uncomment this to use Theme Percentages for projects
@ -980,13 +1134,6 @@ def config(settings):
# Uncomment to make Heliport codes unique
#settings.transport.heliport_code_unique = True
# -------------------------------------------------------------------------
# CAP
settings.get_cap_languages = OrderedDict([
("cs", "Czech"),
("en-US", "English"),
])
# -------------------------------------------------------------------------
# Filter Manager
#settings.search.filter_manager = False
@ -1041,6 +1188,13 @@ def config(settings):
restricted = False,
module_type = None # No Menu
)),
("setup", Storage(
name_nice = T("Setup"),
#description = "WebSetup",
restricted = True,
access = "|1|", # Only Administrators can see this module in the default menu & access the controller
module_type = None # No Menu
)),
("sync", Storage(
name_nice = T("Synchronization"),
#description = "Synchronization",
@ -1057,13 +1211,6 @@ def config(settings):
#description = "Selective translation of strings based on module.",
module_type = None,
)),
# Uncomment to enable internal support requests
#("support", Storage(
# name_nice = T("Support"),
# #description = "Support Requests",
# restricted = True,
# module_type = None # This item is handled separately for the menu
# )),
("gis", Storage(
name_nice = T("Map"),
#description = "Situation Awareness & Geospatial Analysis",
@ -1165,10 +1312,10 @@ def config(settings):
# module_type = 5,
#)),
("dc", Storage(
name_nice = T("Data Collection"),
name_nice = T("Assessments"),
#description = "Data collection tool",
restricted = True,
module_type = 10
module_type = 5
)),
("cr", Storage(
name_nice = T("Shelters"),
@ -1188,6 +1335,12 @@ def config(settings):
# restricted = True,
# module_type = 10
#)),
#("br", Storage(
# name_nice = T("Beneficiary Registry"),
# #description = "Beneficiary Registry and Case Management",
# restricted = True,
# module_type = 10,
#)),
("dvr", Storage(
name_nice = T("Disaster Victim Registry"),
#description = "Allow affected individuals & households to register to receive compensation and distributions",
@ -1262,7 +1415,7 @@ def config(settings):
module_type = 10,
)),
("fire", Storage(
name_nice = T("Firefighters"),
name_nice = T("Fire Stations"),
#description = "Fire Station Management",
restricted = True,
module_type = 1,

View File

@ -18,6 +18,7 @@ SITE_DEFAULT,,"State / Province","County / District","City / Town / Village","Vi
,BR,State,Municipality,District,,
,BT,District,Sub-District,,
,BZ,District,,,
,CD,Province,Territory/Commune,,,,
,CF,Prefecture,Sub-Prefecture,Commune,,
,CK,Island Council,Village Committee,,,
,CL,Region,Province,Commune,,
@ -30,6 +31,7 @@ SITE_DEFAULT,,"State / Province","County / District","City / Town / Village","Vi
,EC,Province,Canton,Parish,,,
,FI,Regional State Administrative Agency,Region,Sub-region,Municipality,,
,FJ,Division,Province,District,,,,
,GB,Country,County,District,Parish,,
,GE,"Region / Republic",District,,,,
,GN,Region,Prefecture,Sub-prefecture,,,
,GR,Decentralized Administration,Region,Regional Unit,Municipality,,
@ -89,3 +91,4 @@ SITE_DEFAULT,,"State / Province","County / District","City / Town / Village","Vi
,VU,Province,Area Council,,
,VN,Province,District,Commune,,,
,XK,District,Municipality,,,,
,YE,Governorate,District,Sub-District,Village,,

Can't render this file because it has a wrong number of fields in line 6.

View File

@ -5,7 +5,7 @@
Template-specific Monitoring Tasks are defined here.
@copyright: 2014-2018 (c) Sahana Software Foundation
@copyright: 2014-2019 (c) Sahana Software Foundation
@license: MIT
Permission is hereby granted, free of charge, to any person
@ -67,7 +67,7 @@ class S3Monitor(object):
try:
output = subprocess.check_output("ping -{} 1 {}".format("n" if platform.system().lower == "windows" else "c", host_ip), shell=True)
except Exception, e:
except Exception as e:
# Critical: Ping failed
return 3
else:

View File

@ -1,4 +1,4 @@
Name,Comments
Type,Comments
Academic,
Bilateral,Donor
Government,

1 Name Type Comments
2 Academic
3 Bilateral Donor
4 Government

View File

@ -5,7 +5,7 @@
Template-specific Message Parsers are defined here.
@copyright: 2012-2017 (c) Sahana Software Foundation
@copyright: 2012-2019 (c) Sahana Software Foundation
@license: MIT
Permission is hereby granted, free of charge, to any person

View File

@ -1,6 +1,6 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="{{=T.accepted_language or "en"}}">{{# class="no-js" needed for modernizr }}
<head>{{theme=response.s3.theme}}
<head>{{theme_styles=response.s3.theme_styles}}
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
{{# Always force latest IE rendering engine (even in intranet) & Chrome Frame }}
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
@ -31,7 +31,7 @@
{{pass}}
{{if s3.debug:}}{{=s3base.s3_include_debug_css()}}{{else:}}
{{# Built by /static/scripts/tools/build.sahana.py }}
<link href="/{{=appname}}/static/themes/{{=theme}}/eden.min.css" rel="stylesheet" type="text/css" />
<link href="/{{=appname}}/static/themes/{{=theme_styles}}/eden.min.css" rel="stylesheet" type="text/css" />
{{pass}}
{{include "scripts_top.html"}}
</head>
@ -84,7 +84,7 @@
<footer role="complementary" class="footer">
<div class='row' id='footer'>
<div class='medium-12 columns'>
{{include "../modules/templates/%s/views/footer.html" % response.s3.theme}}
{{include "../modules/templates/%s/views/footer.html" % response.s3.theme_layouts}}
{{if s3.debug:}}
<div class="row">
<div class='medium-12 columns'>{{=s3.toolbar()}}</div>

View File

@ -47,8 +47,8 @@ settings.database.password = "${SAMBRO_PWD}"
settings.gis.spatialdb = True
# Base settings
#settings.base.system_name = T("Sahana Eden Humanitarian Management Platform")
#settings.base.system_name_short = T("Sahana Eden")
settings.base.system_name = T("Sahana Alerting and Messaging Broker")
settings.base.system_name_short = T("SAMBRO")
# Set this to the Public URL of the instance
settings.base.public_url = "https://sambro.spotter.vm"
@ -68,8 +68,8 @@ settings.base.debug = True
# Uncomment this to prevent automated test runs from remote
# settings.base.allow_testing = False
# Configure the log level ("DEBUG", "INFO", "WARNING", "ERROR" or "CRITICAL"), None = turn off logging
#settings.log.level = "WARNING"
# Configure the log level ("DEBUG", "INFO", "WARNING", "ERROR" or "CRITICAL"), None = turn off logging (default)
#settings.log.level = "ERROR" # DEBUG set automatically when base.debug is True
# Uncomment to prevent writing log messages to the console (sys.stderr)
#settings.log.console = False
# Configure a log file (file name)
@ -87,6 +87,9 @@ settings.base.debug = True
# - should happen automatically if installing using supported scripts
settings.auth.hmac_key = "${SAMBRO_HMAC}"
# If using Masterkey Authentication, then set this to a deployment-specific 32 char string:
#settings.auth.masterkey_app_key = "randomstringrandomstringrandomstring"
# Minimum Password Length
#settings.auth.password_min_length = 8
@ -215,10 +218,8 @@ settings.base.session_db = True
# See http://alerting.worldweather.org/ for oid
# Country root oid. The oid for the organisation includes this base
#settings.cap.identifier_oid = "2.49.0.0.608.0"
# Change this for the offset period in days that the alert will be effective for
# Expire Date = Effective Date + expire_offset
# Default is 2 days
#settings.cap.expire_offset = 2
# Set the period (in days) after which alert info segments expire (default=2)
#settings.cap.info_effective_period = 2
# =============================================================================
# Import the settings from the Template
@ -238,6 +239,7 @@ settings.import_template()
settings.L10n.default_language = "cs"
#settings.security.policy = 7 # Organisation-ACLs
# Enable Additional Module(s)
#from gluon.storage import Storage
#settings.modules["delphi"] = Storage(
# name_nice = T("Delphi Decision Maker"),
# restricted = False,
@ -247,12 +249,12 @@ settings.L10n.default_language = "cs"
# - NB Only templates with adaptive menus will work nicely with this!
#del settings.modules["irs"]
# After 1st_run, set this for Production to save 1x DAL hit/request
#settings.base.prepopulate = 0
# Production instances should set this before prepopulate is run
#settings.base.prepopulate_demo = 0
# After 1st_run, set this for Production to save 1x DAL hit/request
#settings.base.prepopulate = 0
# =============================================================================
# A version number to tell update_check if there is a need to refresh the
# running copy of this file

View File

@ -88,6 +88,7 @@ s3.stylesheets = []
s3.external_stylesheets = []
# To get included at the end of <BODY>
s3.scripts = []
s3.scripts_modules = []
s3.js_global = []
s3.jquery_ready = []
@ -194,20 +195,23 @@ _settings.allow_basic_login = True
_settings.logout_onlogout = s3_auth_on_logout
_settings.login_onaccept = s3_auth_on_login
_settings.login_next = settings.get_auth_login_next()
# Now read in auth.login() to avoid setting unneccesarily in every request
#_settings.login_next = settings.get_auth_login_next()
if settings.has_module("vol") and \
settings.get_auth_registration_volunteer():
_settings.register_next = URL(c="vol", f="person")
# Languages available in User Profiles
if len(s3.l10n_languages) > 1:
_settings.table_user.language.requires = IS_IN_SET(s3.l10n_languages,
zero=None)
else:
field = _settings.table_user.language
field.default = s3.l10n_languages.keys()[0]
field.readable = False
field.writable = False
#if len(s3.l10n_languages) > 1:
# _settings.table_user.language.requires = s3base.IS_ISO639_2_LANGUAGE_CODE(sort = True,
# translate = True,
# zero = None,
# )
#else:
# field = _settings.table_user.language
# field.default = s3.l10n_languages.keys()[0]
# field.readable = False
# field.writable = False
_settings.lock_keys = True
@ -241,14 +245,14 @@ session.information = []
session.warning = []
# Shortcuts for system role IDs, see modules/s3aaa.py/AuthS3
system_roles = auth.get_system_roles()
ADMIN = system_roles.ADMIN
AUTHENTICATED = system_roles.AUTHENTICATED
ANONYMOUS = system_roles.ANONYMOUS
EDITOR = system_roles.EDITOR
MAP_ADMIN = system_roles.MAP_ADMIN
ORG_ADMIN = system_roles.ORG_ADMIN
ORG_GROUP_ADMIN = system_roles.ORG_GROUP_ADMIN
#system_roles = auth.get_system_roles()
#ADMIN = system_roles.ADMIN
#AUTHENTICATED = system_roles.AUTHENTICATED
#ANONYMOUS = system_roles.ANONYMOUS
#EDITOR = system_roles.EDITOR
#MAP_ADMIN = system_roles.MAP_ADMIN
#ORG_ADMIN = system_roles.ORG_ADMIN
#ORG_GROUP_ADMIN = system_roles.ORG_GROUP_ADMIN
if s3.debug:
# Add the developer toolbar from modules/s3/s3utils.py
@ -261,15 +265,15 @@ s3_formstyle = settings.get_ui_formstyle()
s3_formstyle_read = settings.get_ui_formstyle_read()
s3_formstyle_mobile = s3_formstyle
submit_button = T("Save")
_crud = s3.crud
_crud.formstyle = s3_formstyle
_crud.formstyle_read = s3_formstyle_read
_crud.submit_button = submit_button
s3_crud = s3.crud
s3_crud.formstyle = s3_formstyle
s3_crud.formstyle_read = s3_formstyle_read
s3_crud.submit_button = submit_button
# Optional class for Submit buttons
#_crud.submit_style = "submit-button"
_crud.confirm_delete = T("Do you really want to delete these records?")
_crud.archive_not_delete = settings.get_security_archive_not_delete()
_crud.navigate_away_confirm = settings.get_ui_navigate_away_confirm()
#s3_crud.submit_style = "submit-button"
s3_crud.confirm_delete = T("Do you really want to delete these records?")
s3_crud.archive_not_delete = settings.get_security_archive_not_delete()
s3_crud.navigate_away_confirm = settings.get_ui_navigate_away_confirm()
# Content Type Headers, default is application/xml for XML formats
# and text/x-json for JSON formats, other content types must be

View File

@ -19,11 +19,15 @@ def config(settings):
T = current.T
settings.base.system_name = T("Sahana Alerting and Messaging Broker")
settings.base.system_name_short = T("SAMBRO")
## Deprecated because such edits to the title should happen in the 000_config.py file
## specific to the demo deployment and not here because if you change here it affect any
## developments and commits to git etc - nuwan at sahanafoundation dot org
#settings.base.system_name = T("Sahana Alerting and Messaging Broker")
#settings.base.system_name_short = T("SAMBRO")
##
# Pre-Populate
settings.base.prepopulate += ("SAMBRO",)
settings.base.prepopulate_demo += ("SAMBRO/Demo",)
# Theme (folder to use for views/layout.html)
#settings.base.theme = "SAMBRO"
@ -156,12 +160,12 @@ def config(settings):
parser_id = table.insert(channel_id=channel_id, function_name=fn, enabled=True)
s3db.msg_parser_enable(parser_id)
async = current.s3task.async
run_async = current.s3task.run_async
# Poll
async("msg_poll", args=["msg_rss_channel", channel_id])
run_async("msg_poll", args=["msg_rss_channel", channel_id])
# Parse
async("msg_parse", args=[channel_id, fn])
run_async("msg_parse", args=[channel_id, fn])
s3db.configure(tablename,
create_onaccept = onaccept,
@ -173,9 +177,8 @@ def config(settings):
def customise_msg_rss_channel_controller(**attr):
s3 = current.response.s3
table = current.s3db.msg_rss_channel
type = current.request.get_vars.get("type", None)
if type == "cap":
channel_type = current.request.get_vars.get("type", None)
if channel_type == "cap":
# CAP RSS Channel
s3.filter = (FS("type") == "cap")
s3.crud_strings["msg_rss_channel"] = Storage(
@ -202,7 +205,7 @@ def config(settings):
if r.interactive and isinstance(output, dict):
# Modify Open Button
if type == "cap":
if channel_type == "cap":
# CAP RSS Channel
table = r.table
query = (table.deleted == False)
@ -212,43 +215,51 @@ def config(settings):
restrict_e = [str(row.id) for row in rows if not row.enabled]
restrict_d = [str(row.id) for row in rows if row.enabled]
s3.actions = [dict(label=s3_str(T("Open")),
_class="action-btn edit",
url=URL(args=["[id]", "update"],
vars={"type": "cap"}),
s3.actions = [{"label": s3_str(T("Open")),
"_class": "action-btn edit",
"url": URL(args = ["[id]", "update"],
vars = {"type": "cap"},
),
dict(label=s3_str(T("Delete")),
_class="delete-btn",
url=URL(args=["[id]", "delete"],
vars={"type": "cap"}),
},
{"label": s3_str(T("Delete")),
"_class": "delete-btn",
"url": URL(args = ["[id]", "delete"],
vars = {"type": "cap"},
),
dict(label=s3_str(T("Subscribe")),
_class="action-btn",
url=URL(args=["[id]", "enable"],
vars={"type": "cap"}),
restrict = restrict_e),
dict(label=s3_str(T("Unsubscribe")),
_class="action-btn",
url = URL(args = ["[id]", "disable"],
vars={"type": "cap"}),
restrict = restrict_d),
},
{"label": s3_str(T("Subscribe")),
"_class": "action-btn",
"url": URL(args = ["[id]", "enable"],
vars = {"type": "cap"},
),
"restrict": restrict_e,
},
{"label": s3_str(T("Unsubscribe")),
"_class": "action-btn",
"url": URL(args = ["[id]", "disable"],
vars = {"type": "cap"},
),
"restrict": restrict_d
},
]
if not current.s3task._is_alive():
# No Scheduler Running
s3.actions += [dict(label=s3_str(T("Poll")),
_class="action-btn",
url = URL(args = ["[id]", "poll"],
vars={"type": "cap"}),
restrict = restrict_d)
]
s3.actions.append({"label": s3_str(T("Poll")),
"_class": "action-btn",
"url": URL(args = ["[id]", "poll"],
vars = {"type": "cap"},
),
"restrict": restrict_d,
})
if "form" in output and current.auth.s3_has_role("ADMIN"):
# Modify Add Button
add_btn = A(T("Add CAP Feed"),
_class="action-btn",
_href=URL(args=["create"],
vars={"type": "cap"})
_class = "action-btn",
_href = URL(args = ["create"],
vars = {"type": "cap"},
),
)
output["showadd_btn"] = add_btn
@ -276,12 +287,12 @@ def config(settings):
_id = table.insert(channel_id=channel_id, function_name="parse_tweet", enabled=True)
s3db.msg_parser_enable(_id)
async = current.s3task.async
run_async = current.s3task.run_async
# Poll
async("msg_poll", args=["msg_twitter_channel", channel_id])
run_async("msg_poll", args=["msg_twitter_channel", channel_id])
# Parse
async("msg_parse", args=[channel_id, "parse_tweet"])
run_async("msg_parse", args=[channel_id, "parse_tweet"])
s3db.configure(tablename,
create_onaccept = onaccept,
@ -386,12 +397,12 @@ def config(settings):
s3db = current.s3db
def onapprove(record):
# Normal onapprove
s3db.cap_alert_approve(record)
s3db.cap_alert_onapprove(record)
async_task = current.s3task.async
run_async = current.s3task.run_async
# Sync FTP Repository
async_task("cap_ftp_sync")
run_async("cap_ftp_sync")
# @ToDo: Check for LEFT join when required
# this is ok for now since every Alert should have an Info & an Area
@ -432,7 +443,7 @@ def config(settings):
if len(rows):
registration_ids = [s3_str(row.value) for row in rows]
title = get_email_subject(arow, system=False)
async_task("msg_gcm", args=[title,
run_async("msg_gcm", args=[title,
"%s/%s" % (s3_str(arow["cap_info.web"]), "profile"),
s3_str(get_formatted_value(arow["cap_info.headline"],
system=False)),
@ -445,25 +456,35 @@ def config(settings):
except ImportError:
current.log.debug("tweepy module needed for sending tweets")
else:
url = "%s/%s" % (arow["cap_info.web"], "profile")
try:
from pyshorteners import Shortener
except ImportError:
pass
else:
try:
url = Shortener('Tinyurl', timeout=3).short(url)
except:
pass
twitter_text = \
("""%(status)s Alert: %(headline)s
%(sender)s: %(sender_name)s
%(website)s: %(Website)s%(profile)s""") % {"status": s3_str(T(arow["cap_alert.status"])),
%(website)s: %(Website)s""") % {"status": s3_str(T(arow["cap_alert.status"])),
"headline": s3_str(get_formatted_value(arow["cap_info.headline"],
system=False)),
"sender": s3_str(T("Sender")),
"sender_name": s3_str(get_formatted_value(arow["cap_info.sender_name"],
system=False)),
"website": s3_str(T("Website")),
"Website": s3_str(arow["cap_info.web"]),
"profile": "/profile",
"Website": s3_str(url),
}
try:
# @ToDo: shorten url
# @ToDo: Handle the multi-message nicely?
# @ToDo: Send resource url with tweet
current.msg.send_tweet(text=s3_str(twitter_text))
except tweepy.error.TweepError, e:
current.msg.send_tweet(text=s3_str(twitter_text),
alert_id=alert_id,
)
except tweepy.error.TweepError as e:
current.log.debug("Sending tweets failed: %s" % e)
# Facebook Post
@ -471,8 +492,10 @@ def config(settings):
# @ToDo: post resources too?
content = get_facebook_content(arow)
try:
current.msg.post_to_facebook(text=content)
except Exception, e:
current.msg.post_to_facebook(text=content,
alert_id=alert_id,
)
except Exception as e:
current.log.debug("Posting Alert to Facebook failed: %s" % e)
addresses = record["addresses"]
@ -507,9 +530,16 @@ def config(settings):
send_by_pe_id(row.pe_id,
subject,
email_content,
document_ids=cap_document_id)
document_ids=cap_document_id,
alert_id=alert_id,
)
try:
send_by_pe_id(row.pe_id, subject, sms_content, contact_method="SMS")
send_by_pe_id(row.pe_id,
subject,
sms_content,
contact_method="SMS",
alert_id=alert_id,
)
except ValueError:
current.log.error("No SMS Handler defined!")
else:
@ -522,9 +552,16 @@ def config(settings):
send_by_pe_id(row.pe_id,
subject,
email_content,
document_ids=cap_document_id)
document_ids=cap_document_id,
alert_id=alert_id,
)
try:
send_by_pe_id(row.pe_id, subject, sms_content, contact_method="SMS")
send_by_pe_id(row.pe_id,
subject,
sms_content,
contact_method="SMS",
alert_id=alert_id,
)
except ValueError:
current.log.error("No SMS Handler defined!")
@ -734,7 +771,6 @@ def config(settings):
notify_on = meta_data["notify_on"]
last_check_time = meta_data["last_check_time"]
rows = data["rows"]
rfields = data["rfields"]
output = {}
upd = [] # upd as the created alerts might be approved after some time, check is also done
@ -793,18 +829,17 @@ def config(settings):
"""
rows = data["rows"]
subject = "%s %s" % (current.deployment_settings.get_system_name_short(),
subject = "%s %s" % (settings.get_system_name_short(),
T("Alert Notification"))
if len(rows) == 1:
# Since if there are more than one row, the single email has content
# for all rows
last_check_time = meta_data["last_check_time"]
db = current.db
atable = current.s3db.cap_alert
row_ = db(atable.id == rows[0]["cap_alert.id"]).select(atable.approved_on,
limitby=(0, 1)).first()
row_ = current.db(atable.id == rows[0]["cap_alert.id"]).select(atable.approved_on,
limitby=(0, 1)
).first()
if row_ and row_.approved_on is not None:
if s3_utc(row_.approved_on) >= last_check_time:
if s3_utc(row_.approved_on) >= meta_data["last_check_time"]:
subject = get_email_subject(rows[0])
return subject
@ -832,6 +867,44 @@ def config(settings):
settings.msg.notify_attachment = custom_msg_notify_attachment
# -----------------------------------------------------------------------------
def custom_msg_notify_send_data(resource, data, meta_data):
"""
Custom Method to send data containing alert_id to the s3msg.send_by_pe_id
@param resource: the S3Resource
@param data: the data returned from S3Resource.select
@param meta_data: the meta data for the notification
"""
rows = data.rows
data = {}
if len(rows) == 1:
row = rows[0]
if "cap_alert.id" in row:
try:
alert_id = int(row["cap_alert.id"])
data["alert_id"] = alert_id
except ValueError:
pass
return data
settings.msg.notify_send_data = custom_msg_notify_send_data
# -----------------------------------------------------------------------------
def msg_send_postprocess(message_id, **data):
"""
Custom function that links alert_id in cap module to message_id in
message module
"""
alert_id = data.get("alert_id", None)
if alert_id and message_id:
current.s3db.cap_alert_message.insert(alert_id = alert_id,
message_id = message_id)
settings.msg.send_postprocess = msg_send_postprocess
# -------------------------------------------------------------------------
# Comment/uncomment modules here to disable/enable them
# @ToDo: Have the system automatically enable migrate if a module is enabled
@ -969,11 +1042,13 @@ def config(settings):
rows = db(query).select(stable.filter_id,
ftable.query,
left=left)
filter_options = {}
if len(rows) > 0:
T = current.T
etable = s3db.event_event_type
ptable = s3db.cap_warning_priority
filter_options = {}
from s3 import IS_ISO639_2_LANGUAGE_CODE
languages_dict = dict(IS_ISO639_2_LANGUAGE_CODE.language_codes())
for row in rows:
@ -1037,6 +1112,16 @@ def config(settings):
description = row["_row"]["cap_info.description"] if system else row["cap_info.description"]
status = row["cap_alert.status"]
msg_type = row["cap_alert.msg_type"]
url = "%s/%s" % (row["cap_info.web"], "profile")
try:
from pyshorteners import Shortener
except ImportError:
pass
else:
try:
url = Shortener('Tinyurl', timeout=3).short(url)
except:
pass
if event_type_id and event_type_id != current.messages["NONE"]:
if not isinstance(event_type_id, lazyT) and \
@ -1063,8 +1148,7 @@ def config(settings):
if status != "Actual" else "",
BR() if status != "Actual" else "",
BR() if status != "Actual" else "",
A(T("VIEW ALERT ON THE WEB"),
_href = "%s/%s" % (s3_str(row["cap_info.web"]), "profile")),
A(T("VIEW ALERT ON THE WEB"), _href = s3_str(url)),
BR(), BR(),
B(s3_str("%s %s %s %s" % (T(row["cap_alert.scope"]),
T(status),
@ -1146,7 +1230,7 @@ def config(settings):
},
BR(), BR(),
T("For more details visit %(url)s or contact %(contact)s") % \
{"url": "%s/%s" % ((s3_str(row["cap_info.web"])), "profile"),
{"url": s3_str(url),
"contact": s3_str(get_formatted_value(row["cap_info.contact"],
system=system)),
},
@ -1168,7 +1252,7 @@ def config(settings):
# -------------------------------------------------------------------------
def get_email_subject(row, system=True):
"""
prepare the subject for Email
Prepare the subject for Email
"""
itable = current.s3db.cap_info
@ -1194,7 +1278,7 @@ def config(settings):
# -------------------------------------------------------------------------
def get_sms_content(row, ack_id=None, system=True):
"""
prepare the content for SMS
Prepare the content for SMS
@param row: the row from which the sms will be constructed
@param ack_id: cap_alert_ack.id for including the acknowledgement link
@ -1204,6 +1288,16 @@ def config(settings):
itable = current.s3db.cap_info
event_type_id = row["cap_info.event_type_id"]
priority_id = row["cap_info.priority"]
url = "%s/%s" % (row["cap_info.web"], "profile")
try:
from pyshorteners import Shortener
except ImportError:
pass
else:
try:
url = Shortener('Tinyurl', timeout=3).short(url)
except:
pass
if not isinstance(event_type_id, lazyT) and \
not isinstance(event_type_id, DIV):
@ -1249,7 +1343,7 @@ T("""%(status)s %(message_type)s for %(area_description)s with %(priority)s prio
system=system)),
"date": s3_str(row["cap_alert.sent"]),
"identifier": s3_str(row["cap_alert.identifier"]),
"profile": "%s/%s" % (s3_str(row["cap_info.web"]), "profile"),
"profile": s3_str(url),
}
return s3_str(sms_body)
@ -1266,6 +1360,15 @@ T("""%(status)s %(message_type)s for %(area_description)s with %(priority)s prio
response_type = row["cap_info.response_type"]
instruction = row["cap_info.instruction"]
description = row["cap_info.description"]
url = "%s/%s" % (row["cap_info.web"], "profile")
try:
from pyshorteners import Shortener
try:
url = Shortener('Tinyurl', timeout=3).short(url)
except:
pass
except ImportError:
pass
if event_type_id and event_type_id != current.messages["NONE"]:
if not isinstance(event_type_id, lazyT):
@ -1337,12 +1440,12 @@ T("""%(status)s %(message_type)s for %(area_description)s with %(priority)s prio
system=system)),
},
T("For more details visit %(url)s or contact %(contact)s") % \
{"url": "%s/%s" % ((s3_str(row["cap_info.web"])), "profile"),
{"url": s3_str(url),
"contact": s3_str(get_formatted_value(row["cap_info.contact"], system=system)),
}
if row["cap_info.contact"] else
T("For more details visit %(url)s") % \
{"url": "%s/%s" % ((s3_str(row["cap_info.web"])), "profile")}
{"url": s3_str(url)}
]
return "\n\n".join(s3_str(item) for item in facebook_content if item!="")