Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use only one markdown dependency for markdown fields in Django Admin #4483

Merged
merged 11 commits into from
Aug 14, 2023
Merged
4 changes: 1 addition & 3 deletions dependencies/pip/dev_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,7 @@ django-js-asset==2.0.0
# via django-mptt
django-loginas==0.3.10
# via -r dependencies/pip/requirements.in
django-markdownx==3.0.1
# via -r dependencies/pip/requirements.in
django-markitup==4.0.0
django-markdownx==4.0.2
# via -r dependencies/pip/requirements.in
django-mptt==0.13.4
# via -r dependencies/pip/requirements.in
Expand Down
1 change: 0 additions & 1 deletion dependencies/pip/requirements.in
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ django-loginas
django-markdownx
django-prometheus

django-markitup
django-mptt

django-reversion
Expand Down
4 changes: 1 addition & 3 deletions dependencies/pip/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,7 @@ django-js-asset==2.0.0
# via django-mptt
django-loginas==0.3.10
# via -r dependencies/pip/requirements.in
django-markdownx==3.0.1
# via -r dependencies/pip/requirements.in
django-markitup==4.0.0
django-markdownx==4.0.2
# via -r dependencies/pip/requirements.in
django-mptt==0.13.4
# via -r dependencies/pip/requirements.in
Expand Down
8 changes: 7 additions & 1 deletion hub/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
USERNAME_INVALID_MESSAGE,
username_validators,
)
from kobo.apps.markdownx_uploader.admin import MarkdownxModelAdminBase
from kobo.apps.trash_bin.exceptions import TrashIntegrityError
from kobo.apps.trash_bin.models.account import AccountTrash
from kobo.apps.trash_bin.utils import move_to_trash
Expand Down Expand Up @@ -341,8 +342,13 @@ def get_queryset(self, request):
)


class SitewideMessageAdmin(MarkdownxModelAdminBase):

model = SitewideMessage


admin.site.register(ExtraUserDetail, ExtraUserDetailAdmin)
admin.site.register(SitewideMessage)
admin.site.register(SitewideMessage, SitewideMessageAdmin)
admin.site.register(ConfigurationFile)
admin.site.register(PerUserSetting)
admin.site.unregister(User)
Expand Down
3 changes: 1 addition & 2 deletions hub/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# coding: utf-8
from django.db import models, migrations
import markitup.fields


class Migration(migrations.Migration):
Expand All @@ -14,7 +13,7 @@ class Migration(migrations.Migration):
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('slug', models.CharField(max_length=50)),
('body', markitup.fields.MarkupField(no_rendered_field=True)),
('body', models.TextField()),
('_body_rendered', models.TextField(editable=False, blank=True)),
],
options={
Expand Down
23 changes: 23 additions & 0 deletions hub/migrations/0012_replace_markup_with_markdownx.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 3.2.15 on 2023-06-07 17:26

from django.db import migrations
import markdownx.models


class Migration(migrations.Migration):

dependencies = [
('hub', '0011_extrauserdetail_private_data'),
]

operations = [
migrations.RemoveField(
model_name='sitewidemessage',
name='_body_rendered',
),
migrations.AlterField(
model_name='sitewidemessage',
name='body',
field=markdownx.models.MarkdownxField(),
),
]
11 changes: 7 additions & 4 deletions hub/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,23 @@
from django.shortcuts import get_object_or_404
from django.urls import reverse
from django.utils.http import http_date
from markitup.fields import MarkupField

# `was_modified_since` is undocumented(?) but used by django-private-storage,
# whose approach is emulated here
from django.views.static import was_modified_since
from markdownx.models import MarkdownxField

from kobo.apps.markdownx_uploader.models import AbstractMarkdownxModel
from kpi.fields import KpiUidField
from kpi.mixins import StandardizeSearchableFieldMixin
from kpi.utils.object_permission import get_database_user


class SitewideMessage(models.Model):
class SitewideMessage(AbstractMarkdownxModel):

slug = models.CharField(max_length=50)
body = MarkupField()
body = MarkdownxField()

markdown_fields = ['body']

def __str__(self):
return self.slug
Expand Down
6 changes: 3 additions & 3 deletions hub/tests/test_i18n.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ def test_welcome_message(self):
welcome_message_es = I18nUtils.get_sitewide_message(lang="es")
welcome_message = I18nUtils.get_sitewide_message()

self.assertEqual(welcome_message_fr.raw, "Le message de bienvenue")
self.assertEqual(welcome_message.raw, "Global welcome message")
self.assertEqual(welcome_message_es.raw, welcome_message.raw)
self.assertEqual(welcome_message_fr, "Le message de bienvenue")
self.assertEqual(welcome_message, "Global welcome message")
self.assertEqual(welcome_message_es, welcome_message)
14 changes: 5 additions & 9 deletions kobo/apps/help/admin.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
# coding: utf-8
from django.contrib import admin
from markdownx.admin import MarkdownxModelAdmin

from kobo.apps.markdownx_uploader.admin import MarkdownxModelAdminBase
from .models import InAppMessage, InAppMessageFile


class InAppMessageAdmin(MarkdownxModelAdmin):
class InAppMessageAdmin(MarkdownxModelAdminBase):

model = InAppMessage

new_message_warning = (
'⚠ Warning: always create a new message, from scratch, to display new '
'information. If someone has already dismissed a message, editing it '
'here will not cause it to reappear.'
)
drag_drop_warning = (
'⚠ Warning: Please drag and drop photos directly into the Snippet or '
'Body boxes. Copying the URL from the in app message files will '
'likely cause errors.'
)
readonly_fields = ['uid', 'last_editor']

def get_form(self, *args, **kwargs):
Expand All @@ -42,7 +40,6 @@ def get_form(self, *args, **kwargs):
# https://docs.djangoproject.com/en/2.2/ref/contrib/admin/#django.contrib.admin.ModelAdmin.fieldsets
self.fieldsets = [
(self.new_message_warning, {'fields': ''}),
(self.drag_drop_warning, {'fields': form._meta.fields}),
]
return form

Expand All @@ -52,4 +49,3 @@ def save_model(self, request, obj, form, change):


admin.site.register(InAppMessage, InAppMessageAdmin)
admin.site.register(InAppMessageFile, admin.ModelAdmin)
27 changes: 27 additions & 0 deletions kobo/apps/help/migrations/0003_delete_inappmessagefile_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Generated by Django 3.2.15 on 2023-06-06 18:03
from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('markdownx_uploader', '0001_initial'),
('help', '0002_inappmessage_always_display_as_new'),
]

operations = [
migrations.DeleteModel(
name='InAppMessageFile',
),
migrations.CreateModel(
name='InAppMessageFile',
fields=[
],
options={
'proxy': True,
'indexes': [],
'constraints': [],
},
bases=('markdownx_uploader.markdownxuploaderfile',),
),
]
38 changes: 14 additions & 24 deletions kobo/apps/help/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,21 @@

from django.conf import settings
from django.db import models
from django.utils.module_loading import import_string
from markdownx.models import MarkdownxField
from markdownx.settings import MARKDOWNX_MARKDOWNIFY_FUNCTION
from private_storage.fields import PrivateFileField

from kobo.apps.markdownx_uploader.models import (
AbstractMarkdownxModel,
MarkdownxUploaderFile,
MarkdownxUploaderFileReference,
)
from kpi.fields import KpiUidField
from kpi.utils.markdown import markdownify


EPOCH_BEGINNING = datetime.datetime.utcfromtimestamp(0)
markdownify = import_string(MARKDOWNX_MARKDOWNIFY_FUNCTION)


class InAppMessage(models.Model):
class InAppMessage(AbstractMarkdownxModel):
"""
A message, composed in the Django admin interface, displayed to regular
users within the application
Expand All @@ -41,37 +44,24 @@ class InAppMessage(models.Model):
valid_until = models.DateTimeField(default=EPOCH_BEGINNING)
last_editor = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)

markdown_fields = ['snippet', 'body']

def __str__(self):
return '{} ({})'.format(self.title, self.uid)

@property
def html(self):
# TODO: Djangerz template processing...
# Make `request.user.extra_detail` available in the context as `user`
MARKDOWN_FIELDS_TO_CONVERT = ('snippet', 'body')
result = {}
for field in MARKDOWN_FIELDS_TO_CONVERT:
for field in self.markdown_fields:
result[field] = markdownify(getattr(self, field))
return result


class InAppMessageFile(models.Model):
"""
A file uploaded by the django-markdownx editor. It doesn't have a foreign
key to `InAppMessage` because it was likely uploaded while the message was
still being drafted, before ever being saved in the database
"""
# TODO: Clean these up if they're no longer referenced by an
# `InAppMessage`? Parse the Markdown to figure it out? GitHub does it
# somehow…
content = PrivateFileField(
# Avoid collisions with usernames, which must begin with `[a-z]`
# (see `kpi.forms.USERNAME_REGEX`)
upload_to='__in_app_message/%Y/%m/%d/'
)

def __str__(self):
return self.content.name
class InAppMessageFile(MarkdownxUploaderFile):
class Meta:
proxy = True


class InAppMessageUserInteractions(models.Model):
Expand Down
5 changes: 1 addition & 4 deletions kobo/apps/help/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

from .views import (
InAppMessageFileContentView,
InAppMessageImageUploadView,
InAppMessageViewSet,
)

Expand All @@ -13,9 +12,7 @@
router.register(r'in_app_messages', InAppMessageViewSet)

urlpatterns = [
re_path(r'^in_app_message_upload/',
InAppMessageImageUploadView.as_view(),
name='in-app-message-image-upload'),
# keep this route for retro-compatibility
re_path(r'^in_app_message_file/(?P<path>.*)$',
InAppMessageFileContentView.as_view(),
name='in-app-message-file-contents'),
Expand Down
8 changes: 0 additions & 8 deletions kobo/apps/help/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,6 @@
from .serializers import InAppMessageSerializer


class InAppMessageImageUploadView(ImageUploadView):
"""
django-markdownx uses this view to POST files that a user drags-and-drops
onto the editor (per `settings.MARKDOWNX_UPLOAD_URLS_PATH`)
"""
form_class = InAppMessageImageForm


class InAppMessageFileContentView(PrivateStorageView):
"""
A view that allows any authenticated user to access the contents of an
Expand Down
9 changes: 9 additions & 0 deletions kobo/apps/markdownx_uploader/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from django.apps import AppConfig


class MarkdownxUploaderAppConfig(AppConfig):
name = 'kobo.apps.markdownx_uploader'
verbose_name = 'Markdown media files'

def ready(self):
super().ready()
17 changes: 17 additions & 0 deletions kobo/apps/markdownx_uploader/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from django.db import transaction
from markdownx.admin import MarkdownxModelAdmin

from .models import MarkdownxUploaderFileReference


class MarkdownxModelAdminBase(MarkdownxModelAdmin):

def delete_queryset(self, request, queryset):
with transaction.atomic():
object_ids = list(queryset.values_list('pk', flat=True))
super().delete_queryset(request, queryset)
MarkdownxUploaderFileReference.objects.filter(
app_label=self.model._meta.app_label, # noqa
model_name=self.model._meta.model_name, # noqa
object_id__in=object_ids,
).delete()
25 changes: 25 additions & 0 deletions kobo/apps/markdownx_uploader/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import os

from django.urls import reverse
from markdownx.forms import ImageForm

from .models import MarkdownxUploaderFile


class MarkdownxUploaderImageForm(ImageForm):
"""
This custom upload form for django-markdownx allows us to return
an image URL that points at `views.InAppMessageFileContentView`
"""
def save(self, commit=True):
if not commit:
return super().save(commit=False)
image_data = super().save(commit=False)
image_object = MarkdownxUploaderFile()
image_object.content.save(
os.path.split(image_data.path)[1], image_data.image
)
image_object.save()
return reverse(
'markdownx-uploader-file-content', args=(image_object.content.name,)
)