diff --git a/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/SAMBRO/config.py b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/SAMBRO/config.py
deleted file mode 100644
index 36ebaa8..0000000
--- a/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/SAMBRO/config.py
+++ /dev/null
@@ -1,1595 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import json
-import os
-
-from collections import OrderedDict
-from io import BytesIO
-
-from gluon import current
-from gluon.html import *
-from gluon.storage import Storage
-from gluon.languages import lazyT
-
-from s3 import FS, s3_str, s3_truncate, s3_utc
-
-def config(settings):
- """
- Template settings for CAP: Common Alerting Protocol
- """
-
- T = current.T
-
-## 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.append("SAMBRO")
- settings.base.prepopulate_demo += ("SAMBRO/Demo",)
-
- # Theme (folder to use for views/layout.html)
- #settings.base.theme = "SAMBRO"
-
- # The Registration functionality shouldn't be visible to the Public
- #settings.security.registration_visible = True
-
- settings.auth.registration_requires_approval = True
-
- # Link Users to Organisations
- settings.auth.registration_requests_organisation = True
-
- # GeoNames username
- settings.gis.geonames_username = "trendspotter"
- settings.gis.simplify_tolerance = 0
-
- # =========================================================================
- # System Settings
- # -------------------------------------------------------------------------
- # Security Policy
- # http://eden.sahanafoundation.org/wiki/S3/S3AAA#System-widePolicy
- # 1: Simple (default): Global as Reader, Authenticated as Editor
- # 2: Editor role required for Update/Delete, unless record owned by session
- # 3: Apply Controller ACLs
- # 4: Apply both Controller & Function ACLs
- # 5: Apply Controller, Function & Table ACLs
- # 6: Apply Controller, Function, Table ACLs and Entity Realm
- # 7: Apply Controller, Function, Table ACLs and Entity Realm + Hierarchy
- settings.security.policy = 4 # Controller-Function ACLs
-
- # Record Approval
- settings.auth.record_approval = True
- # cap_alert record requires approval before sending
- settings.auth.record_approval_required_for = ("cap_alert",)
- # Don't auto-approve so that can save draft
- settings.auth.record_approval_manual = ("cap_alert",)
-
- # =========================================================================
- # Module Settings
- # -------------------------------------------------------------------------
- # CAP Settings
- # Uncomment this according to country profile
- #settings.cap.restrict_fields = True
-
- # -------------------------------------------------------------------------
- # Notifications
-
- # Template for the subject line in update notifications
- #settings.msg.notify_subject = "%s $s %s" % (T("SAHANA"), T("Alert Notification"))
-
- # Notifications format
- settings.msg.notify_email_format = "html"
-
- # Filename for FTP
- # Characters not allowed are [\ / : * ? " < > | % .]
- # https://en.wikipedia.org/wiki/Filename
- # http://docs.attachmate.com/reflection/ftp/15.6/guide/en/index.htm?toc.htm?6503.htm
- settings.sync.upload_filename = "$s-%s" % ("recent_alert")
-
- # Whether to tweet alerts
- settings.cap.post_to_twitter = True
-
- # Whether to post alerts in facebook?
- settings.cap.post_to_facebook = True
-
- # ALlow RSS to use links of entry if link fails
- settings.cap.rss_use_links = True
-
- # SAMBRO supports ack workflow
- settings.cap.use_ack = True
-
- # -------------------------------------------------------------------------
- # L10n (Localization) settings
- languages = OrderedDict([
- ("cs", "Czech"),
- ("en-US", "English"),
- ])
- settings.cap.languages = languages
- settings.L10n.languages = languages
- # Translate the cap_area name
- settings.L10n.translate_cap_area = True
-
- # Date Format
- #settings.L10n.date_format = "%a, %d %B %Y"
-
- # Time Format
- settings.L10n.time_format = "%H:%M:%S"
-
- # PDF font
- settings.L10n.pdf_export_font = ['Helvetica', 'Helvetica-Bold']
-
- # -------------------------------------------------------------------------
- # Messaging
- # Parser
- settings.msg.parser = "SAMBRO"
- # Subscriptions
- settings.msg.notify_check_subscriptions = True
-
- # -------------------------------------------------------------------------
- # Organisations
- # Enable the use of Organisation Branches
- settings.org.branches = True
-
- # -------------------------------------------------------------------------
- def customise_msg_rss_channel_resource(r, tablename):
-
- # @ToDo: We won't be able to automate this as we have 2 sorts, so will need the user to select manually
- # Can we add a component for the parser for S3CSV imports?
-
- s3db = current.s3db
- def onaccept(form):
- # Normal onaccept
- s3db.msg_channel_onaccept(form)
- db = current.db
- table = db.msg_rss_channel
- form_vars = form.vars
- record_id = form_vars.get("id", None)
- form_type = form_vars.get("type", None)
- type = current.request.get_vars.get("type", None)
- query = (table.id == record_id)
- if type == "cap" or form_type == "cap":
- fn = "parse_rss_2_cap"
- db(query).update(type = "cap")
- else:
- fn = "parse_rss_2_cms"
- db(query).update(type = "cms")
- channel_id = db(query).select(table.channel_id,
- limitby=(0, 1)).first().channel_id
- # Link to Parser
- table = s3db.msg_parser
- parser_id = table.insert(channel_id=channel_id, function_name=fn, enabled=True)
- s3db.msg_parser_enable(parser_id)
-
- run_async = current.s3task.run_async
- # Poll
- run_async("msg_poll", args=["msg_rss_channel", channel_id])
-
- # Parse
- run_async("msg_parse", args=[channel_id, fn])
-
- s3db.configure(tablename,
- create_onaccept = onaccept,
- )
-
- settings.customise_msg_rss_channel_resource = customise_msg_rss_channel_resource
-
- # -------------------------------------------------------------------------
- def customise_msg_rss_channel_controller(**attr):
-
- s3 = current.response.s3
- 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(
- label_create = T("Add CAP Feed"),
- title_display = T("CAP Feed"),
- title_list = T("CAP Feeds"),
- title_update = T("Edit CAP Feed"),
- label_list_button = T("List CAP Feeds"),
- label_delete_button = T("Delete CAP Feed"),
- msg_record_created = T("CAP Feed created"),
- msg_record_modified = T("CAP Feed modified"),
- msg_record_deleted = T("CAP Feed deleted"),
- msg_list_empty = T("No CAP Feed to show"))
- else:
- # CMS RSS Channel
- s3.filter = (FS("type") == "cms")
-
- # Custom postp
- standard_postp = s3.postp
- def custom_postp(r, output):
- # Call standard postp
- if callable(standard_postp):
- output = standard_postp(r, output)
-
- if r.interactive and isinstance(output, dict):
- # Modify Open Button
- if channel_type == "cap":
- # CAP RSS Channel
- table = r.table
- query = (table.deleted == False)
- rows = current.db(query).select(table.id,
- table.enabled,
- )
- 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 = [{"label": s3_str(T("Open")),
- "_class": "action-btn edit",
- "url": URL(args = ["[id]", "update"],
- vars = {"type": "cap"},
- ),
- },
- {"label": s3_str(T("Delete")),
- "_class": "delete-btn",
- "url": URL(args = ["[id]", "delete"],
- vars = {"type": "cap"},
- ),
- },
- {"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.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"},
- ),
- )
- output["showadd_btn"] = add_btn
-
- return output
- s3.postp = custom_postp
-
- return attr
-
- settings.customise_msg_rss_channel_controller = customise_msg_rss_channel_controller
-
- # -------------------------------------------------------------------------
- def customise_msg_twitter_channel_resource(r, tablename):
-
- s3db = current.s3db
- def onaccept(form):
- # Normal onaccept
- s3db.msg_channel_onaccept(form)
- _id = form.vars.id
- db = current.db
- table = db.msg_twitter_channel
- channel_id = db(table.id == _id).select(table.channel_id,
- limitby=(0, 1)).first().channel_id
- # Link to Parser
- table = s3db.msg_parser
- _id = table.insert(channel_id=channel_id, function_name="parse_tweet", enabled=True)
- s3db.msg_parser_enable(_id)
-
- run_async = current.s3task.run_async
- # Poll
- run_async("msg_poll", args=["msg_twitter_channel", channel_id])
-
- # Parse
- run_async("msg_parse", args=[channel_id, "parse_tweet"])
-
- s3db.configure(tablename,
- create_onaccept = onaccept,
- )
-
- settings.customise_msg_twitter_channel_resource = customise_msg_twitter_channel_resource
-
- # -------------------------------------------------------------------------
- def customise_org_organisation_resource(r, tablename):
-
- s3 = current.response.s3
-
- crud_strings_branch = Storage(
- label_create = T("Add Branch"),
- title_display = T("Branch Details"),
- title_list = T("Branches"),
- title_update = T("Edit Branch"),
- title_upload = T("Import Branches"),
- label_list_button = T("List Branches"),
- label_delete_button = T("Delete Branch"),
- msg_record_created = T("Branch added"),
- msg_record_modified = T("Branch updated"),
- msg_record_deleted = T("Branch deleted"),
- msg_list_empty = T("No Branches currently registered"))
-
- if r.component_name == "branch":
- # Make sure branch uses same form as organisation because we need CAP OID
- r.component.actuate = "replace"
- s3.crud_strings[tablename] = crud_strings_branch
-
- if r.method == "hierarchy":
- s3.crud_strings[tablename] = crud_strings_branch
-
- from s3 import S3SQLCustomForm, S3SQLInlineComponent, S3SQLInlineLink
- crud_form = S3SQLCustomForm("name",
- "acronym",
- S3SQLInlineLink("organisation_type",
- field = "organisation_type_id",
- label = T("Type"),
- multiple = False,
- #widget = "hierarchy",
- ),
- S3SQLInlineComponent(
- "tag",
- label = T("CAP OID"),
- multiple = False,
- fields = [("", "value")],
- filterby = {"field": "tag",
- "options": "cap_oid",
- },
- ),
- "website",
- "comments",
- )
-
- current.s3db.configure("org_organisation",
- crud_form = crud_form,
- )
-
- settings.customise_org_organisation_resource = customise_org_organisation_resource
-
- # -------------------------------------------------------------------------
- def customise_pr_person_resource(r, tablename):
-
- # On-delete option
- current.s3db.pr_person_id.attr.ondelete = "SET NULL"
-
- settings.customise_pr_person_resource = customise_pr_person_resource
-
- # -------------------------------------------------------------------------
- def customise_pr_contact_controller(**attr):
-
- s3 = current.response.s3
-
- # Custom prep
- standard_prep = s3.prep
- def custom_prep(r):
- # Call standard prep
- if callable(standard_prep):
- result = standard_prep(r)
- else:
- result = True
-
- table = r.table
- table.priority.writable = False
- table.priority.readable = False
- table.comments.writable = False
- table.comments.readable = False
-
- return result
- s3.prep = custom_prep
-
- return attr
-
- settings.customise_pr_contact_controller = customise_pr_contact_controller
-
- # -------------------------------------------------------------------------
- def customise_cap_alert_resource(r, tablename):
-
- T = current.T
- db = current.db
- s3db = current.s3db
- def onapprove(record):
- # Normal onapprove
- s3db.cap_alert_onapprove(record)
-
- run_async = current.s3task.run_async
-
- # Sync FTP Repository
- 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
- alert_id = int(record["id"])
- table = s3db.cap_alert
- itable = s3db.cap_info
- atable = s3db.cap_area
- query = (table.id == alert_id) & \
- (table.deleted != True) & \
- (itable.alert_id == table.id) & \
- (itable.deleted != True) & \
- (atable.alert_id == table.id) & \
- (atable.deleted != True)
- resource = s3db.resource("cap_alert", filter=query)
- # Fields to extract
- fields = resource.list_fields(key="notify_fields")
- # Extract the data
- data = resource.select(fields,
- raw_data=True)
- # Single row as we are filtering for particular alert_id
- arow = data["rows"][0]
-
- # Create attachment
- cap_document_id = _get_or_create_attachment(alert_id)
-
- if record["scope"] != "Private" and data["numrows"] > 0:
- # Google Cloud Messaging
- stable = s3db.pr_subscription
- ctable = s3db.pr_contact
-
- query = (stable.pe_id == ctable.pe_id) & \
- (ctable.contact_method == "GCM") & \
- (ctable.value != None) & \
- (ctable.deleted != True) & \
- (stable.deleted != True) & \
- (stable.method.like("%GCM%"))
- rows = db(query).select(ctable.value)
- if len(rows):
- registration_ids = [s3_str(row.value) for row in rows]
- title = get_email_subject(arow, system=False)
- 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)),
- json.dumps(registration_ids),
- ])
- # Twitter Post
- if settings.get_cap_post_to_twitter():
- try:
- import tweepy
- 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""") % {"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(url),
- }
- try:
- # @ToDo: Handle the multi-message nicely?
- # @ToDo: Send resource url with tweet
- 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
- if settings.get_cap_post_to_facebook():
- # @ToDo: post resources too?
- content = get_facebook_content(arow)
- try:
- 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"]
- if len(addresses):
- # First Responders
- gtable = s3db.pr_group
- mtable = s3db.pr_group_membership
- ptable = s3db.pr_person
- send_by_pe_id = current.msg.send_by_pe_id
- get_user_id = current.auth.s3_get_user_id
- query_ = (gtable.id == mtable.group_id) & \
- (mtable.person_id == ptable.id) & \
- (gtable.deleted != True) & \
- (mtable.deleted != True) & \
- (ptable.deleted != True)
- count = len(addresses)
- if count == 1:
- query = query_ & (gtable.id == addresses[0])
- else:
- query = query_ & (gtable.id.belongs(addresses))
- rows = db(query).select(ptable.pe_id)
- subject = get_email_subject(arow, system=False)
- if settings.get_cap_use_ack():
- for row in rows:
- ack_id = create_ack(alert_id, get_user_id(pe_id=row.pe_id))
- email_content = "%s%s%s" % ("",
- XML(get_html_email_content(arow,
- ack_id=ack_id,
- system=False)),
- "")
- sms_content = get_sms_content(arow, ack_id=ack_id, system=False)
- send_by_pe_id(row.pe_id,
- subject,
- email_content,
- document_ids=cap_document_id,
- alert_id=alert_id,
- )
- try:
- 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:
- html_content = get_html_email_content(arow, system=False)
- email_content = "%s%s%s" % ("",
- XML(html_content),
- "")
- sms_content = get_sms_content(arow, system=False)
- for row in rows:
- send_by_pe_id(row.pe_id,
- subject,
- email_content,
- document_ids = cap_document_id,
- alert_id = alert_id,
- )
- try:
- 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!")
-
- s3db.configure(tablename,
- onapprove = onapprove,
- )
-
- settings.customise_cap_alert_resource = customise_cap_alert_resource
-
- # -------------------------------------------------------------------------
- def customise_cap_alert_controller(**attr):
-
- s3 = current.response.s3
- auth = current.auth
- if not auth.user:
- # For notifications for group
- r = current.request
- if not r.function == "public":
- if r.get_vars.format == "msg":
- # This is called by notification
- # The request from web looks like r.extension
- s3.filter = (FS("scope") != "Private")
- else:
- auth.permission.fail()
-
- # Custom prep
- standard_prep = s3.prep
- def custom_prep(r):
- # Call standard prep
- if callable(standard_prep):
- result = standard_prep(r)
- else:
- result = True
-
- if r.representation == "msg":
- # Notification
- table = r.table
- table.scope.represent = None
- table.status.represent = None
- table.msg_type.represent = None
-
- itable = current.s3db.cap_info
- itable.severity.represent = None
- itable.urgency.represent = None
- itable.certainty.represent = None
-
- return result
- s3.prep = custom_prep
-
- return attr
-
- settings.customise_cap_alert_controller = customise_cap_alert_controller
-
- # -------------------------------------------------------------------------
- def customise_sync_repository_controller(**attr):
-
- s3 = current.response.s3
-
- # Custom prep
- standard_prep = s3.prep
- def custom_prep(r):
- # Call standard prep
- if callable(standard_prep):
- result = standard_prep(r)
- else:
- result = True
-
- if r.representation == "popup":
- table = r.table
- table.apitype.default = "ftp"
- table.apitype.readable = table.apitype.writable = False
- table.synchronise_uuids.readable = \
- table.synchronise_uuids.writable = False
- table.uuid.readable = table.uuid.writable = False
-
- return result
- s3.prep = custom_prep
-
- return attr
-
- settings.customise_sync_repository_controller = customise_sync_repository_controller
-
- # -------------------------------------------------------------------------
- def customise_pr_subscription_controller(**attr):
-
- from s3 import s3_action_buttons
- s3 = current.response.s3
- s3db = current.s3db
- auth = current.auth
- stable = s3db.pr_subscription
- has_role = auth.s3_has_role
-
- list_fields = [(T("Filters"), "filter_id"),
- (T("Methods"), "method"),
- ]
- manage_recipient = current.request.get_vars["option"] == "manage_recipient"
- role_check = has_role("ADMIN")
-
- if manage_recipient and role_check:
- # Admin based subscription
- s3.filter = (stable.deleted != True) & \
- (stable.owned_by_group != None)
- list_fields.insert(0, (T("People/Groups"), "pe_id"))
- s3.crud_strings["pr_subscription"].title_list = T("Admin Controlled Subscriptions")
- else:
- # Self Subscription
- s3.filter = (stable.deleted != True) & \
- (stable.owned_by_group == None) & \
- (stable.owned_by_user == auth.user.id)
- s3.crud_strings["pr_subscription"].title_list = T("Your Subscriptions")
-
- # Custom prep
- standard_prep = s3.prep
- def custom_prep(r):
- from s3 import S3Represent
- table = r.table
- # Call standard prep
- if callable(standard_prep):
- result = standard_prep(r)
- else:
- result = True
- MSG_CONTACT_OPTS = {"EMAIL": T("EMAIL"),
- "SMS" : T("SMS"),
- "FTP" : T("FTP"),
- }
- table.method.represent = S3Represent(options = MSG_CONTACT_OPTS,
- multiple = True,
- ),
- if r.representation == "html":
- table.filter_id.represent = S3Represent(\
- options=pr_subscription_filter_row_options())
- s3db.configure("pr_subscription",
- list_fields = list_fields,
- list_orderby = "pe_id desc",
- orderby = "pr_subscription.pe_id desc",
- )
-
- return result
- s3.prep = custom_prep
-
- # Custom postp
- standard_postp = s3.postp
- def custom_postp(r, output):
- # Call standard postp
- if callable(standard_postp):
- output = standard_postp(r, output)
-
- if r.interactive and isinstance(output, dict):
- # Modify Open Button
- if manage_recipient and role_check:
- # Admin based subscription
- s3_action_buttons(r,
- update_url = URL(c="default", f="index",
- args = ["subscriptions"],
- vars = {"option": "manage_recipient",
- "subscription_id": "[id]",
- },
- ),
- delete_url = URL(c="pr", f="subscription",
- args = ["[id]", "delete"],
- vars = {"option": "manage_recipient"},
- )
- )
- else:
- # self subscription
- url = URL(c="default", f="index",
- args = ["subscriptions"],
- vars = {"subscription_id": "[id]"},
- )
- s3_action_buttons(r, update_url=url, read_url=url)
-
- if "form" in output:
- # Modify Add Button
- if manage_recipient and role_check:
- # Admin based subscription
- add_btn = A(T("Add Recipient to List"),
- _class="action-btn",
- _href=URL(c="default", f="index",
- args=["subscriptions"],
- vars={"option": "manage_recipient"}
- )
- )
- else:
- # self subscription
- add_btn = A(T("Create Subscription"),
- _class="action-btn",
- _href=URL(c="default", f="index", args=["subscriptions"])
- )
- output["showadd_btn"] = add_btn
-
- return output
- s3.postp = custom_postp
-
- return attr
-
- settings.customise_pr_subscription_controller = customise_pr_subscription_controller
-
- # -----------------------------------------------------------------------------
- def custom_msg_render(resource, data, meta_data, format=None):
- """
- Custom Method to pre-render the contents for the message template
-
- Args:
- resource: the S3Resource
- data: the data returned from S3Resource.select
- meta_data: the meta data for the notification
- format: the contents format ("text" or "html")
- """
-
- notify_on = meta_data["notify_on"]
- last_check_time = meta_data["last_check_time"]
- rows = data["rows"]
- output = {}
- upd = [] # upd as the created alerts might be approved after some time, check is also done
-
- db = current.db
- atable = current.s3db.cap_alert
- if format == "text":
- # For SMS
- append_record = upd.append
- for row in rows:
- row_ = db(atable.id == row["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:
- sms_content = get_sms_content(row)
- append_record(sms_content)
-
- if "upd" in notify_on and len(upd):
- output["upd"] = len(upd)
- output["upd_records"] = upd
- else:
- output["upd"] = None
- else:
- # HTML emails
- elements = []
- append = elements.append
- append_record = upd.append
-
- for row in rows:
- row_ = db(atable.id == row["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:
- content = get_html_email_content(row)
- container = DIV(DIV(content))
- append(container)
- append(BR())
- append_record(container)
- if "upd" in notify_on and len(upd):
- output["upd"] = len(upd)
- output["upd_records"] = DIV(*elements)
- else:
- output["upd"] = None
-
- output.update(meta_data)
- return output
-
- settings.msg.notify_renderer = custom_msg_render
-
- # -----------------------------------------------------------------------------
- def custom_msg_notify_subject(resource, data, meta_data):
- """
- Custom Method to subject for the email
-
- Args:
- resource: the S3Resource
- data: the data returned from S3Resource.select
- meta_data: the meta data for the notification
- """
-
- rows = data["rows"]
- 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
- atable = current.s3db.cap_alert
- 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) >= meta_data["last_check_time"]:
- subject = get_email_subject(rows[0])
-
- return subject
-
- settings.msg.notify_subject = custom_msg_notify_subject
-
- # -----------------------------------------------------------------------------
- def custom_msg_notify_attachment(resource, data, meta_data):
- """
- Custom Method to get the document_ids to be sent as attachment
-
- Args:
- resource: the S3Resource
- data: the data returned from S3Resource.select
- meta_data: the meta data for the notification
- """
-
- rows = data["rows"]
- document_ids = []
- dappend = document_ids.append
- for row in rows:
- alert_id = row["cap_alert.id"]
- document_id = _get_or_create_attachment(alert_id)
- dappend(document_id)
-
- return document_ids
-
- 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
-
- Args:
- resource: the S3Resource
- data: the data returned from S3Resource.select
- 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
- # Modules menu is defined in modules/eden/menu.py
- settings.modules = OrderedDict([
- # Core modules which shouldn't be disabled
- ("default", Storage(
- name_nice = T("Home"),
- restricted = False, # Use ACLs to control access to this module
- access = None, # All Users (inc Anonymous) can see this module in the default menu & access the controller
- module_type = None # This item is not shown in the menu
- )),
- ("admin", Storage(
- name_nice = T("Administration"),
- #description = "Site Administration",
- restricted = True,
- access = "|1|", # Only Administrators can see this module in the default menu & access the controller
- module_type = None # This item is handled separately for the menu
- )),
- ("appadmin", Storage(
- name_nice = T("Administration"),
- #description = "Site Administration",
- restricted = True,
- module_type = None # No Menu
- )),
- ("errors", Storage(
- name_nice = T("Ticket Viewer"),
- #description = "Needed for Breadcrumbs",
- restricted = False,
- module_type = None # No Menu
- )),
- ("sync", Storage(
- name_nice = T("Synchronization"),
- #description = "Synchronization",
- restricted = True,
- access = "|1|", # Only Administrators can see this module in the default menu & access the controller
- module_type = None # This item is handled separately for the menu
- )),
- ("translate", Storage(
- name_nice = T("Translation Functionality"),
- #description = "Selective translation of strings based on module.",
- module_type = None,
- )),
- ("gis", Storage(
- name_nice = T("Mapping"),
- #description = "Situation Awareness & Geospatial Analysis",
- restricted = True,
- module_type = 6, # 6th item in the menu
- )),
- ("pr", Storage(
- name_nice = T("Person Registry"),
- #description = "Central point to record details on People",
- restricted = True,
- access = "|1|", # Only Administrators can see this module in the default menu (access to controller is possible to all still)
- module_type = 10
- )),
- ("org", Storage(
- name_nice = T("Organizations"),
- #description = 'Lists "who is doing what & where". Allows relief agencies to coordinate their activities',
- restricted = True,
- module_type = 10
- )),
- # All modules below here should be possible to disable safely
- #("hrm", Storage(
- # name_nice = T("Staff"),
- # #description = "Human Resources Management",
- # restricted = True,
- # module_type = 2,
- #)),
- ("cap", Storage(
- name_nice = T("Alerting"),
- #description = "Create & broadcast CAP alerts",
- restricted = True,
- module_type = 1,
- )),
- ("cms", Storage(
- name_nice = T("Content Management"),
- #description = "Content Management System",
- restricted = True,
- module_type = 10,
- )),
- ("doc", Storage(
- name_nice = T("Documents"),
- #description = "A library of digital resources, such as photos, documents and reports",
- restricted = True,
- module_type = 10,
- )),
- ("msg", Storage(
- name_nice = T("Messaging"),
- #description = "Sends & Receives Alerts via Email & SMS",
- restricted = True,
- # The user-visible functionality of this module isn't normally required. Rather it's main purpose is to be accessed from other modules.
- module_type = None,
- )),
- ("event", Storage(
- name_nice = T("Events"),
- #description = "Activate Events (e.g. from Scenario templates) for allocation of appropriate Resources (Human, Assets & Facilities).",
- restricted = True,
- module_type = 10,
- )),
- ])
-
- # -------------------------------------------------------------------------
- # Functions which are local to this Template
- # -------------------------------------------------------------------------
- def pr_subscription_filter_row_options():
- """
- Build the options for the pr_subscription filter datatable from query
- @ToDo complete this for locations
- """
-
- db = current.db
- s3db = current.s3db
- auth = current.auth
- has_role = auth.s3_has_role
- stable = s3db.pr_subscription
- ftable = s3db.pr_filter
-
- if current.request.get_vars["option"] == "manage_recipient" and \
- (has_role("ALERT_EDITOR") or has_role("ALERT_APPROVER")):
- # Admin based subscription
- query = (stable.deleted != True) & \
- (stable.owned_by_group != None)
- else:
- # Self Subscription
- query = (stable.deleted != True) & \
- (stable.owned_by_group == None) & \
- (stable.owned_by_user == auth.user.id)
-
- left = ftable.on(ftable.id == stable.filter_id)
- 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
- from s3 import IS_ISO639_2_LANGUAGE_CODE
- languages_dict = dict(IS_ISO639_2_LANGUAGE_CODE.language_codes())
- for row in rows:
- event_type = None
- priorities_id = []
- languages = []
-
- filters = json.loads(row.pr_filter.query)
- filters = [filter for filter in filters if filter[1] is not None]
- if len(filters) > 0:
- for filter in filters:
- # Get the prefix
- prefix = s3_str(filter[0]).strip("[]")
- # Get the value for prefix
- values = filter[1].split(",")
- if prefix == "event_type_id__belongs":
- event_type_id = s3_str(values[0])
- row_ = db(etable.id == event_type_id).select(\
- etable.name,
- limitby=(0, 1)).first()
- event_type = row_.name
- elif prefix == "info.priority__belongs":
- priorities_id = [int(s3_str(value)) for value in values]
- rows_ = db(ptable.id.belongs(priorities_id)).select(ptable.name)
- priorities = [row_.name for row_ in rows_]
- elif prefix == "info.language__belongs":
- languages = [s3_str(languages_dict[value]) for value in values]
- if event_type is not None:
- display_text = "%s: %s" % (T("Event Type"), event_type)
- else:
- display_text = "%s: %s" % (T("Event Type"), T("No filter"))
- if len(priorities_id) > 0:
- display_text = "%s
%s: %s" % (display_text, T("Priorities"), ", ".join(priorities))
- else:
- display_text = "%s
%s: %s" % (display_text, T("Priorities"), T("No filter"))
- if len(languages) > 0:
- display_text = "%s
%s: %s" % (display_text, T("Languages"), ", ".join(languages))
- else:
- display_text = "%s
%s: %s" % (display_text, T("Languages"), T("No filter"))
- filter_options[row["pr_subscription.filter_id"]] = display_text
- else:
- filter_options[row["pr_subscription.filter_id"]] = T("No filters")
-
- return filter_options
-
- # -------------------------------------------------------------------------
- def get_html_email_content(row, ack_id=None, system=True):
- """
- Prepare the content for HTML email
-
- Args:
- row: the row from which the email will be constructed
- ack_id: cap_alert_ack.id for including the acknowledgement link
- system: is this system notification email or email for first responders
- """
-
- itable = current.s3db.cap_info
- event_type_id = row["cap_info.event_type_id"]
- priority_id = row["cap_info.priority"]
- response_type = row["_row"]["cap_info.response_type"] if system else row["cap_info.response_type"]
- instruction = row["_row"]["cap_info.instruction"] if system else row["cap_info.instruction"]
- 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 \
- not isinstance(event_type_id, DIV):
- event_type = itable.event_type_id.represent(event_type_id)
- else:
- event_type = event_type_id
- else:
- event_type = T("None")
-
- if priority_id and priority_id != current.messages["NONE"]:
- if not isinstance(priority_id, lazyT) and \
- not isinstance(priority_id, DIV):
- priority = itable.priority.represent(priority_id)
- else:
- priority = priority_id
- else:
- priority = T("Alert")
-
- email_content = TAG[""](HR(), BR(),
- B(s3_str("%s %s %s" % (T(status.upper()),
- T(status.upper()),
- T(status.upper()))))
- if status != "Actual" else "",
- BR() if status != "Actual" else "",
- BR() if status != "Actual" else "",
- 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),
- T("Alert") if msg_type != "Alert" else "",
- s3_str(msg_type)
- ))),
- H2(T(s3_str(get_formatted_value(row["cap_info.headline"],
- system=system)))),
- BR(),
- XML("%(label)s: %(identifier)s" %
- {"label": B(T("ID")),
- "identifier": s3_str(row["cap_alert.identifier"])
- }),
- BR(), BR(),
- T("""%(priority)s message %(message_type)s in effect for %(area_description)s""") % \
- {"priority": s3_str(priority),
- "message_type": s3_str(msg_type),
- "area_description": s3_str(get_formatted_value(row["cap_area.name"],
- system=system)),
- },
- BR(), BR(),
- T("This %(severity)s %(event_type)s is %(urgency)s and is %(certainty)s") %\
- {"severity": s3_str(row["cap_info.severity"]),
- "event_type": s3_str(event_type),
- "urgency": s3_str(row["cap_info.urgency"]),
- "certainty": s3_str(row["cap_info.certainty"]),
- },
- BR(), BR(),
- T("""Message %(identifier)s: %(event_type)s (%(category)s) issued by %(sender_name)s sent at %(date)s from %(source)s""") % \
- {"identifier": s3_str(row["cap_alert.identifier"]),
- "event_type": s3_str(event_type),
- "category": s3_str(get_formatted_value(row["cap_info.category"],
- represent = itable.category.represent,
- system=system)),
- "sender_name": s3_str(get_formatted_value(row["cap_info.sender_name"],
- system=system)),
- "date": s3_str(get_formatted_value(row["cap_alert.sent"],
- represent = current.s3db.cap_alert.sent.represent,
- system=system)),
- "source": s3_str(row["cap_alert.source"]),
- },
- BR(),
- BR() if description else "",
- XML("%(label)s: %(alert_description)s" %
- {"label": B(T("Alert Description")),
- "alert_description": s3_str(get_formatted_value(description,
- system=False,
- ul=True)),
- })
- if description else "",
- BR() if not isinstance(description, list) else "",
- BR() if response_type else "",
- XML(T("%(label)s: %(response_type)s") %
- {"label": B(T("Expected Response")),
- "response_type": s3_str(get_formatted_value(response_type,
- represent = itable.response_type.represent,
- system=False,
- ul=True)),
- })
- if response_type else "",
- BR() if not isinstance(response_type, list) else "",
- BR() if instruction else "",
- XML(T("%(label)s: %(instruction)s") %
- {"label": B(T("Instructions")),
- "instruction": s3_str(get_formatted_value(instruction,
- system=False,
- ul=True)),
- })
- if instruction else "",
- BR() if not isinstance(instruction, list) else "",
- BR(),
- T("Alert is effective from %(effective)s and expires on %(expires)s") % \
- {"effective": s3_str(get_formatted_value(row["cap_info.effective"],
- represent = itable.effective.represent,
- system=system)),
- "expires": s3_str(get_formatted_value(row["cap_info.expires"],
- represent = itable.expires.represent,
- system=system)),
- },
- BR(), BR(),
- T("For more details visit %(url)s or contact %(contact)s") % \
- {"url": s3_str(url),
- "contact": s3_str(get_formatted_value(row["cap_info.contact"],
- system=system)),
- },
- BR(), BR(),
- T("To acknowledge the alert, use the following link: %(ack_link)s") % \
- {"ack_link": "%s%s" % (current.deployment_settings.get_base_public_url(),
- URL(c="cap", f="alert_ack", args=[ack_id, "update"])),
- } if ack_id else "",
- BR() if ack_id else "",
- BR() if ack_id else "",
- B(s3_str("%s %s %s" % (T(status.upper()),
- T(status.upper()),
- T(status.upper()))))
- if status != "Actual" else "",
- )
-
- return email_content
-
- # -------------------------------------------------------------------------
- def get_email_subject(row, system=True):
- """
- Prepare the subject for Email
- """
-
- itable = current.s3db.cap_info
- event_type_id = row["cap_info.event_type_id"]
- msg_type = T(row["cap_alert.msg_type"])
-
- if event_type_id and event_type_id != current.messages["NONE"]:
- if not isinstance(event_type_id, lazyT) and \
- not isinstance(event_type_id, DIV):
- event_type = itable.event_type_id.represent(event_type_id)
- else:
- event_type = event_type_id
- else:
- event_type = T("None")
-
- subject = "[%s] %s %s" % (get_formatted_value(row["cap_info.sender_name"],
- system=system),
- event_type,
- msg_type)
- # RFC 2822
- return s3_str(s3_truncate(subject, length=78))
-
- # -------------------------------------------------------------------------
- def get_sms_content(row, ack_id=None, system=True):
- """
- Prepare the content for SMS
-
- Args:
- row: the row from which the sms will be constructed
- ack_id: cap_alert_ack.id for including the acknowledgement link
- system: is this system notification email or email for first responders
- """
-
- 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):
- event_type = itable.event_type_id.represent(event_type_id)
- else:
- event_type = event_type_id
-
- if priority_id and priority_id != current.messages["NONE"]:
- if not isinstance(priority_id, lazyT) and \
- not isinstance(priority_id, DIV):
- priority = itable.priority.represent(priority_id)
- else:
- priority = priority_id
- else:
- priority = T("Unknown")
-
- if ack_id:
- sms_body = \
-T("""%(status)s %(message_type)s for %(area_description)s with %(priority)s priority %(event_type)s issued by %(sender_name)s at %(date)s (ID:%(identifier)s) \nTo acknowledge the alert, click: %(ack_link)s \n\n""") % \
- {"status": s3_str(row["cap_alert.status"]),
- "message_type": s3_str(row["cap_alert.msg_type"]),
- "area_description": s3_str(get_formatted_value(row["cap_area.name"],
- system=system)),
- "priority": s3_str(priority),
- "event_type": s3_str(event_type),
- "sender_name": s3_str(get_formatted_value(row["cap_info.sender_name"],
- system=system)),
- "date": s3_str(row["cap_alert.sent"]),
- "identifier": s3_str(row["cap_alert.identifier"]),
- "ack_link": "%s%s" % (current.deployment_settings.get_base_public_url(),
- URL(c="cap", f="alert_ack", args=[ack_id, "update"])),
- }
- else:
- sms_body = \
-T("""%(status)s %(message_type)s for %(area_description)s with %(priority)s priority %(event_type)s issued by %(sender_name)s at %(date)s (ID:%(identifier)s). \nView Alert in web at %(profile)s \n\n""") % \
- {"status": s3_str(row["cap_alert.status"]),
- "message_type": s3_str(row["cap_alert.msg_type"]),
- "area_description": s3_str(get_formatted_value(row["cap_area.name"],
- system=system)),
- "priority": s3_str(priority),
- "event_type": s3_str(event_type),
- "sender_name": s3_str(get_formatted_value(row["cap_info.sender_name"],
- system=system)),
- "date": s3_str(row["cap_alert.sent"]),
- "identifier": s3_str(row["cap_alert.identifier"]),
- "profile": s3_str(url),
- }
-
- return s3_str(sms_body)
-
- # -------------------------------------------------------------------------
- def get_facebook_content(row, system=False):
- """
- prepare the content for facebook post
- """
-
- itable = current.s3db.cap_info
- event_type_id = row["cap_info.event_type_id"]
- priority_id = row["cap_info.priority"]
- 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):
- event_type = itable.event_type_id.represent(event_type_id)
- else:
- event_type = event_type_id
- else:
- event_type = T("None")
-
- if priority_id and priority_id != current.messages["NONE"]:
- if not isinstance(priority_id, lazyT):
- priority = itable.priority.represent(priority_id)
- else:
- priority = priority_id
- else:
- priority = T("Alert")
-
- facebook_content = [
- T("%(scope)s %(status)s Alert") % \
- {"scope": s3_str(row["cap_alert.scope"]),
- "status": s3_str(row["cap_alert.status"]),
- },
- T((s3_str(get_formatted_value(row["cap_info.headline"],
- system=system)))),
- T("ID: %(identifier)s") % {"identifier": s3_str(row["cap_alert.identifier"])},
- T("""%(priority)s message %(message_type)s in effect for %(area_description)s""") % \
- {"priority": s3_str(priority),
- "message_type": s3_str(row["cap_alert.msg_type"]),
- "area_description": s3_str(get_formatted_value(row["cap_area.name"],
- system=system)),
- },
- T("This %(severity)s %(event_type)s is %(urgency)s and is %(certainty)s") % \
- {"severity": s3_str(row["cap_info.severity"]),
- "event_type": s3_str(event_type),
- "urgency": s3_str(row["cap_info.urgency"]),
- "certainty": s3_str(row["cap_info.certainty"]),
- },
- T("""Message %(identifier)s: %(event_type)s (%(category)s) issued by %(sender_name)s sent at %(date)s from %(source)s""") % \
- {"identifier": s3_str(row["cap_alert.identifier"]),
- "event_type": s3_str(event_type),
- "category": s3_str(get_formatted_value(row["cap_info.category"],
- represent = itable.category.represent,
- system=system)),
- "sender_name": s3_str(get_formatted_value(row["cap_info.sender_name"],
- system=system)),
- "date": s3_str(get_formatted_value(row["cap_alert.sent"],
- represent = current.s3db.cap_alert.sent.represent,
- system=system)),
- "source": s3_str(row["cap_alert.source"]),
- },
- T("Alert Description: %(alert_description)s") % \
- {"alert_description": s3_str(get_formatted_value(description,
- system=system)),
- } if description else "",
- T("Expected Response: %(response_type)s") % \
- {"response_type": s3_str(get_formatted_value(response_type,
- represent = itable.response_type.represent,
- system=system)),
- } if response_type else "",
- T("Instruction: %(instruction)s") % \
- {"instruction": s3_str(get_formatted_value(instruction, system=system))}
- if instruction else "",
- T("Alert is effective from %(effective)s and expires on %(expires)s") % \
- {"effective": s3_str(get_formatted_value(row["cap_info.effective"],
- represent = itable.effective.represent,
- system=system)),
- "expires": s3_str(get_formatted_value(row["cap_info.expires"],
- represent = itable.expires.represent,
- system=system)),
- },
- T("For more details visit %(url)s or contact %(contact)s") % \
- {"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": s3_str(url)}
- ]
-
- return "\n\n".join(s3_str(item) for item in facebook_content if item!="")
-
- # -------------------------------------------------------------------------
- def create_ack(alert_id, user_id):
- """
- Create a specific acknowledgement
-
- Args:
- alert_id: The particular alert ID for acknowledging
- user_id: The user ID who owns the record
-
- TODO:
- Use location where the alert is targeted for
- """
-
- ack_data = {"alert_id": alert_id,
- "owned_by_user": int(user_id),
- }
- ack_table = current.s3db.cap_alert_ack
- ack_id = ack_table.insert(**ack_data)
- current.auth.s3_set_record_owner(ack_table, ack_id)
- # Uncomment this when there is onaccept hook
- #s3db.onaccept(ack_table, {"id": ack_id})
-
- return ack_id
-
- # -------------------------------------------------------------------------
- def get_formatted_value(value,
- represent = None,
- system = True,
- ul = False,
- ):
- """
- For non-system notification returns the formatted represented value
- """
-
- if not value:
- return None
- else:
- if system:
- # For system notification value is already properly formatted for representation
- return value
- else:
- if isinstance(value, list):
- nvalue = []
- for value_ in value:
- if value_:
- if represent:
- nvalue.append(represent(value_))
- else:
- nvalue.append(value_)
- if len(nvalue):
- if ul:
- nvalue = UL(nvalue)
- else:
- nvalue = ", ".join(nvalue)
- else:
- return None
- else:
- if represent:
- nvalue = represent(value)
- else:
- nvalue = value
-
- return nvalue
-
- # -------------------------------------------------------------------------
- def _get_or_create_attachment(alert_id):
- """
- Retrieve the CAP attachment for the alert_id if present
- else creates CAP file as attachment to be sent with the
- email
-
- Args:
- alert_id: the cap_alert record ID
-
- Returns:
- doc_id of the CAP file
- """
-
- s3db = current.s3db
- rtable = s3db.cap_resource
- dtable = s3db.doc_document
-
- # Check for existing CAP XML resource
- query = (rtable.alert_id == alert_id) & \
- (rtable.mime_type == "cap") & \
- (rtable.deleted != True) & \
- (dtable.doc_id == rtable.doc_id) & \
- (dtable.deleted != True)
- row = current.db(query).select(dtable.doc_id,
- limitby = (0, 1),
- ).first()
- if row:
- return row.doc_id
-
- # Create a CAP resource for the CAP XML file
- record = {"alert_id": alert_id,
- "resource_desc": T("CAP XML File"),
- "mime_type": "cap" # Hard-coded to separate from attachment from user
- }
- resource_id = rtable.insert(**record)
-
- # Post-process the CAP resource
- record["id"] = resource_id
- s3db.update_super(rtable, record)
- doc_id = record["doc_id"]
- auth = current.auth
- auth.s3_set_record_owner(rtable, resource_id)
- auth.s3_make_session_owner(rtable, resource_id)
- s3db.onaccept("cap_resource", record, method="create")
-
- # Generate the CAP XML
- resource = s3db.resource("cap_alert", id=alert_id)
- cap_xml = resource.export_xml(
- stylesheet = os.path.join(current.request.folder,
- "static",
- "formats",
- "cap",
- "export.xsl",
- ),
- pretty_print = True,
- )
-
- stream = BytesIO(cap_xml)
- filename = "%s_%s.xml" % ("cap_alert", s3_str(alert_id))
-
- # Store the CAP XML as doc_document for the CAP resource
- document = {"file": dtable.file.store(stream, filename),
- "doc_id": doc_id,
- }
- document_id = dtable.insert(**document)
-
- return document_id
-
-# END =========================================================================
diff --git a/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/config.py b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/config.py
index 1fb76b3..1807160 100644
--- a/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/config.py
+++ b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/config.py
@@ -23,7 +23,7 @@ def config(settings):
# ["default", "default/users"]
# Unless doing a manual DB migration, where prepopulate = 0
# In Production, prepopulate = 0 (to save 1x DAL hit every page)
- settings.base.prepopulate.append("Spotter")
+ settings.base.prepopulate.extend(("Spotter", "default/users"))
# Uncomment this to prefer scalability-optimized strategies globally
#settings.base.bigtable = True
@@ -1276,11 +1276,17 @@ def config(settings):
restricted = True,
module_type = 10,
)),
- ("vulnerability", Storage(
- name_nice = T("Vulnerability"),
- #description = "Manages vulnerability indicators",
- restricted = True,
- module_type = 10,
+ #("vulnerability", Storage(
+ # name_nice = T("Vulnerability"),
+ # #description = "Manages vulnerability indicators",
+ # restricted = True,
+ # module_type = 10,
+ #)),
+ ("work", Storage(
+ name_nice = T("Jobs"),
+ #description = "Simple Volunteer Jobs Management",
+ restricted = False,
+ module_type = None,
)),
# These are specialist modules
("cap", Storage(
diff --git a/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/css.cfg b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/css.cfg
index d4e4800..a8c1322 100644
--- a/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/css.cfg
+++ b/sahana-spotter/image.d/srv/web2py/applications/eden/modules/templates/Spotter/css.cfg
@@ -5,6 +5,7 @@
../themes/default/shortcut.css
../themes/default/homepage.css
font-awesome/font-awesome.css
+#plugins/fancyzoom.css
plugins/jquery.cluetip.css
plugins/jquery.dataTables.css
plugins/jquery.dataTables.responsive.css
@@ -46,5 +47,6 @@ d3/nv.d3.css
../themes/default/survey.css
../themes/default/newsfeed.css
../themes/default/theme.css
+
#../themes/default/style.css
# Final line required for parsing