63bcbedb9341

web: add support for Markdown formatting
[view raw] [browse files]
author Steve Losh <steve@stevelosh.com>
date Fri, 18 Jun 2010 22:28:46 -0400
parents 5101c0cba85d
children 24f0d81f3aa5
branches/tags (none)
files review/static/comments.js review/static/style.css review/static/style.less review/templates/changeset.html review/templates/pieces/comment.html review/web.py

Changes

--- a/review/static/comments.js	Fri Jun 18 22:28:31 2010 -0400
+++ b/review/static/comments.js	Fri Jun 18 22:28:46 2010 -0400
@@ -10,6 +10,11 @@
                                       name="new-comment-body"></textarea>\
                         </div>\
                         \
+                        <div class="field cuddly">\
+                            <input type="checkbox" class="checkbox" name="comment-markdown" id="id_comment-line-form_' + currNum + '_markdown" checked="checked" />\
+                            <label for="id_comment-line-form_' + currNum + '_markdown">Use Markdown to format this comment.</label>\
+                        </div>\
+                        \
                         <a class="submit button"><span>Post Comment</span></a>\
                         <a class="cancel-line button"><span>Cancel</span></a>\
                         \
--- a/review/static/style.css	Fri Jun 18 22:28:31 2010 -0400
+++ b/review/static/style.css	Fri Jun 18 22:28:46 2010 -0400
@@ -1,3 +1,11 @@
+.markdown p:last-child, .markdown ol:last-child, .markdown ul:last-child {
+  margin-bottom: 0;
+}
+.plain {
+  font-family: Monaco, Consolas, "Courier New", monospace;
+  font-size: 12px;
+  white-space: pre;
+}
 .group:after {
   clear: both;
   content: ' ';
@@ -180,6 +188,9 @@
   border-radius: 2px;
   border: 1px solid #444;
 }
+form .field.cuddly {
+  margin-top: -13px;
+}
 #index .content table {
   width: 100%;
 }
@@ -477,12 +488,8 @@
   float: right;
 }
 #changeset .content .item-listing .comment .message, #changeset .content .item-listing .signoff .message {
-  font-family: Monaco, Consolas, "Courier New", monospace;
-  font-size: 12px;
   width: 690px;
   padding-top: 3px;
-  white-space: pre;
-  overflow-x: auto;
 }
 #changeset .content .item-listing .comment .avatar img, #changeset .content .item-listing .signoff .avatar img {
   height: 30px;
@@ -641,10 +648,6 @@
   -moz-border-radius: 3px;
   border-radius: 3px;
 }
-#changeset .content .diff table td.comment .message {
-  white-space: pre;
-  font-family: Monaco, Consolas, "Courier New", monospace;
-}
 #changeset .content .diff table td.comment .author {
   padding-bottom: 3px;
 }
--- a/review/static/style.less	Fri Jun 18 22:28:31 2010 -0400
+++ b/review/static/style.less	Fri Jun 18 22:28:46 2010 -0400
@@ -70,6 +70,20 @@
     background-color: @bgcolor;
 }
 
+.markdown {
+    p, ol, ul { 
+        &:last-child {
+            margin-bottom: 0;
+        }
+    }
+}
+.plain {
+    font-family: @font-mono;
+    font-size: 12px;
+    white-space: pre;
+}
+
+
 .group:after {
     clear:both; content:' '; display:block; font-size:0; line-height:0; visibility:hidden; width:0; height:0;
 }
@@ -220,6 +234,9 @@
             .border-radius(2px);
             border: 1px solid #444;
         }
+        &.cuddly {
+            margin-top: -13px;
+        }
     }
 }
 
@@ -421,12 +438,8 @@
                 float: right;
             }
             .message {
-                font-family: @font-mono;
-                font-size: 12px;
                 width: 690px;
                 padding-top: 3px;
-                white-space: pre;
-                overflow-x: auto;
             }
             .avatar img {
                 height: 30px;
@@ -601,10 +614,6 @@
                             .border-radius(3px);
                         }
                     }
-                    .message {
-                        white-space: pre;
-                        font-family: @font-mono;
-                    }
                     .author {
                         padding-bottom: 3px;
                     }
--- a/review/templates/changeset.html	Fri Jun 18 22:28:31 2010 -0400
+++ b/review/templates/changeset.html	Fri Jun 18 22:28:46 2010 -0400
@@ -60,6 +60,11 @@
                     <label class="infield" for="id_comment-review-form_body">Comment</label>
                     <textarea autocomplete="off" id="id_comment-review-form_body" cols="60" rows="6" name="new-comment-body"></textarea>
                 </div>
+                <div class="field cuddly">
+                    <input type="checkbox" class="checkbox" name="comment-markdown" id="id_comment-review-form_markdown" checked="checked" />
+                    <label for="id_comment-review-form_markdown">Use Markdown to format this comment.</label>
+
+                </div>
                 <a class="submit button" href="#"><span>Post Comment</span></a>
                 <a class="cancel button" href="#"><span>Cancel</span></a>
             </form>
@@ -74,6 +79,10 @@
         {% if signoffs %}
             <div class="signoffs item-listing">
                 {% for signoff in signoffs %}
+                    {% if signoff.style == 'markdown' %}
+                        {% set rendered = utils['markdown'](signoff.message) %}
+                    {% endif %}
+
                     <div class="signoff group {{ signoff.opinion or 'neutral' }}">
                         <div class="avatar">
                             <img src="{{ utils['item_gravatar'](signoff, 30) }}" />
@@ -86,7 +95,12 @@
                                 <a href="mailto:{{ utils['email'](signoff.author) }}">{{ utils['templatefilters'].person(signoff.author) }}</a>
                                 signed off as <span class="opinion">{{ signoff.opinion or "neutral" }}</span> on this changeset, saying:
                             </div>
-                            <div class="message">{{ signoff.message }}</div>
+
+                            {% if signoff.style == 'markdown' %}
+                                <div class="message markdown">{{ rendered|safe }}</div>
+                            {% else %}
+                                <div class="message plain">{{ signoff.message }}</div>
+                            {% endif %}
                         </div>
                     </div>
                 {% endfor %}
@@ -107,15 +121,23 @@
             </span>
             <form id="signoff-form" class="disabled" method="POST" action="">
                 <p class="sign-off-as">Sign off as:</p>
+
                 <div class="field">
                     <input id="id_signoff-form_yes" type="radio" name="signoff" value="yes" {% if cu_signoff and cu_signoff.opinion == 'yes' %}checked{% endif %}/><label class="radio" for="id_signoff-form_yes">Yes</label>
                     <input id="id_signoff-form_no"type="radio" name="signoff" value="no" {% if cu_signoff and cu_signoff.opinion == 'no' %}checked{% endif %}/><label class="radio" for="id_signoff-form_no">No</label>
                     <input id="id_signoff-form_neutral"type="radio" name="signoff" value="neutral" {% if cu_signoff and cu_signoff.opinion == '' %}checked{% endif %}/><label class="radio" for="id_signoff-form_neutral">Neutral</label>
                 </div>
+
                 <div class="field">
                     <label class="infield" for="id_signoff-form_body">Signoff message</label>
                     <textarea autocomplete="off" id="id_signoff-form_body" cols="60" rows="6" name="new-signoff-body">{% if cu_signoff %}{{ cu_signoff.message }}{% endif %}</textarea>
                 </div>
+
+                <div class="field cuddly">
+                    <input type="checkbox" class="checkbox" name="signoff-markdown" id="id_signoff-form_markdown" checked="checked" />
+                    <label for="id_signoff-form_markdown">Use Markdown to format this message.</label>
+
+                </div>
                 {% if cu_signoff %}
                     <a class="submit button" href="#"><span>Change Signoff</span></a>
                 {% else %}
@@ -159,6 +181,11 @@
                                 <textarea autocomplete="off" id="id_comment-file-form_{{ loop.index }}_body" cols="60" rows="6" name="new-comment-body"></textarea>
                             </div>
 
+                            <div class="field cuddly">
+                                <input type="checkbox" class="checkbox" name="comment-markdown" id="id_comment-file-form_{{ loop.index }}_markdown" checked="checked" />
+                                <label for="id_comment-file-form_{{ loop.index }}_markdown">Use Markdown to format this comment.</label>
+                            </div>
+
                             <a class="submit button" href="#"><span>Post Comment</span></a>
                             <a class="cancel button" href="#"><span>Cancel</span></a>
 
--- a/review/templates/pieces/comment.html	Fri Jun 18 22:28:31 2010 -0400
+++ b/review/templates/pieces/comment.html	Fri Jun 18 22:28:46 2010 -0400
@@ -1,3 +1,7 @@
+{% if comment.style == 'markdown' %}
+    {% set rendered = utils['markdown'](comment.message) %}
+{% endif %}
+
 <div class="comment group" id="comment-{{ comment.identifier }}">
     <a href="#comment-{{ comment.identifier }}" rel="comments" class="expand" id="comment-expand-{{ comment.identifier }}">&rarr;</a>
     <script type="text/javascript">
@@ -14,7 +18,12 @@
             <a href="mailto:{{ utils['email'](comment.author) }}">{{ utils['templatefilters'].person(comment.author) }}</a>
             said:
         </div>
-        <div class="message">{{ comment.message }}</div>
+
+        {% if comment.style == 'markdown' %}
+            <div class="message markdown">{{ rendered|safe }}</div>
+        {% else %}
+            <div class="message plain">{{ comment.message }}</div>
+        {% endif %}
     </div>
 
     <div id="comment-{{ comment.identifier }}-colorboxed" class="colorboxed">
--- a/review/web.py	Fri Jun 18 22:28:31 2010 -0400
+++ b/review/web.py	Fri Jun 18 22:28:46 2010 -0400
@@ -21,14 +21,17 @@
     jinja2_path = os.path.join(bundled_path, 'jinja2')
     werkzeug_path = os.path.join(bundled_path, 'werkzeug')
     simplejson_path = os.path.join(bundled_path, 'simplejson')
+    markdown2_path = os.path.join(bundled_path, 'markdown2', 'lib')
 
     sys.path.insert(0, flask_path)
     sys.path.insert(0, werkzeug_path)
     sys.path.insert(0, jinja2_path)
     sys.path.insert(0, simplejson_path)
+    sys.path.insert(0, markdown2_path)
 
 unbundle()
 
+import markdown2
 from flask import Flask
 from flask import abort, g, redirect, render_template, request
 app = Flask(__name__)
@@ -48,6 +51,7 @@
     return { 'yes': len(filter(lambda s: s.opinion == 'yes', signoffs)),
              'no': len(filter(lambda s: s.opinion == 'no', signoffs)),
              'neutral': len(filter(lambda s: s.opinion == '', signoffs)),}
+
 utils = {
     'node_short': short,
     'md5': md5,
@@ -61,6 +65,7 @@
     'map': map,
     'str': str,
     'decode': lambda s: s.decode('utf-8'),
+    'markdown': markdown2.markdown,
 }
 
 def _render(template, **kwargs):
@@ -107,21 +112,26 @@
         signoff = ''
 
     body = request.form.get('new-signoff-body', '')
+    style = 'markdown' if request.form.get('signoff-markdown') else ''
+
     rcset = g.datastore[revhash]
-    rcset.add_signoff(body, signoff, force=True)
+    rcset.add_signoff(body, signoff, force=True, style=style)
 
     return redirect("%s/changeset/%s/" % (app.site_root, revhash))
 
 def _handle_comment(revhash):
     filename = request.form.get('filename', '')
+
     lines = str(request.form.get('lines', ''))
     if lines:
         lines = filter(None, [l.strip() for l in lines.split(',')])
+
     body = request.form['new-comment-body']
+    style = 'markdown' if request.form.get('comment-markdown') else ''
     
     if body:
         rcset = g.datastore[revhash]
-        rcset.add_comment(body, filename, lines)
+        rcset.add_comment(body, filename, lines, style=style)
     
     return redirect("%s/changeset/%s/" % (app.site_root, revhash))