diff --git a/config/settings/base.py b/config/settings/base.py
index 4f26af00f91548e74f1756365ae823c1e6d3800a..6cd3f636d08a11b6c9a888f789b8fda284857a7b 100644
--- a/config/settings/base.py
+++ b/config/settings/base.py
@@ -26,7 +26,7 @@ MAINTENANCE_MODE = False
 MAINTENANCE_MODE_MESSAGE = None
 
 # Version ---------------------------------------------------------------------
-PROJECT_VERSION = '2.8.0'
+PROJECT_VERSION = '2.8.1'
 
 # Unauthenticated access ------------------------------------------------------
 # This variable should eventually control whether unauthenticated access is
diff --git a/docker/shibboleth-ds/idpselect_config.js b/docker/shibboleth-ds/idpselect_config.js
index be2cb66689df11f69a391b1b9b7b3724c352a7a2..164dc98eff6693f40021940049cb511d9af8144f 100644
--- a/docker/shibboleth-ds/idpselect_config.js
+++ b/docker/shibboleth-ds/idpselect_config.js
@@ -22,7 +22,7 @@ function IdPSelectUIParms(){
     this.maxResults = 10;            // How many results to show at once or the number at which to
                                      // start showing if alwaysShow is false
     this.myEntityID = null;          // If non null then this string must match the string provided in the DS parms
-    this.preferredIdP = ['https://login.ligo.org/idp/shibboleth', 'https://login2.ligo.org/idp/shibboleth', 'https://google.cirrusidentity.com/gateway'];
+    this.preferredIdP = ['https://login.ligo.org/idp/shibboleth', 'https://shibbi.pki.itc.u-tokyo.ac.jp/idp/shibboleth', 'https://google.cirrusidentity.com/gateway'];
     this.hiddenIdPs = null;          // Array of entityIds to delete
     this.ignoreKeywords = false;     // Do we ignore the <mdui:Keywords/> when looking for candidates
     this.showListFirst = false;      // Do we start with a list of IdPs or just the dropdown
diff --git a/docs/user_docs/source/labels.rst b/docs/user_docs/source/labels.rst
index 3fa3ea690218a4bd5e4219e50cef15af04ec73b8..3c7576843e3b7fd1535fbdc1da75a897f1322179 100644
--- a/docs/user_docs/source/labels.rst
+++ b/docs/user_docs/source/labels.rst
@@ -39,6 +39,8 @@ Here is a table showing the currently available labels and their meanings.
 +-----------------+----------------------------------------------------------------------------------------------------------------------------------------+
 | EM_Throttled    | GraceID is ignored by automatic processing because the corresponding pipeline submitted too many events too quickly.                   |
 +-----------------+----------------------------------------------------------------------------------------------------------------------------------------+
+| EXT_SKYMAP_READY| External skymap is available                                                                                                           |
++-----------------+----------------------------------------------------------------------------------------------------------------------------------------+
 | GCN_PRELIM_SENT | A preliminary GCN has been sent.                                                                                                       |
 +-----------------+----------------------------------------------------------------------------------------------------------------------------------------+
 | GRB_OFFLINE     | Indicates that offline triggered GRB searches found something coincident with this event.                                              |
@@ -65,10 +67,12 @@ Here is a table showing the currently available labels and their meanings.
 +-----------------+----------------------------------------------------------------------------------------------------------------------------------------+
 | L1OPS           | L1 operator signoff requested.                                                                                                         |
 +-----------------+----------------------------------------------------------------------------------------------------------------------------------------+
-| LUMIN_GO        | LUMIN Go                                                                                                                               |
+| LUMIN_GO        | Trigger satisfies basic automated checks and should be vetted by humans. Replaced by ADVREQ                                            |
 +-----------------+----------------------------------------------------------------------------------------------------------------------------------------+
 | LUMIN_NO        | LUMIN No                                                                                                                               |
 +-----------------+----------------------------------------------------------------------------------------------------------------------------------------+
+| NOT_GRB         | Event is likely not an astrophysical gamma ray burst                                                                                   |
++-----------------+----------------------------------------------------------------------------------------------------------------------------------------+
 | PASTRO_READY    | p_astro is available.                                                                                                                  |
 +-----------------+----------------------------------------------------------------------------------------------------------------------------------------+
 | PE_READY        | Parameter estimation results are available                                                                                             |
diff --git a/gracedb/annotations/voevent_utils.py b/gracedb/annotations/voevent_utils.py
index 9541c0b39194fabdee665a13bdc6bf3b17635145..3454e7ac32346e5308f45e4f16dec7411e403574 100644
--- a/gracedb/annotations/voevent_utils.py
+++ b/gracedb/annotations/voevent_utils.py
@@ -344,8 +344,6 @@ def construct_voevent_file(obj, voevent, request=None):
                    "Time_Difference",
                    value=float(deltat),
                    ucd="meta.code",
-                   #dataType="float"
-                   #AEP--> figure this out
                    ac=True,
                )
                p_deltat.Description = ("Time difference between GW candidate "
@@ -359,8 +357,6 @@ def construct_voevent_file(obj, voevent, request=None):
                     "Time_Coincidence_FAR",
                     value=obj.coinc_far,
                     ucd="arith.rate;stat.falsealarm",
-                    #dataType="float",
-                   #AEP--> figure this out
                     ac=True,
                     unit="Hz"
                 )
@@ -375,8 +371,6 @@ def construct_voevent_file(obj, voevent, request=None):
                     "Time_Sky_Position_Coincidence_FAR",
                     value=obj.coinc_far_space,
                     ucd="arith.rate;stat.falsealarm",
-                    #dataType="float",
-                    #AEP--> figure this out
                     ac=True,
                     unit="Hz"
                 )
@@ -545,7 +539,7 @@ def construct_voevent_file(obj, voevent, request=None):
                 value=float(event.central_freq),
                 ucd="gw.frequency",
                 unit="Hz",
-                dataType="float"
+                ac=True,
             )
             p_central_freq.Description = \
                 "Central frequency of GW burst signal"
@@ -557,7 +551,7 @@ def construct_voevent_file(obj, voevent, request=None):
                 value=float(event.duration),
                 unit="s",
                 ucd="time.duration",
-                dataType="float"
+                ac=True,
             )
             p_duration.Description = "Measured duration of GW burst signal"
             v.What.append(p_duration)
@@ -607,7 +601,7 @@ def construct_voevent_file(obj, voevent, request=None):
                 value=float(event.frequency_mean),
                 ucd="gw.frequency",
                 unit="Hz",
-                dataType="float"
+                ac=True,
             )
             p_freq.Description = "Mean frequency of GW burst signal"
             v.What.append(p_freq)
diff --git a/gracedb/core/tests/utils.py b/gracedb/core/tests/utils.py
index 3df7ef510995f80987445d23afbd305ead674122..2df1c84cf65f41d1e6ba6b8493af8a5d6d7c3d88 100644
--- a/gracedb/core/tests/utils.py
+++ b/gracedb/core/tests/utils.py
@@ -12,7 +12,7 @@ from guardian.conf import settings as guardian_settings
 from guardian.models import GroupObjectPermission, UserObjectPermission
 
 from events.models import Tag
-from ligoauth.models import AuthGroup
+from ligoauth.models import AuthGroup, AuthorizedLdapMember
 
 # Set up user model
 UserModel = get_user_model()
@@ -69,6 +69,14 @@ class InternalGroupAndUserSetup(TestCase):
             cls.internal_group.ldap_name = 'internal_ldap_group'
             cls.internal_group.save(update_fields=['ldap_name'])
 
+        # Create AuthorizedLdapMember, link it to internal group:
+        cls.authldapmember, created = AuthorizedLdapMember.objects.get_or_create(
+            name='TestLDAPAuthMember')
+        if created:
+            cls.authldapmember.ldap_gname='internal_ldap_group'
+            cls.authldapmember.ldap_authgroup=cls.internal_group 
+            cls.authldapmember.save()
+
         # Get or create user
         cls.internal_user, _ = UserModel.objects.get_or_create(
             username='internal.user')
@@ -116,6 +124,14 @@ class LvemGroupAndUserSetup(TestCase):
             cls.lvem_group.ldap_name = 'lvem_ldap_group'
             cls.lvem_group.save(update_fields=['ldap_name'])
 
+        # Create AuthorizedLdapMember, link it to LV-EM group:
+        cls.authldapmember_lvem, created = AuthorizedLdapMember.objects.get_or_create(
+            name='TestLDAPAuthMember_LVEM')
+        if created:
+            cls.authldapmember_lvem.ldap_gname='lvem_ldap_group'
+            cls.authldapmember_lvem.ldap_authgroup=cls.lvem_group
+            cls.authldapmember_lvem.save()
+
         # Get or create LV-EM observers group
         lvem_obs_tag, _ = Tag.objects.get_or_create(name='lvem')
         cls.lvem_obs_group, created = AuthGroup.objects.get_or_create(
@@ -125,6 +141,14 @@ class LvemGroupAndUserSetup(TestCase):
             cls.lvem_obs_group.tag = lvem_obs_tag
             cls.lvem_obs_group.save(update_fields=['ldap_name', 'tag'])
 
+        # Create AuthorizedLdapMember, link it to LV-EM observers group:
+        cls.authldapmember_lvemob, created = AuthorizedLdapMember.objects.get_or_create(
+            name='TestLDAPAuthMember_LVEMOB')
+        if created:
+            cls.authldapmember_lvemob.ldap_gname='lvem_observers_ldap_group'
+            cls.authldapmember_lvemob.ldap_authgroup=cls.lvem_group
+            cls.authldapmember_lvemob.save()
+
         # Get or create user
         cls.lvem_user, _ = UserModel.objects.get_or_create(
             username='lvem.user')
@@ -163,6 +187,15 @@ class SupereventManagersGroupAndUserSetup(TestCase):
         if created:
             internal_group.ldap_name = 'internal_ldap_group'
             internal_group.save(update_fields=['ldap_name'])
+
+        # Create AuthorizedLdapMember, link it to internal group:
+        authldapmember, created = AuthorizedLdapMember.objects.get_or_create(
+            name='TestLDAPAuthMember')
+        if created:
+            authldapmember.ldap_gname='internal_ldap_group'
+            authldapmember.ldap_authgroup=internal_group 
+            authldapmember.save()
+
         internal_group.user_set.add(cls.sm_user)
 
         # Get permissions
@@ -212,6 +245,15 @@ class AccessManagersGroupAndUserSetup(TestCase):
         if created:
             internal_group.ldap_name = 'internal_ldap_group'
             internal_group.save(update_fields=['ldap_name'])
+
+        # Create AuthorizedLdapMember, link it to internal group:
+        authldapmember, created = AuthorizedLdapMember.objects.get_or_create(
+            name='TestLDAPAuthMember')
+        if created:
+            authldapmember.ldap_gname='internal_ldap_group'
+            authldapmember.ldap_authgroup=internal_group 
+            authldapmember.save()
+
         internal_group.user_set.add(cls.am_user)
 
         # Get permissions
@@ -248,6 +290,14 @@ class SignoffGroupsAndUsersSetup(TestCase):
             internal_group.ldap_name = 'internal_ldap_group'
             internal_group.save(update_fields=['ldap_name'])
 
+        # Create AuthorizedLdapMember, link it to internal group:
+        cls.authldapmember, created = AuthorizedLdapMember.objects.get_or_create(
+            name='TestLDAPAuthMember')
+        if created:
+            cls.authldapmember.ldap_gname='internal_ldap_group'
+            cls.authldapmember.ldap_authgroup=internal_group 
+            cls.authldapmember.save()
+
         # Get or create IFO control room groups and users, and add perms
         ifos = ['H1', 'L1', 'V1']
         for ifo in ifos:
diff --git a/gracedb/events/migrations/0044_add_skymap_labels.py b/gracedb/events/migrations/0044_add_skymap_labels.py
new file mode 100644
index 0000000000000000000000000000000000000000..1a508d1c778047b2966bf366c537195b92e2ab65
--- /dev/null
+++ b/gracedb/events/migrations/0044_add_skymap_labels.py
@@ -0,0 +1,54 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.5 on 2018-06-28 10:40:45
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+# Create initial Label instances
+
+# List of label names, default colors, and descriptions
+LABELS = [
+    {
+        'name': 'NOT_GRB',
+        'defaultColor': 'black',
+        'description': 'Event is likely not an astrophysical gamma ray burst',
+    },
+    {
+        'name': 'EXT_SKYMAP_READY',
+        'defaultColor': 'green',
+        'description': 'A RAVEN coincidence event is publishable',
+    },
+]
+
+def add_labels(apps, schema_editor):
+    Label = apps.get_model('events', 'Label')
+
+    # Create labels
+    for label_dict in LABELS:
+        l, created = Label.objects.get_or_create(name=label_dict['name'])
+        l.defaultColor = label_dict['defaultColor']
+        l.description = label_dict['description']
+        l.save()
+
+def remove_labels(apps, schema_editor):
+    Label = apps.get_model('events', 'Label')
+
+    # Delete labels
+    for label_dict in LABELS:
+        try:
+            l = Label.objects.get(name=label_dict['name'])
+        except Label.DoesNotExist:
+            print('Label {0} not found to be deleted, skipping.' \
+                .format(label_dict['name']))
+            break
+        l.delete()
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('events', '0043_add_raven_label'),
+    ]
+
+    operations = [
+        migrations.RunPython(add_labels, remove_labels),
+    ]
diff --git a/gracedb/events/serialize.py b/gracedb/events/serialize.py
index f3614334931dfe26a17a6893feb2d204af6731d3..4743f5aacdf5bde0131c6167aa6c4e042e70f853 100644
--- a/gracedb/events/serialize.py
+++ b/gracedb/events/serialize.py
@@ -90,9 +90,11 @@ def write_output_files(root_dir, xmldoc, log_content, \
     # Write xml-formatted coinc table
     # We do it this way instead of using create_versioned_file since the
     # xmldoc is designed to write to a file object.
+
     file_path = os.path.join(root_dir, xml_fname)
     f = VersionedFile(file_path, 'w')
     xmldoc.write(f.file)
+    f.close()
 
     # Write log file
     create_versioned_file(log_fname, root_dir, log_content)
diff --git a/gracedb/events/translator.py b/gracedb/events/translator.py
index 30bef5f52752893a822d444d620c461c18922ed5..7aa442be7e56c46f0e4f720fbf8c5a8fdebe8ac9 100644
--- a/gracedb/events/translator.py
+++ b/gracedb/events/translator.py
@@ -398,12 +398,6 @@ class Translator(object):
         raise(NotImplemented)
 
     def castData(self, data):
-        # convert ints to ints
-        # No longer casting gpstime to integer.
-        #for key in ['gpstime', 'likelihood']:
-        for key in ['likelihood']:
-            if data[key]:
-                data[key] = int(float(data[key]))
 
         # convert floats to floats
         for key in ['far']:
diff --git a/gracedb/events/view_utils.py b/gracedb/events/view_utils.py
index 3ad0477ad39c843580e1de0a23a24bfadd429cb2..6dd15101acec40081c4469be4e4d767f6dadc857 100644
--- a/gracedb/events/view_utils.py
+++ b/gracedb/events/view_utils.py
@@ -375,10 +375,8 @@ def eventLogToDict(log, request=None):
     taglist_uri = api_reverse("events:eventlogtag-list",
             args=[log.event.graceid, log.N],
             request=request)
-    if log.filename:
-        actual_filename = log.filename
-        if log.file_version >= 0:
-            actual_filename += ',%d' % log.file_version
+    if log.filename and log.file_version is not None:
+        actual_filename = log.filename + ',%d' % log.file_version
         # NOTE: the reverse function will return a urlquoted
         # result, so we don't need urlquote here. Effectively
         # escaping twice results in wrong urls. 
diff --git a/gracedb/ligoauth/middleware.py b/gracedb/ligoauth/middleware.py
index e08bd5f645cb13c1cd6bdbaf86a72e8c78e2a458..934bfd16f5fe39c78ea8d458985e16e800b86fb7 100644
--- a/gracedb/ligoauth/middleware.py
+++ b/gracedb/ligoauth/middleware.py
@@ -8,7 +8,7 @@ from django.contrib.auth.middleware import PersistentRemoteUserMiddleware
 from django.core.exceptions import ImproperlyConfigured
 from django.urls import reverse_lazy
 
-from .models import AuthGroup
+from .models import AuthGroup, AuthorizedLdapMember
 
 
 # Set up logger
@@ -88,9 +88,31 @@ class ShibbolethWebAuthMiddleware(PersistentRemoteUserMiddleware):
         # Get groups from session which are in database as a QuerySet
         session_group_names = request.META.get(cls.group_header, '').split(
             cls.group_delimiter)
-        session_groups = AuthGroup.ldap_objects.filter(ldap_name__in=
+
+        #session_groups = AuthGroup.ldap_objects.filter(ldap_name__in=
+        #    session_group_names)
+
+        # Get the authorized ldap membership object based on the request header:
+        session_ldap_membership = AuthorizedLdapMember.objects.filter(ldap_gname__in=
             session_group_names)
 
+        # Get the list of AuthGroup objects that don't have a null set of 
+        # AuthorizedLdapMembers. Note that this step seems redundant right now.
+        # However, it's necessary for the new many-to-one relationship with
+        # multiple ldap memberships. Plus, this will allow for adding new ldap
+        # memberships and multiple group affiliations. Note for me:
+        # If an ldap member needs to be associated with another group (like, 
+        # not just internal_users), then there should be a new authorizedldapmember-ship
+        # with a new ForeignKey relation. FYI.
+
+        group_set = AuthGroup.objects.exclude(authorizedldapmember__isnull=True)
+
+        # Return a queryset of group memberships associated with the ldap
+        # community id
+
+        ldap_group_ids = session_ldap_membership.values_list('id')
+        session_groups = group_set.filter(authorizedldapmember__in=ldap_group_ids)
+        
         # Add groups which are in session but not in database
         user.groups.add(*session_groups)
 
diff --git a/gracedb/ligoauth/migrations/0061_auth_ldap_communities.py b/gracedb/ligoauth/migrations/0061_auth_ldap_communities.py
new file mode 100644
index 0000000000000000000000000000000000000000..e916713ed3d599296cc21e8350f70a240a6b60a2
--- /dev/null
+++ b/gracedb/ligoauth/migrations/0061_auth_ldap_communities.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.23 on 2019-12-27 17:50
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ligoauth', '0060_add_emfollow-test_cert'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='AuthorizedLdapMember',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('ldap_gname', models.CharField(max_length=50, null=True, unique=True)),
+                ('name', models.CharField(max_length=50, null=True, unique=True)),
+                ('ldap_authgroup', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='ligoauth.AuthGroup')),
+            ],
+        ),
+    ]
diff --git a/gracedb/ligoauth/migrations/0062_populate_auth_ldap_communities.py b/gracedb/ligoauth/migrations/0062_populate_auth_ldap_communities.py
new file mode 100644
index 0000000000000000000000000000000000000000..55a7d5c36f74056fd7f2243dc8867b42fb9972a3
--- /dev/null
+++ b/gracedb/ligoauth/migrations/0062_populate_auth_ldap_communities.py
@@ -0,0 +1,56 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.20 on 2019-05-28 18:18
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+GROUP_DATA = [
+    {
+        'name': 'LVC Members',
+        'ldap_gname': 'Communities:LSCVirgoLIGOGroupMembers',
+    },
+    {
+        'name': 'KAGRA',
+        'ldap_gname': 'gw-astronomy:KAGRA-LIGO:members',
+    },
+]
+
+
+def create_ldapauthgroups(apps, schema_editor):
+    AuthGroup = apps.get_model('ligoauth', 'AuthGroup')
+    AuthorizedLdapMember = apps.get_model('ligoauth', 'AuthorizedLdapMember')
+
+    internal_group = AuthGroup.objects.get(name='internal_users')
+
+    # Create AuthrizedLdapMember instances
+    for group in GROUP_DATA:
+
+        lag = AuthorizedLdapMember.objects.create(
+            name=group['name'],
+            ldap_gname=group['ldap_gname'],
+            ldap_authgroup=internal_group)
+        lag.save()
+
+
+def delete_ldapauthgroups(apps, schema_editor):
+    AuthorizedLdapMember = apps.get_model('ligoauth', 'AuthorizedLdapMember')
+
+    # Loop over groups and delete AuthGroup
+    for group in GROUP_DATA:
+        # Get AuthGroup
+        group_name = group['name']
+        ag = AuthorizedLdapMember.objects.get(name=group_name)
+
+        # Delete AuthGroup and keep DjangoGroup base class
+        ag.delete()
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ligoauth', '0061_auth_ldap_communities'),
+    ]
+
+    operations = [
+        migrations.RunPython(create_ldapauthgroups, delete_ldapauthgroups),
+    ]
diff --git a/gracedb/ligoauth/migrations/0063_populate_emadv_auth_ldap_communities.py b/gracedb/ligoauth/migrations/0063_populate_emadv_auth_ldap_communities.py
new file mode 100644
index 0000000000000000000000000000000000000000..528a1f6521c9edcb1484086950f4a0e4fc833a9b
--- /dev/null
+++ b/gracedb/ligoauth/migrations/0063_populate_emadv_auth_ldap_communities.py
@@ -0,0 +1,52 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.20 on 2019-05-28 18:18
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+GROUP_DATA = [
+    {
+        'name': 'EM Advovates',
+        'ldap_gname': 'Communities:LVC:GraceDB:GraceDBAdvocates',
+    },
+]
+
+
+def create_ldapauthgroups(apps, schema_editor):
+    AuthGroup = apps.get_model('ligoauth', 'AuthGroup')
+    AuthorizedLdapMember = apps.get_model('ligoauth', 'AuthorizedLdapMember')
+
+    internal_group = AuthGroup.objects.get(name='em_advocates')
+
+    # Create AuthrizedLdapMember instances
+    for group in GROUP_DATA:
+
+        lag = AuthorizedLdapMember.objects.create(
+            name=group['name'],
+            ldap_gname=group['ldap_gname'],
+            ldap_authgroup=internal_group)
+        lag.save()
+
+
+def delete_ldapauthgroups(apps, schema_editor):
+    AuthorizedLdapMember = apps.get_model('ligoauth', 'AuthorizedLdapMember')
+
+    # Loop over groups and delete AuthGroup
+    for group in GROUP_DATA:
+        # Get AuthGroup
+        group_name = group['name']
+        ag = AuthorizedLdapMember.objects.get(name=group_name)
+
+        # Delete AuthGroup and keep DjangoGroup base class
+        ag.delete()
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ligoauth', '0062_populate_auth_ldap_communities'),
+    ]
+
+    operations = [
+        migrations.RunPython(create_ldapauthgroups, delete_ldapauthgroups),
+    ]
diff --git a/gracedb/ligoauth/migrations/0064_populate_lvem_auth_ldap_communities.py b/gracedb/ligoauth/migrations/0064_populate_lvem_auth_ldap_communities.py
new file mode 100644
index 0000000000000000000000000000000000000000..cfe6b8d494c0360402749946f3357389d3cd9030
--- /dev/null
+++ b/gracedb/ligoauth/migrations/0064_populate_lvem_auth_ldap_communities.py
@@ -0,0 +1,52 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.20 on 2019-05-28 18:18
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+GROUP_DATA = [
+    {
+        'name': 'LVEM',
+        'ldap_gname': 'gw-astronomy:LV-EM',
+    },
+]
+
+
+def create_ldapauthgroups(apps, schema_editor):
+    AuthGroup = apps.get_model('ligoauth', 'AuthGroup')
+    AuthorizedLdapMember = apps.get_model('ligoauth', 'AuthorizedLdapMember')
+
+    internal_group = AuthGroup.objects.get(name='lvem_users')
+
+    # Create AuthrizedLdapMember instances
+    for group in GROUP_DATA:
+
+        lag = AuthorizedLdapMember.objects.create(
+            name=group['name'],
+            ldap_gname=group['ldap_gname'],
+            ldap_authgroup=internal_group)
+        lag.save()
+
+
+def delete_ldapauthgroups(apps, schema_editor):
+    AuthorizedLdapMember = apps.get_model('ligoauth', 'AuthorizedLdapMember')
+
+    # Loop over groups and delete AuthGroup
+    for group in GROUP_DATA:
+        # Get AuthGroup
+        group_name = group['name']
+        ag = AuthorizedLdapMember.objects.get(name=group_name)
+
+        # Delete AuthGroup and keep DjangoGroup base class
+        ag.delete()
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ligoauth', '0063_populate_emadv_auth_ldap_communities'),
+    ]
+
+    operations = [
+        migrations.RunPython(create_ldapauthgroups, delete_ldapauthgroups),
+    ]
diff --git a/gracedb/ligoauth/migrations/0065_populate_lvemo_auth_ldap_communities.py b/gracedb/ligoauth/migrations/0065_populate_lvemo_auth_ldap_communities.py
new file mode 100644
index 0000000000000000000000000000000000000000..b470df3cf11bc2e6a1a0ea6084b828c8661224db
--- /dev/null
+++ b/gracedb/ligoauth/migrations/0065_populate_lvemo_auth_ldap_communities.py
@@ -0,0 +1,52 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.20 on 2019-05-28 18:18
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+GROUP_DATA = [
+    {
+        'name': 'LVEM Observers',
+        'ldap_gname': 'gw-astronomy:LV-EM:Observer',
+    },
+]
+
+
+def create_ldapauthgroups(apps, schema_editor):
+    AuthGroup = apps.get_model('ligoauth', 'AuthGroup')
+    AuthorizedLdapMember = apps.get_model('ligoauth', 'AuthorizedLdapMember')
+
+    internal_group = AuthGroup.objects.get(name='lvem_observers')
+
+    # Create AuthrizedLdapMember instances
+    for group in GROUP_DATA:
+
+        lag = AuthorizedLdapMember.objects.create(
+            name=group['name'],
+            ldap_gname=group['ldap_gname'],
+            ldap_authgroup=internal_group)
+        lag.save()
+
+
+def delete_ldapauthgroups(apps, schema_editor):
+    AuthorizedLdapMember = apps.get_model('ligoauth', 'AuthorizedLdapMember')
+
+    # Loop over groups and delete AuthGroup
+    for group in GROUP_DATA:
+        # Get AuthGroup
+        group_name = group['name']
+        ag = AuthorizedLdapMember.objects.get(name=group_name)
+
+        # Delete AuthGroup and keep DjangoGroup base class
+        ag.delete()
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ligoauth', '0064_populate_lvem_auth_ldap_communities'),
+    ]
+
+    operations = [
+        migrations.RunPython(create_ldapauthgroups, delete_ldapauthgroups),
+    ]
diff --git a/gracedb/ligoauth/models.py b/gracedb/ligoauth/models.py
index 238bb110f0d35da70fd0daeaa941ada81f9bc7f5..9a3d18461a1bead652b8115c03fa1257d5880eef 100644
--- a/gracedb/ligoauth/models.py
+++ b/gracedb/ligoauth/models.py
@@ -21,7 +21,6 @@ class X509Cert(models.Model):
     subject = models.CharField(max_length=255, unique=True, null=False)
     user = models.ForeignKey(User)
 
-
 class AuthGroup(Group):
     """Enhanced version of Django Group model"""
     # Description of the group
@@ -40,3 +39,16 @@ class AuthGroup(Group):
     objects = models.Manager()
     ldap_objects = LdapGroupManager()
     tag_objects = TagGroupManager()
+
+class AuthorizedLdapMember(models.Model):
+    """Model for authorized ldap membership"""
+    # Group membership in ldap. This is analogous to the `ldap_name` in the 
+    # AuthGroup class:
+    ldap_gname = models.CharField(max_length=50, unique=True, null=True)
+
+    # Add support for authorized membership:
+    ldap_authgroup = models.ForeignKey(AuthGroup, null=True)
+
+    # Give it a name:
+    name = models.CharField(max_length=50, unique=True, null=True)
+
diff --git a/gracedb/ligoauth/tests/test_middleware.py b/gracedb/ligoauth/tests/test_middleware.py
index 73e1513412ccb701f7363abe6e52adec65f60764..316fb89c0c2a500c2252154ffeba957911ece65c 100644
--- a/gracedb/ligoauth/tests/test_middleware.py
+++ b/gracedb/ligoauth/tests/test_middleware.py
@@ -8,7 +8,7 @@ from django.urls import reverse
 
 from user_sessions.middleware import SessionMiddleware
 
-from ligoauth.models import AuthGroup
+from ligoauth.models import AuthGroup, AuthorizedLdapMember
 from ligoauth.middleware import (
     ControlRoomMiddleware, ShibbolethWebAuthMiddleware,
 )
@@ -311,8 +311,14 @@ class TestShibbolethWebAuthMiddleware(GraceDbTestBase):
         request = self.factory.get(self.url)
         request.META.update(**{
             settings.SHIB_USER_HEADER: self.lvem_user.username,
-            settings.SHIB_GROUPS_HEADER: self.lvem_obs_group.ldap_name,
+            settings.SHIB_GROUPS_HEADER: self.lvem_obs_group.ldap_name + 'test_lvemapl',
         })
+        new_lvem_ldap_member, created = AuthorizedLdapMember.objects.get_or_create(name='test_lvem')
+        if created:
+            new_lvem_ldap_member.ldap_gname=self.lvem_obs_group.ldap_name  + 'test_lvemapl'
+            new_lvem_ldap_member.ldap_authgroup=self.lvem_obs_group
+            new_lvem_ldap_member.save()
+        
         # Necessary pre-processing middleware
         SessionMiddleware().process_request(request)
         AuthenticationMiddleware().process_request(request)
@@ -386,14 +392,23 @@ class TestShibbolethWebAuthMiddleware(GraceDbTestBase):
         request = self.factory.get(self.url)
         request.META.update(**{
             settings.SHIB_USER_HEADER: new_user_dict['username'],
-            settings.SHIB_GROUPS_HEADER: self.lvem_obs_group.ldap_name,
+            settings.SHIB_GROUPS_HEADER: self.lvem_obs_group.ldap_name + 'test_lvemuc',
             settings.SHIB_ATTRIBUTE_MAP['email']: new_user_dict['email'],
         })
+        
+        # Set up ldap membership
+        new_test_lvem_ldap_member, created = AuthorizedLdapMember.objects.get_or_create(name='new_test_lvem')
+        if created:
+            new_test_lvem_ldap_member.ldap_gname=self.lvem_obs_group.ldap_name  + 'test_lvemuc'
+            new_test_lvem_ldap_member.ldap_authgroup=self.lvem_obs_group
+            new_test_lvem_ldap_member.save()
+
         # Necessary pre-processing middleware
         SessionMiddleware().process_request(request)
         AuthenticationMiddleware().process_request(request)
         self.mw_instance.process_request(request)
 
+        
         # Make sure user is authenticated and was authenticated by
         # the shibboleth backend and that the LV-EM observers group is
         # attached to the user account
@@ -418,6 +433,13 @@ class TestShibbolethWebAuthMiddleware(GraceDbTestBase):
             ldap_name='new_ldap_group')
         # Compile group header - add one random additional group name string
         other_group_ldap_str = 'other_group'
+
+        new_auth_ldap_member, created = AuthorizedLdapMember.objects.get_or_create(name='new_membership')
+        if created:
+            new_auth_ldap_member.ldap_gname=other_group_ldap_str
+            new_auth_ldap_member.ldap_authgroup=new_ldap_group
+            new_auth_ldap_member.save()
+        
         delim = ShibbolethWebAuthMiddleware.group_delimiter
         groups_str = delim.join([self.internal_group.ldap_name,
             new_ldap_group.ldap_name, other_group_ldap_str])
diff --git a/gracedb/migrations/guardian/0007_add_gareth_davies_to_pycbc.py b/gracedb/migrations/guardian/0007_add_gareth_davies_to_pycbc.py
new file mode 100644
index 0000000000000000000000000000000000000000..ae1f3e23068efdeb2f4349d72e0634cb859323f6
--- /dev/null
+++ b/gracedb/migrations/guardian/0007_add_gareth_davies_to_pycbc.py
@@ -0,0 +1,63 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.5 on 2017-11-01 16:19
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+# Creates UserObjectPermission objects which allow specific users
+# to add events for pipelines.  Based on current production database
+# content (27 October 2017)
+
+# List of pipeline names and lists of usernames who should
+# be allowed to add events for them
+PP_LIST = [
+    {
+        'pipeline': 'gstlal',
+        'usernames': [
+            'cody.messick@LIGO.ORG',
+        ]
+    },
+    {
+        'pipeline': 'pycbc',
+        'usernames': [
+            'gareth.davies@LIGO.ORG',
+        ]
+    },
+]
+
+def add_permissions(apps, schema_editor):
+    User = apps.get_model('auth', 'User')
+    Permission = apps.get_model('auth', 'Permission')
+    UserObjectPermission = apps.get_model('guardian', 'UserObjectPermission')
+    Pipeline = apps.get_model('events', 'Pipeline')
+    ContentType = apps.get_model('contenttypes', 'ContentType')
+
+    perm = Permission.objects.get(codename='populate_pipeline')
+    ctype = ContentType.objects.get_for_model(Pipeline)
+    for pp_dict in PP_LIST:
+        pipeline = Pipeline.objects.get(name=pp_dict['pipeline'])
+
+        # Loop over users
+        for username in pp_dict['usernames']:
+
+            # Robot users should have been already created by ligoauth 0003,
+            # but we have to create human user accounts here
+            user, _ = User.objects.get_or_create(username=username)
+
+            # Create UserObjectPermission
+            uop, uop_created = UserObjectPermission.objects.get_or_create(
+                user=user, permission=perm, content_type=ctype,
+                object_pk=pipeline.id)
+
+def remove_permissions(apps, schema_editor):
+    pass
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('guardian', '0006_authorize_emfollow_to_populate_pipelines'),
+    ]
+
+    operations = [
+        migrations.RunPython(add_permissions, remove_permissions),
+    ]
\ No newline at end of file
diff --git a/gracedb/migrations/guardian/0008_add_edoardo_milotti_to_cwb.py b/gracedb/migrations/guardian/0008_add_edoardo_milotti_to_cwb.py
new file mode 100644
index 0000000000000000000000000000000000000000..1da0e8624887c8749c238f4dcdd1c9a0712783df
--- /dev/null
+++ b/gracedb/migrations/guardian/0008_add_edoardo_milotti_to_cwb.py
@@ -0,0 +1,57 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.5 on 2017-11-01 16:19
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+# Creates UserObjectPermission objects which allow specific users
+# to add events for pipelines.  Based on current production database
+# content (27 October 2017)
+
+# List of pipeline names and lists of usernames who should
+# be allowed to add events for them
+PP_LIST = [
+    {
+        'pipeline': 'cwb',
+        'usernames': [
+            'edoardo.milotti@ligo.org',
+        ]
+    },
+]
+
+def add_permissions(apps, schema_editor):
+    User = apps.get_model('auth', 'User')
+    Permission = apps.get_model('auth', 'Permission')
+    UserObjectPermission = apps.get_model('guardian', 'UserObjectPermission')
+    Pipeline = apps.get_model('events', 'Pipeline')
+    ContentType = apps.get_model('contenttypes', 'ContentType')
+
+    perm = Permission.objects.get(codename='populate_pipeline')
+    ctype = ContentType.objects.get_for_model(Pipeline)
+    for pp_dict in PP_LIST:
+        pipeline = Pipeline.objects.get(name=pp_dict['pipeline'])
+
+        # Loop over users
+        for username in pp_dict['usernames']:
+
+            # Robot users should have been already created by ligoauth 0003,
+            # but we have to create human user accounts here
+            user, _ = User.objects.get_or_create(username=username)
+
+            # Create UserObjectPermission
+            uop, uop_created = UserObjectPermission.objects.get_or_create(
+                user=user, permission=perm, content_type=ctype,
+                object_pk=pipeline.id)
+
+def remove_permissions(apps, schema_editor):
+    pass
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('guardian', '0007_add_gareth_davies_to_pycbc'),
+    ]
+
+    operations = [
+        migrations.RunPython(add_permissions, remove_permissions),
+    ]
\ No newline at end of file
diff --git a/gracedb/superevents/views.py b/gracedb/superevents/views.py
index 7015c862346e7a601d0f7a0b4505df838b7d023d..b449feabac6a19d10fb2b61136a966123bc89919 100644
--- a/gracedb/superevents/views.py
+++ b/gracedb/superevents/views.py
@@ -233,9 +233,11 @@ class SupereventPublic(DisplayFarMixin, ListView):
                     ("BBH", voe.prob_bbh),
                     ("Terrestrial", voe.prob_terrestrial),
                     ("MassGap", voe.prob_mass_gap)]
-                pastro_values.sort(reverse=True, key=lambda p_a: p_a[1])
+                pastro_values.sort(reverse=True, key=lambda p_a: 0.0 if p_a[1] is None else p_a[1])
                 sourcelist = []
                 for key, value in pastro_values:
+                    if value is None:
+                        value = 0.0
                     if value > 0.01:
                         prob = int(round(100*value))
                         if prob == 100: prob = '>99'