Feature: bulk creation of bookmarks

This changes creates a new form in addlink page allowing to create
multiple bookmarks at once more easily. It focuses on re-using as much
existing code and template component as  possible.

These changes includes:
  - a new form in addlink (hidden behind a button by default),
containing a text area for URL, and tags/private status to apply to
created links
  - this form displays a new template called editlink.batch, itself
including editlink template multiple times
  - User interation in this new templates are handle by a new JS script
(shaare-batch.js) making AJAX requests, and therefore does not need page
reloading
  - ManageShaareController has been split into 3 distinct controllers:
    + ShaareAdd: displays addlink template
    + ShaareManage: various operation applied on existing shaares
(change visibility, pin, deletion, etc.)
    + ShaarePublish: handles creation/edit forms and saving Shaare's
form
  - Updated translations

Fixes 
This commit is contained in:
ArthurHoaro 2020-10-10 17:40:26 +02:00
parent b8e5a253ab
commit 5d8de7587d
25 changed files with 1028 additions and 527 deletions

View file

@ -56,37 +56,41 @@ function updateThumb(basePath, divElement, id) {
(() => {
const basePath = document.querySelector('input[name="js_base_path"]').value;
const loaders = document.querySelectorAll('.loading-input');
/*
* METADATA FOR EDIT BOOKMARK PAGE
*/
const inputTitle = document.querySelector('input[name="lf_title"]');
if (inputTitle != null) {
if (inputTitle.value.length > 0) {
clearLoaders(loaders);
return;
}
const inputTitles = document.querySelectorAll('input[name="lf_title"]');
if (inputTitles != null) {
[...inputTitles].forEach((inputTitle) => {
const form = inputTitle.closest('form[name="linkform"]');
const loaders = form.querySelectorAll('.loading-input');
const url = document.querySelector('input[name="lf_url"]').value;
if (inputTitle.value.length > 0) {
clearLoaders(loaders);
return;
}
const xhr = new XMLHttpRequest();
xhr.open('GET', `${basePath}/admin/metadata?url=${encodeURI(url)}`, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onload = () => {
const result = JSON.parse(xhr.response);
Object.keys(result).forEach((key) => {
if (result[key] !== null && result[key].length) {
const element = document.querySelector(`input[name="lf_${key}"], textarea[name="lf_${key}"]`);
if (element != null && element.value.length === 0) {
element.value = he.decode(result[key]);
const url = form.querySelector('input[name="lf_url"]').value;
const xhr = new XMLHttpRequest();
xhr.open('GET', `${basePath}/admin/metadata?url=${encodeURI(url)}`, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onload = () => {
const result = JSON.parse(xhr.response);
Object.keys(result).forEach((key) => {
if (result[key] !== null && result[key].length) {
const element = form.querySelector(`input[name="lf_${key}"], textarea[name="lf_${key}"]`);
if (element != null && element.value.length === 0) {
element.value = he.decode(result[key]);
}
}
}
});
clearLoaders(loaders);
};
});
clearLoaders(loaders);
};
xhr.send();
xhr.send();
});
}
/*

View file

@ -0,0 +1,107 @@
const sendBookmarkForm = (basePath, formElement) => {
const inputs = formElement
.querySelectorAll('input[type="text"], textarea, input[type="checkbox"], input[type="hidden"]');
const formData = new FormData();
[...inputs].forEach((input) => {
formData.append(input.getAttribute('name'), input.value);
});
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('POST', `${basePath}/admin/shaare`);
xhr.onload = () => {
if (xhr.status !== 200) {
alert(`An error occurred. Return code: ${xhr.status}`);
reject();
} else {
formElement.remove();
resolve();
}
};
xhr.send(formData);
});
};
const sendBookmarkDelete = (buttonElement, formElement) => (
new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', buttonElement.href);
xhr.onload = () => {
if (xhr.status !== 200) {
alert(`An error occurred. Return code: ${xhr.status}`);
reject();
} else {
formElement.remove();
resolve();
}
};
xhr.send();
})
);
const redirectIfEmptyBatch = (basePath, formElements, path) => {
if (formElements == null || formElements.length === 0) {
window.location.href = `${basePath}${path}`;
}
};
(() => {
const basePath = document.querySelector('input[name="js_base_path"]').value;
const getForms = () => document.querySelectorAll('form[name="linkform"]');
const cancelButtons = document.querySelectorAll('[name="cancel-batch-link"]');
if (cancelButtons != null) {
[...cancelButtons].forEach((cancelButton) => {
cancelButton.addEventListener('click', (e) => {
e.preventDefault();
e.target.closest('form[name="linkform"]').remove();
redirectIfEmptyBatch(basePath, getForms(), '/admin/add-shaare');
});
});
}
const saveButtons = document.querySelectorAll('[name="save_edit"]');
if (saveButtons != null) {
[...saveButtons].forEach((saveButton) => {
saveButton.addEventListener('click', (e) => {
e.preventDefault();
const formElement = e.target.closest('form[name="linkform"]');
sendBookmarkForm(basePath, formElement)
.then(() => redirectIfEmptyBatch(basePath, getForms(), '/'));
});
});
}
const saveAllButtons = document.querySelectorAll('[name="save_edit_batch"]');
if (saveAllButtons != null) {
[...saveAllButtons].forEach((saveAllButton) => {
saveAllButton.addEventListener('click', (e) => {
e.preventDefault();
const promises = [];
[...getForms()].forEach((formElement) => {
promises.push(sendBookmarkForm(basePath, formElement));
});
Promise.all(promises).then(() => {
window.location.href = basePath || '/';
});
});
});
}
const deleteButtons = document.querySelectorAll('[name="delete_link"]');
if (deleteButtons != null) {
[...deleteButtons].forEach((deleteButton) => {
deleteButton.addEventListener('click', (e) => {
e.preventDefault();
const formElement = e.target.closest('form[name="linkform"]');
sendBookmarkDelete(e.target, formElement)
.then(() => redirectIfEmptyBatch(basePath, getForms(), '/'));
});
});
}
})();