From 10525d32c418af5fc42659f1c9ae7788c0dc61c4 Mon Sep 17 00:00:00 2001
From: Brian Moe <brian.moe@ligo.org>
Date: Mon, 2 Jul 2012 12:25:55 -0500
Subject: [PATCH] Mostly working, but not the right goal.

---
 gracedb/models.py                   |   9 +-
 gracedb/urls.py                     |   1 +
 gracedb/views.py                    |  74 +++++++++-
 settings/default.py                 |   3 +
 templates/gracedb/event_detail.html | 205 ++++++++++++++++++++--------
 templates/gracedb/histogram.html    |   3 +-
 templates/gracedb/latest.html       |   3 +-
 7 files changed, 233 insertions(+), 65 deletions(-)

diff --git a/gracedb/models.py b/gracedb/models.py
index cf98c4956..dc95a9976 100644
--- a/gracedb/models.py
+++ b/gracedb/models.py
@@ -14,7 +14,10 @@ import pytz, time
 SERVER_TZ = pytz.timezone(settings.TIME_ZONE)
 
 # Let's say we start here on schema versions
-schema_version = "1.0"
+#
+# 1.0 -> 1.1   changed EventLog.comment from CharField(length=200) -> TextField
+#
+schema_version = "1.1"
 
 class User(models.Model):
     name = models.CharField(max_length=100)
@@ -158,7 +161,7 @@ class EventLog(models.Model):
     created = models.DateTimeField(auto_now_add=True)
     issuer = models.ForeignKey(User)
     filename = models.CharField(max_length=100, default="")
-    comment = models.CharField(max_length=200, null=False, default="")
+    comment = models.TextField(null=False)
 
     def fileurl(self):
         if self.filename:
@@ -176,6 +179,8 @@ class Labelling(models.Model):
     creator = models.ForeignKey(User)
     created = models.DateTimeField(auto_now_add=True)
 
+# XXX Deprecated?  Is this used *anywhere*?
+# Appears to only be used in models.py.  Here and Event class as approval_set
 class Approval(models.Model):
     COLLABORATION_CHOICES = ( ('L','LIGO'), ('V','Virgo'), )
     approver = models.ForeignKey(User)
diff --git a/gracedb/urls.py b/gracedb/urls.py
index 3505e42ab..7c79a0ca4 100644
--- a/gracedb/urls.py
+++ b/gracedb/urls.py
@@ -14,6 +14,7 @@ urlpatterns = patterns('gracedb.views',
     url (r'^skyalert/(?P<graceid>[\w\d]+)', 'skyalert', name="skyalert"),
     url (r'^(?P<graceid>[\w\d]+)$', 'view', name="view2"),
     url (r'^(?P<graceid>[\w\d]+)/files/(?P<filename>.+)$', download, name="file"),
+    url (r'^(?P<graceid>[\w\d]+)/log/(?P<num>([\d]*|preview))$', 'logentry', name="logentry"),
 
 
 #   (r'^view/(?P<uid>[\w\d]+)', 'view'),
diff --git a/gracedb/views.py b/gracedb/views.py
index 1c8ee6ef0..0b93146cf 100644
--- a/gracedb/views.py
+++ b/gracedb/views.py
@@ -3,7 +3,7 @@ from django.http import HttpResponse
 from django.http import HttpResponseRedirect, HttpResponseNotFound, HttpResponseBadRequest, Http404
 from django.template import RequestContext
 from django.core.urlresolvers import reverse, get_script_prefix
-from django.shortcuts import render_to_response
+from django.shortcuts import render_to_response, get_object_or_404
 from django.contrib.sites.models import Site
 from django.utils.html import strip_tags, escape, urlize
 from django.utils.safestring import mark_safe
@@ -16,6 +16,7 @@ from alert import issueAlert, issueAlertForLabel, issueAlertForUpdate
 from translator import handle_uploaded_data
 
 import urllib
+import markdown
 
 import os
 import re
@@ -32,6 +33,7 @@ MAX_QUERY_RESULTS = 1000
 GRACEDB_DATA_DIR = settings.GRACEDB_DATA_DIR
 
 import simplejson
+import json
 
 def index(request):
 #   assert request.ligouser
@@ -387,6 +389,76 @@ def cli_label(request):
 
     return response
 
+import html5lib
+def sanitize_html(data):
+    """
+
+    >>> sanitize_html5lib("foobar<p>adf<i></p>abc</i>")
+    u'foobar<p>adf<i></i></p><i>abc</i>'
+    >>> sanitize_html5lib('foobar<p style="color:red; remove:me; background-image: url(http://example.com/test.php?query_string=bad);">adf<script>alert("Uhoh!")</script><i></p>abc</i>')
+    u'foobar<p style="color: red;">adf&lt;script&gt;alert("Uhoh!")&lt;/script&gt;<i></i></p><i>abc</i>'
+    """
+    from html5lib import treebuilders, treewalkers, serializer, sanitizer
+
+    p = html5lib.HTMLParser(tokenizer=sanitizer.HTMLSanitizer, tree=treebuilders.getTreeBuilder("dom"))
+    dom_tree = p.parseFragment(data)
+
+    walker = treewalkers.getTreeWalker("dom")
+
+    stream = walker(dom_tree)
+
+    s = serializer.htmlserializer.HTMLSerializer(omit_optional_tags=False)
+    return "".join(s.serialize(stream))
+
+
+def logentry(request, graceid, num=None):
+    try:
+        event = Event.getByGraceid(graceid)
+    except Event.DoesNotExist:
+        raise Http404
+    if request.method == "POST":
+        # create a log entry
+        elog = EventLog(event=event, issuer=request.ligouser)
+        elog.comment = request.POST.get('comment') or request.GET.get('comment')
+        elog.save()
+    else:
+        try:
+            elog = event.eventlog_set.order_by('created').all()[int(num)]
+        except Exception, e:
+            raise Http404
+
+    rv = {}
+    rv['comment'] = elog.comment
+    rv['issuer'] = elog.issuer.name
+    rv['created'] = elog.created.isoformat()
+    rv['comment'] = elog.comment
+
+    return HttpResponse(json.dumps(rv), content_type="application/json")
+
+def markdownlogentry(request, graceid, num=None):
+    try:
+        event = Event.getByGraceid(graceid)
+    except Event.DoesNotExist:
+        raise Http404
+    if request.method == "POST":
+        # create a log entry
+        # XXX num can be None or 'preview'
+        comment = request.POST.get('comment') or request.GET.get('comment')
+
+        elog = EventLog(event=event, issuer=request.ligouser)
+        elog.comment = comment
+        elog.save()
+        # XXX janky. optimize this.
+        elogIndex =  list(event.eventlog_set.order_by('created').all()).index(elog)
+        return HttpResponse(reverse(logentry, args=[event.graceid(), elogIndex]))
+    else:
+        try:
+            text = event.eventlog_set.order_by('created').all()[int(num)].comment
+            text = markdown.markdown(text)
+            return HttpResponse(text)
+        except Exception, e:
+            raise Http404
+
 def log(request):
     message = request.POST.get('message')
     graceid = request.POST.get('graceid')
diff --git a/settings/default.py b/settings/default.py
index 140960ed1..72608deae 100644
--- a/settings/default.py
+++ b/settings/default.py
@@ -181,6 +181,9 @@ INSTALLED_APPS = (
     'django.contrib.contenttypes',
     'django.contrib.sessions',
     'django.contrib.sites',
+    'django.contrib.markup',
+    'django_wysiwyg',
     'gracedb',
     'userprofile',
+    'slink',
 )
diff --git a/templates/gracedb/event_detail.html b/templates/gracedb/event_detail.html
index 136b972f0..712de5a31 100644
--- a/templates/gracedb/event_detail.html
+++ b/templates/gracedb/event_detail.html
@@ -1,67 +1,153 @@
 {% extends "base.html" %}
 {% load timeutil %}
 {% load scientific %}
+{% load sanitize_html %}
 {% load markup %}
-{% load wysiwyg %}
+{# load wysiwyg #}
 
 {% block title %}View {{ object.graceid }}{% endblock %}
 {% block heading %}{% endblock %}
+{% block bodyattrs %}class="tundra"{% endblock %}
 
 {% block jscript %}
     {# http/https depending on this pages' protocol #}
-    {% wysiwyg_setup %}
+    {# wysiwyg_setup #}
     <script type="text/javascript"
-      src="https://c328740.ssl.cf1.rackcdn.com/mathjax/latest/MathJax.js?config=AM_HTMLorMML-full">
+      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 type="text/javascript">
-        dojo.require("dojox.image.LightboxNano");
-        dojo.require("dojo.parser");
-        dojo.ready(function(){
+
+        require(["dijit/Editor",
+                 "dojox/editor/plugins/Save",
+                 "dojox/editor/plugins/Preview",
+                 "dojox/image/LightboxNano",
+                 "dijit/_editor/plugins/TextColor",
+                 "dijit/_editor/plugins/LinkDialog",
+                 "dijit/_editor/plugins/ViewSource",
+                 "dijit/_editor/plugins/NewPage",
+                 "dijit/_editor/plugins/FullScreen",
+                 "dojo/parser",
+                 "dojo/domReady!"], function (Editor, Save, Preview) {
+
             dojo.parser.parse();
-            setupform();
-        });
 
-        function handleNewEntry(target, data) {
-            target.innerHTML = data.issuer + " / " + data.created + " / " + data.comment;
-            return;
-            var xhrArgs = {
-                url: data,
-                handleAs: "json",
-                load: function(data) {
-                    target.innerHTML = data;
-                },
-                error: function(error) {
-                    target.innerHTML = "Errorz: " + error;
-                }
-            };
-            dojo.byId("result").innerHTML = "Sending more..."
-            var deferred = dojo.xhrGet(xhrArgs);
-        };
-
-        function setupform() {
+            var newlogdiv = dojo.byId("newlogdiv");
+            var logtitle = dojo.byId("logmessagetitle");
+            var logtext = dojo.byId("newlogtext");
             var form = dojo.byId("newlog");
-            dojo.connect(form, "onsubmit", function(event) {
-                dojo.stopEvent(event);
-                var xhrArgs = {
-                    form: dojo.byId("newlog"),
-                    handleAs: "json",
-                    load: function(data){
-                        //alert("load: " + data);
-                        //dojo.byId("result").innerHTML = data;
-                        handleNewEntry(dojo.byId("result"), data);
+
+            dojo.style(newlogdiv, {'display':'none'});
+
+            logtitle.innerHTML = logtitle.innerHTML + " [click to create a new log entry]";
+            var editor_div = dojo.create('div');
+            var preview_div = dojo.create('div');
+            dojo.place(editor_div, newlogdiv, "after");
+            dojo.place(preview_div, editor_div, "before");
+            dojo.style(preview_div, { 'display':'none'});
+            dojo.style(editor_div, { 'display':'none'});
+
+            var savebutton = new Save({
+                    url: "{% url logentry object.graceid "" %}",
+                    onSuccess: function (resp, ioargs) {
+                        //this.inherited(resp, ioargs);
+                        this.button.set("disabled", false);
+                        location.reload(true);
                     },
-                    error: function(error){
-                        //alert("error: " + error);
-                        dojo.byId("result").innerHTML = error;
+                    onError: function (error, ioargs) {
+                        //this.inherited(error, ioargs);
+                        this.button.set("disabled", false);
+                        alert(error);
+                    },
+                    save: function(postdata) {
+                        var postArgs = {
+                                url: this.url,
+                                content: { comment: postdata },
+                                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));
                     }
-                };
-                dojo.byId("result").innerHTML = "Sending..."
-                var deferred = dojo.xhrPost(xhrArgs);
             });
-        };
+
+            var previewbutton = new Preview({
+                _preview: function(){
+                        var content = this.editor.get("value");
+                        preview_div.innerHTML = editor.get('value');
+                        dojo.style(preview_div, {
+                            'display':'block',
+                            'border': ".2em solid #900",
+                            'padding': '10px'
+                        });
+                        MathJax.Hub.Queue(["Typeset",MathJax.Hub, preview_div]);
+                    }
+            });
+
+
+//          dojo.connect(form, "onsubmit", function(event) {
+//              dojo.stopEvent(event);
+//              var postdata = editor.get('value');
+//              var xhrArgs = {
+//                  url: "{% url logentry object.graceid "" %}",
+//                  content: { comment: postdata},
+//                  handleAs: "json",
+//                  load: function(data) {
+//                      location.reload(true);
+//                  },
+//                  error: function(data) {
+//                      alert("Problem submitting: " + data);
+//                  },
+//              };
+//              var deferred = dojo.xhrPost(xhrArgs);
+//          });
+
+            dojo.connect(logtitle, "click", function(event) {
+                dojo.style(editor_div, {'display':'block'});
+            });
+
+//          var cancelButton = dojo.byId('cancel');
+//          dojo.connect(cancelButton, "onclick", function(event) {
+//              dojo.style(editor_div, {'display':'none'});
+//              dojo.style(logtext, {'display':'block'});
+//          });
+
+//          var previewButton = dojo.byId('preview');
+//          dojo.connect(previewButton, "onclick", function(event) {
+//              var result = dojo.byId("result");
+//              result.innerHTML = editor.get('value');
+//              MathJax.Hub.Queue(["Typeset",MathJax.Hub, result]);
+//          });
+
+            var editor = new Editor({
+                 extraPlugins : ['foreColor','hiliteColor','|','createLink',
+                                 'insertImage','fullscreen','viewsource','newpage', '|', previewbutton, savebutton]
+            }, editor_div);
+
+            dojo.connect(logtext, 'click', function(event) {
+                dojo.style(logtext, {'display':'none'});
+                dojo.style(editor_div, {'display':'block'});
+                editor.set('value', logtext.value);
+                editor.focus();
+            });
+
+        });
+
     </script>
+        <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojox/editor/plugins/resources/css/PageBreak.css" />
+        <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojox/editor/plugins/resources/css/ShowBlockNodes.css" />
+        <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojox/editor/plugins/resources/css/Preview.css" />
+        <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojox/editor/plugins/resources/css/Save.css" />
+        <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojox/editor/plugins/resources/css/Breadcrumb.css" />
+        <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojox/editor/plugins/resources/css/FindReplace.css" />
+        <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojox/editor/plugins/resources/css/PasteFromWord.css" />
+        <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojox/editor/plugins/resources/css/InsertAnchor.css" />
+        <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojox/editor/plugins/resources/css/CollapsibleToolbar.css" />
+        <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojox/editor/plugins/resources/css/Blockquote.css" />
+        <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojox/editor/plugins/resources/css/Smiley.css" />
     <style>
+        @import "https://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dijit/themes/tundra/tundra.css";
         a:hover .dojoxEnlarge {
             display: block !important;
         }
@@ -188,35 +274,34 @@
     </table>
 {% endif %}
 
+
+<div id="newlogdiv">
+    <h3>Create a new log entry</h3>
+    <form id="newlog" action="{% url logentry object.graceid "" %}" method="POST">
+        <textarea id="newlogtext" name="comment" style="width:300px;display:block"></textarea>
+        <input type="submit" value="Submit"/>
+        <input type="button" value="Preview" id="preview" />
+        <input type="button" value="Cancel" id="cancel" />
+    </form>
+</div>
+
 {% if object.eventlog_set.count %}
 
   <p/>
-  <h3>Event Log Messages</h3>
+  <h3 id="logmessagetitle">Event Log Messages</h3>
 
   <table class="event">
         <tr>
-            <th>{{ "logtime"|timeselect:"utc" }} Created / Creator</th>
+            <th>{{ "logtime"|timeselect:"utc" }} Log Entry Created</th>
+            <th>Submitter</th>
             <th>Comment</th>
             <th>&nbsp;</th>
         </tr>
-        <tr>
-            <td></td>
-            <td><div id="result">empty  `x = (-b +- sqrt(b^2-4ac))/(2a) .` </div></td>
-        </tr>
-        <tr>
-            <td>{{ ligouser }}</td>
-            <td><form id="newlog" action="{% url logentry object.graceid "" %}" method="POST">
-                <input type="submit"/>
-                <textarea id="newlogtext" name="comment"></textarea>
-                {% wysiwyg_editor "newlogtext" %}
-                </form>
-            </td>
-            <td></td>
-        </tr>
   {% for log in object.eventlog_set.iterator %}
         <tr class="{% cycle 'odd' 'even'%}">
-           <td>{{log.created|multiTime:"logtime"}}<br/>{{log.issuer}}</td>
-           <td>{{log.comment|safe}}
+           <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 %}
diff --git a/templates/gracedb/histogram.html b/templates/gracedb/histogram.html
index 19ab15124..173a3d3be 100644
--- a/templates/gracedb/histogram.html
+++ b/templates/gracedb/histogram.html
@@ -17,7 +17,8 @@ function toggle(id) {
 }
 </script>
 
-<script src="http://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojo/dojo.js"
+{# http/https depending on this pages' protocol #}
+<script src="http{% if request.is_secure %}s{% endif %}://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojo/dojo.js"
         data-dojo-config="async: true, isDebug: false, parseOnLoad: true">
 </script>
 
diff --git a/templates/gracedb/latest.html b/templates/gracedb/latest.html
index db64abc83..482123e4e 100644
--- a/templates/gracedb/latest.html
+++ b/templates/gracedb/latest.html
@@ -19,7 +19,8 @@
 {% endif %}
 
 
-    <script src="http://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojo/dojo.js" type="text/javascript"></script>
+    {# http/https depending on this pages' protocol #}
+    <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 type="text/javascript">
         dojo.require("dojox.image.LightboxNano");
         dojo.require("dojo.parser");
-- 
GitLab