Implement bookmarklet
This commit is contained in:
parent
4f8a66b3bd
commit
312ab3db17
|
@ -75,7 +75,9 @@ class BookmarkForm(forms.ModelForm):
|
||||||
required=False)
|
required=False)
|
||||||
description = forms.CharField(required=False,
|
description = forms.CharField(required=False,
|
||||||
widget=forms.Textarea())
|
widget=forms.Textarea())
|
||||||
|
# Hidden field that determines whether to close window/tab after saving the bookmark
|
||||||
|
auto_close = forms.CharField(required=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Bookmark
|
model = Bookmark
|
||||||
fields = ['url', 'tag_string', 'title', 'description']
|
fields = ['url', 'tag_string', 'title', 'description', 'auto_close']
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
{% extends "bookmarks/layout.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="columns">
|
||||||
|
<section class="content-area column col-12">
|
||||||
|
<div class="content-area-header">
|
||||||
|
<h2>Bookmarklet</h2>
|
||||||
|
</div>
|
||||||
|
<p>The bookmarklet is a quick way to add new bookmarks without opening the linkding application
|
||||||
|
first. Here's how it works:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Drag the bookmarklet below into your browsers bookmark bar / toolbar</li>
|
||||||
|
<li>Open the website that you want to bookmark</li>
|
||||||
|
<li>Click the bookmarklet in your browsers toolbar</li>
|
||||||
|
<li>linkding opens in a new window or tab and allows you to add a bookmark for the site</li>
|
||||||
|
<li>After saving the bookmark the linkding window closes and you are back on your website</li>
|
||||||
|
</ul>
|
||||||
|
<p>Drag the following bookmarklet to your browsers toolbar:</p>
|
||||||
|
<a href="javascript: {% include 'bookmarks/bookmarklet.js' %}"
|
||||||
|
class="btn btn-primary">📎 Add bookmark</a>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
(function() {
|
||||||
|
var bookmarkUrl = window.location;
|
||||||
|
var applicationUrl = '{{ application_url }}';
|
||||||
|
|
||||||
|
applicationUrl += '?url=' + bookmarkUrl;
|
||||||
|
applicationUrl += '&auto_close';
|
||||||
|
|
||||||
|
window.open(applicationUrl);
|
||||||
|
})();
|
|
@ -0,0 +1,9 @@
|
||||||
|
{% extends "bookmarks/layout.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<script type="application/javascript">
|
||||||
|
window.close()
|
||||||
|
</script>
|
||||||
|
<p>You can now close this window.</p>
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
<div class="bookmarks-form">
|
<div class="bookmarks-form">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
{{ form.auto_close|attr:"type:hidden" }}
|
||||||
<div class="form-group {% if form.url.errors %}has-error{% endif %}">
|
<div class="form-group {% if form.url.errors %}has-error{% endif %}">
|
||||||
<label for="{{ form.url.id_for_label }}" class="form-label">URL</label>
|
<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" }}
|
||||||
|
@ -15,7 +16,8 @@
|
||||||
<label for="{{ form.title.id_for_label }}" class="form-label">Tags</label>
|
<label for="{{ form.title.id_for_label }}" class="form-label">Tags</label>
|
||||||
{{ form.tag_string|add_class:"form-input" }}
|
{{ form.tag_string|add_class:"form-input" }}
|
||||||
<div class="form-input-hint">
|
<div class="form-input-hint">
|
||||||
Enter any number of tags separated by space and <strong>without</strong> the hash (#). If a tag does not exist it will be
|
Enter any number of tags separated by space and <strong>without</strong> the hash (#). If a tag does not
|
||||||
|
exist it will be
|
||||||
automatically created.
|
automatically created.
|
||||||
</div>
|
</div>
|
||||||
{{ form.tag_string.errors }}
|
{{ form.tag_string.errors }}
|
||||||
|
@ -43,7 +45,11 @@
|
||||||
{{ form.description.errors }}
|
{{ form.description.errors }}
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group mt-2">
|
<div class="form-group mt-2">
|
||||||
|
{% if auto_close %}
|
||||||
|
<input type="submit" value="Save and close" class="btn btn-primary mr-2">
|
||||||
|
{% else %}
|
||||||
<input type="submit" value="Save" class="btn btn-primary mr-2">
|
<input type="submit" value="Save" class="btn btn-primary mr-2">
|
||||||
|
{% endif %}
|
||||||
<a href="{% url 'bookmarks:index' %}" class="btn">Nevermind</a>
|
<a href="{% url 'bookmarks:index' %}" class="btn">Nevermind</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -56,7 +62,9 @@
|
||||||
const titleInput = document.getElementById('{{ form.title.id_for_label }}');
|
const titleInput = document.getElementById('{{ form.title.id_for_label }}');
|
||||||
const descriptionInput = document.getElementById('{{ form.description.id_for_label }}');
|
const descriptionInput = document.getElementById('{{ form.description.id_for_label }}');
|
||||||
|
|
||||||
urlInput.addEventListener('input', () => {
|
urlInput.addEventListener('input', updateMetadata);
|
||||||
|
|
||||||
|
function updateMetadata() {
|
||||||
toggleIcon(titleInput, true);
|
toggleIcon(titleInput, true);
|
||||||
toggleIcon(descriptionInput, true);
|
toggleIcon(descriptionInput, true);
|
||||||
|
|
||||||
|
@ -70,12 +78,14 @@
|
||||||
toggleIcon(titleInput, false);
|
toggleIcon(titleInput, false);
|
||||||
toggleIcon(descriptionInput, false);
|
toggleIcon(descriptionInput, false);
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
function toggleIcon(input, show) {
|
function toggleIcon(input, show) {
|
||||||
const icon = input.parentNode.querySelector('i.form-icon');
|
const icon = input.parentNode.querySelector('i.form-icon');
|
||||||
icon.style['visibility'] = show ? 'visible' : 'hidden';
|
icon.style['visibility'] = show ? 'visible' : 'hidden';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (urlInput.value) updateMetadata();
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated %}
|
||||||
<section class="navbar-section">
|
<section class="navbar-section">
|
||||||
<a href="{% url 'bookmarks:new' %}" class="btn btn-primary mr-2">Add bookmark</a>
|
<a href="{% url 'bookmarks:new' %}" class="btn btn-primary mr-2">Add bookmark</a>
|
||||||
<a href="/bookmarks" class="btn btn-link">Bookmarks</a>
|
<a href="/bookmarklet" class="btn btn-link">Bookmarklet</a>
|
||||||
<a href="/settings" class="btn btn-link">Settings</a>
|
<a href="/settings" class="btn btn-link">Settings</a>
|
||||||
<a href="/logout" class="btn btn-link">Logout</a>
|
<a href="/logout" class="btn btn-link">Logout</a>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<h2>New bookmark</h2>
|
<h2>New bookmark</h2>
|
||||||
</div>
|
</div>
|
||||||
<form action="{% url 'bookmarks:new' %}" method="post" class="col-6" novalidate>
|
<form action="{% url 'bookmarks:new' %}" method="post" class="col-6" novalidate>
|
||||||
{% bookmark_form form %}
|
{% bookmark_form form auto_close %}
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,9 +8,10 @@ register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@register.inclusion_tag('bookmarks/form.html', name='bookmark_form')
|
@register.inclusion_tag('bookmarks/form.html', name='bookmark_form')
|
||||||
def bookmark_form(form: BookmarkForm):
|
def bookmark_form(form: BookmarkForm, auto_close: bool = False):
|
||||||
return {
|
return {
|
||||||
'form': form,
|
'form': form,
|
||||||
|
'auto_close': auto_close
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,10 @@ urlpatterns = [
|
||||||
# Bookmarks
|
# Bookmarks
|
||||||
path('bookmarks', views.bookmarks_index, name='index'),
|
path('bookmarks', views.bookmarks_index, name='index'),
|
||||||
path('bookmarks/new', views.bookmarks_new, name='new'),
|
path('bookmarks/new', views.bookmarks_new, name='new'),
|
||||||
|
path('bookmarks/close', views.bookmarks_close, name='close'),
|
||||||
path('bookmarks/<int:bookmark_id>/edit', views.bookmarks_edit, name='edit'),
|
path('bookmarks/<int:bookmark_id>/edit', views.bookmarks_edit, name='edit'),
|
||||||
path('bookmarks/<int:bookmark_id>/remove', views.bookmarks_remove, name='remove'),
|
path('bookmarks/<int:bookmark_id>/remove', views.bookmarks_remove, name='remove'),
|
||||||
|
path('bookmarklet', views.bookmarks_bookmarklet, name='bookmarklet'),
|
||||||
# Settings
|
# Settings
|
||||||
path('settings', views.settings_index, name='settings_index'),
|
path('settings', views.settings_index, name='settings_index'),
|
||||||
path('settings/import', views.settings_bookmark_import, name='settings_import'),
|
path('settings/import', views.settings_bookmark_import, name='settings_import'),
|
||||||
|
|
|
@ -35,16 +35,27 @@ def bookmarks_index(request):
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def bookmarks_new(request):
|
def bookmarks_new(request):
|
||||||
|
initial_url = request.GET.get('url')
|
||||||
|
initial_auto_close = 'auto_close' in request.GET
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
form = BookmarkForm(request.POST)
|
form = BookmarkForm(request.POST)
|
||||||
|
auto_close = form.data['auto_close']
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
current_user = request.user
|
current_user = request.user
|
||||||
create_bookmark(form, current_user)
|
create_bookmark(form, current_user)
|
||||||
|
if auto_close:
|
||||||
|
return HttpResponseRedirect(reverse('bookmarks:close'))
|
||||||
|
else:
|
||||||
return HttpResponseRedirect(reverse('bookmarks:index'))
|
return HttpResponseRedirect(reverse('bookmarks:index'))
|
||||||
else:
|
else:
|
||||||
form = BookmarkForm()
|
form = BookmarkForm()
|
||||||
|
if initial_url:
|
||||||
|
form.initial['url'] = initial_url
|
||||||
|
if initial_auto_close:
|
||||||
|
form.initial['auto_close'] = 'true'
|
||||||
|
|
||||||
return render(request, 'bookmarks/new.html', {'form': form})
|
return render(request, 'bookmarks/new.html', {'form': form, 'auto_close': initial_auto_close})
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -67,3 +78,15 @@ def bookmarks_remove(request, bookmark_id: int):
|
||||||
bookmark = Bookmark.objects.get(pk=bookmark_id)
|
bookmark = Bookmark.objects.get(pk=bookmark_id)
|
||||||
bookmark.delete()
|
bookmark.delete()
|
||||||
return HttpResponseRedirect(reverse('bookmarks:index'))
|
return HttpResponseRedirect(reverse('bookmarks:index'))
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def bookmarks_bookmarklet(request):
|
||||||
|
return render(request, 'bookmarks/bookmarklet.html', {
|
||||||
|
'application_url': request.build_absolute_uri("/bookmarks/new")
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def bookmarks_close(request):
|
||||||
|
return render(request, 'bookmarks/close.html')
|
||||||
|
|
Loading…
Reference in New Issue