Files
FreshRSS/p/scripts/extra.js
Alexis Degrugillier cc0db9af4f Feature/new archiving (#2335)
* Change archiving config page layout

I've changed some wording and moved actions into a
maintenance section.

* Update purge action

Now we have more control on the purge action. The configuration allows
us to choose what to keep and what to discard in a more precise way.
At the moment, the configuration applies for all feeds.

* Add purge configuration on feed level

Now the extend purge configuration is available on feed level.
It is stored as attributes and will be used in the purge action.

* Update purge action

Now the purge action uses the feed configuration if it exists and
defaults on user configuration if not.

* Add empty option in period list

* Fix configuration warnings

* Add archiving configuration on categories

See #2369

* Add user info back

* Add explanations in UI

* Fixes for SQLite + error + misc.

* Fix invalid feed reference

* Short array syntax

Only for new code, so far

* Fix prefix error

* Query performance, default values

Work in progress

* Fix default values and confirm before leaving

Form cancel and confirm changes before leaving were broken.
And start taking advantage of the short echo syntax `<?= ?>` as we have
moved to PHP 5.4+

* More work

* Tuning SQL

* Fix MariaDB + performance issue

* SQL performance

* Fix SQLite bug

* Fix some attributes JSON encoding bugs

Especially for SQLite export/import

* More uniform, fix bugs

More uniform between global, category, feed settings

* Drop special cases for old articles during refresh

Instead will use lastSeen date with the new archiving logic.
This was generating problems anyway
https://github.com/FreshRSS/FreshRSS/issues/2154

* Draft drop index keep_history

Not needed anymore

* MySQL typo

Now properly tested with MySQL, PostgreSQL, SQLite

* More work for legacy values

Important to avoid overriding user's preference and risking deleting
data erroneously

* Fix PHP 7.3 / 7.4 warnings

@aledeg "Trying to use values of type null, bool, int, float or resource
as an array (such as $null["key"]) will now generate a notice. "
https://php.net/migration74.incompatible

* Reintroduce min articles and take care of legacy parameters

* A few changes forgotten

* Draft of migration + DROP of feed.keep_history

* Fix several errors

And give up using const for SQL to allow multiple database types (and we
cannot redefine a const)

* Add keep_min to categories + factorise archiving logic

* Legacy fix

* Fix bug yield from

* Minor: Use JSON_UNESCAPED_SLASHE for attributes

And make more uniform

* Fix sign and missing variable

* Fine tune the logic
2019-10-23 00:52:15 +02:00

256 lines
7.1 KiB
JavaScript

// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0
"use strict";
/* globals context, openNotification, xmlHttpRequestJson */
/* jshint esversion:6, strict:global */
//<crypto form (Web login)>
function poormanSalt() { //If crypto.getRandomValues is not available
const base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ.0123456789/abcdefghijklmnopqrstuvwxyz';
let text = '$2a$04$';
for (let i = 22; i > 0; i--) {
text += base.charAt(Math.floor(Math.random() * 64));
}
return text;
}
function init_crypto_form() {
/* globals dcodeIO */
const crypto_form = document.getElementById('crypto-form');
if (!crypto_form) {
return;
}
if (!(window.dcodeIO)) {
if (window.console) {
console.log('FreshRSS waiting for bcrypt.js…');
}
setTimeout(init_crypto_form, 100);
return;
}
crypto_form.onsubmit = function (e) {
const submit_button = this.querySelector('button[type="submit"]');
submit_button.disabled = true;
let success = false;
const req = new XMLHttpRequest();
req.open('GET', './?c=javascript&a=nonce&user=' + document.getElementById('username').value, false);
req.onerror = function () {
openNotification('Communication error!', 'bad');
};
req.send();
if (req.status == 200) {
const json = xmlHttpRequestJson(req);
if (!json.salt1 || !json.nonce) {
openNotification('Invalid user!', 'bad');
} else {
try {
const strong = window.Uint32Array && window.crypto && (typeof window.crypto.getRandomValues === 'function'),
s = dcodeIO.bcrypt.hashSync(document.getElementById('passwordPlain').value, json.salt1),
c = dcodeIO.bcrypt.hashSync(json.nonce + s, strong ? dcodeIO.bcrypt.genSaltSync(4) : poormanSalt());
document.getElementById('challenge').value = c;
if (!s || !c) {
openNotification('Crypto error!', 'bad');
} else {
success = true;
}
} catch (ex) {
openNotification('Crypto exception! ' + ex, 'bad');
}
}
} else {
req.onerror();
}
submit_button.disabled = false;
return success;
};
}
//</crypto form (Web login)>
function init_share_observers() {
let shares = document.querySelectorAll('.group-share').length;
const shareAdd = document.querySelector('.share.add');
if (shareAdd) {
shareAdd.onclick = function (ev) {
const s = this.parentElement.querySelector('select'),
opt = s.options[s.selectedIndex];
let row = this.closest('form').getAttribute('data-' + opt.getAttribute('data-form'));
row = row.replace(/##label##/g, opt.text);
row = row.replace(/##type##/g, opt.value);
row = row.replace(/##help##/g, opt.getAttribute('data-help'));
row = row.replace(/##key##/g, shares);
row = row.replace(/##method##/g, opt.getAttribute('data-method'));
row = row.replace(/##field##/g, opt.getAttribute('data-field'));
this.closest('.form-group').insertAdjacentHTML('beforebegin', row);
shares++;
return false;
};
}
}
function init_remove_observers() {
document.querySelectorAll('.post').forEach(function (div) {
div.onclick = function (ev) {
const a = ev.target.closest('a.remove');
if (a) {
const remove_what = a.getAttribute('data-remove');
if (remove_what !== undefined) {
const d = document.getElementById(remove_what);
if (d) {
d.remove();
}
}
return false;
}
};
});
}
function init_feed_observers() {
const s = document.getElementById('category');
if (s && s.matches('select')) {
s.onchange = function (ev) {
const detail = document.getElementById('new_category_name').parentElement;
if (this.value === 'nc') {
detail.setAttribute('aria-hidden', 'false');
detail.querySelector('input').focus();
} else {
detail.setAttribute('aria-hidden', 'true');
}
};
}
}
function init_password_observers() {
document.querySelectorAll('.toggle-password').forEach(function (a) {
a.onmousedown = function (ev) {
const passwordField = document.getElementById(this.getAttribute('data-toggle'));
passwordField.setAttribute('type', 'text');
this.classList.add('active');
return false;
};
a.onmouseup = function (ev) {
const passwordField = document.getElementById(this.getAttribute('data-toggle'));
passwordField.setAttribute('type', 'password');
this.classList.remove('active');
return false;
};
});
}
function init_select_observers() {
document.querySelectorAll('.select-change').forEach(function (s) {
s.onchange = function (ev) {
const opt = s.options[s.selectedIndex],
url = opt.getAttribute('data-url');
if (url) {
s.form.querySelectorAll('[type=submit]').forEach(function (b) {
b.disabled = true;
});
location.href = url;
}
};
});
}
function init_slider_observers() {
const slider = document.getElementById('slider'),
closer = document.getElementById('close-slider');
if (!slider) {
return;
}
document.querySelector('.post').onclick = function (ev) {
const a = ev.target.closest('.open-slider');
if (a) {
if (!context.ajax_loading) {
context.ajax_loading = true;
const req = new XMLHttpRequest();
req.open('GET', a.href + '&ajax=1', true);
req.responseType = 'document';
req.onload = function (e) {
slider.innerHTML = this.response.body.innerHTML;
slider.classList.add('active');
closer.classList.add('active');
context.ajax_loading = false;
};
req.send();
return false;
}
}
};
closer.onclick = function (ev) {
if (data_leave_validation() || confirm(context.i18n.confirmation_default)) {
slider.querySelectorAll('form').forEach(function (f) { f.reset(); });
closer.classList.remove('active');
slider.classList.remove('active');
return true;
} else {
return false;
}
};
}
function data_leave_validation() {
const ds = document.querySelectorAll('[data-leave-validation]');
for (let i = ds.length - 1; i >= 0; i--) {
const input = ds[i];
if (input.type === 'checkbox' || input.type === 'radio') {
if (input.checked != input.getAttribute('data-leave-validation')) {
return false;
}
} else if (input.value != input.getAttribute('data-leave-validation')) {
return false;
}
}
return true;
}
function init_configuration_alert() {
window.onsubmit = function (e) {
window.hasSubmit = true;
};
window.onbeforeunload = function (e) {
if (window.hasSubmit) {
return;
}
if (!data_leave_validation()) {
return false;
}
};
}
function init_extra() {
if (!window.context) {
if (window.console) {
console.log('FreshRSS extra waiting for JS…');
}
window.setTimeout(init_extra, 50); //Wait for all js to be loaded
return;
}
init_crypto_form();
init_share_observers();
init_remove_observers();
init_feed_observers();
init_password_observers();
init_select_observers();
init_slider_observers();
init_configuration_alert();
}
if (document.readyState && document.readyState !== 'loading') {
init_extra();
} else {
document.addEventListener('DOMContentLoaded', function () {
if (window.console) {
console.log('FreshRSS extra waiting for DOMContentLoaded…');
}
init_extra();
}, false);
}
// @license-end