diff --git a/gracedb/admin.py b/gracedb/admin.py
index 0920ccd302864b5c7330b54fd82b88842e6ce6f6..7e36d454e8d2ad5b3a2c1897a2a1bd103bf5505a 100644
--- a/gracedb/admin.py
+++ b/gracedb/admin.py
@@ -1,6 +1,6 @@
 
 from models import Event, EventLog, User, Group
-from models import Label, Labelling, Slot
+from models import Label, Labelling, Tag
 from django.contrib import admin
 
 class EventAdmin(admin.ModelAdmin):
@@ -30,8 +30,9 @@ class LabellingAdmin(admin.ModelAdmin):
     list_display = [ 'event', 'label', 'creator' ]
     search_fields = [ 'event__id', 'label__name', 'creator__name' ]
 
-class SlotAdmin(admin.ModelAdmin):
-    list_display = [ 'event', 'name', 'value' ]
+class TagAdmin(admin.ModelAdmin):
+    list_display = [ 'name', 'displayName' ]
+    filter_vertical = [ 'eventlogs' ]
 
 admin.site.register(Event, EventAdmin)
 admin.site.register(EventLog, EventLogAdmin)
@@ -39,4 +40,4 @@ admin.site.register(User, UserAdmin)
 admin.site.register(Group)
 admin.site.register(Label, LabelAdmin)
 admin.site.register(Labelling, LabellingAdmin)
-admin.site.register(Slot, SlotAdmin)
+admin.site.register(Tag, TagAdmin)
diff --git a/gracedb/api.py b/gracedb/api.py
index 11157c5a8a586e204064ad1f0502f6fce41b3935..9b4fe0c753aa15eea22351badb600b8ca49f9608 100644
--- a/gracedb/api.py
+++ b/gracedb/api.py
@@ -8,7 +8,7 @@ from django.conf import settings
 
 import json
 
-from gracedb.models import Event, Group, EventLog, Slot
+from gracedb.models import Event, Group, EventLog, Tag
 from gracedb.views import create_label
 from translator import handle_uploaded_data
 
@@ -115,12 +115,8 @@ def eventToDict(event, columns=None, request=None):
             "filemeta" : reverse("filemeta", args=[graceid], request=request),
             "labels" : reverse("labels", args=[graceid], request=request),
             "self"  : reverse("event-detail", args=[graceid], request=request),
+            "tags"  : reverse("eventtag-list", args=[graceid], request=request),
             }
-    # XXX Jam the slots in here? Could just have a list of slot names instead of
-    # all these links.  But the links might be useful??
-    rv['slots'] = {}
-    for slot in Slot.objects.filter(event=event).order_by('name'):
-        rv['slots'][slot.name] = reverse("slot", args=[graceid, slot.name], request=request)
     return rv
 
 
@@ -472,12 +468,16 @@ class EventLabel(APIView):
 # Janky serialization
 def eventLogToDict(log, n=None, request=None):
     # XXX Messy.  n should not be here but in the model.
+    taglist_uri = None
     if (n is None) and request:
         uri = request.build_absolute_uri()
     elif n is not None and request:
         uri = reverse("eventlog-detail",
                 args=[log.event.graceid(), n],
                 request=request)
+        taglist_uri = reverse("eventlogtag-list",
+                args=[log.event.graceid(), n],
+                request=request)
     else:
         uri = None
     return {
@@ -485,6 +485,7 @@ def eventLogToDict(log, n=None, request=None):
                 "created" : log.created,
                 "issuer"  : log.issuer.name,
                 "self"    : uri,
+                "tags"    : taglist_uri,
            }
 
 class EventLogList(APIView):
@@ -521,8 +522,10 @@ class EventLogList(APIView):
         return Response(rv)
 
     def post(self, request, graceid):
+        logger = logging.getLogger(__name__)
         event = Event.getByGraceid(graceid)
         message = request.DATA.get('message')
+        tagname = request.DATA.get('tagname')
         logentry = EventLog(
                 event=event,
                 issuer=request.ligouser,
@@ -533,6 +536,16 @@ class EventLogList(APIView):
         rv = eventLogToDict(logentry, n, request=request)
         response = Response(rv, status=status.HTTP_201_CREATED)
         response['Location'] = rv['self']
+
+        if tagname:
+            n = logentry.getN()
+            # XXX This is not what these API views are really meant for, but...
+            tmp = EventLogTagDetail()
+            retval = tmp.put(request, graceid, n, tagname) 
+            # XXX This seems like a bizarre way of getting an error message out.
+            if retval.status_code != 201:
+                response['tagWarning'] = 'Error creating tag.'
+
         return response
 
 class EventLogDetail(APIView):
@@ -547,7 +560,264 @@ class EventLogDetail(APIView):
             return Response("Log Entry Not Found",
                     status=status.HTTP_404_NOT_FOUND)
         rv = event.eventlog_set.order_by("created").all()[int(n)]
-        return Response(eventLogToDict(rv, request=request))
+        # XXX I (Branson) put the n argument here.  Why not?  
+        # We might as well since we have it, right?
+        return Response(eventLogToDict(rv, n, request=request))
+
+#==================================================================
+# Tags
+
+
+def tagToDict(tag, columns=None, request=None, event=None, n=None):
+    """Convert a tag to a dictionary.
+       Output depends on the level of specificity.
+    """
+
+    rv = {}
+    rv['name'] = tag.name
+    rv['displayName'] = tag.displayName
+    if event:
+        if n:
+            # We want a link to the self only.  End of the line.
+            rv['links'] = {
+                            "self" : reverse("eventlogtag-detail",
+                                             args=[event.graceid(),n,tag.name],
+                                             request=request)
+                          }
+        else:
+            # Links to all log messages of the event with this tag.
+            rv['links'] = {
+                            "logs" : [reverse("eventlog-detail", 
+                                              args=[event.graceid(),log.getN()], 
+                                              request=request) 
+                                      for log in event.getLogsForTag(tag.name)],
+                            "self" : reverse("eventtag-detail",
+                                             args=[event.graceid(),tag.name],
+                                             request=request)
+                          }
+    else:
+        # XXX Unclear what the tag detail resource should be at this level.
+        # For now, return an empty list.
+        pass
+#         rv['links'] = {
+#                         "events" : [reverse("event-detail", 
+#                                             args=[event.graceid()], 
+#                                             request=request) 
+#                                     for event in tag.getEvents()],
+#                         "self"   : reverse("tag-detail",
+#                                            args=[tag.name],
+#                                            request=request)
+#                       }
+    return rv
+
+class TagList(APIView):
+    """Tag List Resource
+    """
+    authentication_classes = (LigoAuthentication,)
+    permission_classes = (IsAuthenticated,)
+
+    def get(self, request):
+        # Return a list of links to all tag objects.
+        rv = {
+#                 'tags' : [ reverse("tag-detail", args=[tag.name],
+#                                    request=request)
+#                            for tag in Tag.objects.all() ]
+#                For now, we just output the tag names, since we don't know what 
+#                tag-detail should look like.
+                 'tags' : [ tag.name for tag in Tag.objects.all() ]
+             }
+        return Response(rv)
+
+# XXX Unclear what the tag detail resource should be.
+# class TagDetail(APIView):
+#     """Tag Detail Resource
+#     """
+#     authentication_classes = (LigoAuthentication,)
+#     permission_classes = (IsAuthenticated,)
+# 
+#     def get(self, request, tagname):
+#         try:
+#             tag = Tag.objects.filter(name=tagname)[0]
+#         except Tag.DoesNotExist:
+#             return Response("Tag not found.",
+#                     status=status.HTTP_404_NOT_FOUND)
+#         return Response(tagToDict(tag,request=request))
+
+class EventTagList(APIView):
+    """Event Tag List Resource
+    """
+    authentication_classes = (LigoAuthentication,)
+    permission_classes = (IsAuthenticated,)
+
+    def get(self, request, graceid):
+        # Return a list of links to all tags for this event.
+        try:
+            event = Event.getByGraceid(graceid)
+        except Event.DoesNotExist:
+            # XXX Real error message.
+            return Response("Event does not exist.",
+                    status=status.HTTP_404_NOT_FOUND)
+
+        rv = {
+                'tags' : [ reverse("eventtag-detail",args=[graceid,
+                                   tag.name],
+                                   request=request)
+                           for tag in event.getAvailableTags()]
+             }
+
+        return Response(rv)
+
+class EventTagDetail(APIView):
+    """Event Tag List Resource
+    """
+    authentication_classes = (LigoAuthentication,)
+    permission_classes = (IsAuthenticated,)
+
+    def get(self, request, graceid, tagname):
+        try:
+            event = Event.getByGraceid(graceid)
+        except Event.DoesNotExist:
+            # XXX Real error message.
+            return Response("Event does not exist.",
+                    status=status.HTTP_404_NOT_FOUND)
+        try:
+            tag = Tag.objects.filter(name=tagname)[0]
+            rv = tagToDict(tag,event=event,request=request)
+            return Response(rv)
+        except Tag.DoesNotExist:
+            return Response("No such tag for event.",
+                    status=status.HTTP_404_NOT_FOUND)
+
+class EventLogTagList(APIView):
+    """Event Log Tag List Resource
+    """
+    authentication_classes = (LigoAuthentication,)
+    permission_classes = (IsAuthenticated,)
+
+    def get(self, request, graceid, n):
+        # Return a list of links to tags associated with a given log message
+        try:
+            event = Event.getByGraceid(graceid)
+            eventlog = event.eventlog_set.order_by("created").all()[int(n)]
+        except Event.DoesNotExist:
+            # XXX Real error message.
+            return Response("Event does not exist.",
+                    status=status.HTTP_404_NOT_FOUND)
+        except:
+            # XXX Real error message.
+            return Response("Log does not exist.",
+                    status=status.HTTP_404_NOT_FOUND)
+
+        rv = {
+                'tags' : [ reverse("eventlogtag-detail",
+                                    args=[graceid, 
+                                    n, tag.name],
+                                    request=request)
+                           for tag in eventlog.tag_set.all()]
+             }
+
+        return Response(rv)
+
+class EventLogTagDetail(APIView):
+    """Event Log Tag Detail Resource
+    """
+    authentication_classes = (LigoAuthentication,)
+    permission_classes = (IsAuthenticated,)
+
+    def get(self, request, graceid, n, tagname):
+        try:
+            event = Event.getByGraceid(graceid)
+            eventlog = event.eventlog_set.order_by("created").all()[int(n)]
+        except Event.DoesNotExist:
+            # XXX Real error message.
+            return Response("Event does not exist.",
+                    status=status.HTTP_404_NOT_FOUND)
+        except:
+            # XXX Real error message.
+            return Response("Log does not exist.",
+                    status=status.HTTP_404_NOT_FOUND)
+        try:
+            tag = eventlog.tag_set.filter(name=tagname)[0]
+            # Serialize
+            return Response(tagToDict(tag,event=event,n=n,request=request))
+        except:
+            return Response("Tag not found.",status=status.HTTP_404_NOT_FOUND)
+
+    def put(self, request, graceid, n, tagname):
+        logger = logging.getLogger(__name__)
+        try:
+            event = Event.getByGraceid(graceid)
+            eventlog = event.eventlog_set.order_by("created").all()[int(n)]
+        except Event.DoesNotExist:
+            # XXX Real error message.
+            return Response("Event does not exist.",
+                    status=status.HTTP_404_NOT_FOUND)
+        except:
+            # XXX Real error message.
+            return Response("Log does not exist.",
+                    status=status.HTTP_404_NOT_FOUND)
+        try:
+            # Has this tag-eventlog relationship already been created? If so, kick out.
+            # Actually, adding the eventlog to the tag would not hurt anything--no
+            # duplicate entry would be made in the database.  However, we don't want
+            # an extra log entry, or a deceptive HTTP response (i.e., one telling the 
+            # client that the creation was sucessful when, in fact, the database
+            # was unchanged.
+            tag = eventlog.tag_set.filter(name=tagname)[0]
+            msg = "Log already has tag %s" % unicode(tag)
+            return Response(msg,status=status.HTTP_409_CONFLICT)
+        except:
+            # Look for the tag.  If it doesn't already exist, create it.
+            try:
+                tag = Tag.objects.filter(name=tagname)[0]
+            except:
+                displayName = request.DATA.get('displayName')
+                tag = Tag(name=tagname, displayName=displayName)
+                tag.save()
+
+            # Now add the log message to this tag.
+            tag.eventlogs.add(eventlog)
+
+            # Create a log entry to document the tag creation.
+            msg = "Tagged message %s: %s " % (n, tagname)
+            logentry = EventLog(event=event,
+                               issuer=request.ligouser,
+                               comment=msg)
+            logentry.save()
+
+            return Response("Tag created.",status=status.HTTP_201_CREATED)
+
+    def delete(self, request, graceid, n, tagname):
+        try:
+            event = Event.getByGraceid(graceid)
+            eventlog = event.eventlog_set.order_by("created").all()[int(n)]
+        except Event.DoesNotExist:
+            # XXX Real error message.
+            return Response("Event does not exist.",
+                    status=status.HTTP_404_NOT_FOUND)
+        except:
+            # XXX Real error message.
+            return Response("Log does not exist.",
+                    status=status.HTTP_404_NOT_FOUND)
+        try:
+            tag = eventlog.tag_set.filter(name=tagname)[0]
+            tag.eventlogs.remove(eventlog)
+
+            # Is the tag empty now?  If so we can delete it.
+            if not tag.eventlogs.all():
+                tag.delete()
+
+            # Create a log entry to document the tag creation.
+            msg = "Removed tag %s for message %s. " % (tagname, n)
+            logentry = EventLog(event=event,
+                               issuer=request.ligouser,
+                               comment=msg)
+            logentry.save()
+
+            return Response("Tag deleted.",status=status.HTTP_200_OK)
+        except:
+            return Response("Tag not found.",status=status.HTTP_404_NOT_FOUND)
+
 
 #==================================================================
 # Root Resource
@@ -579,9 +849,16 @@ class GracedbRoot(APIView):
         labels = labels.replace("G1200", "{graceid}")
         labels = labels.replace("thelabel", "{label}")
 
-        slot = reverse("slot", args=["G1200", "slotname"], request=request)
-        slot = slot.replace("G1200", "{graceid}")
-        slot = slot.replace("slotname", "{slotname}")
+        taglist = reverse("eventlogtag-list", args=["G1200", "0"], request=request)
+        taglist = taglist.replace("G1200", "{graceid}")
+        taglist = taglist.replace("0", "{n}")
+
+        tag = reverse("eventlogtag-detail", args=["G1200", "0", "tagname"], request=request)
+        tag = tag.replace("G1200", "{graceid}")
+        tag = tag.replace("0", "{n}")
+        tag = tag.replace("tagname", "{tagname}")
+
+        # XXX Need a template for the tag list?
 
         templates = {
                 "event-detail-template" : detail,
@@ -589,7 +866,8 @@ class GracedbRoot(APIView):
                 "event-label-template" : labels,
                 "files-template" : files,
                 "filemeta-template" : filemeta,
-                "slot-template" : slot,
+                "tag-template" : tag,
+                "taglist-template" : taglist,
                 }
 
         return Response({
@@ -816,98 +1094,3 @@ class FileMeta(APIView):
     permission_classes = (IsAuthenticated,)
     pass
 
-#==================================================================
-# Slots
-
-class EventSlot(APIView):
-    """A slot associated with an event.  
-    """
-
-    # Get the value of a slot.  This will be a filename. 
-    def get(self, request, graceid, slotname):
-        try:
-            event = Event.getByGraceid(graceid)
-        except Event.DoesNotExist:
-            # XXX Real error message.
-            return Response("Event does not exist.",
-                    status=status.HTTP_404_NOT_FOUND)
-
-        if not slotname:
-            return Response(dict([(s.name, s.value)
-                for s in Slot.objects.filter(event=event)]))
-
-        try:
-            slot = Slot.objects.filter(event=event).filter(name=slotname)[0]
-        except:
-            # Okay, no slot yet.  Probably want an error message.
-            # Try looking for files that contain the slot name.
-            return Response("No slot.  Search based on slotname not implemented yet.",
-                    status=status.HTTP_404_NOT_FOUND)
-        filename = slot.value
-        rv = {}
-        rv['value'] = filename
-        return Response(rv)
-
-    # Create a slot.  The slot's value will be a filename.
-    # This file is assumed to have already been uploaded.
-    def put(self, request, graceid, slotname):
-        try:
-            event = Event.getByGraceid(graceid)
-        except Event.DoesNotExist:
-            # XXX Real error message.
-            return Response("Event does not exist.",
-                    status=status.HTTP_404_NOT_FOUND)
-        filename = request.DATA.get('filename')
-        # Interestingly, the None object seems to be converted to a string
-        # when encoded in the HTTP request body.  Hence the 'None' string 
-        # below.  If somebody intentionally named a file 'None', then 
-        # they deserve to get this error message.
-        if filename=='' or filename=='None' or filename==None:
-            return Response("Please submit a filename or upload a file.",
-                    status=status.HTTP_400_BAD_REQUEST)
-
-        # UGLY hack to deal with /private vs /general dirs
-        general = False
-        tmpFilename = filename
-        if filename.startswith("general/"):
-            tmpFilename = filename[len("general/"):]
-            general = True
-        filepath = os.path.join(event.datadir(general), tmpFilename)
-
-        # Check for existence of the file.
-        if not os.path.exists(filepath):
-           return Response("No slot created because file does not exist",
-                    status=status.HTTP_404_NOT_FOUND)
-        # Check for existence of the slot.  If it exists, simply update the
-        # existing slot.
-        try:
-            slot = Slot.objects.filter(event=event).filter(name=slotname)[0]
-            slot.value = filename
-            slot.save()
-        except:
-            # Create the slot.
-            slot = Slot(event=event,name=slotname,value=filename)
-            slot.save()
-        return Response("Slot created or updated.",status=status.HTTP_201_CREATED)
-
-    # Delete a slot.
-    def delete(self, request, graceid, slotname):
-        try:
-            event = Event.getByGraceid(graceid)
-        except Event.DoesNotExist:
-            # XXX Real error message.
-            return Response("Event does not exist.",
-                    status=status.HTTP_404_NOT_FOUND)
-
-        # Gotta find the poor devil before we can delete him.
-        try:
-            slot = Slot.objects.filter(event=event).filter(name=slotname)[0]
-        except:
-            # Okay, no slot yet.  Probably want an error message.
-            # Try looking for files that contain the slot name.
-            return Response("No such slot.",
-                    status=status.HTTP_404_NOT_FOUND)
-
-        slot.delete()
-        return Response("Slot deleted.",status=status.HTTP_200_OK)
-
diff --git a/gracedb/migrations/0002_auto__add_tag.py b/gracedb/migrations/0002_auto__add_tag.py
new file mode 100644
index 0000000000000000000000000000000000000000..69ffa13d4bbe5208df06f64c9557463b7edc14a7
--- /dev/null
+++ b/gracedb/migrations/0002_auto__add_tag.py
@@ -0,0 +1,147 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding model 'Tag'
+        db.create_table('gracedb_tag', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=100)),
+            ('displayName', self.gf('django.db.models.fields.CharField')(max_length=200, null=True)),
+        ))
+        db.send_create_signal('gracedb', ['Tag'])
+
+        # Adding M2M table for field eventlogs on 'Tag'
+        db.create_table('gracedb_tag_eventlogs', (
+            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+            ('tag', models.ForeignKey(orm['gracedb.tag'], null=False)),
+            ('eventlog', models.ForeignKey(orm['gracedb.eventlog'], null=False))
+        ))
+        db.create_unique('gracedb_tag_eventlogs', ['tag_id', 'eventlog_id'])
+
+
+    def backwards(self, orm):
+        # Deleting model 'Tag'
+        db.delete_table('gracedb_tag')
+
+        # Removing M2M table for field eventlogs on 'Tag'
+        db.delete_table('gracedb_tag_eventlogs')
+
+
+    models = {
+        'gracedb.approval': {
+            'Meta': {'object_name': 'Approval'},
+            'approvedEvent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Event']"}),
+            'approver': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"}),
+            'approvingCollaboration': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'gracedb.coincinspiralevent': {
+            'Meta': {'ordering': "['-id']", 'object_name': 'CoincInspiralEvent', '_ormbases': ['gracedb.Event']},
+            'combined_far': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'end_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'end_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'event_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['gracedb.Event']", 'unique': 'True', 'primary_key': 'True'}),
+            'false_alarm_rate': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ifos': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}),
+            'mass': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'mchirp': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'minimum_duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'snr': ('django.db.models.fields.FloatField', [], {'null': 'True'})
+        },
+        'gracedb.event': {
+            'Meta': {'ordering': "['-id']", 'object_name': 'Event'},
+            'analysisType': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'far': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'gpstime': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Group']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'instruments': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}),
+            'labels': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['gracedb.Label']", 'through': "orm['gracedb.Labelling']", 'symmetrical': 'False'}),
+            'likelihood': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'nevents': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'submitter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"}),
+            'uid': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'})
+        },
+        'gracedb.eventlog': {
+            'Meta': {'ordering': "['-created']", 'object_name': 'EventLog'},
+            'comment': ('django.db.models.fields.TextField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Event']"}),
+            'filename': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'issuer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"})
+        },
+        'gracedb.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'managers': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['gracedb.User']", 'symmetrical': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '20'})
+        },
+        'gracedb.label': {
+            'Meta': {'object_name': 'Label'},
+            'defaultColor': ('django.db.models.fields.CharField', [], {'default': "'black'", 'max_length': '20'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'})
+        },
+        'gracedb.labelling': {
+            'Meta': {'object_name': 'Labelling'},
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"}),
+            'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Event']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Label']"})
+        },
+        'gracedb.multiburstevent': {
+            'Meta': {'ordering': "['-id']", 'object_name': 'MultiBurstEvent', '_ormbases': ['gracedb.Event']},
+            'amplitude': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'bandwidth': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'central_freq': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'confidence': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'event_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['gracedb.Event']", 'unique': 'True', 'primary_key': 'True'}),
+            'false_alarm_rate': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ifos': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}),
+            'ligo_angle': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ligo_angle_sig': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ligo_axis_dec': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ligo_axis_ra': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'peak_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'peak_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'snr': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'start_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'start_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'})
+        },
+        'gracedb.slot': {
+            'Meta': {'unique_together': "(('event', 'name'),)", 'object_name': 'Slot'},
+            'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Event']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'value': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'gracedb.tag': {
+            'Meta': {'object_name': 'Tag'},
+            'displayName': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
+            'eventlogs': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['gracedb.EventLog']", 'symmetrical': 'False'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'gracedb.user': {
+            'Meta': {'ordering': "['name']", 'object_name': 'User'},
+            'dn': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'principal': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'unixid': ('django.db.models.fields.CharField', [], {'max_length': '25'})
+        }
+    }
+
+    complete_apps = ['gracedb']
\ No newline at end of file
diff --git a/gracedb/migrations/0003_auto__del_slot__del_unique_slot_event_name.py b/gracedb/migrations/0003_auto__del_slot__del_unique_slot_event_name.py
new file mode 100644
index 0000000000000000000000000000000000000000..066c22bff6271796213757f1de8e4df130409556
--- /dev/null
+++ b/gracedb/migrations/0003_auto__del_slot__del_unique_slot_event_name.py
@@ -0,0 +1,136 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Removing unique constraint on 'Slot', fields ['event', 'name']
+        db.delete_unique('gracedb_slot', ['event_id', 'name'])
+
+        # Deleting model 'Slot'
+        db.delete_table('gracedb_slot')
+
+
+    def backwards(self, orm):
+        # Adding model 'Slot'
+        db.create_table('gracedb_slot', (
+            ('value', self.gf('django.db.models.fields.CharField')(max_length=100)),
+            ('event', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['gracedb.Event'])),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=100)),
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+        ))
+        db.send_create_signal('gracedb', ['Slot'])
+
+        # Adding unique constraint on 'Slot', fields ['event', 'name']
+        db.create_unique('gracedb_slot', ['event_id', 'name'])
+
+
+    models = {
+        'gracedb.approval': {
+            'Meta': {'object_name': 'Approval'},
+            'approvedEvent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Event']"}),
+            'approver': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"}),
+            'approvingCollaboration': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'gracedb.coincinspiralevent': {
+            'Meta': {'ordering': "['-id']", 'object_name': 'CoincInspiralEvent', '_ormbases': ['gracedb.Event']},
+            'combined_far': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'end_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'end_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'event_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['gracedb.Event']", 'unique': 'True', 'primary_key': 'True'}),
+            'false_alarm_rate': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ifos': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}),
+            'mass': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'mchirp': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'minimum_duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'snr': ('django.db.models.fields.FloatField', [], {'null': 'True'})
+        },
+        'gracedb.event': {
+            'Meta': {'ordering': "['-id']", 'object_name': 'Event'},
+            'analysisType': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'far': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'gpstime': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Group']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'instruments': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}),
+            'labels': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['gracedb.Label']", 'through': "orm['gracedb.Labelling']", 'symmetrical': 'False'}),
+            'likelihood': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'nevents': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'submitter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"}),
+            'uid': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'})
+        },
+        'gracedb.eventlog': {
+            'Meta': {'ordering': "['-created']", 'object_name': 'EventLog'},
+            'comment': ('django.db.models.fields.TextField', [], {}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Event']"}),
+            'filename': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'issuer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"})
+        },
+        'gracedb.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'managers': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['gracedb.User']", 'symmetrical': 'False'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '20'})
+        },
+        'gracedb.label': {
+            'Meta': {'object_name': 'Label'},
+            'defaultColor': ('django.db.models.fields.CharField', [], {'default': "'black'", 'max_length': '20'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'})
+        },
+        'gracedb.labelling': {
+            'Meta': {'object_name': 'Labelling'},
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"}),
+            'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Event']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Label']"})
+        },
+        'gracedb.multiburstevent': {
+            'Meta': {'ordering': "['-id']", 'object_name': 'MultiBurstEvent', '_ormbases': ['gracedb.Event']},
+            'amplitude': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'bandwidth': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'central_freq': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'confidence': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'duration': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'event_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['gracedb.Event']", 'unique': 'True', 'primary_key': 'True'}),
+            'false_alarm_rate': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ifos': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'}),
+            'ligo_angle': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ligo_angle_sig': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ligo_axis_dec': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'ligo_axis_ra': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'peak_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'peak_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'snr': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
+            'start_time': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'start_time_ns': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'})
+        },
+        'gracedb.tag': {
+            'Meta': {'object_name': 'Tag'},
+            'displayName': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
+            'eventlogs': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['gracedb.EventLog']", 'symmetrical': 'False'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'gracedb.user': {
+            'Meta': {'ordering': "['name']", 'object_name': 'User'},
+            'dn': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'principal': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'unixid': ('django.db.models.fields.CharField', [], {'max_length': '25'})
+        }
+    }
+
+    complete_apps = ['gracedb']
\ No newline at end of file
diff --git a/gracedb/models.py b/gracedb/models.py
index 485b11c1ed509631b32669ee82420c402b264f55..e7c3afea9911fd1fd5ef80f43b0e404ada77f659 100644
--- a/gracedb/models.py
+++ b/gracedb/models.py
@@ -173,6 +173,25 @@ class Event(models.Model):
     def __unicode__(self):
         return self.graceid()
 
+    # Return a list of distinct tags associated with the log messages of this
+    # event.
+    def getAvailableTags(self):
+        tagset_list = [log.tag_set.all() for log in self.eventlog_set.all()]
+        taglist = []
+        for tagset in tagset_list:
+            for tag in tagset:
+                taglist.append(tag)
+        # Eliminate duplicates
+        return list(set(taglist))
+
+    def getLogsForTag(self,tagname):
+        loglist = []
+        for log in self.eventlog_set.all():
+            for tag in log.tag_set.all():
+                if tag.name==tagname:
+                    loglist.append(log)
+        return loglist
+
 class EventLog(models.Model):
     class Meta:
         ordering = ["-created"]
@@ -193,6 +212,16 @@ class EventLog(models.Model):
         # XXX hacky
         return self.filename and self.filename[-3:].lower() in ['png','gif','jpg']
 
+    def getN(self):
+        # XXX also hacky?
+        # I think it would still work if some logs were removed from the database.
+        logset = self.event.eventlog_set.order_by("created")
+        # XXX This actually evaluates the queryset.  This may be a problem if 
+        # there are a huge number of log messages for this event and they 
+        # take up a lot of memory
+        logset = list(logset)
+        return logset.index(self)
+
 class Labelling(models.Model):
     event = models.ForeignKey(Event)
     label = models.ForeignKey(Label)
@@ -240,21 +269,26 @@ class MultiBurstEvent(Event):
     ligo_angle       = models.FloatField(null=True)
     ligo_angle_sig   = models.FloatField(null=True)
 
-## Slots (user-defined event attributes)
-
-class Slot(models.Model):
-    """Slot Model"""
-    # Does the slot need to have a submitter column?
-    class Meta:
-        unique_together = (('event', 'name'))
-    event = models.ForeignKey(Event)
-    name  = models.CharField(max_length=100)
-    value = models.CharField(max_length=100)
+## Tags (user-defined log message attributes)
+class Tag(models.Model):
+    """Tag Model"""
+    # XXX Does the tag need to have a submitter column?
+    # No, because creating a tag will generate a log message.
+    # For the same reason, a timstamp is not necessary.
+    eventlogs   = models.ManyToManyField(EventLog)
+    name        = models.CharField(max_length=100)
+    displayName = models.CharField(max_length=200,null=True)
 
-    # In case the slot value is not a filename, this will just return None.
-    def fileurl(self):
-        if self.value:
-            return reverse('file', args=[self.event.graceid(), self.value])
+    def __unicode__(self):
+        if self.displayName:
+            return self.displayName
         else:
-            return None
+            return self.name
+
+#     def getEvents(self):
+#         # XXX Any way of doing this with filters?
+#         # We would need to filter for a non-null intersection of the 
+#         # set of log messages in the event with the set of log 
+#         # messages in the tag.
+#         eventlist = [log.event for log in self.eventlogs.all()]
 
diff --git a/gracedb/templatetags/logtags.py b/gracedb/templatetags/logtags.py
new file mode 100644
index 0000000000000000000000000000000000000000..c19075255941e756ca736c0ad6b0386a404267ed
--- /dev/null
+++ b/gracedb/templatetags/logtags.py
@@ -0,0 +1,69 @@
+
+from django import template
+from django.utils.encoding import force_unicode
+from django.utils.safestring import mark_safe
+from ..models import Tag, EventLog
+
+register = template.Library()
+
+@register.filter("getLogsForTag")
+def getLogsForTag(event,name=None):
+    if event is None:
+        return None
+    try:
+        if name:
+            return event.getLogsForTag(name)
+        else:
+            return None
+    except:
+        # Either there is no such tag or something went wrong.
+        # In either case, we want the template to just ignore it.
+        return None
+
+@register.filter("tagUnicode")
+def tagUnicode(tag):
+    return unicode(tag);
+
+@register.filter("logsForTagHaveImage")
+def logsForTagAllHaveImages(event,name=None):
+    if event is None:
+        return None
+    try:
+        if name:
+            loglist = event.getLogsForTag(name)
+            # Start by assuming no images
+            bool_rv = False 
+            for log in loglist:
+                # If any of the log messages do have an image
+                # attached, we flip the return value to true.
+                if log.hasImage():
+                    bool_rv = True
+                    break
+            return bool_rv
+        else:
+            return None
+    except:
+        # Either there is no such tag or something went wrong.
+        # In either case, we want the template to just ignore it.
+        return None
+   
+@register.filter("logsForTagHaveText")
+def logsForTagHaveText(event,name=None):
+    if event is None:
+        return None
+    try:
+        if name:
+            loglist = event.getLogsForTag(name)
+            bool_rv = False
+            for log in loglist:
+                if not log.hasImage():
+                    bool_rv = True
+                    break
+            return bool_rv
+        else:
+            return None
+    except:
+        # Either there is no such tag or something went wrong.
+        # In either case, we want the template to just ignore it.
+        return None
+
diff --git a/gracedb/templatetags/slot.py b/gracedb/templatetags/slot.py
deleted file mode 100644
index 63e7b16827c8d94711d2563cde26fb788f473ebd..0000000000000000000000000000000000000000
--- a/gracedb/templatetags/slot.py
+++ /dev/null
@@ -1,21 +0,0 @@
-
-from django import template
-from django.utils.encoding import force_unicode
-from django.utils.safestring import mark_safe
-from ..models import Slot, EventLog
-register = template.Library()
-
-@register.filter("slot")
-def slot(event,name=None):
-    if event is None:
-        return None
-    try:
-        if name:
-            return Slot.objects.filter(event=event).filter(name__exact=name)[0]
-        else:
-            return Slot.objects.filter(event=event)
-    except:
-        # Either there is no such slot or something went wrong.
-        # In either case, we want the template to just ignore it.
-        return None
-
diff --git a/gracedb/urls.py b/gracedb/urls.py
index a5839868cc51c5f263f8fd719a6634d81c5f9d4a..959ceb7a04eb0ced59096056d45a7bc4d8debca0 100644
--- a/gracedb/urls.py
+++ b/gracedb/urls.py
@@ -15,6 +15,7 @@ urlpatterns = patterns('gracedb.views',
     url (r'^(?P<graceid>[GEHT]\d+)$', 'view', name="view2"),
     url (r'^(?P<graceid>[GEHT]\d+)/files/(?P<filename>.*)$', download, name="file"),
     url (r'^(?P<graceid>[GEHT]\d+)/log/(?P<num>([\d]*|preview))$', 'logentry', name="logentry"),
+    url (r'^(?P<graceid>[GEHT]\d+)/log/(?P<num>\d+)/tag/(?P<tagname>\w+)$', 'taglogentry', name="taglogentry"),
 
 
 #   (r'^view/(?P<uid>[\w\d]+)', 'view'),
diff --git a/gracedb/urls_rest.py b/gracedb/urls_rest.py
index d915fe9e4996cdfb41625b04da04a7d4318aa751..828bfec477bde347f8b5592539e343a79e6f9209 100644
--- a/gracedb/urls_rest.py
+++ b/gracedb/urls_rest.py
@@ -5,7 +5,10 @@ from django.conf.urls.defaults import patterns, url
 from gracedb.api import GracedbRoot
 from gracedb.api import EventList, EventDetail
 from gracedb.api import EventLogList, EventLogDetail
-from gracedb.api import EventSlot
+from gracedb.api import TagList
+# from gracedb.api import TagDetail
+from gracedb.api import EventTagList, EventTagDetail
+from gracedb.api import EventLogTagList, EventLogTagDetail
 from gracedb.api import Files, FileMeta
 from gracedb.api import EventNeighbors, EventLabel
 
@@ -27,6 +30,21 @@ urlpatterns = patterns('gracedb.api',
     url (r'events/(?P<graceid>[GEHT]\d+)/log/(?P<n>\d+)$',
         EventLogDetail.as_view(), name='eventlog-detail'),
 
+    # Tag Resources
+    url (r'^tag/$', 
+        TagList.as_view(), name='tag-list'),
+    # XXX unclear what the tag detail resource should be.
+    #url (r'^tag/(?P<tagname>\w+)$', 
+    #    TagDetail.as_view(), name='tag-detail'),
+    url (r'events/(?P<graceid>[GEHT]\d+)/tag/$',
+        EventTagList.as_view(), name='eventtag-list'),
+    url (r'events/(?P<graceid>[GEHT]\d+)/tag/(?P<tagname>\w+)$',
+        EventTagDetail.as_view(), name='eventtag-detail'),
+    url (r'events/(?P<graceid>[GEHT]\d+)/log/(?P<n>\d+)/tag/$',
+        EventLogTagList.as_view(), name='eventlogtag-list'),
+    url (r'events/(?P<graceid>[GEHT]\d+)/log/(?P<n>\d+)/tag/(?P<tagname>\w+)$',
+        EventLogTagDetail.as_view(), name='eventlogtag-detail'),
+
     # Event File Resources
     # events/{graceid}/files/[{filename}[/{version}]]
     url (r'^events/(?P<graceid>\w[\d]+)/files/(?P<filename>.+)?$',
@@ -40,11 +58,6 @@ urlpatterns = patterns('gracedb.api',
     url (r'^events/(?P<graceid>\w[\d]+)/labels/(?P<label>.+)?$',
         EventLabel.as_view(), name="labels"),
 
-    # Event Slots
-    # events/{graceid}/slot/[{slotname}]
-    url (r'^events/(?P<graceid>[GEHT]\d+)/slot/(?P<slotname>.+)?$',
-        EventSlot.as_view(), name="slot"),
-
     # Event Neighbors
     # events/{graceid}/neighbors/[?delta=(N|(N,N))]
     url (r'^events/(?P<graceid>\w[\d]+)/neighbors/$',
diff --git a/gracedb/views.py b/gracedb/views.py
index 3961a80c75021cc03cc3644aa77552e8f1d09975..09b9b3a8ff2511ab6322f214a4997e6eb58bacbb 100644
--- a/gracedb/views.py
+++ b/gracedb/views.py
@@ -10,7 +10,7 @@ from django.utils.safestring import mark_safe
 
 from django.views.generic.list_detail import object_detail, object_list
 
-from models import Event, Group, EventLog, Labelling, Label, User
+from models import Event, Group, EventLog, Labelling, Label, User, Tag
 from models import CoincInspiralEvent
 from models import MultiBurstEvent
 from forms import CreateEventForm, EventSearchForm, SimpleSearchForm
@@ -29,6 +29,7 @@ from django.conf import settings
 from templatetags.scientific import scientific
 
 from buildVOEvent import buildVOEvent, submitToSkyalert
+import logging
 
 # XXX This should be configurable / moddable or something
 MAX_QUERY_RESULTS = 1000
@@ -443,6 +444,7 @@ def sanitize_html(data):
 
 
 def logentry(request, graceid, num=None):
+    logger = logging.getLogger(__name__)
     try:
         event = Event.getByGraceid(graceid)
     except Event.DoesNotExist:
@@ -452,6 +454,28 @@ def logentry(request, graceid, num=None):
         elog = EventLog(event=event, issuer=request.ligouser)
         elog.comment = request.POST.get('comment') or request.GET.get('comment')
         elog.save()
+        logger.debug("just saved log entry")
+        tagname = request.POST.get('tagname')
+        logger.debug("tagname = %s" % tagname)
+        if tagname:
+            # Look for the tag.  If it doesn't already exist, create it.
+            try:
+                tag = Tag.objects.filter(name=tagname)[0]
+            except:
+                displayName = request.POST.get('displayName')
+                logger.debug("disp name = %s" % displayName)
+                tag = Tag(name=tagname, displayName=displayName)
+                tag.save()
+                logger.debug("just saved tag")
+
+            tag.eventlogs.add(elog)
+            # Create a log entry to document the tag creation.
+            num = elog.getN()
+            msg = "Tagged message %s: %s " % (num, tagname)
+            tlog = EventLog(event=event,
+                               issuer=request.ligouser,
+                               comment=msg)
+            tlog.save()
     else:
         try:
             elog = event.eventlog_set.order_by('created').all()[int(num)]
@@ -466,6 +490,8 @@ def logentry(request, graceid, num=None):
     rv['issuer'] = elog.issuer.name
     rv['created'] = elog.created.isoformat()
     rv['comment'] = elog.comment
+    if tagname:
+        rv['tagname'] = tagname
 
     return HttpResponse(json.dumps(rv), content_type="application/json")
 
@@ -537,6 +563,7 @@ def view(request, graceid):
     context['nearby'] = [(event.gpstime - a.gpstime, event)
                             for event in a.neighbors()]
     context['skyalert_authorized'] = skyalert_authorized(request)
+    context['blessed_tags'] = settings.BLESSED_TAGS
     return render_to_response(
         [ 'gracedb/event_detail_{0}.html'.format(a.analysisType),
           'gracedb/event_detail.html'],
@@ -993,3 +1020,53 @@ def latest(request):
             context,
             context_instance=RequestContext(request))
 
+#-----------------------------------------------------------------------------------
+# For tags.  A new view function.  We need this because the API one would want users
+# to have certs stored in their browser.
+#-----------------------------------------------------------------------------------
+
+def taglogentry(request, graceid, num, tagname):
+    try:
+        event = Event.getByGraceid(graceid)
+        eventlog = event.eventlog_set.order_by("created").all()[int(num)]
+    except:
+        # Either the event or the log does not exist.
+        raise Http404
+
+    if request.method == "POST":
+        try:
+            # Has this tag-eventlog relationship already been created? 
+            tag = eventlog.tag_set.filter(name=tagname)[0]
+            msg = "Log already has tag %s" % tagname
+            return HttpResponse(msg, content_type="text")
+        except:
+            # Look for the tag.  If it doesn't already exist, create it.
+            try:
+                tag = Tag.objects.filter(name=tagname)[0]
+            except:
+                displayName = request.POST['displayName']
+                tag = Tag(name=tagname, displayName=displayName)
+                tag.save()
+
+            # Now add the log message to this tag.
+            tag.eventlogs.add(eventlog)
+
+            # Create a log entry to document the tag creation.
+            msg = "Tagged message %s: %s " % (num, tagname)
+            logentry = EventLog(event=event,
+                               issuer=request.ligouser,
+                               comment=msg)
+            logentry.save()
+    else:
+        # We will only allow PUT here.  Anything else is a bad request: 400
+        return HttpResponseBadRequest
+
+    # Hopefully, this will only ever be called form inside a script.  Just in case...
+    if not request.is_ajax():
+        return HttpResponseRedirect(reverse(view, args=[graceid]))
+
+    # no need for a JSON response. 
+    msg = "Successfully applied tag %s to log message %s." % (tagname, num)
+    msg = msg + "  Refresh to see chages (if any) to the presentation."
+    return HttpResponse(msg, content_type="text")
+
diff --git a/settings/default.py b/settings/default.py
index 08d7db56cd86e95bd58108f84cc30dd5d782fad6..4e6d3484c6e5a4c94a571e228cb9e44f6979dc87 100644
--- a/settings/default.py
+++ b/settings/default.py
@@ -37,6 +37,15 @@ XMPP_ALERT_CHANNELS = [
                         'test_grb',
                       ]
 
+BLESSED_TAGS = [
+                 'psd',
+                 'sky_loc',
+                 'strain',
+                 'tfplots',
+                 'sig_info',
+                 'audio',
+               ]
+
 DATABASES = {
     'default' : {
         'NAME'     : 'gracedb',
diff --git a/static/css/style.css b/static/css/style.css
index 268f6f7f9d010987cbcaf2fe4eadebac91d2996f..76f5aaf68f16c7035bd9566681124b00821f54be 100644
--- a/static/css/style.css
+++ b/static/css/style.css
@@ -9,6 +9,45 @@ html, body {
 
 table.event th {padding:3px;border:none;text-align:center;vertical-align:bottom;}
 table.event {border-bottom:1px solid gray;}
+/* Branson added */
+table.event {width:100%}
+
+table.analysis_specific {width:100%}
+
+.content-area {
+    margin-top: 10px;
+    margin-bottom: 30px;
+    margin-left: 15px;
+    margin-right: 15px;
+}
+
+table.figures tr.figrow  {text-align:center;} 
+table.figures {width:300px;height:270px;border:1px solid gray;}
+
+.tundra.eventDetail .pmTitlePaneClass .dijitOpen .dijitArrowNode {
+    background-repeat: no-repeat;
+    height: 14px;
+    width: 14px;
+}
+
+.tundra.eventDetail .pmTitlePaneClass .dijitClosed .dijitArrowNode {
+    background-repeat: no-repeat;
+    height: 14px;
+    width: 14px;
+}
+
+.tundra.eventDetail .pmTitlePaneClass .dijitClosed .dijitArrowNode {
+    background-image: url('/branson-static/images/plusButton.gif');
+    background-position: 0px 0px;
+}
+
+.tundra.eventDetail .pmTitlePaneClass .dijitOpen .dijitArrowNode {
+    background-image: url('/branson-static/images/minusButton.gif');
+    background-position: 0px 0px;
+}
+
+
+/* End Branson */
 
 td, th {padding:3px;border:none;}
 tr th {text-align:left;background-color:#f0f0f0;color:#333;}
diff --git a/static/images/minusButton.gif b/static/images/minusButton.gif
new file mode 100644
index 0000000000000000000000000000000000000000..48d5172470f250383e7696c7b61c727b9df06eb0
Binary files /dev/null and b/static/images/minusButton.gif differ
diff --git a/static/images/plusButton.gif b/static/images/plusButton.gif
new file mode 100644
index 0000000000000000000000000000000000000000..103c021daa0668c5b0ba53ac0bace43eb1f1f7ee
Binary files /dev/null and b/static/images/plusButton.gif differ
diff --git a/templates/base.html b/templates/base.html
index f9eec575d1aba079a5c7d27d8730603ecf10cb99..dd0b46c33bbb695399ebaa2467ecbf2c5ae88b13 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -2,7 +2,7 @@
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
 <head>
-    <link rel="stylesheet" href="/gracedb-static/css/style.css" />
+    <link rel="stylesheet" href="/branson-static/css/style.css" />
     <title>GraceDb | {% block title %}{% endblock %}</title>
 <!-- START TESTING -->
 <script type="text/javascript">
diff --git a/templates/gracedb/event_detail.html b/templates/gracedb/event_detail.html
index aa948a79a168fe73d48dd8b6cf076b6596e425c4..c09d576e378d2cc6eb45e5953affdf1869014cee 100644
--- a/templates/gracedb/event_detail.html
+++ b/templates/gracedb/event_detail.html
@@ -2,27 +2,32 @@
 {% load timeutil %}
 {% load scientific %}
 {% load sanitize_html %}
-{% load slot %}
-
+{% load logtags %}
 {% block title %}View {{ object.graceid }}{% endblock %}
 {% block heading %}{% endblock %}
-{% block bodyattrs %}class="tundra"{% endblock %}
+{% block bodyattrs %}class="tundra eventDetail"{% endblock %}
 
 {% block jscript %}
     {# XXX http/https depending on this pages' protocol #}
     <script type="text/javascript"
       src="https://c328740.ssl.cf1.rackcdn.com/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
     </script>
-    <script src="http{% if request.is_secure %}s{% endif %}://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojo/dojo.js" type="text/javascript"></script>
+    <script src="http{% if request.is_secure %}s{% endif %}://ajax.googleapis.com/ajax/libs/dojo/1.8.1/dojo/dojo.js" type="text/javascript"></script>
     <script src="{{MEDIA_URL}}js/labeltips.js" type="text/javascript"></script>
     <link rel="stylesheet" href="{{MEDIA_URL}}css/labeltips.css">
     <script type="text/javascript">
-        var bb = null;
-
-        require(["dijit/Editor",
+        require(["dojo/dom",
+                 "dojo/on",
+                 "dojo/request",
+                 "dojo/dom-form",
+                 "dijit/Editor",
                  "dojox/editor/plugins/Save",
                  "dojox/editor/plugins/Preview",
                  "dijit/form/Button",
+                 "dijit/TitlePane",
+                 "dojox/layout/ScrollPane",
+                 "dojo/store/Memory",
+                 "dijit/form/ComboBox",
                  "dojox/image/LightboxNano",
                  "dijit/_editor/plugins/TextColor",
                  "dijit/_editor/plugins/LinkDialog",
@@ -30,19 +35,26 @@
                  "dijit/_editor/plugins/NewPage",
                  "dijit/_editor/plugins/FullScreen",
                  "dojo/parser",
-                 "dojo/domReady!"], function (Editor, Save, Preview, Button) {
+                 "dojo/domReady!"], function (dom, on, request, domForm, Editor, Save, Preview, 
+                                              Button, TitlePane, ScrollPane, Memory, ComboBox) {
 
             dojo.parser.parse();
 
-            //var newlogdiv = dojo.byId("newlogdiv");
-            var logtitle = dojo.byId("logmessagetitle");
-            var logtext = dojo.byId("newlogtext");
+            //var newlogdiv = dom.byId("newlogdiv");
+            var logtitle = dom.byId("logmessagetitle");
+            var logtext = dom.byId("newlogtext");
+
+            var editor_div = dom.byId("editor");
+            var preview_div = dom.byId("previewer");
 
-            var editor_div = dojo.byId("editor");
-            var preview_div = dojo.byId("previewer");
+            // A pane holder for the form that will tag new log messages.
+            // I need it up here because we're going to integrate it with the
+            // editor components.
+            var newLogTag_pane_holder = dom.byId("newLogTag_pane_holder");
 
             dojo.style(preview_div, { 'display':'none'});
             dojo.style(editor_div, { 'display':'none'});
+            dojo.style(newLogTag_pane_holder, { 'display':'none'});
 
             var button_element = dojo.create('button');
             dojo.place(button_element, logtitle, "right");
@@ -52,12 +64,14 @@
                 onClick: function(){
                     if (this.state == 'add') {
                         dojo.style(editor_div, {'display':'block'});
+                        dojo.style(newLogTag_pane_holder, {'display':'block'});
                         button.set('label', "Cancel Log Entry");
                         button.set('state', 'cancel');
                         editor.focus();
                     }
                     else {
                         dojo.style(editor_div, {'display':'none'});
+                        dojo.style(newLogTag_pane_holder, {'display':'none'});
                         dojo.style(preview_div, {'display':'none'});
                         button.set('label', "Add Log Entry");
                         button.set('state', 'add');
@@ -66,9 +80,6 @@
                 },
             }, button_element);
 
-
-            //dojo.style(newlogdiv, {'display':'none'});
-
             var savebutton = new Save({
                     url: "{% url logentry object.graceid "" %}",
                     onSuccess: function (resp, ioargs) {
@@ -82,15 +93,19 @@
                         alert("o hai " + error);
                     },
                     save: function(postdata) {
-                        var postArgs = {
+                    var newTagName = domForm.fieldToObject("newTagSelect");
+                    var newTagDispName = domForm.fieldToObject("newTagDispName");
+                    var postArgs = {
                                 url: this.url,
-                                content: { comment: postdata },
+                                content: { comment: postdata, tagname: newTagName, displayName: newTagDispName },
                                 handleAs: "json"
                         };
                         this.button.set("disabled", true);
                         var deferred = dojo.xhrPost(postArgs);
                         deferred.addCallback(dojo.hitch(this, this.onSuccess));
                         deferred.addErrback(dojo.hitch(this, this.onError));
+                        // Call whatever function is necessary to attach the tag
+                        // or add to the postdata and handle in the django view?
                     }
             });
 
@@ -107,11 +122,133 @@
                     }
             });
 
+
             var editor = new Editor({
                  extraPlugins : ['foreColor','hiliteColor','|','createLink',
                                  'insertImage','fullscreen','viewsource','newpage', '|', previewbutton, savebutton]
             }, editor_div);
 
+            // If we have blessed tags *and* some log messages have been tagged,
+            // we will want some TitlePanes.
+{% if blessed_tags and object.getAvailableTags %}
+            var pane_holder = dom.byId("pane_holder");
+            var panes = new Array();
+            var titles = new Array();
+    {% for tag in object.getAvailableTags %}
+        {% if tag.name in blessed_tags %}
+            panes.push(dom.byId("{{tag.name}}"));
+            titles.push("{{tag|tagUnicode}}");
+        {% endif %}
+    {% endfor %}
+    
+            for (var i = 0; i<panes.length; i++) {
+                tp = new dijit.TitlePane({title:titles[i],
+                                         content:panes[i].innerHTML,
+                                         open:true});
+                pane_holder.appendChild(tp.domNode);
+                dojo.destroy(panes[i]);
+            };
+
+            // Handle event log seperately.  It will be closed by default.
+            var event_log = dom.byId("event_log");
+            tp = new dijit.TitlePane({title:"Full Event Log",
+                                     content:event_log.innerHTML,
+                                     open:false});
+            pane_holder.appendChild(tp.domNode);
+            dojo.destroy(event_log);
+
+            // Now, deal with the silly figure containers.
+            var figure_container_ids = new Array();
+    {% for tag in object.getAvailableTags %}
+        {% if tag.name in blessed_tags %}
+            if (dom.byId("{{tag.name}}_figure_container")) {
+                figure_container_ids.push("{{tag.name}}_figure_container");
+            }
+        {% endif %}
+    {% endfor %}
+            var figure_scrollpanes = new Array();
+            for (var i = 0; i<figure_container_ids.length; i++) {
+                figure_scrollpanes.push(new dojox.layout.ScrollPane({
+                            orientation:"horizontal",
+                            style:"overflow:hidden;"},
+                            figure_container_ids[i]));
+            }
+{% endif %} {# Endif for getAvailableTags and blessed_tags #}
+
+            // Set up infrastructure for tagging log messages.
+{% if blessed_tags %}
+            var blessedTagStore = new Memory({
+                data: [
+    {% for bt in blessed_tags %}
+                        {name:"{{bt}}"},
+    {% endfor %}
+                ]
+            });
+{% endif %}
+
+            // Panes for the tagging forms.
+            var newLogTag                  = dom.byId("newLogTag");
+            var existingLogTag             = dom.byId("existingLogTag");
+            var existingLogTag_pane_holder = dom.byId("existingLogTag_pane_holder");
+
+            tp_new = new dijit.TitlePane({title:"Tag this message",
+                                     content:newLogTag.innerHTML,
+                                     open:false});
+            newLogTag_pane_holder.appendChild(tp_new.domNode);
+            dojo.destroy(newLogTag);        
+
+            tp_existing = new dijit.TitlePane({title:"Add tag",
+                                     content:existingLogTag.innerHTML,
+                                     open:false});
+            existingLogTag_pane_holder.appendChild(tp_existing.domNode);
+            dojo.destroy(existingLogTag);       
+
+            // Form for tagging new log messages.
+            var cb1 = new ComboBox({
+                id: "newTagSelect",
+                name: "newTagSelect",
+                value: "insert tag here",
+{% if blessed_tags %}                
+                store: blessedTagStore,
+                searchAttr: "name"
+{% endif %}
+            }, "newTagSelect");
+
+            // Form for tagging existing log messages.
+            var cb2 = new ComboBox({
+                id: "existingTagSelect",
+                name: "existingTagSelect",
+                value: "None",
+{% if blessed_tags %}                
+                store: blessedTagStore,
+                searchAttr: "name"
+{% endif %}
+            }, "existingTagSelect");
+
+            var form = dom.byId('existingTagForm');
+            on(form, "submit", function(evt){
+                evt.stopPropagation();
+                evt.preventDefault();
+
+                var n = domForm.fieldToObject("n");
+                var tagName = domForm.fieldToObject("existingTagSelect");
+                var tagDispName = domForm.fieldToObject("existingTagDispName");
+                var tagUrl = "{% url taglogentry object.graceid "000" "temp" %}"
+                tagUrl = tagUrl.replace("000",n);
+                tagUrl = tagUrl.replace("temp",tagName);
+
+                request.post(tagUrl, {
+                    data: {displayName: tagDispName}
+                }).then(
+                    function(text){
+                        alert(text);
+                        // You may want to force the page to reload.
+                        //location.reload(true);
+                    },
+                    function(error){alert(error);}
+                );
+            });
+
         });
 
     </script>
@@ -157,9 +294,7 @@
 {% block content %}
 <p>{{ message }}</p>
 
-<table>
-<tr><td valign="top">
-
+<div class="content-area">
 <h3> Basic Info </h3>
 
 <table class="event">
@@ -208,14 +343,17 @@
     </tr>
 
 </table>
+</div>
 
+<div class="content-area">
 {# Analysis-specific attributes #}
 {% block analysis_specific %}
 {# This block is empty in the base event_detail template #}
 {% endblock %}
+</div>
 
 {% if nearby %}
-    <p/>
+    <div id="neighbors" class="content-area">
     <h3>Neighbors</h3>
     <table class="event">
         <tr>
@@ -255,6 +393,99 @@
         </tr>
         {% endfor %}
     </table>
+    </div>
+{% endif %}
+
+<div id="pane_holder" class="content-area">
+    {# XXX Hacky #}
+    {# With the title here, people will know that all of the things in the #}
+    {# panes are log messages #}
+{% if object.getAvailableTags %}
+    <h3 id="logmessagetitle">Event Log Messages</h3>
+        <div id="newLogTag">
+                <p> Use this form to apply a tag to your new log entry. Choose a tag
+                name from the dropdown menu or enter a new one.  If you are 
+                creating a new tag, please also provide a display name. </p>
+            <form>
+                &nbsp; Tag name <input id="newTagSelect" name="newTagSelect" /> 
+                &nbsp; Display name <input type="text" id="newTagDispName" name="newTagDispName" />
+            </form>  
+        </div>
+        <div id="newLogTag_pane_holder" class="pmTitlePaneClass"></div>
+        <div id="previewer"></div>
+        <div id="editor"></div>
+{% endif %}
+
+{#  Loop over the available tags. #}
+{% if object.getAvailableTags %}
+    {% for tag in object.getAvailableTags %}
+    {% if tag.name in blessed_tags %}
+        {# Create a div to hold the pane innerHTML for this tag #}
+        <div id="{{tag.name}}">
+
+            {# A section for the images #}
+            {% if object|logsForTagHaveImage:tag.name %}        
+            <!-- figure container table is inside a scrolling div -->
+            <div id="{{tag.name}}_figure_container">
+                <table class="figure_container">
+                    <tr>
+                        {% for log in object|getLogsForTag:tag.name %} 
+                            {% if log.hasImage %}
+                            <td>
+                                <table class="figures">
+                                    <!-- A row for the images themselves -->
+                                    <tr class="figrow">
+                                        <td>  <a href="{{ log.fileurl }}" dojoType="dojox.image.LightboxNano"><img height="180" src="{{ log.fileurl }}"></a>
+                                        </td>
+                                    </tr>
+                                    <!-- A row for the captions -->
+                                    <tr>
+                                        <td> {{log.comment|sanitize}}
+                                           {% if log.fileurl %}
+                                            <a href="{{log.fileurl}}">{{log.filename}}.</a>
+                                           {% endif %}
+                                            Submitted by {{log.issuer}} on {{log.created}}
+                                        </td>
+                                    </tr>
+                                </table>
+                            </td>                                
+                            {% endif %}
+                        {% endfor %}
+                     </tr>
+                </table>
+            </div>
+            {% endif %}
+          
+            {# A section for the text-only captions #}
+            {% if object|logsForTagHaveText:tag.name %}
+                {# Set up regular log message table #}
+                <table class="event">
+                    <tr>
+                        <th>No.</th>
+                        <th>{{ "logtime"|timeselect:"utc" }} Log Entry Created</th>
+                        <th>Submitter</th>
+                        <th>Comment</th>
+                    </tr>
+                {% for log in object|getLogsForTag:tag.name %} 
+                    {% if not log.hasImage %}
+                        <tr class="{% cycle 'odd' 'even'%}">
+                           <td>{{log.getN}}</td>
+                           <td>{{log.created|multiTime:"logtime"}}</td>
+                           <td>{{log.issuer}}</td>
+                           <td>{{log.comment|sanitize}}
+                               {% if log.fileurl %}
+                                <a href="{{log.fileurl}}">{{log.filename}}</a>
+                               {% endif %}
+                           </td>
+                        </tr>
+                    {% endif %}
+                {% endfor %}
+                </table>
+            {% endif %}
+
+        </div>
+    {% endif %} {# endif the tag is blessed #}
+    {% endfor %}
 {% endif %}
 
 <noscript>
@@ -266,15 +497,44 @@
 </noscript>
 
 
-  <p/>
-  <h3 id="logmessagetitle">Event Log Messages</h3>
-
+  <div id="event_log">
+    {# XXX Hacky.  We don't want the title here if we're doing panes. #}
+    {# And we only do panes if there are available tags. #}
+{% if not object.getAvailableTags %}
+    <h3 id="logmessagetitle">Event Log Messages</h3>
+        <div id="newLogTag">
+                <p> Use this form to apply a tag to your new log entry. Choose a tag
+                name from the dropdown menu or enter a new one.  If you are 
+                creating a new tag, please also provide a display name. </p>
+            <form>
+                &nbsp; Tag name <input id="newTagSelect" name="newTagSelect" /> 
+                &nbsp; Display name <input type="text" id="newTagDispName" name="newTagDispName" />
+            </form>  
+        </div>
+        <div id="newLogTag_pane_holder" class="pmTitlePaneClass"></div>
         <div id="previewer"></div>
         <div id="editor"></div>
+{% endif %}
 
 {% if object.eventlog_set.count %}
+  {% if blessed_tags %}
+    <div id="existingLogTag">
+        <p>  Use this form to tag an existing log message. Choose a tag name from the
+             dropdown menu or enter a new one.  If you are creating a new tag, please
+             also provide a display name.
+    <form id="existingTagForm">
+        Log No. <input id="n" name="n" type="text" size="4" />
+        &nbsp;&nbsp; Tag name <input id="existingTagSelect" name="existingTagSelect" />
+        &nbsp;&nbsp; Display name <input type="text" id="existingTagDispName" name="existingTagDispName"/> 
+        <input type="submit" name="submit" value="Submit" /> </p>
+    </form>  
+    </div>
+    <div id="existingLogTag_pane_holder" class="pmTitlePaneClass"></div>
+  {% endif %}
+
   <table class="event">
         <tr>
+            <th>No.</th>
             <th>{{ "logtime"|timeselect:"utc" }} Log Entry Created</th>
             <th>Submitter</th>
             <th>Comment</th>
@@ -282,6 +542,7 @@
         </tr>
   {% for log in object.eventlog_set.iterator %}
         <tr class="{% cycle 'odd' 'even'%}">
+           <td>{{log.getN}}
            <td>{{log.created|multiTime:"logtime"}}</td>
            <td>{{log.issuer}}</td>
            <td>{{log.comment|sanitize}}
@@ -298,32 +559,7 @@
   {% endfor %}
   </table>
 {% endif %}
+  </div>
 
-</td>
-<td rowspan="2" valign="top">
-
-{% if eventdesc %}
-<h3>Event Log File</h3>
-    {{ eventdesc }}
-    <br/>
-{% endif %}
-
-{% if userdesc %}
-    <br/>
-<h3>User Info</h3>
-    {{ userdesc }}
-    <br/>
-{% endif %}
-
-<p/>
-<h3>Available Slots</h3>
-{% if not object|slot %}
-    None.
-{% else %}
-    {% for slot in object|slot %}
-    <a href="{{slot.fileurl}}">{{ slot.name }}</a> <br/>
-    {% endfor %}
-{% endif %}
-
-</td></tr></table>
+</div> <!-- End of pane_holder div -->
 {% endblock %}
diff --git a/templates/gracedb/event_detail_CWB.html b/templates/gracedb/event_detail_CWB.html
index e91ae26ad951849a519617374f223b5a543d31ee..de184254918e290919a0d69ea6fdde66fff6ed64 100644
--- a/templates/gracedb/event_detail_CWB.html
+++ b/templates/gracedb/event_detail_CWB.html
@@ -4,10 +4,9 @@
 
 {# Analysis-specific attributes for a cWB event#}
 {% block analysis_specific %}
-<p/>
 <h3>Analysis-Specific Attributes</h3>
 
-<table> <tbody>
+<table class="analysis_specific"> <tbody>
     <tr>
         <td>  <table class="event"> <tbody>
                     <!-- Note:  commenting out ifos so that we'll have a multiple of three --> 
diff --git a/templates/gracedb/event_detail_HM.html b/templates/gracedb/event_detail_HM.html
index 735c28dbfddfa175ac660ea6cb8204d642bd3fec..d1f0f693c88452194a4ec483d96f7d5782f5243b 100644
--- a/templates/gracedb/event_detail_HM.html
+++ b/templates/gracedb/event_detail_HM.html
@@ -1,10 +1,32 @@
 {% extends "gracedb/event_detail.html" %}
 
-{# Analysis-specific attributes for a LowMass event#}
-{% block analysis_specific %}
- <p/>
-<h3>Analysis-specific attributes</h3>
+{% load scientific %}
 
+{# Analysis-specific attributes for an LM event#}
+{% block analysis_specific %}
+<h3>Analysis-Specific Attributes</h3>
 
+<table class="analysis_specific"> <tbody>
+    <tr>
+        <td>  <table class="event"> <tbody>
+                    <tr> <th> ifos </th> <td> {{object.ifos}} </td> </tr> 
+                    <tr> <th> end_time </th> <td> {{object.end_time}} </td> </tr>
+                    <tr> <th> end_time_ns </th> <td> {{object.end_time_ns}} </td> </tr>
+              </tbody></table>
+        </td>
+        <td>  <table class="event"> <tbody>
+                    <tr> <th> mass </th> <td> {{object.mass|floatformat:"-4"}} </td> </tr> 
+                    <tr> <th> mchirp </th> <td> {{object.mchirp|floatformat:"-4"}} </td> </tr>
+                    <tr> <th> minimum_duration </th> <td> {{object.minimum_duration|scientific}} </td> </tr>
+              </tbody></table>
+        </td>
+        <td>  <table class="event"> <tbody>
+                    <tr> <th> snr </th> <td> {{object.snr|floatformat:"-4"}} </td> </tr> 
+                    <tr> <th> false_alarm_rate </th> <td> {{object.false_alarm_rate|floatformat:"-4"}} </td> </tr>
+                    <tr> <th> combined_far </th> <td> {{object.combined_far|scientific}} </td> </tr>
+              </tbody></table>
+        </td>
 
+    </tr>
+</tbody> </table> 
 {% endblock %}
diff --git a/templates/gracedb/event_detail_LM.html b/templates/gracedb/event_detail_LM.html
index 3a1e4dfeb5f7e217b731968383b5563e42ce6fc5..d1f0f693c88452194a4ec483d96f7d5782f5243b 100644
--- a/templates/gracedb/event_detail_LM.html
+++ b/templates/gracedb/event_detail_LM.html
@@ -4,10 +4,9 @@
 
 {# Analysis-specific attributes for an LM event#}
 {% block analysis_specific %}
-<p/>
 <h3>Analysis-Specific Attributes</h3>
 
-<table> <tbody>
+<table class="analysis_specific"> <tbody>
     <tr>
         <td>  <table class="event"> <tbody>
                     <tr> <th> ifos </th> <td> {{object.ifos}} </td> </tr> 
@@ -30,5 +29,4 @@
 
     </tr>
 </tbody> </table> 
-
 {% endblock %}
diff --git a/templates/gracedb/event_detail_MBTA.html b/templates/gracedb/event_detail_MBTA.html
index 735c28dbfddfa175ac660ea6cb8204d642bd3fec..81ba7576fa013ec7644aa556f4ce01c85cb5dd3b 100644
--- a/templates/gracedb/event_detail_MBTA.html
+++ b/templates/gracedb/event_detail_MBTA.html
@@ -3,8 +3,7 @@
 {# Analysis-specific attributes for a LowMass event#}
 {% block analysis_specific %}
  <p/>
-<h3>Analysis-specific attributes</h3>
-
-
+<h3>Event Log File</h3>
+{{eventdesc}}
 
 {% endblock %}