favicon: Redo favicon customization

pull/3881/head
Richard Hansen 2021-04-20 00:53:22 -04:00
parent 92e0bff80c
commit ea8846154f
10 changed files with 64 additions and 13 deletions

View File

@ -1,3 +1,12 @@
# Next release
### Compatibility changes
* The `favicon` setting is now interpreted as a pathname to a favicon file, not
a URL. Please see the documentation comment in `settings.json.template`.
* The undocumented `faviconPad` and `faviconTimeslider` settings have been
removed.
# 1.8.13 # 1.8.13
### Notable fixes ### Notable fixes

View File

@ -80,10 +80,12 @@
"title": "${TITLE:Etherpad}", "title": "${TITLE:Etherpad}",
/* /*
* favicon default name * Pathname of the favicon you want to use. If null, the skin's favicon is
* alternatively, set up a fully specified Url to your own favicon * used if one is provided by the skin, otherwise the default Etherpad favicon
* is used. If this is a relative path it is interpreted as relative to the
* Etherpad root directory.
*/ */
"favicon": "${FAVICON:favicon.ico}", "favicon": "${FAVICON}",
/* /*
* Skin name. * Skin name.

View File

@ -71,10 +71,12 @@
"title": "Etherpad", "title": "Etherpad",
/* /*
* favicon default name * Pathname of the favicon you want to use. If null, the skin's favicon is
* alternatively, set up a fully specified Url to your own favicon * used if one is provided by the skin, otherwise the default Etherpad favicon
* is used. If this is a relative path it is interpreted as relative to the
* Etherpad root directory.
*/ */
"favicon": "favicon.ico", "favicon": null,
/* /*
* Skin name. * Skin name.

View File

@ -80,6 +80,7 @@ exports.expressCreateServer = (hookName, args, cb) => {
args.app.get('/favicon.ico', (req, res, next) => { args.app.get('/favicon.ico', (req, res, next) => {
(async () => { (async () => {
const fns = [ const fns = [
...(settings.favicon ? [path.resolve(settings.root, settings.favicon)] : []),
path.join(settings.root, 'src', 'static', 'skins', settings.skinName, 'favicon.ico'), path.join(settings.root, 'src', 'static', 'skins', settings.skinName, 'favicon.ico'),
path.join(settings.root, 'src', 'static', 'favicon.ico'), path.join(settings.root, 'src', 'static', 'favicon.ico'),
]; ];

View File

@ -50,11 +50,12 @@ console.log('All relative paths will be interpreted relative to the identified '
exports.title = 'Etherpad'; exports.title = 'Etherpad';
/** /**
* The app favicon fully specified url, visible e.g. in the browser window * Pathname of the favicon you want to use. If null, the skin's favicon is
* used if one is provided by the skin, otherwise the default Etherpad favicon
* is used. If this is a relative path it is interpreted as relative to the
* Etherpad root directory.
*/ */
exports.favicon = 'favicon.ico'; exports.favicon = null;
exports.faviconPad = `../${exports.favicon}`;
exports.faviconTimeslider = `../../${exports.favicon}`;
/* /*
* Skin name. * Skin name.

View File

@ -8,7 +8,7 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="referrer" content="no-referrer"> <meta name="referrer" content="no-referrer">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
<link rel="shortcut icon" href="<%=settings.favicon%>"> <link rel="shortcut icon" href="favicon.ico">
<link rel="localizations" type="application/l10n+json" href="locales.json"> <link rel="localizations" type="application/l10n+json" href="locales.json">
<script type="text/javascript" src="static/js/vendors/html10n.js?v=<%=settings.randomVersionString%>"></script> <script type="text/javascript" src="static/js/vendors/html10n.js?v=<%=settings.randomVersionString%>"></script>

View File

@ -39,7 +39,7 @@
<meta name="robots" content="noindex, nofollow"> <meta name="robots" content="noindex, nofollow">
<meta name="referrer" content="no-referrer"> <meta name="referrer" content="no-referrer">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
<link rel="shortcut icon" href="<%=settings.faviconPad%>"> <link rel="shortcut icon" href="../favicon.ico">
<% e.begin_block("styles"); %> <% e.begin_block("styles"); %>
<link href="../static/css/pad.css?v=<%=settings.randomVersionString%>" rel="stylesheet"> <link href="../static/css/pad.css?v=<%=settings.randomVersionString%>" rel="stylesheet">

View File

@ -33,7 +33,7 @@
<meta name="robots" content="noindex, nofollow"> <meta name="robots" content="noindex, nofollow">
<meta name="referrer" content="no-referrer"> <meta name="referrer" content="no-referrer">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
<link rel="shortcut icon" href="<%=settings.faviconTimeslider%>"> <link rel="shortcut icon" href="../../favicon.ico">
<% e.begin_block("timesliderStyles"); %> <% e.begin_block("timesliderStyles"); %>
<link rel="stylesheet" href="../../static/css/pad.css?v=<%=settings.randomVersionString%>"> <link rel="stylesheet" href="../../static/css/pad.css?v=<%=settings.randomVersionString%>">
<link rel="stylesheet" href="../../static/css/iframe_editor.css?v=<%=settings.randomVersionString%>"> <link rel="stylesheet" href="../../static/css/iframe_editor.css?v=<%=settings.randomVersionString%>">

Binary file not shown.

After

Width:  |  Height:  |  Size: 635 B

View File

@ -12,11 +12,13 @@ describe(__filename, function () {
let agent; let agent;
let backupSettings; let backupSettings;
let skinDir; let skinDir;
let wantCustomIcon;
let wantDefaultIcon; let wantDefaultIcon;
let wantSkinIcon; let wantSkinIcon;
before(async function () { before(async function () {
agent = await common.init(); agent = await common.init();
wantCustomIcon = await fsp.readFile(path.join(__dirname, 'favicon-test-custom.png'));
wantDefaultIcon = await fsp.readFile(path.join(settings.root, 'src', 'static', 'favicon.ico')); wantDefaultIcon = await fsp.readFile(path.join(settings.root, 'src', 'static', 'favicon.ico'));
wantSkinIcon = await fsp.readFile(path.join(__dirname, 'favicon-test-skin.png')); wantSkinIcon = await fsp.readFile(path.join(__dirname, 'favicon-test-skin.png'));
}); });
@ -28,6 +30,7 @@ describe(__filename, function () {
}); });
afterEach(async function () { afterEach(async function () {
delete settings.favicon;
delete settings.skinName; delete settings.skinName;
Object.assign(settings, backupSettings); Object.assign(settings, backupSettings);
try { try {
@ -38,8 +41,40 @@ describe(__filename, function () {
} catch (err) { /* intentionally ignored */ } } catch (err) { /* intentionally ignored */ }
}); });
it('uses custom favicon if set (relative pathname)', async function () {
settings.favicon =
path.relative(settings.root, path.join(__dirname, 'favicon-test-custom.png'));
assert(!path.isAbsolute(settings.favicon));
const {body: gotIcon} = await agent.get('/favicon.ico')
.accept('png').buffer(true).parse(superagent.parse.image)
.expect(200);
assert(gotIcon.equals(wantCustomIcon));
});
it('uses custom favicon if set (absolute pathname)', async function () {
settings.favicon = path.join(__dirname, 'favicon-test-custom.png');
assert(path.isAbsolute(settings.favicon));
const {body: gotIcon} = await agent.get('/favicon.ico')
.accept('png').buffer(true).parse(superagent.parse.image)
.expect(200);
assert(gotIcon.equals(wantCustomIcon));
});
it('falls back if custom favicon is missing', async function () {
// The previous default for settings.favicon was 'favicon.ico', so many users will continue to
// have that in their settings.json for a long time. There is unlikely to be a favicon at
// path.resolve(settings.root, 'favicon.ico'), so this test ensures that 'favicon.ico' won't be
// a problem for those users.
settings.favicon = 'favicon.ico';
const {body: gotIcon} = await agent.get('/favicon.ico')
.accept('png').buffer(true).parse(superagent.parse.image)
.expect(200);
assert(gotIcon.equals(wantDefaultIcon));
});
it('uses skin favicon if present', async function () { it('uses skin favicon if present', async function () {
await fsp.writeFile(path.join(skinDir, 'favicon.ico'), wantSkinIcon); await fsp.writeFile(path.join(skinDir, 'favicon.ico'), wantSkinIcon);
settings.favicon = null;
const {body: gotIcon} = await agent.get('/favicon.ico') const {body: gotIcon} = await agent.get('/favicon.ico')
.accept('png').buffer(true).parse(superagent.parse.image) .accept('png').buffer(true).parse(superagent.parse.image)
.expect(200); .expect(200);
@ -47,6 +82,7 @@ describe(__filename, function () {
}); });
it('falls back to default favicon', async function () { it('falls back to default favicon', async function () {
settings.favicon = null;
const {body: gotIcon} = await agent.get('/favicon.ico') const {body: gotIcon} = await agent.get('/favicon.ico')
.accept('png').buffer(true).parse(superagent.parse.image) .accept('png').buffer(true).parse(superagent.parse.image)
.expect(200); .expect(200);