Get editing working
authorDavid Kerkeslager <kerkeslager@gmail.com>
Fri, 3 Jul 2020 02:27:06 +0000 (22:27 -0400)
committerDavid Kerkeslager <kerkeslager@gmail.com>
Fri, 3 Jul 2020 02:27:06 +0000 (22:27 -0400)
txt_house/models.py
txt_house/templates/base.html
txt_house/templates/edit.html [new file with mode: 0644]
txt_house/templates/index.html
txt_house/templates/text_file.html [new file with mode: 0644]
txt_house/urls.py
txt_house/views.py

index 2f57ca0..f394478 100644 (file)
@@ -9,11 +9,19 @@ class TextFile(models.Model):
     text = models.TextField()
     edit_key = models.CharField(max_length=43)
 
-    def get_absolute_url(self):
-        pk = base64.urlsafe_b64encode(
+    def get_pk(self):
+        return base64.urlsafe_b64encode(
             self.pk.to_bytes(math.ceil(self.pk.bit_length() / 8), 'big'),
         ).decode('utf-8')
 
+    def get_view_url(self):
+        pk = self.get_pk()
+
         return reverse('text-file', kwargs={ 'pk': pk })
 
+    def get_edit_url(self):
+        pk = self.get_pk()
+
+        return reverse('edit', kwargs={ 'pk': pk })
+
 admin.site.register(TextFile)
index 1856c0d..a9fb172 100644 (file)
@@ -4,6 +4,121 @@
     <meta charset="utf-8"/>
     <meta name="viewport" content="width=device-width, initial-scale=1"/>
     <title>txt.house</title>
+
+    <style>
+      html {
+        margin: 0;
+        padding: 0;
+
+        font-size: 16px;
+
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+      }
+
+      body {
+        margin: 0 3rem;
+        padding: 0;
+
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+
+        font-family: Candara, Roboto, "Lucida Sans Unicode", "Lucida Grande", Verdana, sans-serif;
+
+        width: calc(100% - 6rem);
+        max-width: 40rem;
+      }
+
+      nav {
+        display: flex;
+        flex-direction: row;
+        align-items: center;
+
+        width: 40rem;
+        height: 5rem;
+      }
+
+      nav a {
+        margin-right: 2rem;
+      }
+
+      nav a:active,
+      nav a:hover,
+      nav a:link,
+      nav a:visited {
+        color: black;
+      }
+
+      nav label {
+        margin-right: 0.2rem;
+      }
+
+      nav select {
+        border: none;
+        font-size: 16px;
+        font-family: Candara, Roboto, "Lucida Sans Unicode", "Lucida Grande", Verdana, sans-serif;
+      }
+
+      form.editor {
+        margin-top: 3.6rem;
+        width: 100%;
+
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+      }
+
+      form.editor span {
+        display: flex;
+        flex-direction: row;
+        align-items: center;
+
+        margin-bottom: 1rem;
+        width: 100%;
+      }
+
+      form.editor label {
+        margin-right: 0.5rem;
+      }
+
+      form.editor input[type=text] {
+        flex-grow: 1;
+        border: 1.5px solid black;
+        border-radius: 0;
+        padding: 0.3rem;
+      }
+
+      form.editor textarea {
+        width: calc(100% - 3px - 1rem);
+        height: 20rem;
+
+        border: 1.5px solid black;
+
+        padding: 0.5rem;
+        margin: 0;
+        margin-bottom: 3rem;
+      }
+
+      form.editor input[type=submit] {
+        border: 1.5px solid black;
+        border-radius: 8px;
+        padding: 1rem;
+
+        font-family: Roboto, "Lucida Sans Unicode", "Lucida Grande", Verdana, sans-serif;
+        font-size: 16px;
+
+        color: black;
+        background: white;
+
+        cursor: pointer;
+      }
+
+      form.editor input[type=submit]:hover {
+        background: #dddddd;
+      }
+    </style>
   </head>
 
   <body>
diff --git a/txt_house/templates/edit.html b/txt_house/templates/edit.html
new file mode 100644 (file)
index 0000000..ba59df2
--- /dev/null
@@ -0,0 +1,61 @@
+{% extends "base.html" %}
+
+{% block body %}
+
+<nav>
+  <a href='{% url "index" %}' title='Home'>Home</a>
+  <a href='{{ text_file.get_view_url }}' title='View'>View</a>
+</nav>
+
+<style>
+  .copy-area {
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+
+    margin-top: 1.5rem;
+  }
+
+  .copy-area label {
+    font-weight: bold;
+    margin-right: 1rem;
+  }
+
+  small {
+    margin-top: 0.5rem;
+  }
+
+  .error {
+    color: #bb0000;
+    text-align: center;
+    font-weight: bold;
+  }
+</style>
+
+{% if edit_key %}
+  <span class='copy-area'>
+    <label for='edit-key-display'>Edit key:</label>
+    <span id='edit-key-display'>{{ edit_key }}</span>
+  </span>
+  <small>Save this key in order to edit this file in the future.</small>
+{% endif %}
+
+<form class='editor' action='{{ text_file.get_edit_url }}' method='post'>
+  {% csrf_token %}
+  {% if edit_key %}
+    <input id='edit_key' name='edit_key' type='hidden' value='{{ edit_key }}'></input>
+  {% else %}
+    <span>
+      <label for='edit_key'>Edit key:</label>
+      <input id='edit_key' name='edit_key' type='text'></input>
+    </span>
+
+    {% if incorrect_key %}
+    <span class='error'>You must enter the correct edit key to save your changes.</span>
+    {% endif %}
+  {% endif %}
+  <textarea name='text'>{{ text }}</textarea>
+  <input type='submit'></input>
+</form>
+
+{% endblock %}
index fcd6fe9..8d8535a 100644 (file)
@@ -1,9 +1,24 @@
 {% extends 'base.html' %}
 
 {% block body %}
-<form action='create' method='post'>
+<style>
+  h1 {
+    margin: 3rem 0;
+  }
+
+  p {
+    margin: 0;
+  }
+</style>
+
+<h1>txt.house</h1>
+
+<p>Paste or edit in text and submit to get a permanent link for it.</p>
+
+<form class='editor' action='{% url "create" %}' method='post'>
   {% csrf_token %}
   <textarea name='text'></textarea>
   <input type='submit'></input>
 </form>
+
 {% endblock %}
diff --git a/txt_house/templates/text_file.html b/txt_house/templates/text_file.html
new file mode 100644 (file)
index 0000000..6961756
--- /dev/null
@@ -0,0 +1,87 @@
+{% extends 'base.html' %}
+
+{% block body %}
+  <style>
+    .sans {
+      font-family: Candara, Roboto, "Lucida Sans Unicode", "Lucida Grande", Verdana, sans-serif;
+      line-height: 1.6;
+    }
+
+    .serif {
+      font-family: Bookman, Garamond, "Palatino Linotype", "Book Antiqua", Palatino, serif;
+      font-size: 1.2rem;
+      line-height: 1.6;
+    }
+
+    .mono {
+      font-family: Monaco, "Courier New", Courier, monospace;
+      line-height: 1.6;
+    }
+
+    main {
+      max-width: 40rem;
+      white-space: pre-wrap;
+      margin: 0 0 3rem;
+    }
+  </style>
+
+  <nav>
+    <a href='{% url "index" %}' title='Home'>Home</a>
+    <a href='{{ text_file.get_edit_url }}' title='Edit'>Edit</a>
+
+    <label for='font-select'>Font:</label>
+
+    <select id='font-select'>
+      <option value='sans' {% if font == 'sans' %}selected{% endif %}>Sans</option>
+      <option value='serif' {% if font == 'serif' %}selected{% endif %}>Serif</option>
+      <option value='mono' {% if font == 'mono' %}selected{% endif %}>Mono</option>
+    </select>
+  </nav>
+
+  <script type='text/javascript'>
+    function updateQueryParameter(key, value) {
+      if(window.location.search === '') {
+        window.location.search = '?' + key + '=' + value;
+        return;
+      }
+
+      var queryParameters = window.location.search.substring(1).split('&').map(function(kvp) {
+        return kvp.split('=');
+      });
+
+      queryParameters.forEach(function(kvp) {
+        if(kvp[0] === key) {
+          kvp[1] = value;
+        }
+      });
+
+      window.location.search = '?' + queryParameters.map(function(kvp) {
+        return kvp.join('=');
+      }).join('&');
+    }
+
+    (function(fn) {
+      if (document.readyState != 'loading'){
+        fn();
+      } else {
+        document.addEventListener('DOMContentLoaded', fn);
+      }
+    })(function() {
+      var main = document.querySelector('main');
+      var fontSelect = document.querySelector('#font-select');
+
+      fontSelect.addEventListener('change', function(e) {
+        updateQueryParameter('font', e.target.value);
+
+        main.classList.add(e.target.value);
+
+        ['mono', 'sans', 'serif'].forEach(function(c) {
+          if(c != e.target.value) main.classList.remove(c);
+        });
+      });
+    });
+  </script>
+
+  <main class='{{ font }}'>{{ text }}</main>
+
+{% endblock %}
index a58345d..1665870 100644 (file)
@@ -23,4 +23,5 @@ urlpatterns = [
     path('', views.index, name='index'),
     path('create', views.create, name='create'),
     path('t/<str:pk>', views.text_file, name='text-file'),
+    path('t/<str:pk>/edit', views.edit, name='edit'),
 ]
index 5a8f922..7a1f078 100644 (file)
@@ -2,7 +2,7 @@ import base64
 import secrets
 
 from django.http import HttpResponse, Http404
-from django.shortcuts import get_object_or_404, redirect, render
+from django.shortcuts import get_object_or_404, render
 
 from . import models
 
@@ -21,7 +21,15 @@ def create(request):
     tf = models.TextFile(text=text, edit_key=secrets.token_urlsafe())
     tf.save()
 
-    return redirect(tf.get_absolute_url())
+    return render(
+        request,
+        'edit.html',
+        {
+            'text_file': tf,
+            'text': tf.text,
+            'edit_key': tf.edit_key,
+        },
+    )
 
 def text_file(request, pk):
     if request.method != 'GET':
@@ -31,4 +39,49 @@ def text_file(request, pk):
 
     tf = get_object_or_404(models.TextFile, pk=pk_int)
 
-    return HttpResponse(tf.text)
+    if request.GET.get('raw'):
+        return HttpResponse(tf.text, content_type='text/plain')
+
+    font = request.GET.get('font', 'sans')
+
+    return render(
+        request,
+        'text_file.html',
+        {
+            'text_file': tf,
+            'font': font,
+            'text': tf.text.strip(),
+        },
+    )
+
+def edit(request, pk):
+    pk_int = int.from_bytes(base64.urlsafe_b64decode(pk), 'big')
+
+    tf = get_object_or_404(models.TextFile, pk=pk_int)
+
+    context = {
+        'text_file': tf,
+    }
+
+    if request.method == 'POST':
+        edit_key = request.POST.get('edit_key','')
+        text = request.POST.get('text','')
+
+        context['text'] = text
+
+        if secrets.compare_digest(edit_key, tf.edit_key):
+            context['edit_key'] = edit_key
+            tf.text = text
+            tf.save()
+
+        else:
+            context['incorrect_key'] = True
+
+    elif request.method == 'GET':
+        context['text'] = tf.text
+
+    return render(
+        request,
+        'edit.html',
+        context,
+    )