Switch all Pagedown editors to Martor

This commit is contained in:
Evan 2023-11-01 23:50:26 +00:00 committed by Tudor Brindus
parent 912fe4d277
commit ce1196f74f
13 changed files with 71 additions and 63 deletions

View File

@ -20,7 +20,7 @@ from reversion.models import Revision, Version
from judge.dblock import LockModel
from judge.models import Comment, CommentLock
from judge.widgets import HeavyPreviewPageDownWidget
from judge.widgets import MartorWidget
class CommentForm(ModelForm):
@ -31,9 +31,7 @@ class CommentForm(ModelForm):
'parent': forms.HiddenInput(),
}
if HeavyPreviewPageDownWidget is not None:
widgets['body'] = HeavyPreviewPageDownWidget(preview=reverse_lazy('comment_preview'),
preview_timeout=1000, hide_preview_button=True)
widgets['body'] = MartorWidget(attrs={'data-markdownfy-url': reverse_lazy('comment_preview')})
def __init__(self, request, *args, **kwargs):
self.request = request

View File

@ -20,7 +20,7 @@ from judge.models import Contest, Language, Organization, Problem, ProblemPoints
WebAuthnCredential
from judge.utils.mail import validate_email_domain
from judge.utils.subscription import newsletter_id
from judge.widgets import HeavyPreviewPageDownWidget, Select2MultipleWidget, Select2Widget
from judge.widgets import MartorWidget, Select2MultipleWidget, Select2Widget
TOTP_CODE_LENGTH = 6
@ -63,11 +63,7 @@ class ProfileForm(ModelForm):
fields.append('math_engine')
widgets['math_engine'] = Select2Widget(attrs={'style': 'width:200px'})
if HeavyPreviewPageDownWidget is not None:
widgets['about'] = HeavyPreviewPageDownWidget(
preview=reverse_lazy('profile_preview'),
attrs={'style': 'max-width:700px;min-width:700px;width:700px'},
)
widgets['about'] = MartorWidget(attrs={'data-markdownfy-url': reverse_lazy('profile_preview')})
def clean_about(self):
if 'about' in self.changed_data and not self.instance.has_any_solves:
@ -173,8 +169,7 @@ class EditOrganizationForm(ModelForm):
model = Organization
fields = ['about', 'logo_override_image', 'admins']
widgets = {'admins': Select2MultipleWidget(attrs={'style': 'width: 200px'})}
if HeavyPreviewPageDownWidget is not None:
widgets['about'] = HeavyPreviewPageDownWidget(preview=reverse_lazy('organization_preview'))
widgets['about'] = MartorWidget(attrs={'data-markdownfy-url': reverse_lazy('organization_preview')})
class CustomAuthenticationForm(AuthenticationForm):

View File

@ -7,6 +7,7 @@ from django.forms.models import ModelForm
from django.http import Http404, HttpResponse, HttpResponseBadRequest, HttpResponseForbidden, HttpResponseNotFound, \
HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.urls import reverse_lazy
from django.utils.translation import gettext as _
from django.views.decorators.http import require_POST
from django.views.generic import DetailView, UpdateView
@ -16,7 +17,7 @@ from reversion.models import Version
from judge.dblock import LockModel
from judge.models import Comment, CommentVote
from judge.utils.views import TitleMixin
from judge.widgets import MathJaxPagedownWidget
from judge.widgets import MartorWidget
__all__ = ['upvote_comment', 'downvote_comment', 'CommentEditAjax', 'CommentContent',
'CommentEdit']
@ -122,8 +123,14 @@ class CommentEditForm(ModelForm):
class Meta:
model = Comment
fields = ['body']
if MathJaxPagedownWidget is not None:
widgets = {'body': MathJaxPagedownWidget(attrs={'id': 'id-edit-comment-body'})}
widgets = {
'body': MartorWidget(
attrs={
'id': 'id_edit',
'data-markdownfy-url': reverse_lazy('comment_preview'),
},
),
}
class CommentEditAjax(LoginRequiredMixin, CommentMixin, UpdateView):

View File

@ -22,11 +22,9 @@ from judge.utils.diggpaginator import DiggPaginator
from judge.utils.tickets import filter_visible_tickets, own_ticket_filter
from judge.utils.views import SingleObjectFormView, TitleMixin, paginate_query_context
from judge.views.problem import ProblemMixin
from judge.widgets import HeavyPreviewPageDownWidget
from judge.widgets import MartorWidget
ticket_widget = (forms.Textarea() if HeavyPreviewPageDownWidget is None else
HeavyPreviewPageDownWidget(preview=reverse_lazy('ticket_preview'),
preview_timeout=1000, hide_preview_button=True))
ticket_widget = MartorWidget(attrs={'data-markdownfy-url': reverse_lazy('ticket_preview')})
class TicketForm(forms.Form):

View File

@ -6,9 +6,9 @@ from urllib.parse import urljoin
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.core.files.storage import default_storage
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden, HttpResponseRedirect
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden, HttpResponseNotFound, \
HttpResponseRedirect
from django.views.decorators.http import require_POST
from martor.api import imgur_uploader
from judge.models import Submission
@ -51,12 +51,11 @@ def django_uploader(image):
@login_required
def martor_image_uploader(request):
if not request.user.is_staff:
return HttpResponseNotFound()
if request.method != 'POST' or not request.is_ajax() or 'markdown-image-upload' not in request.FILES:
return HttpResponseBadRequest('Invalid request')
image = request.FILES['markdown-image-upload']
if request.user.is_staff:
data = django_uploader(image)
else:
data = imgur_uploader(image)
data = django_uploader(image)
return HttpResponse(data, content_type='application/json')

View File

@ -12,6 +12,8 @@ class MartorWidget(OldMartorWidget):
class AdminMartorWidget(OldAdminMartorWidget):
UPLOADS_ENABLED = True
class Media:
css = MartorWidget.Media.css
js = ['admin/js/jquery.init.js', 'martor-mathjax.js']

View File

@ -60,6 +60,10 @@ a {
box-sizing: border-box;
}
.comment-edit-form {
min-width: 60em;
}
.comment-post-wrapper {
padding-bottom: 5px;

View File

@ -2,6 +2,8 @@
form .martor-preview {
@include content-description;
min-height: 400px;
ul li {
list-style: unset !important;
@ -21,7 +23,7 @@ form .martor-preview {
}
.section-martor {
div[data-tab="editor-tab-description"] {
div[data-tab^="editor-tab-"] {
padding: 0;
}
@ -33,6 +35,12 @@ form .martor-preview {
font-size: 0.8rem;
}
.martor-toolbar {
flex: 0 1 auto !important;
overflow-x: auto;
overflow-y: hidden;
}
.martor-field {
height: 400px;
}

View File

@ -35,7 +35,7 @@
}
.centered-form {
max-width: 700px;
max-width: 800px;
margin: auto;
.submit-bar {

View File

@ -3,7 +3,7 @@
form#ticket-form {
display: block;
margin: 0 auto;
max-width: 750px;
max-width: 800px;
padding-top: 1em;
#id_title {
@ -72,7 +72,7 @@ div.ticket-title {
display: flex;
flex-direction: row;
flex-wrap: wrap-reverse;
max-width: 1000px;
max-width: 1200px;
}
.ticket-sidebar {

View File

@ -1,4 +1,4 @@
<div class="comment-submit">
<div class="comment-submit comment-edit-form">
<form id="comment-edit" action="{{ request.get_full_path() }}" method="post">
<span style="display: none" class="comment-id">{{ comment.id }}</span>
<span style="display: none" class="read-back">{{ url('comment_content', comment.id) }}</span>

View File

@ -2,25 +2,40 @@
{% compress js %}
{{ comment_form.media.js }}
<script type="text/javascript">
function rename_martor_widget(widget, old_name, new_name) {
function rename_data(data_name, prefix) {
widget.find('[data-' + data_name + '=' + prefix + old_name + ']')
.data(data_name, prefix + new_name)
.attr('data-' + data_name, prefix + new_name);
}
function rename_id(prefix) {
widget.find('#' + prefix + old_name).prop('id', prefix + new_name);
}
function rename_class(prefix) {
widget.find('.' + prefix + old_name).removeClass(prefix + old_name).addClass(prefix + new_name);
}
rename_data('tab', 'editor-tab-');
rename_data('tab', 'preview-tab-');
rename_data('field-name', '');
rename_id('id_');
rename_id('martor-');
rename_class('martor-field-');
rename_class('main-martor-');
widget.find('.main-martor').martor();
}
$(document).ready(function () {
window.reply_comment = function (parent) {
var $comment_reply = $('#comment-' + parent + '-reply');
var reply_id = 'reply-' + parent;
var new_id = 'id' + parent + '_body';
if ($comment_reply.find('#' + reply_id).length == 0) {
var $reply_form = $('#new-comment').clone(true).prop('id', reply_id);
var $reply_form = $('#new-comment').clone(false).prop('id', reply_id);
$reply_form.find('h3').html('{{ _('Replying to comment') }}');
$reply_form.prepend('<a class="close">x</a>');
$reply_form.find('form.comment-submit-form input#id_parent').val(parent);
$reply_form.find('div#id_body-wmd-wrapper').prop('id', new_id + '-wmd-wrapper');
$reply_form.find('div#id_body_wmd_button_bar').empty().prop('id', new_id + '_wmd_button_bar');
$reply_form.find('textarea.wmd-input').val('').prop('id', new_id);
$reply_form.find('div#id_body-preview').attr('data-textarea-id', new_id).prop('id', new_id + '-preview');
$reply_form.appendTo($comment_reply);
register_dmmd_preview($reply_form.find('div#' + new_id + '-preview'));
if ('DjangoPagedown' in window) {
window.DjangoPagedown.createEditor($reply_form.find('textarea.wmd-input').get(0));
}
$reply_form.find('form.comment-submit-form input#id_parent').val(parent);
rename_martor_widget($reply_form, 'body', reply_id);
ace.edit('martor-' + reply_id).setValue('');
}
$comment_reply.fadeIn();
@ -148,19 +163,8 @@
$comments.find('a.edit-link').featherlight({
afterOpen: function () {
if ('DjangoPagedown' in window) {
var $wmd = $('.featherlight .wmd-input');
if ($wmd.length) {
window.DjangoPagedown.createEditor($wmd.get(0));
if ('MathJax' in window) {
var preview = $('.featherlight div.wmd-preview')[0];
window.editors[$wmd.attr('id')].hooks.chain('onPreviewRefresh', function () {
MathJax.typesetPromise([preview]);
});
MathJax.typesetPromise([preview]);
}
}
}
var $widget = $('.featherlight #comment-form-body');
rename_martor_widget($widget, 'body', 'edit');
$('#comment-edit').submit(function (event) {
event.preventDefault();
var id = $('#comment-edit').find('.comment-id').text();
@ -186,14 +190,6 @@
});
});
},
beforeClose: function () {
if ('DjangoPagedown' in window) {
var $wmd = $('.featherlight .wmd-input');
if ($wmd.length) {
window.DjangoPagedown.destroyEditor($wmd.get(0));
}
}
},
variant: 'featherlight-edit'
});

View File

@ -42,7 +42,8 @@
#edit-form {
border: unset;
background: unset;
max-width: 700px;
display: block;
padding-bottom: 30px;
}
.settings {