Skip to content

拡張スタイルの仕組み ​

第6版作成 最終更新 (日本時間)
確認バージョン1.5.2.01.5.8.1

拡張スタイルは、プリザンター全体(または条件で絞った範囲)に適用できる CSS です。管理画面の「スタイル」がサイト単位の設定であるのに対し、拡張スタイルはシステム管理者が設定するグローバルなカスタマイズ手段です。このページでは、拡張スタイルがどう読み込まれ、HTML のどこに挿入され、どんな条件で絞り込まれるかを整理します。

管理画面のスタイルとの違い ​

拡張スタイル管理画面のスタイル
設定方法ファイル配置、または DB の Extensions テーブル管理画面から編集
適用範囲システム全体(フィルタリングで絞り込み可)サイト単位
HTML への挿入形式<link rel="stylesheet" href="...">(外部ファイル)<style>(インライン)
反映タイミングファイル・DB とも再起動またはパラメータリロードが必要保存後すぐ反映
サイトエディタ表示中適用される適用されない

設定方法 ​

ファイルで設定する ​

App_Data/Parameters/ExtendedStyles/ に .css ファイルを置きます。

text
App_Data/
└── Parameters/
    └── ExtendedStyles/
        ├── 01_base.css        ← ファイル名順で読み込まれる
        ├── 02_custom.css
        └── theme/
            └── theme.css      ← サブディレクトリも再帰的に読み込まれる

ファイルはファイル名のアルファベット順に処理されます。サブディレクトリも再帰的に走査されるので、テーマや機能ごとにディレクトリを分けて管理できます。

WARNING

ファイルはアプリ起動時(初期化処理)に読み込まれ、メモリに保持されます。ファイルを追加・変更・削除したら、プリザンターを再起動するか、特権ユーザでパラメータリロード(/admins/reloadparameters)を実行するまで反映されません。 確認したソースでは、パラメータリロードでも ExtendedStyles を読み直します(Initializer.cs)。

Extensions テーブルで設定する 1.4.7.0 以降 ​

1.4.7.0 以降は、DB の Extensions テーブルで管理することもできます。ExtensionType に "Style"、Body に CSS 本文、ExtensionSettings に絞り込み条件を JSON で書きます。

確認したソースでは、Extensions テーブルの内容も起動時とパラメータリロードのときにだけ読み込まれ、メモリ上の Parameters.ExtendedStyles に追加されます(ExtensionInitializer.cs、ParametersInitializer.cs)。DB を書き換えただけでは反映されないため、再起動またはパラメータリロードが必要です。DB の拡張スタイルはファイルの拡張スタイルの後ろに、ExtensionName の順で並びます。

json
{
  "SiteIdList": [123, 456],
  "Controllers": ["items"],
  "Actions": ["index"]
}

読み込みの仕組み ​

起動時の初期化(ファイルの場合) ​

アプリ起動時に Implem.DefinitionAccessor.Initializer が App_Data/Parameters/ExtendedStyles/ を走査し、すべての .css ファイルをメモリに読み込んで Parameters.ExtendedStyles リストに保持します。

csharp
private static List<ExtendedStyle> ExtendedStyles(
    string path = null, List<ExtendedStyle> list = null)
{
    list = list ?? new List<ExtendedStyle>();
    path = path ?? Path.Combine(ParametersPath, "ExtendedStyles");
    var files = new DirectoryInfo(path)
        .GetFiles("*.css")
        .OrderBy(file => file.Name);   // ファイル名順
    foreach (var file in files)
    {
        var style = Files.Read(file.FullName);
        if (style != null)
        {
            list.Add(new ExtendedStyle()
            {
                Name = file.Name,
                Path = file.FullName,
                Style = style
            });
        }
    }
    // サブディレクトリも再帰処理
    foreach (var dir in new DirectoryInfo(path).GetDirectories()
                             .OrderBy(dir => dir.Name))
    {
        list = ExtendedStyles(dir.FullName, list);
    }
    return list;
}

/resources/styles エンドポイント ​

拡張スタイルは HTML にインラインで埋め込まれるのではなく、/resources/styles エンドポイントを参照する <link rel="stylesheet"> タグとして出力されます。エンドポイントはクエリ文字列の site-id・id・controller・action とログインユーザの組織・グループ・ユーザ ID をもとに、該当する拡張スタイルを結合して返します。

csharp
public static ContentResultInheritance Get(Context context)
{
    var siteId = context.QueryStrings.Long("site-id");
    var id = context.QueryStrings.Long("id");
    var controller = context.QueryStrings.Data("controller");
    var action = context.QueryStrings.Data("action");
    // ...
    return new ContentResultInheritance
    {
        ContentType = "text/css",
        Content = HtmlStyles.ExtendedStyles(
            context: context,
            deptId: context.DeptId,
            groups: context.Groups,
            userId: context.UserId,
            siteTop: siteTop,
            siteId: siteId,
            id: id,
            controller: controller,
            action: action)
    };
}

このエンドポイントには [ResponseCache(Duration = int.MaxValue)] が付いており、ブラウザによる長期キャッシュが有効です。

図を読み込み中…

フィルタリングの仕組み ​

拡張スタイルは、拡張スクリプトと同じ ExtensionWhere<T> メソッドで絞り込まれます。

条件リストの状態評価結果
リストが空(null を含む)常に一致(条件なし扱い)
値が指定されている一致するものだけ適用(肯定指定)
- を先頭に付けた値一致するもの以外に適用(否定指定)

指定できる条件は次のとおりです。

プロパティ説明
DeptIdList組織 ID のリスト
GroupIdListグループ ID のリスト
UserIdListユーザ ID のリスト
SiteIdListサイト ID のリスト
IdListレコード ID のリスト
Controllersコントローラ名("items"、"users" など)
Actionsアクション名("index"、"edit"、"new" など)

ファイルの拡張スタイルは絞り込めない

ファイルから読み込んだ ExtendedStyle は絞り込み用のプロパティがすべて空なので、条件なし扱いとなりすべてのページに適用されます。特定のページやサイトだけに適用したい場合は、CSS のセレクタで絞り込むか、Extensions テーブルを使ってください。

HTML への挿入位置 ​

拡張スタイルは <head> 内に <link rel="stylesheet" href="/resources/styles?..."> として挿入されます。そのページの条件に合う拡張スタイルが 1 つもない(結合結果が空の)ときはタグ自体が出力されません(_using: !extendedStyles.IsNullOrEmpty()。HtmlStyles.cs)。サイトのトップ(一覧の最上位)では、テナントの管理画面で設定したトップのスタイル(TopStyle)も先頭に結合されます(L56-L58)。

csharp
public static HtmlBuilder ExtendedStyles(this HtmlBuilder hb, Context context)
{
    var extendedStyles = ExtendedStyles(context: context);
    return hb
        .Link(
            rel: "stylesheet",
            href: $"resources/styles?v={extendedStyles.Sha512Cng()}"
                + $"&site-id={context.SiteId}"
                + $"&id={context.Id}"
                + $"&controller={context.Controller}"
                + $"&action={context.Action}",
            _using: !extendedStyles.IsNullOrEmpty());
}

<head> 内のスタイルシートと、<body> 内の管理画面のスタイルの並び順は次のとおりです(HtmlTemplates.cs、L136-L142)。

html
<head>
  <!-- Normalize.css, jQuery-UI CSS, Material Symbolsなど標準CSS -->
  <link rel="stylesheet" href="assets/plugins/Normalize.css">
  <link rel="stylesheet" href="assets/themes/...">
  <!-- プリザンター本体のスタイルシート (legacy.min.css または style.min.css) -->
  <link rel="stylesheet" href="assets/css/legacy.min.css?v=...">
  <!-- 外部リンクスタイル(管理画面の「ヘッドリンク」設定) -->
  <link rel="stylesheet" href="...">
  <!-- 拡張スタイル(ここ) -->
  <link rel="stylesheet" href="/resources/styles?v=...">
  <title>...</title>
</head>
<body>
  <!-- メインコンテンツ -->
  <!-- 管理画面のスタイル(<style>インラインタグ。body 内、スクリプトより前) -->
  <style>/* 管理画面のスタイル(All) */</style>
  <style>/* ページ固有の管理画面スタイル */</style>
  ...
</body>

拡張スタイルはプリザンター本体のスタイルシートの後に入るため、標準スタイルを上書きする CSS を書きやすい位置です。管理画面のスタイル(<style> タグ)は <head> ではなく <body> 内に出力され、文書の中ではさらに後に来るので、セレクタと詳細度が同じ場合の優先順位は次のようになります。

図を読み込み中…

v パラメータとキャッシュバスティング ​

/resources/styles?v=... の v には、すべての拡張スタイルを結合した文字列の SHA512 ハッシュが入ります。スタイルの内容が変わるとハッシュも変わり、ブラウザのキャッシュが無効になります。逆に、内容が変わらない限りキャッシュは効き続けます。

管理画面のスタイルの場合 ​

管理画面の「スタイル」(SiteSettings.Styles)はサイトごとに設定し、適用する画面(新規・編集・一覧・カレンダーなど)を選べます。出力は <style> のインラインタグです。

csharp
public string GetStyleBody(Context context, Func<Style, bool> peredicate)
{
    return !IsSiteEditor(context: context)
        ? Styles?
            .Where(style => style.Disabled != true && peredicate(style))
            .Select(o => o.Body).Join("\n")
        : null;
}

INFO

管理画面でサイトを編集している間(サイトエディタを開いている状態)は、管理画面のスタイルは適用されません(IsSiteEditor が true のとき null を返します)。スタイルが管理画面の表示に影響しないようにするための制御です。拡張スタイルにはこの制限はありません。

運用上の注意点 ​

  • 変更は再起動またはパラメータリロードまで反映されません。 ファイルでも Extensions テーブルでも、読み込みは起動時とパラメータリロードのときだけです。
  • ファイルの拡張スタイルは全ページに効きます。 範囲を絞るにはセレクタで絞り込むか、Extensions テーブルを使います。
  • 詳細度が最優先です。 挿入順が効くのは詳細度が同じ場合だけです。意図どおり上書きできているか、ブラウザの開発者ツールで確認してください。
  • !important は多用しないでください。 プリザンターの一部のスタイルには !important が使われており、それを上書きするには同じく !important が必要ですが、多用するとデバッグが難しくなります。できるだけセレクタの詳細度を上げる方法を優先します。
  • 広いセレクタは避けてください。 * や body など広い範囲に効くセレクタは、他の機能や画面に意図しない影響を与えることがあります。影響範囲の狭い具体的なセレクタを使います。
  • 開発中はキャッシュに注意してください。 内容が変わらない限りキャッシュが効き続けるため、開発・デバッグ中は Ctrl + F5(ハードリロード)やブラウザのキャッシュ無効化設定を使って最新の状態を確認します。

まとめ ​

項目内容
ファイルの配置場所App_Data/Parameters/ExtendedStyles/*.css(サブディレクトリも再帰、ファイル名順)
読み込みタイミングファイル・DB ともアプリ起動時とパラメータリロード時
HTML への挿入形式<head> 内に <link rel="stylesheet" href="/resources/styles?v=...">
挿入位置プリザンター標準 CSS の後、管理画面のスタイルの前
フィルタリング組織・グループ・ユーザ・サイト ID・レコード ID・コントローラ・アクション(DB で設定した場合のみ)
キャッシュ内容の SHA512 ハッシュで URL が変わり、キャッシュが無効になる

関連ページ ​

変更履歴

第6版記事の確認版を繰り返す表現を整理する
第5版「拡張機能」「画面カスタマイズ集」に対応バージョンを表示
第4版本文から元記事や以前の版への言及を除き、正しい動作だけを書く形に整理
第3版「拡張機能」「API」を 1.5.8.1 のソースで検証して修正
第2版記事のファイル名に並び順の番号を付け、元記事リンクを frontmatter の sources に移行
第1版拡張 HTML・拡張スタイル・拡張スクリプト・拡張フィールドの仕組みを追加し、拡張 SQL の設定パラメータを拡充