アプリケーション構成

アプリケーションオプション

UI アプリケーションは一連のオプションを定義することでカスタマイズできます。 オプションのオブジェクトは JSON 内で定義され、以下の順序で実行時に読み込まれマージされます。

さまざまなオプションを収集し、マージするこのプロセスは @c8y/cli に含まれるブートストラップ レイヤによって実行されます。

すべてのオプションは 3 段階のどの段階でも定義できますが、ビルド時にのみ影響するオプションであったり、URL クエリパラメータとして記述するのが難しい複雑なオブジェクトを必要とするため、実行時では意味をなさないものもあります。

静的オプション

package.jsonc8y.application フラグメント内で定義されます。

{
  "c8y": {
    "application": {
      "name": "cockpit",
      "contextPath": "cockpit",
      "key": "cockpit-application-key",
      "tabsHorizontal": true,
      "upgrade": true,
      "rightDrawer": true,
      "contentSecurityPolicy": "default-src 'self' 'unsafe-inline' http: https: ws: wss:; script-src 'self' *.mapquestapi.com 'unsafe-inline' 'unsafe-eval' data:; style-src * 'unsafe-inline' blob:; img-src * data:; font-src * data:; frame-src *;"
    }
  }
}

c8ycli コマンド実行時に渡すことも可能です。

c8ycli build --app.contextPath=cockpit2 --app.dynamicOptionsUrl="/apps/public/public-options/options.json"

動的に取得されるオプション

静的オプションである dynamicOptionsUrl を使用し、アプリケーションは起動時に特定の URL から json を読み込もうとします。Things Cloud の組み込みアプリケーションでは、このオプションはインスタンスレベルとエンタープライズテナントのカスタマイズを提供するメカニズムとして/apps/public/public-options/options.json に設定されます。このプロパティはビルド時に静的に定義されるため、アプリケーションが実行時に動的に取得されるオプションを読み込むべきかどうか、またどこから読み込むべきかを開発者が決定できます。

URL オプション

URL オプションはクエリパラメータとしてアプリケーションの URL に追加するだけです。

https://<instance domain>/apps/cockpit?dynamicOptionsUrl=/apps/my-options/options.json&rightDrawer:false

組み込み済みオプション

標準でサポートされているオプションのリストについては、アプリケーションオプション のドキュメントをご覧ください。 これらのオプションは、上記のように静的、動的または URL の方法で追加するだけで、開発者がアプリケーションや拡張モジュールに含めたいと思うようなカスタム プロパティで簡単に拡張することができます。

Tip
現在のアプリケーションの contentSecurityPolicy は以下の場所で確認することができます。

  • c8ycli new my-cockpit cockpit -a @c8y/apps@1004.11.0 と実行すると、package.json のパスの下に contentSecurityPolicy の値を見つけることができます。c8y.application.contentSecurityPolicy が定義されていれば、その値を確認できます。
  • ページを確認すると、<meta http-equiv="Content-Security-Policy" content="..."><head> タグ内に見つかります。アクティブな値は content 属性で囲まれています。
備考
標準アプリケーションをベースにカスタムアプリケーションをビルドする場合は、必ずデフォルト値にCSP値を追加してください。

ブランディングと言語のカスタマイズ

ブランディング

アプリケーションのスタイルには LESS で作成されたグローバル CSS が使用されています。これらのスタイルは Bootstrap3 に基づいており、元の LESS は npm パッケージの @c8y/style で配布されています。 既存のスタイルを拡張することによって、アプリケーション設定の詳細まで変更できますが、多くの開発者は色やロゴ、フォントなど数個の変数を置き換えるだけで簡単に実現したいと思っているでしょう。

変数を上書きするには以下を使用できます。

CSS カスタムプロパティ

CSS のカスタムプロパティを介して公開されているのは、利用可能な LESS 変数の一部のみです。 以下が利用可能な変数の一覧です。

:root {
--brand-primary: gold ;
--brand-complementary: darkgreen;
--brand-dark:  red;
--brand-light: purple;
--gray-text: #333;
--link-color: var(--brand-primary);
--link-hover-color: var(--brand-complementary);
--body-background-color:#f2f3f4;
--brand-logo-img: url('/apps/ui-assets-management/logo-nav.svg');
--brand-logo-img-height: 20%;
--navigator-platform-logo: url('/apps/ui-assets-management/logo-nav.svg');
--navigator-platform-logo-height: 36px; /* height of the logo set to 0 to hide the element */

--navigator-font-family: inherit;
--navigator-app-name-size: 16px; /* font size of the application name set to 0 to hide app's name */
--navigator-app-icon-size: 46px; /* size of the application icon. set to 0 to hide the application icon.*/
--navigator-bg-color: var(--brand-primary);
--navigator-header-bg: var(--navigator-bg-color);
--navigator-text-color: #ffffff;
--navigator-separator-color: rgba(0,0,0,.05);
--navigator-active-color: var(--navigator-text-color);
--navigator-active-bg: var(--brand-complementary);

--header-color: #ffffff;
--header-text-color: var(--brand-dark);
--header-hover-color:var(--brand-primary);
--header-border-color: rgba(57,72,82,.05);

--font-family-base: "Roboto", Helvetica, Arial, sans-serif;
--headings-font-family: var(--font-family-base);
}

これらは brandingCssVars プロパティを利用した アプリケーションオプション を使用して実行時にカスタマイズできることに注意してください。このオプションは 9.22.0 以降のバージョンでのみ使用可能です。

LESSの使用

前提条件

@c8y/cliを使用していなければ、npm パッケージから元となるスタイルをインストールしてください。

npm install @c8y/style
  1. 例えば branding.less という名前の LESS ファイルを作成する
  2. 好きな名前の新しいフォルダに保存する
  3. フォルダ内に画像用のサブフォルダを作成する
my-application
│   app.modules.ts
│   index.ts
│   packages.json
|   ...
└───branding
│   │   branding.less
│   └───img
│       │   favicon.ico
│       │   main-logo.svg
│       │   tenant-brand.svg
│

branding.less の一行目は以下である必要があります。

@import '~@c8y/style/extend.less';

カスタマイズ例

現時点では必要に応じて好きな変数を変更することが可能です。

例として、ブランディングで最も重要な brand-color と呼ばれるメインカラーを変更してみましょう。

それぞれの LESS 変数を新しい色に変更することでメインカラーを変更できます。

@brand-color: red;

ボタンや有効になっているナビゲーションノード、有効になっているタブ、またホバー状態のボタンのようなユーザーインターフェース要素が赤色に変化しました。

ログインダイアログの上部に位置しているメインロゴを変更するのはどうでしょうか?以下をご覧ください。

@{logo-login} { background-image: url('./img/logo-main.svg')}
@brand-logo-height: 48%;

@c8y/cliを使用してブランディングの変更もできます。

c8ycli server --app.brandingEntry="<path-to-your-branding.less>"

ブランディング例が適用されたチュートリアルアプリケーションもご覧ください。

c8ycli new <appName> tutorial

ブランディングの詳細

ブランディングには簡単に制御できる 3 つの項目があります。

編集できる色は以下のような複数のカテゴリに分けられます。

Brand colors

@brand-color:                 #53cd61;
@brand-primary:               @brand-color;
@brand-complementary:         #a8b3b5;
@brand-primary-light:         lighten(@brand-primary, 20%);

Status colors

@brand-success:               #5cb85c;
@brand-info:                  @brand-color;
@brand-warning:               #f0ad4e;
@brand-danger:                #d9534f;
@danger:                      #D90000;
@warning:                     #FDC000;
@dark-warning:                #FF8000;
@success:                     #5cb85c;

Gray shades

@gray-text:                   #444;
@gray-darker:                 #2c3637;
@gray-dark:                   #3b4748;
@gray-medium-dark:            #49595B;
@gray-medium:                 #6D7A7C;
@gray:                        #8A9596;
@gray-light:                  #cacece;
@gray-lighter:                #f8f8f8;
@gray-white:                  #fcfcfc;
@text-muted:                  @gray;

Component colors

ヘッダーとナビゲータの 2 つのコンポーネントは、常にユーザーから見えています。このため、注意してこれらの見た目を決める必要があります。

/* HEADER */
@headerColor:                 white;
@header-text-color:           @gray-medium-dark;
@header-text-color-hover:     @brand-primary;
@header-active-color:         darken(@gray-medium-dark, 15%);

/* NAVIGATOR */
@navColor:                    @gray-darker;
@navColorHeader:              transparent;
@navigator-title-color:       white;
@navigator-text-color:        @gray-lighter;
@navigator-separator-color:   fade(white, 5%);
@navigator-font-family:       @headings-font-family;
@navigator-font-size:         13px;
@navigator-active-color:      white;
@navigator-active-bg:         @brand-primary;

上記からわかるように、変数の一部は他の変数を再利用します。エラー発生を避けるために、これらの変数はすべて宣言されている必要があることに注意してください。

ロゴ

ロゴ無しではブランディングとは言えません。

ログインダイアログの上部にあるロゴ、テナントブランドロゴ、ファビコンは変更可能です。

ファビコンを変更するには、以下を追加してください。

// to be loaded by webpack
.favicon-webpack-loader { background: url('./img/favicon.ico') }

メインロゴを変更するには、以下を追加してください。

@{logo-login} { background-image: url('./img/main-logo.svg') }
@brand-logo-height: 48%;

ナビゲータ内のテナント ブランドロゴを変更するには、以下を追加してください。

@{logo-navigator} { background-image: url('./img/tenant-brand.svg') }
@navigator-platform-logo-height: 100px;
タイポグラフィー

アプリケーションの見た目にはタイポグラフィーも重要です。フォントも変更可能です。

@font-family-sans-serif:      "Lato",Arial, Verdana, sans-serif;
@font-family-base:            @font-family-sans-serif; @headings-font-family:        "Roboto",Arial, Verdana, sans-serif;

ブランディング例

上記では、カスタムブランディング作成についての使用可能なオプションについて詳細に説明しました。すべてのアプリケーションで 0 からブランディング設定をしたくない場合は、スニペットとして以下のブランディング例を使用してください。以下の例では、最も重要な変数が定義されています。

@import '~@c8y/style/extend.less';

// Replace and uncomment each variable as you need them
/* LOGOS */
.favicon-webpack-loader { background: url('./img/favicon.ico') } // to be loaded by webpack
@{logo-login} { background-image: url('./img/logo-main.svg') }
@brand-logo-height: 48%; // percentage - height / width * 100
@{logo-navigator} { background-image: url('./img/logo.svg') }
@navigator-platform-logo-height: 100px;

/* COLORS */
@brand-color:                 #53cd61; // main color
@brand-primary:               @brand-color;
@brand-complementary:         #a8b3b5;
@brand-primary-light:         lighten(@brand-primary, 20%);
// status colors
@brand-success:               #5cb85c;
@brand-info:                  @brand-color;
@brand-warning:               #f0ad4e;
@brand-danger:                #d9534f;
@danger:                      #D90000;
@warning:                     #FDC000;
@dark-warning:                #FF8000;
@success:                     #5cb85c;
// grays
@gray-text:                   #444;
@gray-darker:                 #2c3637;
@gray-dark:                   #3b4748;
@gray-medium-dark:            #49595B;
@gray-medium:                 #6D7A7C;
@gray:                        #8A9596;
@gray-light:                  #cacece;
@gray-lighter:                #f8f8f8;
@gray-white:                  #fcfcfc;
@text-muted:                  @gray;

@body-background-color:       #f8f8f8; // page background color - always use a light background

/* HEADER */
@headerColor:                 white;
@header-text-color:           @gray-medium-dark;
@header-text-color-hover:     @brand-primary;
@header-active-color:         darken(@gray-medium-dark, 15%);

/* NAVIGATOR */
@navColor:                    @gray-darker;
@navColorHeader:              transparent;
@navigator-title-color:       white;
@navigator-text-color:        @gray-lighter;
@navigator-separator-color:   fade(white, 5%);
@navigator-font-family:       @headings-font-family;
@navigator-font-size:         13px;
@navigator-active-color:      white;
@navigator-active-bg:         @brand-primary;
// when set adds a vertical gradient in the navigator background
// @grad-top:                    "";
// @grad-bottom:                 "";

/* TYPOGRAPHY */
// @font-family-sans-serif:      "Lato",Arial, Verdana, sans-serif;
// @font-family-base:            @font-family-sans-serif;
// @headings-font-family:        "Roboto",Arial, Verdana, sans-serif;

/* BUTTONS */
// @btn-border-radius-base:      2px;
// @btn-border-radius-large:     @btn-border-radius-base;
// @btn-border-radius-small:     @btn-border-radius-base;
// @btn-shadow:                  none;

/* COMPONENTS */
// @spinner-color:               lighten(@brand-primary, 30%);
// @link-color:                  #337ab7;
// @link-hover-color:            darken(@link-color, 15%);
// @input-focus-color:           #66afe9;

// @body-background-pattern:     "";
// @darker-header:               @gray-dark;
// @appswitcher-background:      none;
// @table-bg-hover:              fade(black, 1.5%);
// @header-app-name:             @header-text-color;
// @image-path:                  'img/';

ブランディングと言語のカスタマイズ

アプリケーションオプションを使用し、各テナントで組み込みアプリケーションの見た目をカスタマイズしたり、アプリケーションで利用可能な言語を追加・置き換えすることができます。アプリケーションオプションで記述されているように、根底にある仕組みは静的にホスティングされている web アプリケーションです。

このチュートリアルでは、2つのwebアプリケーションを公開します。

デプロイには、コマンドでインストールできるnodejsの @c8y/cli を使用します。

npm install -g @c8y/cli

初期リポジトリのダウンロードもしくは複製

https://github.com/Cumulocity/ui-customization で利用可能なリポジトリをダウンロードもしくは複製できます。この文書では、ブランディングや新しい言語の追加例を見ることができます。

git clone https://github.com/Cumulocity/ui-customization

このフォルダ内には、以下の 2 つのフォルダが存在します。

public-options
ui-assets

ブランディングオプション

public-options/options.json ファイルを編集し、brandingCssVars のサブプロパティを変更します。これらのプロパティは、実行時に CSSカスタムプロパティに変換されます。

brand-logo-imgプロパティと navigator-platform-logoプロパティは、URLであることに注意してください。このため、対応するファイルは ui-assets フォルダ内に配置しなければなりません。

ファビコンを変更するには faviconUrl プロパティを編集する、もしくは ui-assetsフォルダ内の対応するファイルを追加してください。

ブラウザのウィンドウに表示されるタイトルを変更するには、globalTitleプロパティを変更してください。

これらの設定が十分でない場合、URL のリストをextraCssUrls プロパティに追加し、実行時に追加の CSS を読み込むこともできます。

{
  "extraCssUrls": [
    "/apps/ui-assets/extra.css"
  ]
}

言語

言語の国際化に使用されているプラットフォーム UI 文字列は gettextに保存されています。プラットフォームに新しい言語を追加したい場合、これらのファイルを編集するソフトウェアが必要です(Poedit など)。

翻訳されている各カタログは JSON 形式で実行時に読み込まれます。.po (gettext) 拡張子のファイルを.json ファイルに変換するには、最初の手順でインストールした @c8y/cli を使用します。

ビルド時に独自翻訳の追加方法
  1. @c8y/ngx-components@1004.0.6/locales/locales.potから文字列カタログをダウンロードします(バージョン1004.0.6以降では、latest を現在使用しているバージョンに変更することができます)。
  2. ダウンロードしたlocales.potテンプレートファイルを、任意の.potファイルエディタにロードし、そこから新しい翻訳を作成します。 翻訳対象言語(Afrikaansなど)を選択し、各文字列を翻訳します。必要な数の言語について、このプロセスを繰り返します。 この手順の成果物として、各言語の.po カタログファイルができ上がります。これらのファイルは後続のバージョンで文字列を更新するときに使うため、安全な場所に保存しておいてください。
  3. c8ycli を利用して、新しく作成された.po ファイルを.json ファイルへ変換します。
c8ycli locale-compile path/to/language.po
  1. ui-assets フォルダへ生成された.json ファイルをコピーします。
  2. public-options/options.json の language フラグメントを更新します。
languages?: {
  [langCode: string]: {
    name: string;
    nativeName: string;
    url: string;
  }
}

ダウンロードしたリポジトリでは、以下のようなロシア語への翻訳例があります。

"languages": {
  "ru": {
    "name": "Russian",
    "nativeName": "русский язык",
    "url": "/apps/public/ui-assets/ru.json"
  }
}

インポートされた言語は、ログイン後にUIで変更できます。これを行うには、右上の [User] アイコンをクリックし、メニューから [ユーザー設定] を選択して、表示されるウィンドウで目的の言語を選択します。

実行時に独自翻訳の追加方法

特定の文字列を実行時に翻訳することができます。つまり、ビルドに含める必要はなく、単にアプリケーション オプションに追加するだけです。しかし、この考え方では、新しい言語を追加することはできません。新しい文字列を既存の言語に追加するか、特定の翻訳を既存の言語に揃えることしかできません。特定キーを翻訳するためには、アプリケーション オプションに次の構造を追加する必要があります。

  i18nExtra?: {
    [langCode: string]: {
      [key: string]: string;
    };
  };

例えば、次のようにすると、独自のCookieバナーが翻訳されます。

   "i18nExtra": {
      "de": {
        "About cookies on Things Cloud": "Informationen zu Cookies in Things Cloud",
        "Click Agree and Proceed to accept cookies and go directly to the platform or click on Privacy Policy to see detailed descriptions of the used cookies.": "Klicken Sie auf Zustimmen und fortfahren, um Cookies zu akzeptieren und direkt zur Plattform zu gelangen, oder klicken Sie auf Datenschutzrichtlinie, um detaillierte Beschreibungen der verwendeten Cookies anzuzeigen."
      }
   }

デプロイ

public-optionsui-assetsの両方を含むui-customizationフォルダ内で、以下のコマンドを実行します。

c8ycli deploy public-options ui-assets

テナント/インスタンス情報を入力すると、アプリケーションがデプロイされ、特定のテナントとサブテナントで有効化されます。

備考
パフォーマンスの理由から、オプションはキャッシュされています。変更を有効にするためにはブラウザを 2 回リロードする必要があります。