aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorluxagraf <sng@luxagraf.net>2018-11-24 22:29:02 -0600
committerluxagraf <sng@luxagraf.net>2018-11-24 22:29:02 -0600
commit0c2a092e8d8ad33a1c306ee9efca0da96eb56415 (patch)
tree0aaf48f20771c97ec30e005ef818ef6ce4856097
parent7a284139f6b97bb06548e69d47eef188bc99099d (diff)
way to much for a single commit
-rw-r--r--apps/accounts/migrations/0001_initial.py2
-rw-r--r--apps/accounts/models.py2
-rw-r--r--apps/notes/admin.py6
-rw-r--r--apps/notes/forms.py4
-rw-r--r--apps/notes/migrations/0001_initial.py57
-rw-r--r--apps/notes/migrations/0002_auto_20181111_1825.py31
-rw-r--r--apps/notes/migrations/0003_note_folder.py19
-rw-r--r--apps/notes/migrations/0004_auto_20181117_2039.py27
-rw-r--r--apps/notes/migrations/0005_auto_20181119_1145.py28
-rw-r--r--apps/notes/models.py59
-rw-r--r--apps/notes/serializers.py10
-rw-r--r--apps/notes/tests/test_models.py24
-rw-r--r--apps/notes/tests/test_views.py32
-rw-r--r--apps/notes/urls.py20
-rw-r--r--apps/notes/views.py68
-rw-r--r--apps/utils/util.py23
-rw-r--r--config/base_urls.py32
-rw-r--r--config/requirements.txt2
-rw-r--r--config/settings.py24
-rw-r--r--design/sass/_forms.scss34
-rw-r--r--design/sass/_global.scss7
-rw-r--r--design/sass/_header.scss1
-rw-r--r--design/sass/_mixins.scss1
-rw-r--r--design/sass/_notes.scss71
-rw-r--r--design/sass/screenv1.scss2
-rw-r--r--design/templates/base.html29
-rw-r--r--design/templates/notes/create.html18
-rw-r--r--design/templates/notes/notes_create.html52
-rw-r--r--design/templates/notes/notes_detail.html78
-rw-r--r--design/templates/notes/notes_list.html2
-rw-r--r--design/templates/pages/page.html16
-rwxr-xr-xminify.py5
-rw-r--r--scripts/util.js76
33 files changed, 614 insertions, 248 deletions
diff --git a/apps/accounts/migrations/0001_initial.py b/apps/accounts/migrations/0001_initial.py
index 0e2775e..36aa6ba 100644
--- a/apps/accounts/migrations/0001_initial.py
+++ b/apps/accounts/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.1.2 on 2018-11-02 19:01
+# Generated by Django 2.1.2 on 2018-11-24 04:41
from django.conf import settings
import django.contrib.auth.models
diff --git a/apps/accounts/models.py b/apps/accounts/models.py
index a930081..5ed7634 100644
--- a/apps/accounts/models.py
+++ b/apps/accounts/models.py
@@ -15,6 +15,8 @@ class UserProfile(models.Model):
website = models.CharField(max_length=300, null=True, blank=True, default='')
location = models.CharField(max_length=300, null=True, blank=True, default='')
bio = models.TextField(null=True, blank=True, default='')
+ #default_note_public = models.BooleanField(default=False)
+ #default_note_folder = models.ForeignKey('notes.Folder', null=True, on_delete=models.SET_NULL)
def __str__(self):
return self.user.username
diff --git a/apps/notes/admin.py b/apps/notes/admin.py
index dbac05c..3958d55 100644
--- a/apps/notes/admin.py
+++ b/apps/notes/admin.py
@@ -1,6 +1,6 @@
from django.contrib import admin
-from .models import Note, Folder
+from .models import Note, Notebook
@admin.register(Note)
@@ -8,6 +8,6 @@ class NoteAdmin(admin.ModelAdmin):
pass
-@admin.register(Folder)
-class FolderAdmin(admin.ModelAdmin):
+@admin.register(Notebook)
+class NotebookAdmin(admin.ModelAdmin):
pass
diff --git a/apps/notes/forms.py b/apps/notes/forms.py
index 6a27ec9..a7a4f8d 100644
--- a/apps/notes/forms.py
+++ b/apps/notes/forms.py
@@ -7,9 +7,9 @@ from .models import Note
class NoteForm(forms.ModelForm):
class Meta:
model = Note
- fields = ['title', 'body_markdown', 'url', 'tags']
+ fields = ['title', 'body_text', 'body_html', 'body_qjson', 'notebook', 'url', 'tags']
labels = {
- "body_markdown": _("Note"),
+ "body": _("Note"),
}
def __init__(self, *args, **kwargs):
diff --git a/apps/notes/migrations/0001_initial.py b/apps/notes/migrations/0001_initial.py
index 3f04eb7..f8e2fff 100644
--- a/apps/notes/migrations/0001_initial.py
+++ b/apps/notes/migrations/0001_initial.py
@@ -1,9 +1,11 @@
-# Generated by Django 2.1.2 on 2018-11-11 18:01
+# Generated by Django 2.1.2 on 2018-11-24 13:55
from django.conf import settings
+import django.contrib.postgres.fields.jsonb
from django.db import migrations, models
import django.db.models.deletion
import taggit.managers
+import uuid
class Migration(migrations.Migration):
@@ -11,28 +13,61 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
- migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('taggit', '0002_auto_20150616_2121'),
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
- name='Folder',
+ name='Note',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('name', models.CharField(max_length=250)),
- ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+ ('unique_id', models.UUIDField(default=uuid.uuid4, editable=False)),
+ ('date_created', models.DateTimeField(auto_now_add=True)),
+ ('date_updated', models.DateTimeField(auto_now=True)),
+ ('title', models.CharField(max_length=250)),
+ ('body_text', models.TextField(null=True)),
+ ('body_html', models.TextField(blank=True, null=True)),
+ ('body_qjson', django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True)),
+ ('url', models.CharField(blank=True, max_length=250, null=True)),
+ ('slug', models.SlugField(blank=True)),
+ ('is_public', models.BooleanField(default=False)),
],
),
migrations.CreateModel(
- name='Note',
+ name='Notebook',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('title', models.CharField(max_length=250)),
- ('body_markdown', models.TextField(null=True)),
- ('url', models.CharField(max_length=250)),
- ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
- ('tags', taggit.managers.TaggableManager(blank=True, help_text='Tags', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags')),
+ ('unique_id', models.UUIDField(default=uuid.uuid4, editable=False)),
+ ('name', models.CharField(max_length=250)),
+ ('url', models.CharField(blank=True, max_length=250, null=True)),
+ ('slug', models.SlugField(blank=True)),
+ ('date_created', models.DateTimeField(auto_now_add=True)),
+ ('date_updated', models.DateTimeField(auto_now=True)),
+ ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
+ migrations.AddField(
+ model_name='note',
+ name='notebook',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='notes.Notebook'),
+ ),
+ migrations.AddField(
+ model_name='note',
+ name='owner',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
+ ),
+ migrations.AddField(
+ model_name='note',
+ name='tags',
+ field=taggit.managers.TaggableManager(blank=True, help_text='Tags', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'),
+ ),
+ migrations.AlterUniqueTogether(
+ name='notebook',
+ unique_together={('owner', 'slug')},
+ ),
+ migrations.AlterUniqueTogether(
+ name='note',
+ unique_together={('owner', 'slug')},
+ ),
]
diff --git a/apps/notes/migrations/0002_auto_20181111_1825.py b/apps/notes/migrations/0002_auto_20181111_1825.py
deleted file mode 100644
index a7bc1c7..0000000
--- a/apps/notes/migrations/0002_auto_20181111_1825.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# Generated by Django 2.1.2 on 2018-11-11 18:25
-
-import datetime
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('notes', '0001_initial'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='note',
- name='date_created',
- field=models.DateTimeField(blank=True, default=datetime.datetime(2018, 11, 11, 18, 25, 29, 645561), editable=False),
- preserve_default=False,
- ),
- migrations.AddField(
- model_name='note',
- name='date_updated',
- field=models.DateTimeField(blank=True, default=datetime.datetime(2018, 11, 11, 18, 25, 42, 867666), editable=False),
- preserve_default=False,
- ),
- migrations.AddField(
- model_name='note',
- name='slug',
- field=models.SlugField(blank=True, unique=True),
- ),
- ]
diff --git a/apps/notes/migrations/0003_note_folder.py b/apps/notes/migrations/0003_note_folder.py
deleted file mode 100644
index d548429..0000000
--- a/apps/notes/migrations/0003_note_folder.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 2.1.2 on 2018-11-14 15:41
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('notes', '0002_auto_20181111_1825'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='note',
- name='folder',
- field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='notes.Folder'),
- ),
- ]
diff --git a/apps/notes/migrations/0004_auto_20181117_2039.py b/apps/notes/migrations/0004_auto_20181117_2039.py
deleted file mode 100644
index 6fc6f2d..0000000
--- a/apps/notes/migrations/0004_auto_20181117_2039.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# Generated by Django 2.1.2 on 2018-11-18 02:39
-
-import datetime
-from django.db import migrations, models
-from django.utils.timezone import utc
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('notes', '0003_note_folder'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='folder',
- name='date_created',
- field=models.DateTimeField(blank=True, default=datetime.datetime(2018, 11, 18, 2, 38, 45, 996162, tzinfo=utc), editable=False),
- preserve_default=False,
- ),
- migrations.AddField(
- model_name='folder',
- name='date_updated',
- field=models.DateTimeField(blank=True, default=datetime.datetime(2018, 11, 18, 2, 39, 0, 850658, tzinfo=utc), editable=False),
- preserve_default=False,
- ),
- ]
diff --git a/apps/notes/migrations/0005_auto_20181119_1145.py b/apps/notes/migrations/0005_auto_20181119_1145.py
deleted file mode 100644
index 9b6cee7..0000000
--- a/apps/notes/migrations/0005_auto_20181119_1145.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# Generated by Django 2.1.2 on 2018-11-19 17:45
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('notes', '0004_auto_20181117_2039'),
- ]
-
- operations = [
- migrations.RenameField(
- model_name='note',
- old_name='body_markdown',
- new_name='body',
- ),
- migrations.AddField(
- model_name='note',
- name='is_public',
- field=models.BooleanField(default=False),
- ),
- migrations.AddField(
- model_name='note',
- name='rendered_body',
- field=models.TextField(null=True),
- ),
- ]
diff --git a/apps/notes/models.py b/apps/notes/models.py
index 318b079..83766a9 100644
--- a/apps/notes/models.py
+++ b/apps/notes/models.py
@@ -1,48 +1,67 @@
+import uuid
from django.db import models
from django.utils import timezone
+from django.utils.functional import cached_property
from django.template.defaultfilters import slugify
from django.urls import reverse
+from django.contrib.postgres.fields import JSONField
from taggit.managers import TaggableManager
from django.conf import settings
+from utils.util import unique_slug_generator
-class Folder(models.Model):
- created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
+
+class Notebook(models.Model):
+ unique_id = models.UUIDField(default=uuid.uuid4, editable=False)
+ owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
name = models.CharField(max_length=250)
- date_created = models.DateTimeField(blank=True, editable=False)
- date_updated = models.DateTimeField(blank=True, editable=False)
+ url = models.CharField(max_length=250, null=True, blank=True)
+ slug = models.SlugField(blank=True)
+ date_created = models.DateTimeField(blank=True, auto_now_add=True, editable=False)
+ date_updated = models.DateTimeField(blank=True, auto_now=True, editable=False)
+
+ class Meta:
+ unique_together = ("owner", "slug")
def __str__(self):
return self.name
+ def save(self, **kwargs):
+ if self._state.adding:
+ self.slug = unique_slug_generator(self)
+ super(Notebook, self).save()
+
class Note(models.Model):
- created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
- date_created = models.DateTimeField(blank=True, editable=False)
- date_updated = models.DateTimeField(blank=True, editable=False)
+ unique_id = models.UUIDField(default=uuid.uuid4, editable=False)
+ owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
+ date_created = models.DateTimeField(blank=True, auto_now_add=True, editable=False)
+ date_updated = models.DateTimeField(blank=True, auto_now=True, editable=False)
title = models.CharField(max_length=250)
- body = models.TextField(null=True)
- rendered_body = models.TextField(null=True)
- url = models.CharField(max_length=250)
- slug = models.SlugField(unique=True, blank=True)
- folder = models.ForeignKey(Folder, null=True, on_delete=models.SET_NULL)
+ body_text = models.TextField(null=True)
+ body_html = models.TextField(null=True, blank=True)
+ body_qjson = JSONField(null=True, blank=True)
+ url = models.CharField(max_length=250, null=True, blank=True)
+ slug = models.SlugField(blank=True)
+ notebook = models.ForeignKey(Notebook, null=True, blank=True, on_delete=models.SET_NULL)
tags = TaggableManager(blank=True, help_text='Tags')
is_public = models.BooleanField(default=False)
+ class Meta:
+ unique_together = ("owner", "slug")
+
def __str__(self):
return self.title
+ @cached_property
def get_absolute_url(self):
- return reverse("notes:notes-detail", kwargs={"pk": self.pk})
+ return reverse("notes:note-detail", kwargs={"user": self.owner.username, "slug": self.slug})
def save(self, **kwargs):
- # On save, update timestamps (users updated through admin.py)
- if not self.id:
- self.date_created = timezone.now()
- self.slug = slugify(self.title)[:50]
- if not self.title:
- self.title = str(self.date_created)
- self.date_updated = timezone.now()
+ if not self.title:
+ self.title = str(self.body_text)[:50]
+ if self._state.adding:
+ self.slug = unique_slug_generator(self)
super(Note, self).save()
diff --git a/apps/notes/serializers.py b/apps/notes/serializers.py
index 0929e79..2fbcc1f 100644
--- a/apps/notes/serializers.py
+++ b/apps/notes/serializers.py
@@ -1,18 +1,18 @@
from rest_framework import serializers
from taggit_serializer.serializers import TagListSerializerField, TaggitSerializer
-from .models import Note, Folder
+from .models import Note, Notebook
-class NoteSerializer(TaggitSerializer, serializers.HyperlinkedModelSerializer):
+class NoteSerializer(TaggitSerializer, serializers.ModelSerializer):
tags = TagListSerializerField()
class Meta:
model = Note
- fields = ('title', 'body', 'url', 'tags')
+ fields = ('title', 'body_text', 'body_qjson', 'body_html', 'url', 'notebook', 'tags')
-class FolderSerializer(serializers.HyperlinkedModelSerializer):
+class NotebookSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
- model = Folder
+ model = Notebook
fields = ('name',)
diff --git a/apps/notes/tests/test_models.py b/apps/notes/tests/test_models.py
index ddb731e..cf21aca 100644
--- a/apps/notes/tests/test_models.py
+++ b/apps/notes/tests/test_models.py
@@ -1,32 +1,32 @@
from django.test import TestCase
from mixer.backend.django import mixer
-from notes.models import Note, Folder
+from notes.models import Note, Notebook
from accounts.models import User
-class FolderModelTest(TestCase):
+class NotebookModelTest(TestCase):
def test_string_representation(self):
user = mixer.blend(User, username='tpynchon')
- folder = Folder(created_by=user, name="My Folder")
- self.assertEqual(str(folder), "My Folder")
+ notebook = Notebook(owner=user, name="San Miguel Notes")
+ self.assertEqual(str(notebook), "San Miguel Notes")
class NoteModelTest(TestCase):
def setUp(self):
- self.user = mixer.blend(User, username='test')
+ self.user = mixer.blend(User, username='tpynchon')
self.note = Note.objects.create(
- created_by=self.user,
+ owner=self.user,
title="test note",
- body="the body of the note",
+ body_text="the body of the note",
url="https://luxagraf.net/",
tags="mine,cool site"
)
self.note.save()
self.note_no_title = Note.objects.create(
- created_by=self.user,
- body="the body of the note",
+ owner=self.user,
+ body_text="the body of the note",
url="https://luxagraf.net/",
tags="mine,cool site"
)
@@ -34,11 +34,11 @@ class NoteModelTest(TestCase):
def test_string_representation(self):
self.assertEqual(str(self.note), "test note")
- self.assertEqual(str(self.note.body), "the body of the note")
+ self.assertEqual(str(self.note.body_text), "the body of the note")
self.assertEqual(str(self.note.url), "https://luxagraf.net/")
self.assertEqual(str(self.note.tags), "mine,cool site")
# titleless note gets date
- self.assertEqual(str(self.note_no_title), str(self.note_no_title.date_created))
+ self.assertEqual(str(self.note_no_title), str(self.note_no_title.body_text)[:50])
def test_get_absolute_url(self):
- self.assertEqual(str(self.note.get_absolute_url()), "/notes/%s/" % (self.note.id))
+ self.assertEqual(str(self.note.get_absolute_url), "/notes/%s/%s" % (self.note.owner.username, self.note.slug))
diff --git a/apps/notes/tests/test_views.py b/apps/notes/tests/test_views.py
index 05cb8db..4f9a4ce 100644
--- a/apps/notes/tests/test_views.py
+++ b/apps/notes/tests/test_views.py
@@ -2,6 +2,7 @@ import json
from django.test import Client
from django.test import RequestFactory, TestCase
from django.urls import reverse
+from django.template.defaultfilters import slugify
from rest_framework.test import force_authenticate
from rest_framework.test import APIRequestFactory
@@ -25,9 +26,9 @@ class NotesViewsTest(TestCase):
self.user = User.objects.create(username='testuser', password='password')
self.bad_user = User.objects.create(username='someoneelse', password='password')
self.note = Note.objects.create(
- created_by=self.user,
+ owner=self.user,
title="test note",
- body="the body of the note",
+ body_text="the body of the note",
url="https://luxagraf.net/",
)
self.note.tags.add("mine,cool site")
@@ -40,6 +41,19 @@ class NotesViewsTest(TestCase):
self.assertEqual(response.status_code, 200)
# bad_user
+ def test_note_create_view(self):
+ data = {
+ 'title': "test note post",
+ 'body_text': "the body of the note",
+ 'url': "https://luxagraf.net/",
+ 'tags': [],
+ }
+ self.client.force_login(self.user)
+ url = reverse("notes:note-create")
+ response = self.client.post(url, data)
+ self.assertEqual(response.status_code, 302)
+ self.assertRedirects(response, '/notes/%s/%s' % (self.user.username, slugify(data['title'])))
+
def test_api_list(self):
# Make an authenticated request to the view...
request = self.factory.get('/api/notes/')
@@ -51,36 +65,36 @@ class NotesViewsTest(TestCase):
self.assertEqual(api_data['title'], 'test note')
self.assertEqual(api_data['tags'], ['mine,cool site'])
- def test_note_create(self):
+ def test_api_note_create(self):
'''
- post some data to create a new note
+ Post some data to create a new note
'''
data = {
'title': "test note post",
- 'body': "the body of the note",
+ 'body_text': "the body of the note",
'url': "https://luxagraf.net/",
'tags': [],
}
self.apiclient.force_authenticate(self.user)
- url = reverse("notes:notes-list")
+ url = reverse("notes-api-list")
response = self.apiclient.post(url, data, format='json')
self.assertEqual(response.status_code, 201)
self.assertEqual(Note.objects.count(), 2)
response.render()
api_data = json.loads(response.content.decode('utf8'))
self.assertEqual(api_data['title'], 'test note post')
- self.assertEqual(api_data['body'], 'the body of the note')
+ self.assertEqual(api_data['body_text'], 'the body of the note')
self.assertEqual(api_data['tags'], [])
def test_note_create_bad(self):
# create another user
data = {
'title': "",
- 'body': "the body of the note",
+ 'body_text': "the body of the note",
'url': "https://luxagraf.net/",
'tags': [],
}
- url = reverse("notes:notes-list")
+ url = reverse("notes-api-list")
self.apiclient.force_authenticate(self.user)
response = self.apiclient.post(url, data, format='json')
self.assertEqual(response.status_code, 400)
diff --git a/apps/notes/urls.py b/apps/notes/urls.py
index cbb1884..ccfcc9e 100644
--- a/apps/notes/urls.py
+++ b/apps/notes/urls.py
@@ -1,17 +1,17 @@
from django.urls import path
-from django.conf.urls import include
-from rest_framework import routers
-
-from .views import NoteViewSet, FolderViewSet, NoteListView
-
-router = routers.DefaultRouter()
-router.register(r'notes/folder', FolderViewSet, basename="folder")
-router.register(r'notes', NoteViewSet, basename="notes")
+from .views import (
+ NoteDetailView,
+ NoteCreateView,
+ NoteListView,
+ NoteListRedirectView,
+)
app_name = "notes"
urlpatterns = [
- path(r'', NoteListView.as_view(), name='homepage',),
- path(r'', include(router.urls)),
+ path(r'create/', NoteCreateView.as_view(), name='note-create',),
+ path(r'<str:user>/<slug>', NoteDetailView.as_view(), name='note-detail',),
+ path(r'<str:user>/', NoteListView.as_view(), name='note-list',),
+ path(r'', NoteListRedirectView.as_view(), name='note-redirect',),
]
diff --git a/apps/notes/views.py b/apps/notes/views.py
index e4b8fda..b971390 100644
--- a/apps/notes/views.py
+++ b/apps/notes/views.py
@@ -1,21 +1,23 @@
from django.views.generic import CreateView, ListView, UpdateView, DeleteView
from django.views.generic.detail import DetailView
+from django.views.generic.base import View, RedirectView
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404, render, redirect
-from django.urls import reverse
+from django.urls import reverse, reverse_lazy
from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework.decorators import list_route
from rest_framework import permissions
-from .serializers import NoteSerializer, FolderSerializer
-from .models import Note, Folder
+from .serializers import NoteSerializer, NotebookSerializer
+from .models import Note, Notebook
+from .forms import NoteForm
@method_decorator(login_required, name='dispatch')
-class LoggedInCreateViewWithUser(CreateView):
+class LoggedInViewWithUser(View):
def get_form_kwargs(self, **kwargs):
kwargs = super().get_form_kwargs(**kwargs)
@@ -23,20 +25,56 @@ class LoggedInCreateViewWithUser(CreateView):
return kwargs
-class NoteListView(ListView):
+class NoteListView(LoggedInViewWithUser, ListView):
model = Note
def get_queryset(self):
if not self.request.user.is_anonymous:
- return Note.objects.filter(created_by=self.request.user)
+ return Note.objects.filter(owner=self.request.user)
def get_template_names(self):
+ # print("IP Address for debug-toolbar: " + self.request.META['REMOTE_ADDR'])
if not self.request.user.is_anonymous:
return ['notes/notes_list.html']
else:
return ['sell.html']
+class NoteListRedirectView(RedirectView, LoggedInViewWithUser):
+
+ def get_redirect_url(self, *args, **kwargs):
+ return reverse_lazy("notes:note-list", kwargs={"user": self.request.user.username})
+
+
+class NoteDetailView(UpdateView, LoggedInViewWithUser):
+ model = Note
+ form_class = NoteForm
+ template_name = 'notes/notes_detail.html'
+
+ def get_queryset(self):
+ if not self.request.user.is_anonymous:
+ return Note.objects.filter(owner=self.request.user)
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context['notes_list'] = Note.objects.filter(owner=self.request.user)
+ return context
+
+
+class NoteCreateView(CreateView, LoggedInViewWithUser):
+ model = Note
+ form_class = NoteForm
+ template_name = 'notes/notes_create.html'
+
+ def form_valid(self, form):
+ form.instance.owner = self.request.user
+ self.object = form.save()
+ return super(NoteCreateView, self).form_valid(form)
+
+ def get_success_url(self):
+ return reverse_lazy('notes:note-detail', kwargs={'user': self.request.user.username, 'slug': self.object.slug})
+
+
class IsOwnerOrDeny(permissions.BasePermission):
"""
Custom permission to only allow owners to post to their endpoint
@@ -52,15 +90,13 @@ class NoteViewSet(viewsets.ModelViewSet):
API endpoint that allows notes to be viewed or edited.
"""
serializer_class = NoteSerializer
- permission_classes = (permissions.IsAuthenticated, IsOwnerOrDeny,)
+ permission_classes = (permissions.IsAuthenticated,)
def get_queryset(self):
- return Note.objects.filter(created_by=self.request.user).order_by('-date_created')
+ return Note.objects.filter(owner=self.request.user).order_by('-date_created')
- @list_route(methods=['post'])
def perform_create(self, serializer):
- serializer.save(created_by=self.request.user)
- return super(NoteViewSet, self).perform_create(serializer)
+ serializer.save(owner=self.request.user)
def get_object(self):
obj = get_object_or_404(self.get_queryset(), pk=self.kwargs["pk"])
@@ -71,14 +107,14 @@ class NoteViewSet(viewsets.ModelViewSet):
return obj
-class FolderViewSet(viewsets.ModelViewSet):
+class NotebookViewSet(viewsets.ModelViewSet):
"""
- API endpoint that allows folder to be viewed or edited.
+ API endpoint that allows botebook to be viewed or edited.
"""
- serializer_class = FolderSerializer
+ serializer_class = NotebookSerializer
def get_queryset(self):
- return Folder.objects.filter(created_by=self.request.user).order_by('-date_created')
+ return Notebook.objects.filter(owner=self.request.user).order_by('-date_created')
def perform_create(self, serializer):
- serializer.save(created_by=self.request.user)
+ serializer.save(owner=self.request.user)
diff --git a/apps/utils/util.py b/apps/utils/util.py
index 0c089ee..899b73f 100644
--- a/apps/utils/util.py
+++ b/apps/utils/util.py
@@ -1,5 +1,8 @@
import re
+import random
+import string
from django.apps import apps
+from django.utils.text import slugify
from django.template.loader import render_to_string
from bs4 import BeautifulSoup
import markdown
@@ -82,3 +85,23 @@ def parse_video(s):
if soup.find('video'):
return True
return False
+
+
+def random_string_generator(size=10, chars=string.ascii_lowercase + string.digits):
+ return ''.join(random.choice(chars) for _ in range(size))
+
+
+def unique_slug_generator(instance, new_slug=None):
+ if new_slug is not None:
+ slug = new_slug
+ else:
+ slug = slugify(instance.title)
+ Klass = instance.__class__
+ qs_exists = Klass.objects.filter(slug=slug).exists()
+ if qs_exists:
+ new_slug = "{slug}-{randstr}".format(
+ slug=slug,
+ randstr=random_string_generator(size=4)
+ )
+ return unique_slug_generator(instance, new_slug=new_slug)
+ return slug
diff --git a/config/base_urls.py b/config/base_urls.py
index 84dd56d..953ceae 100644
--- a/config/base_urls.py
+++ b/config/base_urls.py
@@ -9,25 +9,39 @@ from django_registration.backends.activation.views import RegistrationView
from rest_framework import routers
from pages.views import PageDetailView
-from notes.views import NoteViewSet, FolderViewSet, NoteListView
from accounts.forms import UserForm
-
+from notes.views import (
+ NoteViewSet,
+ NotebookViewSet,
+ NoteListView,
+)
router = routers.DefaultRouter()
-router.register(r'<str:user>/notes/folder/', FolderViewSet, basename="folder-api")
-router.register(r'<str:user>/notes/', NoteViewSet, basename="notes-api")
-
+router.register(r'notes/notebook/', NotebookViewSet, basename="notebook-api")
+router.register(r'notes', NoteViewSet, basename="notes-api")
+ADMIN_URL = 'https://docs.djangoproject.com/en/dev/ref/contrib/admin/'
urlpatterns = static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
urlpatterns += [
- path('admin/', admin.site.urls),
+ path('admin/', RedirectView.as_view(url=ADMIN_URL)),
+ path('magux/', admin.site.urls),
path(r'accounts/', include('django_registration.backends.activation.urls')),
path(r'accounts/', include('django.contrib.auth.urls')),
path(r'register/', RegistrationView.as_view(form_class=UserForm), name='django_registration_register',),
path(r'settings/', include('accounts.urls')),
path(r'', include('django_registration.backends.activation.urls')),
path(r'', include('django.contrib.auth.urls')),
- path(r'', include('notes.urls')),
- path(r'<slug>', PageDetailView.as_view(), name="pages"),
- path(r'<path>/<slug>/', PageDetailView.as_view(), name="pages"),
+ path(r'', NoteListView.as_view(), name='homepage',),
+ path(r'notes/', include('notes.urls')),
path(r'api/', include(router.urls)),
+ path(r'<slug>', PageDetailView.as_view(), name="pages"),
+ #path(r'<path>/<slug>/', PageDetailView.as_view(), name="pages"),
path(r'api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]
+if settings.DEBUG:
+ import debug_toolbar
+ urlpatterns = [
+ path('__debug__/', include(debug_toolbar.urls)),
+
+ # For django versions before 2.0:
+ # url(r'^__debug__/', include(debug_toolbar.urls)),
+
+ ] + urlpatterns
diff --git a/config/requirements.txt b/config/requirements.txt
index fb66a23..cfe5b7f 100644
--- a/config/requirements.txt
+++ b/config/requirements.txt
@@ -7,6 +7,7 @@ confusable-homoglyphs==3.2.0
coverage==4.5.1
decorator==4.3.0
Django==2.1.2
+django-debug-toolbar==1.10.1
django-extensions==2.1.3
django-registration==3.0
django-storages==1.7.1
@@ -37,6 +38,7 @@ python-decouple==3.1
pytz==2018.7
requests==2.20.1
six==1.11.0
+sqlparse==0.2.4
text-unidecode==1.2
traitlets==4.3.2
urllib3==1.24.1
diff --git a/config/settings.py b/config/settings.py
index 52d2314..b95c18d 100644
--- a/config/settings.py
+++ b/config/settings.py
@@ -25,6 +25,7 @@ AUTH_PROFILE_MODULE = "accounts.UserProfile"
AUTH_USER_MODEL = "accounts.User"
DEFAULT_FILE_STORAGE = config('DEFAULT_FILE_STORAGE')
+INTERNAL_IPS = config('INTERNAL_IPS')
AWS_S3_OBJECT_PARAMETERS = {
'CacheControl': 'max-age=86400',
@@ -39,6 +40,9 @@ REGISTRATION_SALT = 'Astra inclinant, sed non obligant'
# Django Rest Framework
REST_FRAMEWORK = {
+ 'DEFAULT_AUTHENTICATION_CLASSES': (
+ 'rest_framework.authentication.SessionAuthentication',
+ ),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
)
@@ -60,6 +64,7 @@ THIRD_PARTY_APPS = [
'taggit_serializer',
'django_extensions',
'rest_framework',
+ 'debug_toolbar'
]
LOCAL_APPS = [
'utils',
@@ -75,6 +80,7 @@ MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
+ 'debug_toolbar.middleware.DebugToolbarMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
@@ -164,5 +170,23 @@ STATIC_ROOT = BASE_DIR+'/static/'
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR+'/media/'
+LOGOUT_REDIRECT_URL = "/"
+
EMAIL_BACKEND = "django.core.mail.backends.filebased.EmailBackend"
EMAIL_FILE_PATH = os.path.join(BASE_DIR, "sent_emails")
+
+LOGGING = {
+ 'version': 1,
+ 'disable_existing_loggers': False,
+ 'handlers': {
+ 'console': {
+ 'class': 'logging.StreamHandler',
+ },
+ },
+ 'loggers': {
+ 'django': {
+ 'handlers': ['console'],
+ 'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'),
+ },
+ },
+}
diff --git a/design/sass/_forms.scss b/design/sass/_forms.scss
index cac2442..d6b0931 100644
--- a/design/sass/_forms.scss
+++ b/design/sass/_forms.scss
@@ -86,6 +86,35 @@ table {
border: 1px solid $link_hover_color;
}
}
+.btn-small {
+ @include fontsize(10);
+ @include smcaps;
+}
+.btn-subtle {
+ padding: 3px 5px;
+ border: none; //1px solid $body_font_light;
+ color: $body_font_light !important;
+ background: white;
+ &:hover {
+ background: white;
+ border: none; //1px solid;
+ color: $link_color !important;
+ }
+}
+.btn-accent {
+ padding: 3px 5px;
+ border: 1px solid $text_accent;
+ background: $text_accent;
+ color: white !important;
+ outline: $text_accent;
+ &:hover {
+ background: $text_accent;
+ border: 1px solid $text_accent;
+ }
+}
+.btn-inline {
+ display: inline;
+}
.form-narrow {
margin: 0 auto;
max-width: 60%;
@@ -94,3 +123,8 @@ table {
@include fontsize(24);
@include fancy-serif;
}
+.highlight {
+ border: 1px solid lighten($body_font_light, 20);
+ border-radius: 4px;
+ padding: 6px;
+}
diff --git a/design/sass/_global.scss b/design/sass/_global.scss
index e852855..d394042 100644
--- a/design/sass/_global.scss
+++ b/design/sass/_global.scss
@@ -153,11 +153,11 @@ h3 {
}
.wrapper {
@include constrain_wide;
- margin-top: 5rem;
+ //margin-top: 5rem;
}
//************** Universals ************************
.hide {
- display: none;
+ display: none !important;
}
.strike {
@@ -183,6 +183,9 @@ h3 {
.sm {
max-width: 80px;
}
+.left-margin-2 {
+ margin-left: 2px;
+}
//************** other global classes ************************
.sans {
@include generic_sans;
diff --git a/design/sass/_header.scss b/design/sass/_header.scss
index 8b8c3da..7f98419 100644
--- a/design/sass/_header.scss
+++ b/design/sass/_header.scss
@@ -13,6 +13,7 @@ header {
}
}
.right {
+ margin-top: 4px;
float: right;
}
}
diff --git a/design/sass/_mixins.scss b/design/sass/_mixins.scss
index 1aeeb83..bfccc17 100644
--- a/design/sass/_mixins.scss
+++ b/design/sass/_mixins.scss
@@ -9,6 +9,7 @@ $headline_font_serif: Georgia, 'Times New Roman', serif;
$body_p_font: normal 100% / 1.5 "proxima-nova",helvetica,arial,sans-serif;
$body_font_color: #6a6a6a;
$body_font_light: #b3aeae;
+$text_accent: #1be223;
$archive_p_line_height: 1.6;
//$light;
diff --git a/design/sass/_notes.scss b/design/sass/_notes.scss
new file mode 100644
index 0000000..09d234a
--- /dev/null
+++ b/design/sass/_notes.scss
@@ -0,0 +1,71 @@
+.note-title {
+ @include fontsize(22);
+}
+.note-header {
+ @extend %clearfix;
+}
+.note-header-float {
+ width: 30%;
+ float: right;
+ text-align: right;
+}
+.note-time, .note-url {
+ text-align: right;
+ @include fancy-sans;
+ @include fontsize(13);
+}
+.note-container {
+ @include constrain(80%);
+}
+#note-body {
+ @include fancy-sans;
+ @include fontsize(15);
+}
+.inactive {
+ .ql-editor {
+ padding:0;
+ line-height:inherit;
+ p {
+ padding:inherit;
+ margin-bottom:10px;
+ }
+ }
+ .ql-toolbar {
+ display:none;
+ }
+ .ql-container.ql-snow {
+ border:none;
+ font-family:inherit;
+ font-size:inherit;
+ }
+}
+.note-list-container {
+ max-width: 300px;
+ ul {
+ padding: 0;
+ list-style-type: none;
+ }
+ li {
+ @include fontsize(13);
+ height: 4.5rem;
+ box-shadow: 0 -1px 0 #e7e2ee inset;
+ margin: 0;
+ a {
+ color: lighten($body_font_color, 15);
+ text-decoration: none;
+ }
+ }
+ h4 {
+ @include fontsize(15);
+ margin: 0;
+ padding: 8px 0 8px 6px;
+ font-weight: normal;
+ overflow: hidden;
+ white-space: nowrap;
+ }
+ .note-preview {
+ padding-left: 6px;
+ overflow: hidden;
+ white-space: nowrap;
+ }
+}
diff --git a/design/sass/screenv1.scss b/design/sass/screenv1.scss
index 73a1fd1..d54027e 100644
--- a/design/sass/screenv1.scss
+++ b/design/sass/screenv1.scss
@@ -6,3 +6,5 @@
@import "_footer.scss";
@import "_forms.scss";
@import "_modal.scss";
+@import "_breadcrumbs.scss";
+@import "_notes.scss";
diff --git a/design/templates/base.html b/design/templates/base.html
index 6b6ef8c..f14e5df 100644
--- a/design/templates/base.html
+++ b/design/templates/base.html
@@ -1,4 +1,4 @@
-<!DOCTYPE html>
+{% load static %}<!DOCTYPE html>
<!--[if lt IE 8]> <html class="lte8"> <![endif]-->
<!--[if IE 8]> <html class="ie8 lte8"> <![endif]-->
<!--[if IE 9]> <html class="ie9"> <![endif]-->
@@ -10,9 +10,10 @@
<meta property="og:description" content="Note taking for writers">
<meta property="og:site_name" content="Notes">
<meta property="og:image" content="">
-<link rel="stylesheet" href="/media/screenv1.css?{{now}}" type="text/css">
+<link rel="stylesheet" href="/media/screenv1.css?{%now "u"%}" type="text/css">
+{% block extrastyles %}{%endblock%}
<link rel="icon" type="image/png" href="">
-<link rel="manifest" href="/webmanifest.json">
+<!--<link rel="manifest" href="/webmanifest.json">-->
<link rel="apple-touch-icon" sizes="256x256" href="">
</head>
<body class="{% block bodyclass %}{% endblock %}">
@@ -37,11 +38,17 @@
</header>
</div>
<div class="wrapper">
+ <ul class="breadcrumb" id="breadcrumbs" itemscope itemtype="http://data-vocabulary.org/Breadcrumb">
+ <li>
+ <a href="/" title="home" itemprop="url"><span itemprop="title">Home</span></a>
+ </li>
+ {% block breadcrumbs %}{% endblock %}
+ </ul>
{% block content %}
{% endblock %}
</div>
<footer>
- <p>&copy; Luxagraf Software. Problems or questions? Contact <a href="{% url 'pages' slug='terms-of-service' %}" title="">support@notes.tld</a>.</p>
+ <p>&copy; Arkhangelsk Software. Problems or questions? Contact <a href="{% url 'pages' slug='terms-of-service' %}" title="">support@notes.tld</a>.</p>
<nav>
<ul>
<li><a href="{% url 'pages' slug='terms-of-service' %}" title="">Terms of Service</a></li>
@@ -56,21 +63,11 @@
{% block extra %}
{%endblock%}
<script async src="/media/js/package.min.js"></script>
+{% block jsinclude %}{%endblock%}
<script>
// Waiting for the DOM to load
document.addEventListener("DOMContentLoaded", function () {
- // Select your overlay trigger
- var trigger = document.querySelector('#overlay-trigger');
- trigger.addEventListener('click', function(e){
- e.preventDefault();
- novicell.overlay.create({
- 'selector': trigger.getAttribute('data-element'),
- 'class': 'selector-overlay',
- "onCreate": function() { console.log('created'); },
- "onLoaded": function() { console.log('loaded'); },
- "onDestroy": function() { console.log('Destroyed'); }
- });
- });
+ {% block jsdomready %}{%endblock%}
});
</script>
</body>
diff --git a/design/templates/notes/create.html b/design/templates/notes/create.html
deleted file mode 100644
index 3bd765d..0000000
--- a/design/templates/notes/create.html
+++ /dev/null
@@ -1,18 +0,0 @@
-{% extends 'base.html' %}
-{% block content %}
-<main>
- <h1>Create a new note</h1>
-<form action="" method="post">
-{% csrf_token %}
-{{ form.non_field_errors }}
-{% for field in form %}
-<fieldset {% if field.errors %}class="error"{%endif%}>
-{{field.label_tag}}
-{{field}}
-{% if field.errors %}{{field.errors}}{% endif %}
-</fieldset>
-{% endfor %}
-<p><input class="btn" value="submit" type="submit" /></p>
-</form>
-</main>
-{% endblock %}
diff --git a/design/templates/notes/notes_create.html b/design/templates/notes/notes_create.html
new file mode 100644
index 0000000..9bbdb26
--- /dev/null
+++ b/design/templates/notes/notes_create.html
@@ -0,0 +1,52 @@
+{% extends 'base.html' %}
+
+{% block extrastyles %}
+<link rel="stylesheet" href="/media/quill.snow.css" />
+{% endblock %}
+{% block content %}
+<main>
+ <h1>Create a new note</h1>
+ <form id="new-note-form" action="{% url 'notes:note-create' %}" method="post">
+{% csrf_token %}
+{{ form.non_field_errors }}
+{% for field in form %}
+<fieldset class="{% if field.errors %}error {%endif%}{% if field.name == 'body_qjson' or field.name == 'body_html' %}hide {%endif%}" id="fs-{{field.name}}" >
+{{field.label_tag}}
+{{field}}
+{% if field.errors %}{{field.errors}}{% endif %}
+</fieldset>
+{% if field.name == 'body_qjson' %}
+<div id="q-container">
+ <div id="note-body"></div>
+</div>
+{% endif %}
+{% endfor %}
+<p><input class="btn btn-inline" value="submit" type="submit" /></p>
+</form>
+</main>
+{% endblock %}
+
+{% block jsinclude %}
+<script src="/media/js/highlight.pack.js"></script>
+<script src="/media/js/quill.min.js"></script>
+{% endblock %}
+
+<script>
+{% block jsdomready %}
+ var note_text = document.getElementById('id_body_text');
+ note_text.innerHTML = "q";
+ var plaintext = document.getElementById("fs-body_text");
+ plaintext.classList.add('hide')
+ initQuill("#note-body");
+ var form = document.getElementById('new-note-form');
+ console.log(form);
+ form.onsubmit = function() {
+ var note_qjson = document.getElementById('id_body_qjson');
+ note_qjson.innerHTML= JSON.stringify(window.quill.getContents());
+ var note_html = document.getElementById('id_body_html');
+ note_html.innerHTML = window.quill.root.innerHTML;
+ var note_text = document.getElementById('id_body_text');
+ note_text.innerHTML = window.quill.getText();
+ };
+{% endblock %}
+</script>
diff --git a/design/templates/notes/notes_detail.html b/design/templates/notes/notes_detail.html
new file mode 100644
index 0000000..bf46ab8
--- /dev/null
+++ b/design/templates/notes/notes_detail.html
@@ -0,0 +1,78 @@
+{% extends 'base.html' %}
+{% block extrastyles %}
+<link rel="stylesheet" href="/media/quill.snow.css" />
+{% endblock %}
+{% block breadcrumbs %}
+<li><a href="{%url 'notes:note-list' user.username %}">Notes</a></li>
+{% endblock %}
+
+{% block content %}
+<main>
+ <article class="note-container">
+ <header class="note-header">
+ <button class="hide btn btn-accent" id="edit-toggle-btn">Edit</button>
+ <div class="note-header-float">
+ <h2 class="note-time">{{object.date_created|date:"M d, Y"}}</h2>
+ {% if object.url %}<h3 class="note-url"><a class="btn btn-small btn-subtle" href="{{object.url}}">Source</a><a class="btn btn-small btn-subtle left-margin-2" href="object.cache">Archive</a></h3>{% endif %}
+ </div>
+ </header>
+ <h1 id="note-title" class="note-title">{{object.title}}</h1>
+ <div id="q-container" class="inactive"><div id="note-body">{% if object.body_html %}{{object.body_html|safe}}{%else%}{{object.body_text}}{%endif%}</div></div>
+ <form action="" method="post" id="note-edit-form">{% csrf_token %}
+ {% for field in form %}{% if field.name in "title body_text" %}
+ <div class="hide">{{field}}</div>
+ {% endif%}{% endfor %}
+ <input id="btn-js-hide" type="submit" class="btn sm" value="Save" >
+ </form>
+ </article>
+ <aside class="note-list-container">
+ <div class="">
+ <ul>{% for obj in notes_list %}
+ <li>
+ <a href="{% url 'notes:note-detail' user.username obj.slug %}">
+ <h4>{{obj.title}}</h4>
+ <div class="note-preview">{{obj.body_text|truncatewords:12}}</div>
+ </a>
+ </li>
+ {% endfor %}</ul>
+ </div>
+ </aside>
+</main>
+{% endblock %}
+
+{% block jsinclude %}
+<script src="/media/js/highlight.pack.js"></script>
+<script src="/media/js/quill.min.js"></script>
+{% endblock %}
+ <script>
+{% block jsdomready %}
+ var btn = document.getElementById("edit-toggle-btn"),
+ qcontainer = document.getElementById('q-container'),
+ title = document.getElementById('note-title'),
+ form = document.getElementById('note-edit-form'),
+ note_html = document.createElement('textarea'),
+ note_qjson = document.createElement('textarea');
+
+ window.editing = false;
+ window.quillchange = false;
+
+ btn.classList.remove('hide');
+ initQuill("#note-body");
+ note_html.setAttribute('name', 'body_html');
+ note_html.setAttribute('class', 'hide');
+ note_html.setAttribute('id', 'id_body_html');
+ note_qjson.setAttribute('name', 'body_qjson');
+ note_qjson.setAttribute('id', 'id_body_qjson');
+ note_qjson.setAttribute('class', 'hide');
+ form.appendChild(note_html);
+ form.appendChild(note_qjson);
+ document.getElementById("btn-js-hide").classList.add("hide");
+ btn.addEventListener('click', function(){edit_note(this, title, qcontainer, window.quill, "{% url 'notes-api-detail' object.pk %}" )}, false)
+
+{%endblock%}
+ </script>
+
+'indent
+'align
+'direction
+'code-block
diff --git a/design/templates/notes/notes_list.html b/design/templates/notes/notes_list.html
index 4451588..8066369 100644
--- a/design/templates/notes/notes_list.html
+++ b/design/templates/notes/notes_list.html
@@ -3,7 +3,7 @@
<main>
<h1> Notes</h1>
<ul>{% for obj in object_list %}
- <li><a href="{{obj.get_absolute_url}}">{{obj}}</a></li>
+ <li><a href="{% url 'notes:note-detail' user.username obj.slug %}">{{obj}}</a></li>
{% endfor %}</ul>
</main>
{% endblock %}
diff --git a/design/templates/pages/page.html b/design/templates/pages/page.html
index 91d4732..3feff75 100644
--- a/design/templates/pages/page.html
+++ b/design/templates/pages/page.html
@@ -13,3 +13,19 @@
</div>
{% endif %}
{%endblock%}
+{% block jsdomready %}
+{% if login_form %}
+ // Select your overlay trigger
+ var trigger = document.querySelector('#overlay-trigger');
+ trigger.addEventListener('click', function(e){
+ e.preventDefault();
+ novicell.overlay.create({
+ 'selector': trigger.getAttribute('data-element'),
+ 'class': 'selector-overlay',
+ "onCreate": function() { console.log('created'); },
+ "onLoaded": function() { console.log('loaded'); },
+ "onDestroy": function() { console.log('Destroyed'); }
+ });
+ });
+{% endif %}
+{%endblock%}
diff --git a/minify.py b/minify.py
index 6ce2775..52d27a6 100755
--- a/minify.py
+++ b/minify.py
@@ -3,8 +3,9 @@ import os
minified = ""
for filename in os.listdir('scripts'):
- with open(os.path.join('scripts', filename)) as js_file:
- minified += jsmin(js_file.read())
+ if not os.path.isdir(os.path.join('scripts', filename)):
+ with open(os.path.join('scripts', filename)) as js_file:
+ minified += jsmin(js_file.read())
with open('media/js/package.min.js', 'w') as jscompressed:
jscompressed.write(minified)
jscompressed.close()
diff --git a/scripts/util.js b/scripts/util.js
index d70c012..99a5ef3 100644
--- a/scripts/util.js
+++ b/scripts/util.js
@@ -1,14 +1,56 @@
+function edit_note(btn, title, qcontainer, quill, url){
+ console.log(editing);
+ var formElement = document.querySelector("form");
+ if (editing === false) {
+ title.setAttribute("contenteditable", true);
+ title.classList.add('highlight')
+ qcontainer.classList.remove('inactive')
+ quill.enable(true);
+ btn.innerHTML = "Save"
+ editing = true;
+ } else {
+ if (window.quillchange === true) {
+ var form_note_title = document.getElementById('id_title');
+ var note_html = document.getElementById('id_body_html');
+ var note_qjson = document.getElementById('id_body_qjson');
+ form_note_title.value = title.innerHTML;
+ note_html.innerHTML = quill.root.innerHTML;
+ note_qjson.innerHTML = JSON.stringify(quill.getContents());
+ var request = new XMLHttpRequest();
+ request.open("PATCH", url);
+ var csrftoken = Cookies.get('csrftoken');
+ request.setRequestHeader("X-CSRFToken", csrftoken)
+ request.onload = function() {
+ if (request.status >= 200 && request.status < 400) {
+ console.log(request);
+ window.quillchange = false;
+ } else {
+ console.log(request);
+ console.log("server error");
+ }
+ };
+ request.onerror = function() {
+ console.log("error on request");
+ };
+ request.send(new FormData(formElement));
+ }
+ title.setAttribute("contenteditable", false);
+ title.classList.remove('highlight')
+ qcontainer.classList.add('inactive');
+ quill.enable(false);
+ btn.innerHTML = "Edit"
+ document.body.focus();
+ editing = false;
+ }
+ return false;
+}
+
+
function get_login_form() {
var request = new XMLHttpRequest();
request.open('GET', '/login/', true);
request.onload = function() {
if (request.status >= 200 && request.status < 400) {
- var data =
- for(var i in data) {
- var u = data[i]['fields']['part_number'] + ' - ' + data[i]['fields']['part_name']
- choices.push({ value: String(data[i].pk), label: u, });
- }
- populateParts(choices);
} else {
console.log("server error");
}
@@ -18,3 +60,25 @@ function get_login_form() {
};
request.send();
}
+
+//Global init for Quill
+function initQuill(el) {
+ window.quill = new Quill(el, {
+ modules: {
+ syntax: true, // Include syntax module
+ toolbar: [
+ [{ header: [1, 2, 3, 4, false] }],
+ ['bold', 'italic', 'underline', 'blockquote'],
+ [{ 'list': 'bullet'}, { 'list': 'ordered'},{ 'list': 'check'} ],
+ ['link', 'code-block', 'image', 'video', 'formula',],
+ [{ 'color': [] }, { 'background': [] }], // dropdown with defaults from theme
+ [{ 'font': [] }],
+ ]
+ },
+ theme: 'snow',
+ enable: false
+ });
+ window.quill.on('text-change', function() {
+ window.quillchange = true;
+ });
+}