~linuxgoose/bocpress

a9d67bd9982533c3e42a9e8dcb61cbffc0e14945 — Jordan Robinson 2 months ago 4add3fb
add new setting for auto markdown link formatting on paste in the editor
A main/migrations/0118_user_markdown_link_paste_on_and_more.py => main/migrations/0118_user_markdown_link_paste_on_and_more.py +23 -0
@@ 0,0 1,23 @@
# Generated by Django 5.2.5 on 2025-09-17 20:58

from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('main', '0117_alter_reallysimplelicensing_license_and_more'),
    ]

    operations = [
        migrations.AddField(
            model_name='user',
            name='markdown_link_paste_on',
            field=models.BooleanField(default=False, help_text='Enable/disable automatic markdown link formatting on paste.', verbose_name='Auto Markdown link formatting'),
        ),
        migrations.AlterField(
            model_name='user',
            name='subscribe_note',
            field=models.CharField(blank=True, default='Subscribe via [RSS](/rss/) / [Atom](/atom/) / [via Email](/newsletter/).', help_text='Default: Subscribe via [RSS](/rss/) / [Atom](/atom/) / [via Email](/newsletter/).', max_length=350, null=True),
        ),
    ]

M main/models.py => main/models.py +5 -0
@@ 156,6 156,11 @@ class User(AbstractUser):
        verbose_name="Webring next URL",
        help_text="URL for your webring's next website.",
    )
    markdown_link_paste_on = models.BooleanField(
        default=False,
        help_text="Enable/disable automatic markdown link formatting on paste.",
        verbose_name="Auto Markdown link formatting",
    )

    # billing
    stripe_customer_id = models.CharField(max_length=100, blank=True, null=True)

A main/templates/assets/markdown-paste-link.js => main/templates/assets/markdown-paste-link.js +30 -0
@@ 0,0 1,30 @@
// get body element, used for paste into it
var bodyElem = document.querySelector('textarea[name="body"]');

function formatOnPaste(event) {
    const clipboardData = event.clipboardData || window.clipboardData;
    const pastedData = clipboardData.getData('text');

    const bodyElem = document.querySelector('textarea[name="body"]');

    const start = bodyElem.selectionStart;
    const end = bodyElem.selectionEnd;

    if (start !== end) {
        event.preventDefault(); // Stop the default paste

        const selectedText = bodyElem.value.substring(start, end);
        const before = bodyElem.value.substring(0, start);
        const after = bodyElem.value.substring(end);

        const markdownLink = `[${selectedText}](${pastedData})`;

        bodyElem.value = before + markdownLink + after;

        // Move cursor after inserted markdown
        const newCursorPosition = before.length + markdownLink.length;
        bodyElem.setSelectionRange(newCursorPosition, newCursorPosition);
    }
}

bodyElem.addEventListener('paste', formatOnPaste);
\ No newline at end of file

M main/templates/main/page_form.html => main/templates/main/page_form.html +3 -1
@@ 64,7 64,9 @@
<script>
    // when page loads, focus on title
    document.querySelector('input[name="title"]').focus();

    {% if request.user.markdown_link_paste_on %}
    {% include "assets/markdown-paste-link.js" %}
    {% endif %}
    {% include "assets/drag-and-drop-upload.js" %}
</script>
{% endblock scripts %}

M main/templates/main/post_form.html => main/templates/main/post_form.html +3 -1
@@ 82,7 82,9 @@
<script>
    {% include "assets/drag-and-drop-upload.js" %}
    {% include "assets/make-draft-button.js" %}

    {% if request.user.markdown_link_paste_on %}
    {% include "assets/markdown-paste-link.js" %}
    {% endif %}
    {% if request.user.post_backups_on %}
    {% include "assets/save-snapshot.js" %}
    {% endif %}

M main/tests/test_users.py => main/tests/test_users.py +21 -0
@@ 289,3 289,24 @@ class UserDomainCheckTestCase(TestCase):
    def test_domain_unknown(self):
        response = self.client.get(reverse("domain_check") + "?domain=randomdomain.com")
        self.assertEqual(response.status_code, 403)

class UserMarkdownLinkOnPaste(TestCase):
    def setUp(self):
        self.user = models.User.objects.create(username="alice")
        self.client.force_login(self.user)

    def test_markdown_link_turned_on(self):
        self.user.markdown_link_paste_on = True
        self.user.save()
        response = self.client.get(
            reverse("post_create"),
        )
        self.assertContains(response, "formatOnPaste")

    def test_markdown_link_turned_off(self):
        self.user.markdown_link_turned_on = False
        self.user.save()
        response = self.client.get(
            reverse("post_create"),
        )
        self.assertNotContains(response, "formatOnPaste")
\ No newline at end of file

M main/views/general.py => main/views/general.py +1 -0
@@ 266,6 266,7 @@ class UserUpdate(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
        "comments_on",
        "notifications_on",
        "mail_export_on",
        "markdown_link_paste_on",
        "redirect_domain",
        "post_backups_on",
        "show_posts_on_homepage",