mirror of
https://github.com/sissbruecker/linkding.git
synced 2025-08-07 18:58:30 +02:00
Add option for custom CSS (#652)
* Add option for adding custom CSS * add missing migration
This commit is contained in:
18
bookmarks/migrations/0026_userprofile_custom_css.py
Normal file
18
bookmarks/migrations/0026_userprofile_custom_css.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.0.2 on 2024-03-16 23:05
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("bookmarks", "0025_userprofile_search_preferences"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="userprofile",
|
||||||
|
name="custom_css",
|
||||||
|
field=models.TextField(blank=True),
|
||||||
|
),
|
||||||
|
]
|
@@ -331,6 +331,7 @@ class UserProfile(models.Model):
|
|||||||
enable_favicons = models.BooleanField(default=False, null=False)
|
enable_favicons = models.BooleanField(default=False, null=False)
|
||||||
display_url = models.BooleanField(default=False, null=False)
|
display_url = models.BooleanField(default=False, null=False)
|
||||||
permanent_notes = models.BooleanField(default=False, null=False)
|
permanent_notes = models.BooleanField(default=False, null=False)
|
||||||
|
custom_css = models.TextField(blank=True, null=False)
|
||||||
search_preferences = models.JSONField(default=dict, null=False)
|
search_preferences = models.JSONField(default=dict, null=False)
|
||||||
|
|
||||||
|
|
||||||
@@ -348,6 +349,7 @@ class UserProfileForm(forms.ModelForm):
|
|||||||
"enable_favicons",
|
"enable_favicons",
|
||||||
"display_url",
|
"display_url",
|
||||||
"permanent_notes",
|
"permanent_notes",
|
||||||
|
"custom_css",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@@ -7,6 +7,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
textarea.custom-css {
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
.input-group > input[type=submit] {
|
.input-group > input[type=submit] {
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
@@ -34,6 +34,9 @@
|
|||||||
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#161822">
|
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#161822">
|
||||||
<meta name="theme-color" media="(prefers-color-scheme: light)" content="#5856e0">
|
<meta name="theme-color" media="(prefers-color-scheme: light)" content="#5856e0">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if request.user_profile.custom_css %}
|
||||||
|
<style>{{ request.user_profile.custom_css }}</style>
|
||||||
|
{% endif %}
|
||||||
</head>
|
</head>
|
||||||
<body ld-global-shortcuts>
|
<body ld-global-shortcuts>
|
||||||
|
|
||||||
|
@@ -124,6 +124,18 @@
|
|||||||
href="{% url 'bookmarks:shared' %}">shared bookmarks page</a>.
|
href="{% url 'bookmarks:shared' %}">shared bookmarks page</a>.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<details {% if form.custom_css.value %}open{% endif %}>
|
||||||
|
<summary>Custom CSS</summary>
|
||||||
|
<label for="{{ form.custom_css.id_for_label }}" class="text-assistive">Custom CSS</label>
|
||||||
|
<div class="mt-2">
|
||||||
|
{{ form.custom_css|add_class:"form-input custom-css"|attr:"rows:6" }}
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
<div class="form-input-hint">
|
||||||
|
Allows to add custom CSS to the page.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="submit" name="update_profile" value="Save" class="btn btn-primary mt-2">
|
<input type="submit" name="update_profile" value="Save" class="btn btn-primary mt-2">
|
||||||
{% if update_profile_success_message %}
|
{% if update_profile_success_message %}
|
||||||
|
21
bookmarks/tests/test_custom_css.py
Normal file
21
bookmarks/tests/test_custom_css.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
from bookmarks.tests.helpers import BookmarkFactoryMixin
|
||||||
|
|
||||||
|
|
||||||
|
class CustomCssTestCase(TestCase, BookmarkFactoryMixin):
|
||||||
|
def setUp(self):
|
||||||
|
self.client.force_login(self.get_or_create_test_user())
|
||||||
|
|
||||||
|
def test_does_not_render_custom_style_tag_by_default(self):
|
||||||
|
response = self.client.get(reverse("bookmarks:index"))
|
||||||
|
self.assertNotContains(response, "<style>")
|
||||||
|
|
||||||
|
def test_renders_custom_style_tag_if_user_has_custom_css(self):
|
||||||
|
profile = self.get_or_create_test_user().profile
|
||||||
|
profile.custom_css = "body { background-color: red; }"
|
||||||
|
profile.save()
|
||||||
|
|
||||||
|
response = self.client.get(reverse("bookmarks:index"))
|
||||||
|
self.assertContains(response, "<style>body { background-color: red; }</style>")
|
@@ -32,6 +32,7 @@ class SettingsGeneralViewTestCase(TestCase, BookmarkFactoryMixin):
|
|||||||
"tag_search": UserProfile.TAG_SEARCH_STRICT,
|
"tag_search": UserProfile.TAG_SEARCH_STRICT,
|
||||||
"display_url": False,
|
"display_url": False,
|
||||||
"permanent_notes": False,
|
"permanent_notes": False,
|
||||||
|
"custom_css": "",
|
||||||
}
|
}
|
||||||
|
|
||||||
return {**form_data, **overrides}
|
return {**form_data, **overrides}
|
||||||
@@ -63,6 +64,7 @@ class SettingsGeneralViewTestCase(TestCase, BookmarkFactoryMixin):
|
|||||||
"tag_search": UserProfile.TAG_SEARCH_LAX,
|
"tag_search": UserProfile.TAG_SEARCH_LAX,
|
||||||
"display_url": True,
|
"display_url": True,
|
||||||
"permanent_notes": True,
|
"permanent_notes": True,
|
||||||
|
"custom_css": "body { background-color: #000; }",
|
||||||
}
|
}
|
||||||
response = self.client.post(reverse("bookmarks:settings.general"), form_data)
|
response = self.client.post(reverse("bookmarks:settings.general"), form_data)
|
||||||
html = response.content.decode()
|
html = response.content.decode()
|
||||||
@@ -93,6 +95,7 @@ class SettingsGeneralViewTestCase(TestCase, BookmarkFactoryMixin):
|
|||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.user.profile.permanent_notes, form_data["permanent_notes"]
|
self.user.profile.permanent_notes, form_data["permanent_notes"]
|
||||||
)
|
)
|
||||||
|
self.assertEqual(self.user.profile.custom_css, form_data["custom_css"])
|
||||||
self.assertInHTML(
|
self.assertInHTML(
|
||||||
"""
|
"""
|
||||||
<p class="form-input-hint">Profile updated</p>
|
<p class="form-input-hint">Profile updated</p>
|
||||||
|
@@ -131,8 +131,6 @@ STATIC_ROOT = os.path.join(BASE_DIR, "static")
|
|||||||
|
|
||||||
# Turn off SASS compilation by default
|
# Turn off SASS compilation by default
|
||||||
SASS_PROCESSOR_ENABLED = False
|
SASS_PROCESSOR_ENABLED = False
|
||||||
# Location where generated CSS files are saved
|
|
||||||
SASS_PROCESSOR_ROOT = os.path.join(BASE_DIR, "bookmarks", "static")
|
|
||||||
|
|
||||||
# Add SASS preprocessor finder to resolve generated CSS
|
# Add SASS preprocessor finder to resolve generated CSS
|
||||||
STATICFILES_FINDERS = [
|
STATICFILES_FINDERS = [
|
||||||
|
Reference in New Issue
Block a user