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 #137
This commit is contained in:
parent
b8e5a253ab
commit
5d8de7587d
25 changed files with 1028 additions and 527 deletions
assets/common/js
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
107
assets/common/js/shaare-batch.js
Normal file
107
assets/common/js/shaare-batch.js
Normal 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(), '/'));
|
||||
});
|
||||
});
|
||||
}
|
||||
})();
|
Loading…
Add table
Add a link
Reference in a new issue