Theming ownCloud
Introduction
Themes can be used to customize the look and feel of any aspect of an ownCloud installation. They can override the default JavaScript, CSS, image, and template files, as well as the user interface translations with custom versions. They can also affect both the web front-end and the ownCloud Desktop client. However, this documentation only covers customizing the web front-end, so far.
Before ownCloud 10, theming was done via the config.php entry 'theme' ⇒ '' . This is deprecated in ownCloud 10. Users who have this entry in their config.php should remove it and use a theme app to customize their ownCloud instance instead.
|
Quick Start
For the impatient, who want to play with themes right away, the following commands get a theme installed quickly. Adjust the names and paths according to your setup. For docker installations use the directory /var/www/owncloud/custom
.
-
Install unzip if not available
sudo apt install unzip
-
If it exists, go into your
apps-external
folder, else useapps
cd /var/www/owncloud/apps-external
-
Download the theme
wget https://github.com/owncloud/theme-example/archive/master.zip
-
Extract the theme
unzip master.zip
-
Remove the zip file
rm master.zip
-
Rename the theme
mv theme-example-master mynewtheme
-
Change the app ID in info.xml
sed -i "s#<id>theme-example<#<id>mynewtheme<#" "mynewtheme/appinfo/info.xml"
-
Adjust the permissions
sudo chown -R www-data: mynewtheme
-
Activate your theme
sudo -u www-data ./occ app:enable mynewtheme
-
Exclude your new theme from integrity checking. Add the following setting to
config/config.php
:'integrity.ignore.missing.app.signature' => [ 'mynewtheme', ],
The last step is necessary since you’re not likely to provide a signature.json file. The theme is treated like an app. The ID is usually the name of the subfolder for the theme, in this case mynewtheme.
Throughout this section of the documentation, for the sake of simplicity, it will be assumed that your ownCloud installation directory is /owncloud
. If you’re following this guide to create or customize a theme, make sure you change any references to match the location of your ownCloud installation.
To save time and effort, you can use the shell script below to create the basis of a new theme from ownCloud’s example theme.
Using this script (and the following one, read-config.php
), you will have a new theme, ready to go, in less than five seconds. You can execute this script with two variables; the first one is the theme name and the second one is your ownCloud directory.
For example:
theme-bootstrap.sh mynewtheme /var/www/owncloud
Don’t forget to create read-config.php from the included code below, before you attempt to run theme-bootstrap.sh , otherwise theme-bootstrap.sh will fail.
|
theme-bootstrap.sh
#!/bin/bash
# theme-bootstrap.sh
# Invoke this script with two arguments, the new theme's name and the path to ownCloud root.
# Written by Dmitry Mayorov <dmitry@owncloud.com>, Matthew Setter <matthew@matthewsetter.com> & Martin Mattel <github@diemattels.at>
# Copyright (c) ownCloud 2018.
set -e
# Clone a copy of the ownCloud example theme
# It won't override an existing app directory of the same name.
function clone_example_theme
{
local APP_NAME="$1"
local INSTALL_BASE_DIR="$2"
local MAINFILE=master.zip
local UNZIPDIR=/tmp
local MASTERNAME=theme-example-master
local DOWNLOAD_FILE=$UNZIPDIR/$MAINFILE
local THEME_ARCHIVE_URL=https://github.com/owncloud/theme-example/archive/master.zip
# check if the app name already exists
if [ -d "$INSTALL_BASE_DIR/$APP_NAME" ]
then
echo "An app with name ('$INSTALL_BASE_DIR/$APP_NAME') already exists."
echo "Please remove or rename it before running this script again."
return 1
fi;
# delete an existing downloaded zip file
if [ -e "$DOWNLOAD_FILE" ]
then
rm "$DOWNLOAD_FILE"
fi
echo "Downloading ownCloud example theme."
# getting the example theme from git
if ! wget --output-document="$DOWNLOAD_FILE" --tries=3 --continue \
--timeout=3 --dns-timeout=3 --connect-timeout=3 --read-timeout=3 \
"$THEME_ARCHIVE_URL" >/dev/null 2>&1
then
echo "Download error, exiting"
return 1
fi
# first test if unzip would error then extract
if unzip -t "$DOWNLOAD_FILE" >/dev/null 2>&1
then
# unzip with overwriting existing files and directories and suppressed output
echo "Unzipping download"
unzip -oq "$DOWNLOAD_FILE" -d "$UNZIPDIR"
echo "Moving to target location"
mv "$UNZIPDIR/$MASTERNAME" "$INSTALL_BASE_DIR/$APP_NAME"
echo "Removing download"
rm "$DOWNLOAD_FILE"
else
echo "Cannot complete setup of the ownCloud example theme as it is corrupted."
return 1
fi
}
E_BADARGS=85
# Remembers the directory where this script was called from
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
# Check if run as sudo (root), needed for sub script calling and changing file permissions
if (( $EUID != 0 )); then
echo "Please run this script with sudo or as root"
exit
fi
# Check if enough parameters have been applied
if (( $# != 2 ))
then
echo "Not enough arguments provided."
echo "Usage: $( basename "$0" ) [new theme name] [owncloud root directory]"
exit $E_BADARGS
fi
# Check if read-config.php file exists in the same directory
if [ ! -f $SCRIPT_DIR/read-config.php ]
then
echo "File read-config.php not found! Must be in the same dir as this script"
exit
fi
# Check if php file is set to be executable, script will else not work
if [ ! -x $SCRIPT_DIR/read-config.php ]
then
echo "File read-config.php is not set executable"
exit
fi
app_name="$1"
owncloud_root="$2"
apps=$(php "$SCRIPT_DIR/read-config.php" "$owncloud_root")
# Check if the php script returned an error message. This is when the string does not start with /
if [[ ! $apps = '/'* ]]
then
echo $apps
echo "Script read-config.php returned no usable app path"
exit
fi
if clone_example_theme "$app_name" "$apps"
then
# Remove the default signature, which will cause a code integrity violation
[ -f "$apps/$app_name/appinfo/signature.json" ] && rm "$apps/$app_name/appinfo/signature.json"
# Replace the default theme id / theme name
echo "Updating theme id / theme name"
sed -i "s#<id>theme-example<#<id>$app_name<#" "$apps/$app_name/appinfo/info.xml"
# Set the appropriate permissions
echo "Setting new theme file permissions"
chown -R www-data:www-data "$apps/$app_name"
# Enable the new theme app
if [ -e "$owncloud_root/occ" ]
then
echo "Enabling new theme in ownCloud"
php "$owncloud_root/occ" app:enable "$app_name"
else
echo
echo "occ command not found, please enable the app manually"
fi
echo
echo "Finished bootstrapping the new theme."
fi
read-config.php
#!/usr/bin/php
<?php
/**
* @author Matthew Setter <matthew@matthewsetter.com> & Martin Mattel <github@diemattels.at>
* @copyright Copyright (c) 2018, ownCloud GmbH
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
/**
* Class SimpleConfigReader
* @package ConfigReader
*/
class SimpleConfigReader
{
/**
* @var array
*/
private $config = [];
/**
* String returned to the user.
* @var string
*/
private $output = '';
/**
* SimpleConfigReader constructor.
* @param string $config
*/
public function __construct($config = '')
{
$this->config = $config;
}
/**
* Find a writable app directory path that is either defined by key 'apps_paths'
* or use the default owncloud_root/apps path if the key is not set
*
* @return string
* @throws \Exception
*/
function findPath($ocAppsPath) {
// default path = /apps
if (!array_key_exists('apps_paths', $this->config)) {
$this->output = $ocAppsPath;
return $this->output;
}
foreach ($this->config['apps_paths'] as $path) {
if ($path['writable'] == true && is_writable($path['path'])) {
$this->output = $path['path'];
return $this->output;
}
}
return "Key 'apps_paths' found, but no writable path defined or path found not writeable";
}
}
/*
* As per the PHP manual: The first argument $argv[0] is always the name that
* was used to run the script. So we need at least two to access the new app's
* name, as well as the running script's name.
* @see https://www.php.net/manual/en/reserved.variables.argv.php
*/
if (count($argv) != 2) {
echo "Command usage: read-config.php <full path to ownCloud root dir> \n";
echo "Please provide the path to the ownCloud directory. \n";
exit(1);
}
// create a realpath and remove trailing "/" from argument if present
$ocRoot = rtrim( (string) $argv[1], "/");
$ownCloudConfigFile = sprintf("%s/config/config.php", $ocRoot);
if (!realpath($ownCloudConfigFile)) {
// if path/file does not exist, return an error message
echo 'File not found: ' . $ownCloudConfigFile . PHP_EOL;
} else {
// return the path, identified by a leading "/" and no new line character at the end
require_once($ownCloudConfigFile);
$result = (new SimpleConfigReader($CONFIG))->findPath($ocRoot . '/apps');
if (!strpos($result, '/')) {
// return an error string which does not start with a leading "/"
echo $result . PHP_EOL;
} else {
// return the path, identified by a leading "/" and no new line character at the end
echo $result;
}
}
How to Create a New Theme
Now in more detail for those who want to understand the procedures presented above and work on their new theme. Creating a theme requires two basic steps:
-
Copy and extend ownCloud’s example theme or create one from scratch.
-
Enable the theme which can be found in
All themes, whether copied or new, must meet two key criteria:
-
They must be stored in an app directory of your ownCloud installation, preferably in
apps-external
or a custom app directoryTo ensure that custom themes aren’t lost during upgrades, we strongly encourage you to store them in a custom app directory. -
They require a configuration file called
appinfo/info.xml
to be present.
appinfo/info.xml
Here’s an example of the bare minimum which the file needs to contain:
<?xml version="1.0"?>
<info>
<id>theme-example</id>
<name>Example Theme</name>
<types>
<theme/>
</types>
<dependencies>
<owncloud min-version="10" max-version="10" />
</dependencies>
</info>
And here’s a longer, more complete example:
<?xml version="1.0"?>
<info>
<id>theme-example</id>
<name>Example Theme</name>
<description>This App provides the ownCloud theme.</description>
<licence>AGPL</licence>
<author>John Doe</author>
<version>0.0.1</version>
<types>
<theme/>
</types>
<dependencies>
<owncloud min-version="10" max-version="10" />
</dependencies>
</info>
The value of the id
element needs to be the name of your theme’s folder. We recommend that it always be prefixed with theme-
. The main reason for doing so, is that it is alphabetically sorted in a terminal
when handling app folders.
The type
element needs to be the same as is listed above, so that ownCloud knows to handle the app as a theme. The dependencies element needs to be present to set the minimum and maximum versions of ownCloud which are supported. If it’s not present, a warning will be displayed in ownCloud 10 and an error will be thrown in the upcoming ownCloud 11.
While the remaining elements are optional, they help when working with the theme in the ownCloud Admin dashboard. Consider filling out as many as possible, as completely as possible.
Theme Signing
If you are going to publish the theme as an app in the marketplace, you need to sign it. However, if you are only creating a private theme for your own ownCloud installation, then you do not need to.
That said, to avoid a signature warning in the ownCloud UI, you need to add it to the integrity.ignore.missing.app.signature
list in config/config.php
. The following example allows the app whose application ID is app-id
to have no signature.
'integrity.ignore.missing.app.signature' => [
'app-id',
],
How to Override Images
Any image, such as the default logo, can be overridden by including one with the same path structure in your theme. For example, let’s say that you want to replace the logo on the login page above the credentials box which, by default, has the path: owncloud/core/img/logo-icon.svg
. To override it, assuming that your custom theme was called theme-example
(which will be assumed for the remainder of the theming documentation), add a new file with the following path: owncloud/apps/theme-example/core/img/logo-icon.svg
. After the theme is activated, this image will override the default one.
Default Image Paths
To make building a new theme easier, find below a list of a range of the image paths used in the default theme.
Description | Section | Location |
---|---|---|
The logo at the login page above the credentials box |
General |
|
The logo in the left upper corner after login |
|
|
All files folder image |
|
|
Favorites star image |
|
|
Shared with you/others image |
|
|
Shared by link image |
|
|
Tags image |
|
|
Deleted files image |
|
|
Settings image |
|
|
Search image |
|
|
Breadcrumbs home image |
|
|
Breadcrumbs separator |
|
|
Dropdown arrow |
Admin Menu |
|
Personal image |
|
|
Users image |
|
|
Help image |
|
|
Admin image |
|
|
Logout image |
|
|
Apps menu - Files image |
|
|
Apps menu - Plus image |
|
|
Upload image |
Personal |
|
Folder image |
|
|
Trash can image |
|
When overriding the favicon, make sure your custom theme includes an override for both owncloud/apps/core/img/favicon.svg and owncloud/apps/core/img/favicon.png , to cover any future updates to favicon handling.
|
When using custom filetype icons in a custom theme, it is necessary to run occ maintenance:mimetype:update-js to activate them. For more information, refer to
mimetypes management.
|
How to Change the Background Image
This guide assumes that you are in the folder of your custom theme, for example: /var/www/owncloud/apps/my_custom_theme/ .
|
How to Change the Login Background Image
-
Put the new background image in
core/img/background.jpg
. -
Change the owner and group permissions of the file to your web server user and group.
-
Update
background-image
incore/css/styles.css
.
#body-login {
background-image: url("../img/background.jpg"); /* path to image */
background-position: 50% 50%; /* ensure optimal scalability */
background-repeat: no-repeat; /* prevent tiled background */
background-size: cover; /* ensure screen coverage */
text-align: center; /* Center Entitlement text, copyright */
background-color: #000000 !important; /* Fallback for old browsers */
}
If your image size is 1920px x 1680px, you don’t need the lines below the path. However, they ensure optimal positioning and scaling.
You can also change the background color, logo, and slogan. |
How to Change the Login Background Image to a Color
In core/css/styles.css
, search for the following CSS code:
#body-login {
background: #745bca; /* Old browsers */
background: -moz-linear-gradient(top, #947bea 0%, #745bca 100%);
}
Replace it with the following:
#body-login {
background: rgb(31,9,121);
background: linear-gradient(90deg, rgba(31,9,121,1) 38%, rgba(2,0,36,1) 58%);
}
If you only want one color, replace the existing CSS code with this:
#body-login {
background: rgb(31,9,121);
}
If you are not sure what color to pick, CSS Gradient may be able to help. |
To change the icon, replace the files in core/img
(logo.png
, and logo.svg
) with your icons. The reason for the PNG files is to have a fallback option for older browsers.
If you keep the names, you don’t need to change the path in core/css/styles.css
. If you change the names, adjust the styles.css
file accordingly like in the following example:
#header .logo {
background-image: url('../img/logo.svg');
width: 250px;
height: 121px;
}
How to Change the Header
The image above is the default ownCloud header.
You can change it to a custom color with a custom logo.
Search for #body-public #header
in core/css/styles.css
.
#body-public #header {
background-color: #745bca;
}
You can also write your own, like the one from the login page for example:
#body-public #header {
background: rgb(31,9,121);
background: linear-gradient(90deg, rgba(31,9,121,1) 38%, rgba(2,0,36,1) 58%);
}
Change the logo by replacing logo-icon.png
and logo-icon.svg
with your logo, in core/img
. If you change the names of the logos, adjust the path accordingly in core/css/styles.css
.
#header .logo-icon {
background-image: url('../img/logo-icon.svg');
height: 34px;
}
How to Override Settings Page Icons
ownCloud provides the ability to override Personal and Admin settings page icons. To do so requires two things:
-
A custom (SVG) icon; and
-
Refactoring
SettingsManager.php
For example, if you want to override the Admin Encryption settings icon, first add a custom icon to an app’s img
directory, e.g. </path/to/owncloud>/apps/encryption/img/gnu.svg
.
Then, update the getBuiltInSections
function of lib/private/Settings/SettingsManager.php
. You need to change the final argument of the relevant call to new Section
for the settings section that you want to change, like in the example below.
new Section('encryption', $this->l->t('Encryption'), 85, 'gnu'),
In the example above, I’ve changed the encryption section’s icon, by changing the final argument to be the custom icon’s name — minus the file extension.
Icons must be in SVG format. No other file formats are supported. |
How to Override the Default Colors
To override the default style sheet, create a new CSS style sheet called styles.css
in the theme’s css
directory.
How to Override Translations
You can override the translation of any string in your theme. To do so:
-
Create the
l10n
folder inside your theme for the app that you want to override. -
In the
l10n
folder, create the translation file for the language that you want to customize.
For example, if you want to overwrite the German translation of `Download` in the files app, create the file owncloud/apps/theme-example/apps/files/l10n/de_DE.js
. Note that the structure is the same as for images. You just mimic the original file location inside your theme. Put the following code in the file:
OC.L10N.register(
"files",
{
"Download" : "Herunterladen"
},
"nplurals=2; plural=(n != 1);"
);
Next, create a second translation file, owncloud/apps/theme-example/apps/files/l10n/de_DE.json
, which looks like this:
{
"translations": {
"Download" : "Herunterladen"
},
"pluralForm" :"nplurals=2; plural=(n != 1);"
}
Both files (.js
and .json
) are needed. The first is needed to enable translations in the JavaScript code and the second one is read by the PHP code and provides the data for translated terms.
How to Override Names, Slogans, and URLs
In addition to translations, the ownCloud theme allows you to change a lot of the names shown on the web interface. This is done in defaults.php
, which needs to be located within the theme’s root folder. You can find a sample version in owncloud/app/theme-example/defaults.php
. In the file, you need to define a class named OC_Theme
and implement the methods that you want to overwrite.
class OC_Theme {
public function getAndroidClientUrl() {
return 'https://play.google.com/store/apps/details?id=com.owncloud.android';
}
public function getName() {
return 'ownCloud';
}
}
Each method must return a string. The following methods are available:
Method | Description |
---|---|
|
Returns the URL to Google Play for the Android Client. |
|
Returns the base URL. |
|
Returns the documentation URL. |
|
Returns the entity (e.g., company name) used in footers and copyright notices. |
|
Returns the short name of the software. |
|
Returns the short name of the software containing HTML strings. |
|
Returns the URL to the ownCloud Marketplace for the iOS Client. |
|
Returns the AppId for the ownCloud Marketplace for the iOS Client. |
|
Returns the logo claim. |
|
Returns the long version of the footer. |
|
Returns the mail header color. |
|
Returns the URL where the sync clients are listed. |
|
Returns the title. |
|
Returns short version of the footer. |
|
Returns the slogan. |
Only these methods are available in the templates, because we internally wrap around hardcoded method names.
One exception is the method buildDocLinkToKey
which gets passed in a key as its first parameter. For core, we do something similar to build the documentation
public function buildDocLinkToKey($key) {
return $this->getDocBaseUrl() . '/server/latest/go.php?to=' . $key;
}
How to Test a Theme
There are different options for testing themes:
-
If you’re using a tool like the Inspector tools of Mozilla, you can test the CSS styles immediately inside the css-attributes, while you’re looking at the page.
-
If you have a development server, you can test out the effects in a live environment.
Settings Page Registration
How Can an App Register a Section in the Admin or Personal Section?
As of ownCloud 10.0, apps must register Admin and Personal section settings in info.xml
. As a result, all calls to
OC_App::registerPersonal
and OC_App::registerAdmin
should now be removed. The settings panels of any apps that are still using these calls will now be rendered in the Additional
section of the dashboard.
For each panel an app wishes to register, two things are required:
-
An update to
info.xml
-
A controller class
Updating info.xml
First, an entry must be added into the <settings>
element in info.xml
, specifying the class name responsible for rendering the panel. These will be loaded automatically when an app is enabled. For example, to register an Admin and a Personal section requires the following configuration:
<settings>
<personal>OCA\MyApp\PersonalPanel::class</personal>
<admin>OCA\MyApp\AdminPanel::class</admin>
</settings>
The Controller Class
Next, a controller class which implements the OCP\Settings\ISettings
interface must be created to represent the panel. Doing so enforces that the necessary settings panel information is returned. The interface specifies three methods:
-
getSectionID
-
getPanel
-
getPriority
getSectionID: This method returns the identifier of the section that this panel should be shown under. ownCloud Server comes with a predefined list of sections which group related settings together; the intention of which is to improve the user experience. This can be found here in this example:
getPanel: This method returns the OCP\Template
or OCP\TemplateReponse
which is used to render the panel. The method may also return null
if the panel should not be shown to the user.
getPriority: An integer between 0 and 100 representing the importance of the panel (higher is more important). Most apps should return a value:
-
between 20 and 50 for general information.
-
greater than 50 for security information and notices.
-
lower than 20 for tips and debug output.
Here’s an example implementation of a controller class for creating a personal panel in the security section.
<?php
namespace OCA\YourApp
use OCP\Settings\ISettings;
use OCP\Template;
class PersonalPanel extends ISettings {
const PRIORITY = 10;
public function getSectionID() {
return 'security';
}
public function getPriority() {
return self::PRIORITY;
}
public function getPanel() {
// Set the template and assign a template variable
return (new Template('app-name', 'template-name'))->assign('var', 'value');
}
}
Create Custom Sections
At the moment, there is no provision for apps creating their own settings sections. This is to encourage sensible grouping of the settings panels which in turn should improve the overall user experience. However, if you think a new section should be added to core, please create a PR with the appropriate changes to OC\Settings\SettingsManager
.