Fix #456: make Funkwhale themable by loading external stylesheets
This commit is contained in:
parent
14e0ac97dc
commit
d7f12caf5c
|
@ -0,0 +1,8 @@
|
||||||
|
Make funkwhale themable by loading external stylesheets (#456)
|
||||||
|
|
||||||
|
Custom themes for Funkwhale
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
If you ever wanted to give a custom look and feel to your instance, this is now possible.
|
||||||
|
|
||||||
|
Check https://docs.funkwhale.audio/configuration.html#theming if you want to know more!
|
|
@ -158,3 +158,79 @@ permissions are:
|
||||||
There is no dedicated interface to manage users permissions, but superusers
|
There is no dedicated interface to manage users permissions, but superusers
|
||||||
can login on the Django's admin at ``/api/admin/`` and grant permissions
|
can login on the Django's admin at ``/api/admin/`` and grant permissions
|
||||||
to users at ``/api/admin/users/user/``.
|
to users at ``/api/admin/users/user/``.
|
||||||
|
|
||||||
|
Theming
|
||||||
|
-------
|
||||||
|
|
||||||
|
Funkwhale supports custom themes, which are great if you want to personnalize the
|
||||||
|
look and feel of your instance. Theming is achieved by declaring
|
||||||
|
additionnal stylesheets you want to load in the front-end.
|
||||||
|
|
||||||
|
Customize the settings
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
In order to know what stylesheets to load, the front-end requests the following
|
||||||
|
url: ``https://your.instance/settings.json``. On typical deployments, this url
|
||||||
|
returns a 404 error, which is simply ignored.
|
||||||
|
|
||||||
|
However, if you return the appropriate payload on this url, you can make the magic
|
||||||
|
work. We will store the necessary files in the ``/srv/funkwhale/custom`` directory:
|
||||||
|
|
||||||
|
.. code-block:: shell
|
||||||
|
|
||||||
|
cd /srv/funkwhale/
|
||||||
|
mkdir custom
|
||||||
|
cat <<EOF > custom/settings.json
|
||||||
|
{
|
||||||
|
"additionalStylesheets": ["/custom/custom.css"]
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
cat <<EOF > custom/custom.css
|
||||||
|
body {
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
By executing the previous commands, you will end up with two files in your ``/srv/funkwhale/custom``
|
||||||
|
directory:
|
||||||
|
|
||||||
|
- ``settings.json`` will tell the front-end what stylesheets you want to load (``/custom/custom.css`` in this example)
|
||||||
|
- ``custom.css`` will hold your custom CSS
|
||||||
|
|
||||||
|
The last step to make this work is to ensure both files are served by the reverse proxy.
|
||||||
|
|
||||||
|
On nginx, add the following snippet to your vhost config::
|
||||||
|
|
||||||
|
location /settings.json {
|
||||||
|
alias /srv/funkwhale/custom/settings.json;
|
||||||
|
}
|
||||||
|
location /custom {
|
||||||
|
alias /srv/funkwhale/custom;
|
||||||
|
}
|
||||||
|
|
||||||
|
On apache, use the following one::
|
||||||
|
|
||||||
|
Alias /settings.json /srv/funkwhale/custom/settings.json
|
||||||
|
Alias /custom /srv/funkwhale/custom
|
||||||
|
|
||||||
|
<Directory "/srv/funkwhale/custom">
|
||||||
|
Options FollowSymLinks
|
||||||
|
AllowOverride None
|
||||||
|
Require all granted
|
||||||
|
</Directory>
|
||||||
|
|
||||||
|
Once done, reload your reverse proxy, refresh Funkwhale in your web browser, and you should see
|
||||||
|
a red background.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
You can reference external urls as well in ``settings.json``, simply use
|
||||||
|
the full urls. Be especially careful with external urls as they may affect your users
|
||||||
|
privacy.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
Loading additional stylesheets and CSS rules can affect the performance and
|
||||||
|
usability of your instance. If you encounter issues with the interfaces and use
|
||||||
|
custom stylesheets, try to disable those to ensure the issue is not caused
|
||||||
|
by your customizations.
|
||||||
|
|
|
@ -29,6 +29,9 @@ module.exports = {
|
||||||
assetsSubDirectory: 'static',
|
assetsSubDirectory: 'static',
|
||||||
assetsPublicPath: '/',
|
assetsPublicPath: '/',
|
||||||
proxyTable: {
|
proxyTable: {
|
||||||
|
'/settings.json': {
|
||||||
|
target: 'http://127.0.0.1:8000/static/',
|
||||||
|
},
|
||||||
'**': {
|
'**': {
|
||||||
target: 'http://nginx:6001',
|
target: 'http://nginx:6001',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
|
<!-- here, we display custom stylesheets, if any -->
|
||||||
|
<link v-for="url in customStylesheets" rel="stylesheet" property="stylesheet" :href="url" :key="url">
|
||||||
<div class="ui main text container instance-chooser" v-if="!$store.state.instance.instanceUrl">
|
<div class="ui main text container instance-chooser" v-if="!$store.state.instance.instanceUrl">
|
||||||
<div class="ui padded segment">
|
<div class="ui padded segment">
|
||||||
<h1 class="ui header"><translate>Choose your instance</translate></h1>
|
<h1 class="ui header"><translate>Choose your instance</translate></h1>
|
||||||
|
@ -175,6 +177,11 @@ export default {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return _.get(this.nodeinfo, 'software.version')
|
return _.get(this.nodeinfo, 'software.version')
|
||||||
|
},
|
||||||
|
customStylesheets () {
|
||||||
|
if (this.$store.state.instance.frontSettings) {
|
||||||
|
return this.$store.state.instance.frontSettings.additionalStylesheets || []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
|
|
@ -126,6 +126,8 @@ axios.interceptors.response.use(function (response) {
|
||||||
return Promise.reject(error)
|
return Promise.reject(error)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
store.dispatch('instance/fetchFrontSettings')
|
||||||
|
|
||||||
/* eslint-disable no-new */
|
/* eslint-disable no-new */
|
||||||
new Vue({
|
new Vue({
|
||||||
el: '#app',
|
el: '#app',
|
||||||
|
|
|
@ -6,6 +6,7 @@ export default {
|
||||||
namespaced: true,
|
namespaced: true,
|
||||||
state: {
|
state: {
|
||||||
maxEvents: 200,
|
maxEvents: 200,
|
||||||
|
frontSettings: {},
|
||||||
instanceUrl: process.env.INSTANCE_URL,
|
instanceUrl: process.env.INSTANCE_URL,
|
||||||
events: [],
|
events: [],
|
||||||
settings: {
|
settings: {
|
||||||
|
@ -53,6 +54,9 @@ export default {
|
||||||
events: (state, value) => {
|
events: (state, value) => {
|
||||||
state.events = value
|
state.events = value
|
||||||
},
|
},
|
||||||
|
frontSettings: (state, value) => {
|
||||||
|
state.frontSettings = value
|
||||||
|
},
|
||||||
instanceUrl: (state, value) => {
|
instanceUrl: (state, value) => {
|
||||||
if (value && !value.endsWith('/')) {
|
if (value && !value.endsWith('/')) {
|
||||||
value = value + '/'
|
value = value + '/'
|
||||||
|
@ -110,6 +114,13 @@ export default {
|
||||||
}, response => {
|
}, response => {
|
||||||
logger.default.error('Error while fetching settings', response.data)
|
logger.default.error('Error while fetching settings', response.data)
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
fetchFrontSettings ({commit}) {
|
||||||
|
return axios.get('/settings.json').then(response => {
|
||||||
|
commit('frontSettings', response.data)
|
||||||
|
}, response => {
|
||||||
|
logger.default.error('Error when fetching front-end configuration (or no customization available)')
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
/* This is a custom CSS file that can be loaded thanks to settings.json */
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"additionalStylesheets": ["/static/custom.css"]
|
||||||
|
}
|
Loading…
Reference in New Issue