OwnClowd のカスタマイズ方法


この記事は[公式ドキュメント](https://doc.owncloud.org/server/10.2/developer_manual/core/theming.html)の翻訳になります。
追加として公式ドキュメントに従ってテーマをカスタマイズした際に起きたエラーや、それの回避方法を記載しています。

### Agenda
+ はじめに
+ 新しいテーマの作成
+ 画像の変更
+ デフォルトカラーの変更
+ 文言の変更
+ 名前、スローガン、URLの変更
+ カスタマイズしたテーマのテスト
+ 設定ページの登録

## はじめに
テーマを使用して、ownCloudの外観をカスタマイズできます。
これにより、デフォルトのJavaScript、CSS、画像、およびテンプレートファイル、およびカスタムバージョンでのユーザーインターフェイスを変更できます。
それはWebフロントエンドとownCloudデスクトップクライアントの両方にも影響を与える可能性がありますが、このドキュメントではWebフロントエンドのカスタマイズについてのみ説明しています。


~~~Note
ownCloud 10より前では、テーマ設定はconfig.phpエントリ 'theme'⇒ '' を介して行われていました。
これはownCloud 10では推奨されません。
config.phpにこのエントリがあるユーザはそれを削除し、代わりにテーマアプリを使ってownCloudインスタンスをカスタマイズするべきです。
~~~


説明を簡単にするために、ここではowncloudインストールディレクトリを `/owncloud` と仮定します。
このガイドに従ってテーマの作成やカスタマイズを行う場合は、インストールの場所に合わせて参照を変更してください。

時間と労力を節約するために、下記のシェルスクリプトを使用して、[ownCloudのサンプルテーマ](https://github.com/owncloud/theme-example)から新しいテーマの基礎を作成することができます。


このスクリプトを使えば、5秒以内に新しいテーマが完成します。
このスクリプトは2つの引数で実行できます。
最初のものはテーマ名で、二番目のものはあなたのownCloudディレクトリです。

```Example
theme-bootstrap.sh mynewtheme /var/www/owncloud
```

~~~IMPORTANT
theme-bootstrap.sh を実行する前に、read-config.phpを作成してください。
~~~

```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 exmple 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://secure.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;
    }
}
```

### トラブルシューティング
##### Apache ユーザーの変更
デフォルトではwww-dataユーザーになっています。（theme-bootstrap.sh:116行目）
適宜、環境に合わせて変えてください。
(e.g. apache:apache)

##### PHP Fatal error
> PHP Fatal error:  Allowed memory size of bytes exhausted (tried to allocate bytes) in /var/www/owncloud/lib/composer/composer/autoload_static.php on line 430

上記のようなエラーが発生した場合、DBの`max_allowed_packet`を確認してください。

```
mysql> show variables like 'max_allowed_packet';
+--------------------+---------+
| Variable_name      | Value   |
+--------------------+---------+
| max_allowed_packet | 4194304 |
+--------------------+---------+
```

デフォルトでは4Mとなっていましたが、これでは足りませんでした。

```
mysql> show variables like 'max_allowed_packet';
+--------------------+----------+
| Variable_name      | Value    |
+--------------------+----------+
| max_allowed_packet | 16777216 |
+--------------------+----------+
```

16Mに変更すると正常に動作しました。