diff options
| author | Bobby <[email protected]> | 2023-01-01 11:29:52 -0500 |
|---|---|---|
| committer | Bobby <[email protected]> | 2023-01-01 11:29:52 -0500 |
| commit | cd4df61201e53ac32d6e49ac7cec0d6045a2c3f5 (patch) | |
| tree | 4314abf1adf26991eb5a4b53ec16be5fe2c966d6 | |
| parent | 3d15ed922526ae52237fa1f4c7dfb9b7fec25c53 (diff) | |
| download | thatcomputerscientist-cd4df61201e53ac32d6e49ac7cec0d6045a2c3f5.tar.xz thatcomputerscientist-cd4df61201e53ac32d6e49ac7cec0d6045a2c3f5.zip | |
Major comment area update - go see for yourself
| -rw-r--r-- | blog/context_processors.py | 25 | ||||
| -rw-r--r-- | blog/models.py | 2 | ||||
| -rw-r--r-- | blog/views.py | 54 | ||||
| -rw-r--r-- | static/css/main.css | 30 | ||||
| -rw-r--r-- | templates/blog/post.html | 104 |
5 files changed, 179 insertions, 36 deletions
diff --git a/blog/context_processors.py b/blog/context_processors.py index 0faeeb5f..782ff2e9 100644 --- a/blog/context_processors.py +++ b/blog/context_processors.py @@ -2,6 +2,10 @@ from .models import Post, Category, Comment import os from django.conf import settings from bs4 import BeautifulSoup +from pygments import highlight +from pygments.lexers import get_lexer_by_name +from pygments.lexers import guess_lexer +from pygments.formatters import HtmlFormatter def add_excerpt(post): soup = BeautifulSoup(post.body, 'html.parser') @@ -47,3 +51,24 @@ def avatar_list(): if file.startswith('.'): avatar_list[directory].remove(file) return avatar_list + +def highlight_code_blocks(code_block): + # replace with space + try: + cb = code_block.string + except: + cb = code_block + print(cb) + cb = cb.replace(u'\xa0', u' ') + + # guess the language as there is no data-lang attribute + try: + lexer = guess_lexer(cb) + except: + lexer = get_lexer_by_name('text') + + # highlight the code + formatter = HtmlFormatter(noclasses=True, style='native') + highlighted_code = highlight(cb, lexer, formatter) + + return highlighted_code diff --git a/blog/models.py b/blog/models.py index d9d41eeb..496365c1 100644 --- a/blog/models.py +++ b/blog/models.py @@ -60,4 +60,4 @@ class Comment(models.Model): edited_at = models.DateTimeField(blank=True, null=True) def __str__(self): - return self.body + return self.body[:50] + '...' if len(self.body) > 50 else self.body diff --git a/blog/views.py b/blog/views.py index d31b3f8f..65e69c3e 100644 --- a/blog/views.py +++ b/blog/views.py @@ -2,15 +2,17 @@ from datetime import datetime from django.shortcuts import render, redirect, reverse from django.http import HttpResponse from users.models import UserProfile -import hashlib +from django.core.paginator import Paginator from random import choice from string import ascii_letters, digits from .models import Category, Post, Comment -from .context_processors import recent_posts, avatar_list, add_excerpt, add_num_comments +from .context_processors import recent_posts, avatar_list, add_excerpt, add_num_comments, highlight_code_blocks from announcements.models import Announcement from users.forms import RegisterForm from users.tokens import CaptchaTokenGenerator from django.contrib import messages +from bs4 import BeautifulSoup +import re # Create your views here. @@ -67,32 +69,11 @@ def post(request, slug): try: post = Post.objects.get(slug=slug) - # Highlight code blocks, if any in the post body - from pygments import highlight - from pygments.lexers import get_lexer_by_name - from pygments.lexers import guess_lexer - from pygments.formatters import HtmlFormatter - from bs4 import BeautifulSoup - # code stored in .ql-syntax class soup = BeautifulSoup(post.body, 'html.parser') code_blocks = soup.find_all('pre', class_='ql-syntax') for code_block in code_blocks: - # replace with space - code_block.string = code_block.string.replace(u'\xa0', u' ') - - # guess the language as there is no data-lang attribute - try: - lexer = guess_lexer(code_block.string) - except: - lexer = get_lexer_by_name('text') - - # highlight the code - formatter = HtmlFormatter(noclasses=True, style='native') - highlighted_code = highlight(code_block.string, lexer, formatter) - - # replace the code block with the highlighted code - code_block.replace_with(BeautifulSoup(highlighted_code, 'html.parser')) + code_block.replace_with(BeautifulSoup(highlight_code_blocks(code_block), 'html.parser')) post.body = str(soup) @@ -102,6 +83,30 @@ def post(request, slug): for comment in comments: user_profile = UserProfile.objects.get(user=comment.user) comment.avatar_url = user_profile.avatar_url + + comment.processed_body = comment.body + + # escape html tags + comment.processed_body = re.sub(r'<', '<', comment.processed_body) + comment.processed_body = re.sub(r'>', '>', comment.processed_body) + + # any text between ``` and ``` must be highlighted as code + code_blocks = re.findall(r'```(.+?)```', comment.processed_body, re.DOTALL) + for code_block in code_blocks: + comment.processed_body = comment.processed_body.replace('```' + code_block + '```', highlight_code_blocks(code_block)) + + # retain line breaks, for every newline character, add a <br> tag + comment.processed_body = comment.processed_body.replace('\n', '<br>') + + # replace multiple <br> tags with a single <br> tag + comment.processed_body = re.sub(r'<br>(\s*<br>)+', '<br><br>', comment.processed_body) + + comment.processed_body = re.sub(r'\*\*(.+?)\*\*', r'<b>\1</b>', comment.processed_body) + comment.processed_body = re.sub(r'__(.+?)__', r'<i>\1</i>', comment.processed_body) + comment.processed_body = re.sub(r'~~(.+?)~~', r'<s>\1</s>', comment.processed_body) + + + if post.is_public: return render(request, 'blog/post.html', {'title': post.title, 'post': post, 'tags': tags, 'comments': comments}) else: @@ -205,7 +210,6 @@ def search(request): posts = posts.order_by('-date') return render(request, 'blog/search.html', {'title': 'Search', 'posts': posts, 'categories': categories, 'tags': tags, 'cate': category, 'query': query}) -from django.core.paginator import Paginator def articles(request): page = request.GET.get('page') if request.GET.get('page') else 1 order_by = request.GET.get('order_by') if request.GET.get('order_by') else 'date' diff --git a/static/css/main.css b/static/css/main.css index ea7a7a6a..8aafbc16 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -50,6 +50,22 @@ textarea { background: transparent; border: solid 1px whitesmoke; color: white; + font-family: 'Courier New', Courier, monospace; + white-space: pre-line; +} + +kbd { + background-color: #eee; + border-radius: 3px; + border: 1px solid #b4b4b4; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 0 2px 0 0 rgba(255, 255, 255, 0.7) inset; + color: #333; + display: inline-block; + font-size: 0.85em; + font-weight: 700; + line-height: 1; + padding: 2px 4px; + white-space: nowrap; } /* Full width auto spacing table... ellipsis if text overflows */ @@ -180,6 +196,20 @@ textarea { background: none !important; } +.comment { + width: 630px; + margin-bottom: 40px; +} + +.comment .highlight { + width: 630px; + overflow-x: scroll; +} + +.comment pre { + margin: 0px; +} + #wrap { width: 1000px; margin: auto; diff --git a/templates/blog/post.html b/templates/blog/post.html index 8bce0765..96032aab 100644 --- a/templates/blog/post.html +++ b/templates/blog/post.html @@ -24,8 +24,8 @@ <td style="width: 60px; vertical-align: top;"> <img src="{{ comment.avatar_url }}" alt="Profile Picture" style="width: 50px; height: auto;"> </td> - <td style="vertical-align: top;"> - <p style="margin-top: 0;"> + <td style="vertical-align: top; width: 668px;"> + <div style="margin-bottom: 13px; border-bottom: dashed 1px white; padding-bottom: 13px;"> <a href="#">{{ comment.user.username }}</a> on <em>{{ comment.created_at | date:"M d, Y" }}</em> {% if comment.edited %} <em>(Edited)</em> @@ -36,19 +36,21 @@ <a href="{% url 'blog:delete_comment' post.slug comment.id %}" onclick="return confirm('Are you sure you want to delete this comment?')">Delete</a> {% endif %} - </p> - <p> - <span id="comment-body-{{ comment.id }}">{{ comment.body }}</span> - {% if comment.user == user %} - <form action="{% url 'blog:edit_comment' post.slug %}" method="POST" id="edit-form-{{ comment.id }}" style="display: none;"> + </div> + <div id="comment-body-{{ comment.id }}" class="comment"> + {{ comment.processed_body|safe }} + </div> + {% if comment.user == user %} + <div id="edit-form-{{ comment.id }}" style="display: none; margin-bottom: 20px;"> + <form action="{% url 'blog:edit_comment' post.slug %}" method="POST"> {% csrf_token %} <input type = "hidden" name="comment_id" value="{{ comment.id }}"> <textarea name="body" id="body" cols="78" rows="10" style="width: 640px; display: block; margin-bottom: 10px;">{{ comment.body }}</textarea> <input type="submit" value="Update" class="button button-special"> <input type="button" value="Cancel" onclick="cancelEdit({{ comment.id }})" class="button"> </form> - {% endif %} - </p> + </div> + {% endif %} </td> </tr> </table> @@ -57,13 +59,43 @@ </div> {% if user.is_authenticated %} -<div id="new-comment" class="mtsbitem"> +<div id="new-comment"> <h2>Leave a Comment</h2> <form action="{% url 'blog:comment' post.slug %}" method="POST"> {% csrf_token %} <textarea name="comment" id="comment" cols="88" rows="10" style="width: 710px; display: block; margin-bottom: 15px;" placeholder="Your comment here..."></textarea> <input type="submit" value="Submit" class="button button-special"> </form> + <div id="comment-tips" style="color: #d8c6df;"> + <p style="margin-bottom: 0;">Simple markup is supported:</p> + <ul style="list-style-type: disc;margin: 0;padding: 0;margin: 5px 0px 0px 30px;"> + <li>Wrap text in double underscores (__) to make it <i>italic</i>. + <br> +   e.g. <code>__italic__</code> +   shortkey: <kbd>Ctrl or Cmd + I</kbd> + <br><br> + </li> + <li>Wrap text in double asterisks (**) to make it <b>bold</b>. + <br> +   e.g. <code>**bold**</code> +   shortkey: <kbd>Ctrl or Cmd + B</kbd> + <br><br> + </li> + <li>Wrap text in double tildes (~~) to <s>strikethrough</s> it. + <br> +   e.g. <code>~~strikethrough~~</code> +   shortkey: <kbd>Ctrl or Cmd + D</kbd> + <br><br> + </li> + <li>Wrap text in triple backticks (```) to make it a code block. + <br> +   e.g. <code>```code block```</code>, +   shortkey: <kbd>Ctrl or Cmd + K</kbd> + <br><br> + </li> + </ul> + <p style="margin-top: -7px;">Any links, images or other markup will be kept as plain text. Also, free speech is good and all, but please keep it civil. Sufficient JavaScript support will be required to bind shortkeys. Please type the markup manually if you are on an older browser.</p> + </div> </div> {% else %} <div id="new-comment" class="mtsbitem"> @@ -82,5 +114,57 @@ document.getElementById('comment-body-' + id).style.display = 'block'; document.getElementById('edit-form-' + id).style.display = 'none'; } + + // We are aiming to keep the JS to ES3 for compatibility with older browsers. + // Get all textareas and bind the shortkeys. + var textareas = document.getElementsByTagName('textarea'); + + for (var i = 0; i < textareas.length; i++) { + textareas[i].addEventListener('keydown', function(e) { + if (e.keyCode == 66 && (e.ctrlKey || e.metaKey)) { + e.preventDefault(); + var start = this.selectionStart; + var end = this.selectionEnd; + this.value = this.value.substring(0, start) + '**' + this.value.substring(start, end) + '**' + this.value.substring(end); + this.selectionStart = start + 2; + this.selectionEnd = end + 2; + } else if (e.keyCode == 73 && (e.ctrlKey || e.metaKey)) { + e.preventDefault(); + var start = this.selectionStart; + var end = this.selectionEnd; + this.value = this.value.substring(0, start) + '__' + this.value.substring(start, end) + '__' + this.value.substring(end); + this.selectionStart = start + 2; + this.selectionEnd = end + 2; + } else if (e.keyCode == 68 && (e.ctrlKey || e.metaKey)) { + e.preventDefault(); + var start = this.selectionStart; + var end = this.selectionEnd; + this.value = this.value.substring(0, start) + '~~' + this.value.substring(start, end) + '~~' + this.value.substring(end); + this.selectionStart = start + 2; + this.selectionEnd = end + 2; + } else if (e.keyCode == 75 && (e.ctrlKey || e.metaKey)) { + e.preventDefault(); + var start = this.selectionStart; + var end = this.selectionEnd; + this.value = this.value.substring(0, start) + '```' + this.value.substring(start, end) + '```' + this.value.substring(end); + this.selectionStart = start + 3; + this.selectionEnd = end + 3; + } + }); + } + + // Bind the tab key to indent textareas by 4 spaces. + for (var i = 0; i < textareas.length; i++) { + textareas[i].addEventListener('keydown', function(e) { + if (e.keyCode == 9) { + e.preventDefault(); + var start = this.selectionStart; + var end = this.selectionEnd; + this.value = this.value.substring(0, start) + ' ' + this.value.substring(end); + this.selectionStart = start + 4; + this.selectionEnd = end + 4; + } + }); + } </script> {% endblock %} |
