mirror of
https://github.com/sissbruecker/linkding.git
synced 2026-03-11 20:24:41 +08:00
233 lines
9.0 KiB
HTML
233 lines
9.0 KiB
HTML
{% load static %}
|
|
{% load shared %}
|
|
<div class="bookmarks-form">
|
|
{% csrf_token %}
|
|
{{ form.auto_close }}
|
|
<div class="form-group">
|
|
{% formlabel form.url "URL" %}
|
|
<div class="has-icon-right">
|
|
{% formfield form.url autofocus=True %}
|
|
<i class="form-icon loading"></i>
|
|
</div>
|
|
{{ form.url.errors }}
|
|
<div class="form-input-hint bookmark-exists">
|
|
This URL is already bookmarked.
|
|
The form has been pre-filled with the existing bookmark, and saving the form will update the existing bookmark.
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
{% formlabel form.tag_string "Tags" %}
|
|
{% formfield form.tag_string has_help=True %}
|
|
{% formhelp form.tag_string %}
|
|
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.
|
|
{% endformhelp %}
|
|
<div class="form-input-hint auto-tags"></div>
|
|
</div>
|
|
<div class="form-group">
|
|
<div class="d-flex justify-between align-baseline">
|
|
{% formlabel form.title "Title" %}
|
|
<div class="flex">
|
|
<button id="refresh-button" class="btn btn-link suffix-button" type="button">Refresh from website</button>
|
|
<ld-clear-button data-for="{{ form.title.id_for_label }}">
|
|
<button class="ml-2 btn btn-link suffix-button" type="button">Clear</button>
|
|
</ld-clear-button>
|
|
</div>
|
|
</div>
|
|
{% formfield form.title %}
|
|
</div>
|
|
<div class="form-group">
|
|
<div class="d-flex justify-between align-baseline">
|
|
{% formlabel form.description "Description" %}
|
|
<ld-clear-button data-for="{{ form.description.id_for_label }}">
|
|
<button class="btn btn-link suffix-button" type="button">Clear</button>
|
|
</ld-clear-button>
|
|
</div>
|
|
{% formfield form.description rows="3" %}
|
|
</div>
|
|
<div class="form-group">
|
|
<details class="notes"{% if form.has_notes %} open{% endif %}>
|
|
<summary>
|
|
<span class="form-label d-inline-block">Notes</span>
|
|
</summary>
|
|
<label for="{{ form.notes.id_for_label }}" class="text-assistive">Notes</label>
|
|
{% formfield form.notes rows="8" has_help=True %}
|
|
{% formhelp form.notes %}
|
|
Additional notes, supports Markdown.
|
|
{% endformhelp %}
|
|
</details>
|
|
</div>
|
|
<div class="form-group">
|
|
{% formfield form.unread label="Mark as unread" has_help=True %}
|
|
{% formhelp form.unread %}
|
|
Unread bookmarks can be filtered for, and marked as read after you had a chance to look at them.
|
|
{% endformhelp %}
|
|
</div>
|
|
{% if request.user_profile.enable_sharing %}
|
|
<div class="form-group">
|
|
{% formfield form.shared label="Share" has_help=True %}
|
|
{% formhelp form.shared %}
|
|
{% if request.user_profile.enable_public_sharing %}
|
|
Share this bookmark with other registered users and anonymous users.
|
|
{% else %}
|
|
Share this bookmark with other registered users.
|
|
{% endif %}
|
|
{% endformhelp %}
|
|
</div>
|
|
{% endif %}
|
|
<div class="divider"></div>
|
|
<div class="form-group d-flex justify-between">
|
|
{% if form.is_auto_close %}
|
|
<input type="submit" value="Save and close" class="btn btn-primary btn-wide">
|
|
{% else %}
|
|
<input type="submit"
|
|
value="Save"
|
|
class="btn btn-primary btn btn-primary btn-wide">
|
|
{% endif %}
|
|
<a href="{{ return_url }}" class="btn">Cancel</a>
|
|
</div>
|
|
<script type="application/javascript">
|
|
/**
|
|
* - Pre-fill title and description with metadata from website as soon as URL changes
|
|
* - Show hint if URL is already bookmarked
|
|
*/
|
|
(function init() {
|
|
const urlInput = document.getElementById('{{ form.url.id_for_label }}');
|
|
const titleInput = document.getElementById('{{ form.title.id_for_label }}');
|
|
const descriptionInput = document.getElementById('{{ form.description.id_for_label }}');
|
|
const notesDetails = document.querySelector('form details.notes');
|
|
const notesInput = document.getElementById('{{ form.notes.id_for_label }}');
|
|
const unreadCheckbox = document.getElementById('{{ form.unread.id_for_label }}');
|
|
const sharedCheckbox = document.getElementById('{{ form.shared.id_for_label }}');
|
|
const refreshButton = document.getElementById('refresh-button');
|
|
const bookmarkExistsHint = document.querySelector('.form-input-hint.bookmark-exists');
|
|
const editedBookmarkId = parseInt('{{ form.instance.id|default:0 }}');
|
|
let isTitleModified = !!titleInput.value;
|
|
let isDescriptionModified = !!descriptionInput.value;
|
|
|
|
function toggleLoadingIcon(input, show) {
|
|
const icon = input.parentNode.querySelector('i.form-icon');
|
|
icon.style['visibility'] = show ? 'visible' : 'hidden';
|
|
}
|
|
|
|
function updateInput(input, value) {
|
|
if (!input) {
|
|
return;
|
|
}
|
|
input.value = value;
|
|
input.dispatchEvent(new Event('value-changed'));
|
|
}
|
|
|
|
function updateCheckbox(input, value) {
|
|
if (!input) {
|
|
return;
|
|
}
|
|
input.checked = value;
|
|
}
|
|
|
|
function checkUrl() {
|
|
if (!urlInput.value) {
|
|
return;
|
|
}
|
|
|
|
toggleLoadingIcon(urlInput, true);
|
|
|
|
const websiteUrl = encodeURIComponent(urlInput.value);
|
|
const requestUrl = `{% url 'linkding:api-root' %}bookmarks/check?url=${websiteUrl}`;
|
|
fetch(requestUrl)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
const metadata = data.metadata;
|
|
toggleLoadingIcon(urlInput, false);
|
|
|
|
// Display hint if URL is already bookmarked
|
|
const existingBookmark = data.bookmark;
|
|
bookmarkExistsHint.style['display'] = existingBookmark ? 'block' : 'none';
|
|
refreshButton.style['display'] = existingBookmark ? 'inline-block' : 'none';
|
|
|
|
// Prefill form with existing bookmark data
|
|
if (existingBookmark) {
|
|
// Workaround: tag input will be replaced by tag autocomplete, so
|
|
// defer getting the input until we need it
|
|
const tagsInput = document.getElementById('{{ form.tag_string.id_for_label }}');
|
|
|
|
bookmarkExistsHint.style['display'] = 'block';
|
|
notesDetails.open = !!existingBookmark.notes;
|
|
updateInput(titleInput, existingBookmark.title);
|
|
updateInput(descriptionInput, existingBookmark.description);
|
|
updateInput(notesInput, existingBookmark.notes);
|
|
updateInput(tagsInput, existingBookmark.tag_names.join(" "));
|
|
updateCheckbox(unreadCheckbox, existingBookmark.unread);
|
|
updateCheckbox(sharedCheckbox, existingBookmark.shared);
|
|
} else {
|
|
// Update title and description with website metadata, unless they have been modified
|
|
if (!isTitleModified) {
|
|
updateInput(titleInput, metadata.title);
|
|
}
|
|
if (!isDescriptionModified) {
|
|
updateInput(descriptionInput, metadata.description);
|
|
}
|
|
}
|
|
|
|
// Preview auto tags
|
|
const autoTags = data.auto_tags;
|
|
const autoTagsHint = document.querySelector('.form-input-hint.auto-tags');
|
|
|
|
if (autoTags.length > 0) {
|
|
autoTags.sort();
|
|
autoTagsHint.style['display'] = 'block';
|
|
autoTagsHint.innerHTML = `Auto tags: ${autoTags.join(" ")}`;
|
|
} else {
|
|
autoTagsHint.style['display'] = 'none';
|
|
}
|
|
});
|
|
}
|
|
|
|
function refreshMetadata() {
|
|
if (!urlInput.value) {
|
|
return;
|
|
}
|
|
|
|
toggleLoadingIcon(urlInput, true);
|
|
|
|
const websiteUrl = encodeURIComponent(urlInput.value);
|
|
const requestUrl = `{% url 'linkding:api-root' %}bookmarks/check?url=${websiteUrl}&ignore_cache=true`;
|
|
|
|
fetch(requestUrl)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
const metadata = data.metadata;
|
|
const existingBookmark = data.bookmark;
|
|
toggleLoadingIcon(urlInput, false);
|
|
|
|
if (metadata.title && metadata.title !== existingBookmark?.title) {
|
|
titleInput.value = metadata.title;
|
|
titleInput.classList.add("modified");
|
|
}
|
|
|
|
if (metadata.description && metadata.description !== existingBookmark?.description) {
|
|
descriptionInput.value = metadata.description;
|
|
descriptionInput.classList.add("modified");
|
|
}
|
|
});
|
|
}
|
|
|
|
refreshButton.addEventListener('click', refreshMetadata);
|
|
|
|
// Fetch website metadata when page loads and when URL changes, unless we are editing an existing bookmark
|
|
if (!editedBookmarkId) {
|
|
checkUrl();
|
|
urlInput.addEventListener('input', checkUrl);
|
|
titleInput.addEventListener('input', () => {
|
|
isTitleModified = true;
|
|
});
|
|
descriptionInput.addEventListener('input', () => {
|
|
isDescriptionModified = true;
|
|
});
|
|
} else {
|
|
refreshButton.style['display'] = 'inline-block';
|
|
}
|
|
})();
|
|
</script>
|
|
</div>
|