diff --git a/changes/changelog.d/456.feature b/changes/changelog.d/456.feature new file mode 100644 index 0000000000000000000000000000000000000000..f05188e98c51fffa2bbcde613e62e62d3d46f2ed --- /dev/null +++ b/changes/changelog.d/456.feature @@ -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! diff --git a/docs/configuration.rst b/docs/configuration.rst index 3e7b5aa25df89add324d1aab753b2303504c9efb..6874476974dc250e2110f3e9bf80e057178dfdf8 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -158,3 +158,79 @@ permissions are: There is no dedicated interface to manage users permissions, but superusers can login on the Django's admin at ``/api/admin/`` and grant permissions 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. diff --git a/front/config/index.js b/front/config/index.js index d10f35e9146258cc4b2271ca2270bd6ae09b4c98..44a3b640bfa3805b9af88dcf209b600d862d9f72 100644 --- a/front/config/index.js +++ b/front/config/index.js @@ -29,6 +29,9 @@ module.exports = { assetsSubDirectory: 'static', assetsPublicPath: '/', proxyTable: { + '/settings.json': { + target: 'http://127.0.0.1:8000/static/', + }, '**': { target: 'http://nginx:6001', changeOrigin: true, diff --git a/front/src/App.vue b/front/src/App.vue index 58ed698aa98cc1236dbfebd7d5345b5739033b00..8b1ac775a18356753b01c172013190337fb5b765 100644 --- a/front/src/App.vue +++ b/front/src/App.vue @@ -1,5 +1,7 @@ <template> <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 padded segment"> <h1 class="ui header"><translate>Choose your instance</translate></h1> @@ -175,6 +177,11 @@ export default { return null } return _.get(this.nodeinfo, 'software.version') + }, + customStylesheets () { + if (this.$store.state.instance.frontSettings) { + return this.$store.state.instance.frontSettings.additionalStylesheets || [] + } } }, watch: { diff --git a/front/src/main.js b/front/src/main.js index 7f60c602c9559f71484c9e45d1bed0c9334adad5..fde332acc7485e3c0e5a48a74c73cc44d39e8d36 100644 --- a/front/src/main.js +++ b/front/src/main.js @@ -126,6 +126,8 @@ axios.interceptors.response.use(function (response) { return Promise.reject(error) }) +store.dispatch('instance/fetchFrontSettings') + /* eslint-disable no-new */ new Vue({ el: '#app', diff --git a/front/src/store/instance.js b/front/src/store/instance.js index 95de94171ece68fe69f57ffbe9b4101e42097e86..64e48f7b4b557e87f141a47a231069a84853220d 100644 --- a/front/src/store/instance.js +++ b/front/src/store/instance.js @@ -6,6 +6,7 @@ export default { namespaced: true, state: { maxEvents: 200, + frontSettings: {}, instanceUrl: process.env.INSTANCE_URL, events: [], settings: { @@ -53,6 +54,9 @@ export default { events: (state, value) => { state.events = value }, + frontSettings: (state, value) => { + state.frontSettings = value + }, instanceUrl: (state, value) => { if (value && !value.endsWith('/')) { value = value + '/' @@ -110,6 +114,13 @@ export default { }, response => { 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)') + }) } } } diff --git a/front/static/custom.css b/front/static/custom.css new file mode 100644 index 0000000000000000000000000000000000000000..a5bbb2cff2be83d5920f18110082708b06882881 --- /dev/null +++ b/front/static/custom.css @@ -0,0 +1 @@ +/* This is a custom CSS file that can be loaded thanks to settings.json */ diff --git a/front/static/settings.json b/front/static/settings.json new file mode 100644 index 0000000000000000000000000000000000000000..a8bd9b91df975a32c698f33fa96cd350c4744a61 --- /dev/null +++ b/front/static/settings.json @@ -0,0 +1,3 @@ +{ + "additionalStylesheets": ["/static/custom.css"] +}