mirror of
https://github.com/sissbruecker/linkding
synced 2024-11-10 06:04:15 +00:00
Implement dark theme (#49)
This commit is contained in:
parent
3e5e825032
commit
119d8f7efb
28 changed files with 314 additions and 83 deletions
|
@ -16,6 +16,7 @@ The name comes from:
|
|||
- Bookmark archive
|
||||
- Extensions for [Firefox](https://addons.mozilla.org/de/firefox/addon/linkding-extension/) and [Chrome](https://chrome.google.com/webstore/detail/linkding-extension/beakmhbijpdhipnjhnclmhgjlddhidpe)
|
||||
- Bookmarklet that should work in most browsers
|
||||
- Dark mode
|
||||
- Easy to set up using Docker
|
||||
- Uses SQLite as database
|
||||
- Works without Javascript
|
||||
|
|
|
@ -7,7 +7,7 @@ from django.utils.translation import ngettext, gettext
|
|||
from rest_framework.authtoken.admin import TokenAdmin
|
||||
from rest_framework.authtoken.models import Token
|
||||
|
||||
from bookmarks.models import Bookmark, Tag
|
||||
from bookmarks.models import Bookmark, Tag, UserProfile
|
||||
from bookmarks.services.bookmarks import archive_bookmark, unarchive_bookmark
|
||||
|
||||
|
||||
|
@ -77,8 +77,24 @@ class AdminTag(admin.ModelAdmin):
|
|||
), messages.SUCCESS)
|
||||
|
||||
|
||||
class AdminUserProfileInline(admin.StackedInline):
|
||||
model = UserProfile
|
||||
can_delete = False
|
||||
verbose_name_plural = 'Profile'
|
||||
fk_name = 'user'
|
||||
|
||||
|
||||
class AdminCustomUser(UserAdmin):
|
||||
inlines = (AdminUserProfileInline,)
|
||||
|
||||
def get_inline_instances(self, request, obj=None):
|
||||
if not obj:
|
||||
return list()
|
||||
return super(AdminCustomUser, self).get_inline_instances(request, obj)
|
||||
|
||||
|
||||
linkding_admin_site = LinkdingAdminSite()
|
||||
linkding_admin_site.register(Bookmark, AdminBookmark)
|
||||
linkding_admin_site.register(Tag, AdminTag)
|
||||
linkding_admin_site.register(User, UserAdmin)
|
||||
linkding_admin_site.register(User, AdminCustomUser)
|
||||
linkding_admin_site.register(Token, TokenAdmin)
|
||||
|
|
|
@ -264,17 +264,4 @@
|
|||
z-index: 2;
|
||||
}
|
||||
|
||||
/* TODO: Should be read from theme */
|
||||
.menu-item.selected > a {
|
||||
background: #f1f1fc;
|
||||
color: #5755d9;
|
||||
}
|
||||
|
||||
.group-item, .group-item:hover {
|
||||
color: #999999;
|
||||
text-transform: uppercase;
|
||||
background: none;
|
||||
font-size: 0.6rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
|
@ -124,10 +124,4 @@
|
|||
.menu.open {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* TODO: Should be read from theme */
|
||||
.menu-item.selected > a {
|
||||
background: #f1f1fc;
|
||||
color: #5755d9;
|
||||
}
|
||||
</style>
|
43
bookmarks/migrations/0007_userprofile.py
Normal file
43
bookmarks/migrations/0007_userprofile.py
Normal file
|
@ -0,0 +1,43 @@
|
|||
# Generated by Django 2.2.18 on 2021-03-26 22:39
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
def forwards(apps, schema_editor):
|
||||
User = apps.get_model('auth', 'User')
|
||||
UserProfile = apps.get_model('bookmarks', 'UserProfile')
|
||||
for user in User.objects.all():
|
||||
try:
|
||||
if user.profile:
|
||||
continue
|
||||
except UserProfile.DoesNotExist:
|
||||
profile = UserProfile(user=user)
|
||||
profile.save()
|
||||
|
||||
|
||||
def reverse(apps, schema_editor):
|
||||
pass
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('bookmarks', '0006_bookmark_is_archived'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='UserProfile',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('theme',
|
||||
models.CharField(choices=[('auto', 'Auto'), ('light', 'Light'), ('dark', 'Dark')], default='auto',
|
||||
max_length=10)),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile',
|
||||
to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
migrations.RunPython(forwards, reverse),
|
||||
]
|
|
@ -2,7 +2,10 @@ from typing import List
|
|||
|
||||
from django import forms
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import User
|
||||
from django.db import models
|
||||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
|
||||
from bookmarks.utils import unique
|
||||
from bookmarks.validators import BookmarkURLValidator
|
||||
|
@ -93,3 +96,33 @@ class BookmarkForm(forms.ModelForm):
|
|||
class Meta:
|
||||
model = Bookmark
|
||||
fields = ['url', 'tag_string', 'title', 'description', 'auto_close', 'return_url']
|
||||
|
||||
|
||||
class UserProfile(models.Model):
|
||||
THEME_AUTO = 'auto'
|
||||
THEME_LIGHT = 'light'
|
||||
THEME_DARK = 'dark'
|
||||
THEME_CHOICES = [
|
||||
(THEME_AUTO, 'Auto'),
|
||||
(THEME_LIGHT, 'Light'),
|
||||
(THEME_DARK, 'Dark'),
|
||||
]
|
||||
user = models.OneToOneField(get_user_model(), related_name='profile', on_delete=models.CASCADE)
|
||||
theme = models.CharField(max_length=10, choices=THEME_CHOICES, blank=False, default=THEME_AUTO)
|
||||
|
||||
|
||||
class UserProfileForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = UserProfile
|
||||
fields = ['theme']
|
||||
|
||||
|
||||
@receiver(post_save, sender=get_user_model())
|
||||
def create_user_profile(sender, instance, created, **kwargs):
|
||||
if created:
|
||||
UserProfile.objects.create(user=instance)
|
||||
|
||||
|
||||
@receiver(post_save, sender=get_user_model())
|
||||
def save_user_profile(sender, instance, **kwargs):
|
||||
instance.profile.save()
|
||||
|
|
BIN
bookmarks/static/logo.png
Normal file
BIN
bookmarks/static/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
|
@ -10,20 +10,22 @@ header {
|
|||
|
||||
.navbar-brand {
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.logo {
|
||||
background-color: $primary-color;
|
||||
color: $light-color;
|
||||
padding: 14px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-transform: uppercase;
|
||||
display: inline-block;
|
||||
margin: 0 0 0 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-toggle {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,9 +41,18 @@ h2 {
|
|||
color: $gray-color-dark;
|
||||
}
|
||||
|
||||
// Button color should not change for anchor elements
|
||||
.btn:visited:not(.btn-primary) {
|
||||
color: $primary-color;
|
||||
// Fix up visited styles
|
||||
a:visited {
|
||||
color: $link-color;
|
||||
}
|
||||
a:visited:hover {
|
||||
color: $link-color-dark;
|
||||
}
|
||||
.btn-link:visited:not(.btn-primary) {
|
||||
color: $link-color;
|
||||
}
|
||||
.btn-link:visited:not(.btn-primary):hover {
|
||||
color: $link-color-dark;
|
||||
}
|
||||
|
||||
// Increase spacing between columns
|
||||
|
@ -58,3 +69,19 @@ h2 {
|
|||
.tab-block {
|
||||
border-bottom: solid 1px $border-color;
|
||||
}
|
||||
|
||||
// Form auto-complete menu
|
||||
.form-autocomplete .menu {
|
||||
.menu-item.selected > a, .menu-item > a:hover {
|
||||
background: $secondary-color;
|
||||
color: $primary-color;
|
||||
}
|
||||
|
||||
.group-item, .group-item:hover {
|
||||
color: $gray-color;
|
||||
text-transform: uppercase;
|
||||
background: none;
|
||||
font-size: 0.6rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ ul.bookmark-list {
|
|||
.description {
|
||||
color: $gray-color-dark;
|
||||
|
||||
a {
|
||||
a, a:visited:hover {
|
||||
color: $alternative-color;
|
||||
}
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ ul.bookmark-list {
|
|||
|
||||
.tag-cloud {
|
||||
|
||||
a {
|
||||
a, a:visited:hover {
|
||||
color: $alternative-color;
|
||||
}
|
||||
|
||||
|
|
27
bookmarks/styles/dark.scss
Normal file
27
bookmarks/styles/dark.scss
Normal file
|
@ -0,0 +1,27 @@
|
|||
/* Dark theme overrides */
|
||||
|
||||
/* Buttons */
|
||||
.btn.btn-primary {
|
||||
background: $dt-primary-button-color;
|
||||
border-color: darken($dt-primary-button-color, 5%);
|
||||
|
||||
&:hover, &:active, &:focus {
|
||||
background: darken($dt-primary-button-color, 5%);
|
||||
border-color: darken($dt-primary-button-color, 10%);
|
||||
}
|
||||
}
|
||||
|
||||
/* Focus ring*/
|
||||
a:focus, .btn:focus {
|
||||
box-shadow: 0 0 0 .1rem rgba($primary-color, .5);
|
||||
}
|
||||
|
||||
/* Forms */
|
||||
.has-error .form-input, .form-input.is-error, .has-error .form-select, .form-select.is-error {
|
||||
background: darken($error-color, 40%);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination .page-item.active a {
|
||||
background: $dt-primary-button-color;
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
// Font sizes
|
||||
$html-font-size: 18px !default;
|
||||
|
||||
//$alternative-color: #c84e00;
|
||||
//$alternative-color: #FF84E8;
|
||||
//$alternative-color: #98C1D9;
|
||||
//$alternative-color: #7B287D;
|
||||
$alternative-color: #05a6a3;
|
||||
$alternative-color-dark: darken($alternative-color, 5%);
|
||||
|
||||
// Import Spectre CSS lib
|
||||
@import "../../node_modules/spectre.css/src/spectre";
|
||||
@import "../../node_modules/spectre.css/src/autocomplete";
|
||||
// Import Spectre icons
|
||||
@import "../../node_modules/spectre.css/src/icons/icons-core";
|
||||
@import "../../node_modules/spectre.css/src/icons/icons-navigation";
|
||||
@import "../../node_modules/spectre.css/src/icons/icons-action";
|
||||
@import "../../node_modules/spectre.css/src/icons/icons-object";
|
||||
|
||||
|
||||
// Import style modules
|
||||
@import "base";
|
||||
@import "util";
|
||||
@import "shared";
|
||||
@import "bookmarks";
|
||||
@import "settings";
|
||||
@import "auth";
|
17
bookmarks/styles/theme-dark.scss
Normal file
17
bookmarks/styles/theme-dark.scss
Normal file
|
@ -0,0 +1,17 @@
|
|||
// Import custom variables
|
||||
@import "variables-dark";
|
||||
|
||||
// Import Spectre CSS lib
|
||||
@import "../../node_modules/spectre.css/src/spectre";
|
||||
@import "../../node_modules/spectre.css/src/autocomplete";
|
||||
|
||||
// Import style modules
|
||||
@import "base";
|
||||
@import "util";
|
||||
@import "shared";
|
||||
@import "bookmarks";
|
||||
@import "settings";
|
||||
@import "auth";
|
||||
|
||||
// Dark theme overrides
|
||||
@import "dark";
|
14
bookmarks/styles/theme-light.scss
Normal file
14
bookmarks/styles/theme-light.scss
Normal file
|
@ -0,0 +1,14 @@
|
|||
// Import custom variables
|
||||
@import "variables-light";
|
||||
|
||||
// Import Spectre CSS lib
|
||||
@import "../../node_modules/spectre.css/src/spectre";
|
||||
@import "../../node_modules/spectre.css/src/autocomplete";
|
||||
|
||||
// Import style modules
|
||||
@import "base";
|
||||
@import "util";
|
||||
@import "shared";
|
||||
@import "bookmarks";
|
||||
@import "settings";
|
||||
@import "auth";
|
28
bookmarks/styles/variables-dark.scss
Normal file
28
bookmarks/styles/variables-dark.scss
Normal file
|
@ -0,0 +1,28 @@
|
|||
$html-font-size: 18px !default;
|
||||
|
||||
$body-bg: #161822 !default;
|
||||
$bg-color: lighten($body-bg, 5%) !default;
|
||||
$bg-color-light: lighten($body-bg, 5%) !default;
|
||||
|
||||
$border-color: #4C4E53 !default;
|
||||
$border-color-dark: $border-color !default;
|
||||
|
||||
$body-font-color: #b5bec8 !default;
|
||||
$light-color: #fafafa !default;
|
||||
|
||||
$gray-color: #7f879b !default;
|
||||
$gray-color-dark: lighten($gray-color, 20%) !default;
|
||||
|
||||
$primary-color: #a8b1ff !default;
|
||||
$primary-color-dark: saturate($primary-color, 5%) !default;
|
||||
$secondary-color: lighten($body-bg, 10%) !default;
|
||||
|
||||
$link-color: $primary-color !default;
|
||||
$link-color-dark: darken($link-color, 5%) !default;
|
||||
$link-color-light: $link-color !default;
|
||||
|
||||
$alternative-color: #59bdb9;
|
||||
$alternative-color-dark: #73f1eb;
|
||||
|
||||
/* Dark theme specific */
|
||||
$dt-primary-button-color: #5761cb !default;
|
4
bookmarks/styles/variables-light.scss
Normal file
4
bookmarks/styles/variables-light.scss
Normal file
|
@ -0,0 +1,4 @@
|
|||
$html-font-size: 18px !default;
|
||||
|
||||
$alternative-color: #05a6a3;
|
||||
$alternative-color-dark: darken($alternative-color, 5%);
|
|
@ -2,7 +2,7 @@
|
|||
<p class="empty-title h5">You have no bookmarks yet</p>
|
||||
<p class="empty-subtitle">
|
||||
You can get started by <a href="{% url 'bookmarks:new' %}">adding</a> bookmarks,
|
||||
<a href="{% url 'bookmarks:settings.data' %}">importing</a> your existing bookmarks or configuring the
|
||||
<a href="{% url 'bookmarks:settings.general' %}">importing</a> your existing bookmarks or configuring the
|
||||
<a href="{% url 'bookmarks:settings.integrations' %}">browser extension</a> or the <a href="{% url 'bookmarks:settings.integrations' %}">bookmarklet</a>.
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
{{ form.return_url|attr:"type:hidden" }}
|
||||
<div class="form-group {% if form.url.errors %}has-error{% endif %}">
|
||||
<label for="{{ form.url.id_for_label }}" class="form-label">URL</label>
|
||||
{{ form.url|add_class:"form-input"|attr:"autofocus" }}
|
||||
{{ form.url|add_class:"form-input"|attr:"autofocus"|attr:"placeholder: " }}
|
||||
{% if form.url.errors %}
|
||||
<div class="form-input-hint">
|
||||
{{ form.url.errors }}
|
||||
|
|
|
@ -12,14 +12,24 @@
|
|||
<meta name="author" content="Sascha Ißbrücker">
|
||||
<title>linkding</title>
|
||||
{# Include SASS styles, files are resolved from bookmarks/styles #}
|
||||
<link href="{% sass_src 'index.scss' %}" rel="stylesheet" type="text/css"/>
|
||||
{# Include specific theme variant based on user profile setting #}
|
||||
{% if request.user.profile.theme == 'light' %}
|
||||
<link href="{% sass_src 'theme-light.scss' %}" rel="stylesheet" type="text/css"/>
|
||||
{% elif request.user.profile.theme == 'dark' %}
|
||||
<link href="{% sass_src 'theme-dark.scss' %}" rel="stylesheet" type="text/css"/>
|
||||
{% else %}
|
||||
{# Use auto theme as fallback #}
|
||||
<link href="{% sass_src 'theme-dark.scss' %}" rel="stylesheet" type="text/css"
|
||||
media="(prefers-color-scheme: dark)"/>
|
||||
<link href="{% sass_src 'theme-light.scss' %}" rel="stylesheet" type="text/css"
|
||||
media="(prefers-color-scheme: light)"/>
|
||||
{% endif %}
|
||||
</head>
|
||||
<body>
|
||||
<header class="navbar container grid-lg">
|
||||
<section class="navbar-section">
|
||||
<a href="/" class="navbar-brand text-bold">
|
||||
<i class="logo icon icon-link s-circle"></i>
|
||||
|
||||
<img class="logo" src="{% static 'logo.png' %}" alt="Application logo">
|
||||
<h1>linkding</h1>
|
||||
</a>
|
||||
</section>
|
||||
|
|
|
@ -7,12 +7,16 @@
|
|||
</div>
|
||||
{# Menu drop-down for smaller devices #}
|
||||
<div class="show-md">
|
||||
<a href="{% url 'bookmarks:new' %}" class="btn btn-primary mr-2">
|
||||
<i class="icon icon-plus"></i>
|
||||
<a href="{% url 'bookmarks:new' %}" class="btn btn-primary">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" style="width: 24px; height: 24px">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
|
||||
</svg>
|
||||
</a>
|
||||
<div class="dropdown dropdown-right">
|
||||
<a href="#" id="mobile-nav-menu-trigger" class="btn btn-link dropdown-toggle" tabindex="0">
|
||||
<i class="icon icon-menu icon-2x"></i>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" style="width: 24px; height: 24px">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
|
||||
</svg>
|
||||
</a>
|
||||
<!-- menu component -->
|
||||
<ul class="menu">
|
||||
|
|
|
@ -20,11 +20,11 @@
|
|||
{% endif %}
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="{{ form.username.id_for_label }}">Username</label>
|
||||
{{ form.username|add_class:'form-input' }}
|
||||
{{ form.username|add_class:'form-input'|attr:"placeholder: " }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="{{ form.password.id_for_label }}">Password</label>
|
||||
{{ form.password|add_class:'form-input' }}
|
||||
{{ form.password|add_class:'form-input'|attr:"placeholder: " }}
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
|
|
|
@ -1,10 +1,26 @@
|
|||
{% extends "bookmarks/layout.html" %}
|
||||
{% load widget_tweaks %}
|
||||
|
||||
{% block content %}
|
||||
<div class="settings-page">
|
||||
|
||||
{% include 'settings/nav.html' %}
|
||||
|
||||
{# Profile section #}
|
||||
<section class="content-area">
|
||||
<h2>Profile</h2>
|
||||
<form action="{% url 'bookmarks:settings.general' %}" method="post" novalidate>
|
||||
{% csrf_token %}
|
||||
<div class="form-group">
|
||||
<label for="{{ form.theme.id_for_label }}" class="form-label">Theme</label>
|
||||
{{ form.theme|add_class:"form-select col-2 col-sm-12" }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="submit" value="Save" class="btn btn-primary mt-2">
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
{# Import section #}
|
||||
<section class="content-area">
|
||||
<h2>Import</h2>
|
|
@ -1,11 +1,11 @@
|
|||
{% url 'bookmarks:settings.index' as index_url %}
|
||||
{% url 'bookmarks:settings.data' as data_url %}
|
||||
{% url 'bookmarks:settings.general' as general_url %}
|
||||
{% url 'bookmarks:settings.integrations' as integrations_url %}
|
||||
{% url 'bookmarks:settings.api' as api_url %}
|
||||
|
||||
<ul class="tab tab-block">
|
||||
<li class="tab-item {% if request.get_full_path == index_url or request.get_full_path == data_url%}active{% endif %}">
|
||||
<a href="{% url 'bookmarks:settings.data' %}">Data</a>
|
||||
<li class="tab-item {% if request.get_full_path == index_url or request.get_full_path == general_url%}active{% endif %}">
|
||||
<a href="{% url 'bookmarks:settings.general' %}">General</a>
|
||||
</li>
|
||||
<li class="tab-item {% if request.get_full_path == integrations_url %}active{% endif %}">
|
||||
<a href="{% url 'bookmarks:settings.integrations' %}">Integrations</a>
|
||||
|
@ -16,7 +16,10 @@
|
|||
<li class="tab-item tooltip tooltip-bottom" data-tooltip="The admin panel provides additional features 
 such as user management and bulk operations.">
|
||||
<a href="{% url 'admin:index' %}" target="_blank">
|
||||
<span>Admin</span>
|
||||
<i class="icon icon-share ml-1" style="font-size: 12px"></i>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="ml-1" style="width: 1.2em; height: 1.2em; vertical-align: -0.2em;">
|
||||
<path d="M11 3a1 1 0 100 2h2.586l-6.293 6.293a1 1 0 101.414 1.414L15 6.414V9a1 1 0 102 0V4a1 1 0 00-1-1h-5z" />
|
||||
<path d="M5 5a2 2 0 00-2 2v8a2 2 0 002 2h8a2 2 0 002-2v-3a1 1 0 10-2 0v3H5V7h3a1 1 0 000-2H5z" />
|
||||
</svg>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
12
bookmarks/tests/test_user_profile_model.py
Normal file
12
bookmarks/tests/test_user_profile_model.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
from django.test import TestCase
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from bookmarks.models import UserProfile
|
||||
|
||||
|
||||
class UserProfileTestCase(TestCase):
|
||||
|
||||
def test_create_user_should_init_profile(self):
|
||||
user = User.objects.create_user('testuser', 'test@example.com', 'password123')
|
||||
profile = UserProfile.objects.all().filter(user_id=user.id).first()
|
||||
self.assertIsNotNone(profile)
|
|
@ -19,8 +19,8 @@ urlpatterns = [
|
|||
path('bookmarks/<int:bookmark_id>/archive', views.bookmarks.archive, name='archive'),
|
||||
path('bookmarks/<int:bookmark_id>/unarchive', views.bookmarks.unarchive, name='unarchive'),
|
||||
# Settings
|
||||
path('settings', views.settings.data, name='settings.index'),
|
||||
path('settings/data', views.settings.data, name='settings.data'),
|
||||
path('settings', views.settings.general, name='settings.index'),
|
||||
path('settings/general', views.settings.general, name='settings.general'),
|
||||
path('settings/integrations', views.settings.integrations, name='settings.integrations'),
|
||||
path('settings/api', views.settings.api, name='settings.api'),
|
||||
path('settings/import', views.settings.bookmark_import, name='settings.import'),
|
||||
|
|
|
@ -7,6 +7,7 @@ from django.shortcuts import render
|
|||
from django.urls import reverse
|
||||
from rest_framework.authtoken.models import Token
|
||||
|
||||
from bookmarks.models import UserProfileForm
|
||||
from bookmarks.queries import query_bookmarks
|
||||
from bookmarks.services.exporter import export_netscape_html
|
||||
from bookmarks.services.importer import import_netscape_html
|
||||
|
@ -15,10 +16,18 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
|
||||
@login_required
|
||||
def data(request):
|
||||
def general(request):
|
||||
if request.method == 'POST':
|
||||
form = UserProfileForm(request.POST, instance=request.user.profile)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
else:
|
||||
form = UserProfileForm(instance=request.user.profile)
|
||||
|
||||
import_success_message = _find_message_with_tag(messages.get_messages(request), 'bookmark_import_success')
|
||||
import_errors_message = _find_message_with_tag(messages.get_messages(request), 'bookmark_import_errors')
|
||||
return render(request, 'settings/data.html', {
|
||||
return render(request, 'settings/general.html', {
|
||||
'form': form,
|
||||
'import_success_message': import_success_message,
|
||||
'import_errors_message': import_errors_message,
|
||||
})
|
||||
|
@ -61,7 +70,7 @@ def bookmark_import(request):
|
|||
messages.error(request, 'An error occurred during bookmark import.', 'bookmark_import_errors')
|
||||
pass
|
||||
|
||||
return HttpResponseRedirect(reverse('bookmarks:settings.data'))
|
||||
return HttpResponseRedirect(reverse('bookmarks:settings.general'))
|
||||
|
||||
|
||||
@login_required
|
||||
|
@ -77,7 +86,7 @@ def bookmark_export(request):
|
|||
|
||||
return response
|
||||
except:
|
||||
return render(request, 'settings/data.html', {
|
||||
return render(request, 'settings/general.html', {
|
||||
'export_error': 'An error occurred during bookmark export.'
|
||||
})
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ confusable-homoglyphs==3.2.0
|
|||
Django==2.2.18
|
||||
django-appconf==1.0.3
|
||||
django-compressor==2.3
|
||||
django-debug-toolbar==3.2
|
||||
django-generate-secret-key==1.0.2
|
||||
django-picklefield==2.0
|
||||
django-registration==3.0.1
|
||||
|
|
|
@ -11,6 +11,14 @@ DEBUG = True
|
|||
# Turn on SASS compilation
|
||||
SASS_PROCESSOR_ENABLED = True
|
||||
|
||||
# Enable debug toolbar
|
||||
INSTALLED_APPS.append('debug_toolbar')
|
||||
MIDDLEWARE.append('debug_toolbar.middleware.DebugToolbarMiddleware')
|
||||
|
||||
INTERNAL_IPS = [
|
||||
'127.0.0.1',
|
||||
]
|
||||
|
||||
# Enable debug logging
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
|
|
|
@ -17,7 +17,7 @@ from django.contrib.auth import views as auth_views
|
|||
from django.urls import path, include
|
||||
|
||||
from bookmarks.admin import linkding_admin_site
|
||||
from .settings import ALLOW_REGISTRATION
|
||||
from .settings import ALLOW_REGISTRATION, DEBUG
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', linkding_admin_site.urls),
|
||||
|
@ -28,5 +28,9 @@ urlpatterns = [
|
|||
path('', include('bookmarks.urls')),
|
||||
]
|
||||
|
||||
if DEBUG:
|
||||
import debug_toolbar
|
||||
urlpatterns.append(path('__debug__/', include(debug_toolbar.urls)))
|
||||
|
||||
if ALLOW_REGISTRATION:
|
||||
urlpatterns.append(path('', include('django_registration.backends.one_step.urls')))
|
||||
|
|
Loading…
Reference in a new issue