Skip to content

Extensions テーブルで拡張機能を DB 管理 ​

第9版作成 最終更新 (日本時間)
対応バージョンPleasanter 1.4.7.0 以降確認バージョン1.4.7.01.5.8.1

拡張機能(拡張スクリプト・拡張 SQL・拡張 CSS など)は、通常 App_Data/Parameters/ExtendedXxx/ フォルダに JSON ファイルを置いて管理しますが、データベースの Extensions テーブルで管理することもできます。複数インスタンスで同じ拡張設定を共有したいマルチクラスタ環境・コンテナ環境で便利で、API 経由の CRUD による自動化もできます。

対応バージョン

Pleasanter 1.4.7.0 以降

テーブル構造 ​

カラム名型説明
ExtensionIdint拡張機能 ID(自動採番)
TenantIdintテナント ID
ExtensionTypenvarchar(128)拡張機能種別
ExtensionNamenvarchar(256)拡張機能名
ExtensionSettingsnvarchar(max)拡張機能設定(JSON 形式)
Bodynvarchar(max)本文(スクリプト・SQL・CSS など)
Descriptionnvarchar(max)説明
Disabledbit無効フラグ(true で無効化)

このほかに、ほかのテーブルと同じ共通列(Ver・Comments・Creator・Updator・CreatedTime・UpdatedTime)があります。起動時とパラメータリロード時に、Disabled が false のレコードを ExtensionName 順に読み込みます(ExtensionInitializer.cs#L16-L21)。

ExtensionType の値 ​

ExtensionType説明
Fields拡張フィールド(カスタム項目)
Html拡張 HTML(画面への HTML 差し込み)
NavigationMenu拡張ナビゲーションメニュー
Script拡張スクリプト(クライアント JS)
ServerScript拡張サーバースクリプト
Sql拡張 SQL
Style拡張スタイル(CSS)
Plugin拡張プラグイン
CustomAppsユーザーテンプレート

CustomApps は拡張機能の読み込み(ExtensionInitializer)の対象外で、サイトのテンプレート機能がサイトパッケージの JSON を保存・読み出すために使います(SiteUtilities.cs#L3170)。拡張機能として登録する種別は、ほかの 8 種類です。

ExtensionSettings ​

ExtensionSettings には、ExtensionType に応じた JSON を設定します。次のフィルタリング設定はすべての種別で共通です。

プロパティ型説明
SiteIdListList<long>適用対象のサイト ID リスト(空 = 全サイト)
UserIdListList<int>適用対象のユーザー ID リスト
DeptIdListList<int>適用対象の部署 ID リスト
GroupIdListList<int>適用対象のグループ ID リスト
ActionsList<string>適用アクション(edit, new など。小文字で書く)
ControllersList<string>適用コントローラ(items など。小文字で書く)
IdListList<long>適用対象のレコード ID リスト
  • リストが空の場合は「条件なし(全適用)」として扱われます。
  • 否定条件は値の先頭に - を付けます(例: ["-5"] は ID=5 を除外)。
  • ExtensionSettings を空のオブジェクト {} にすると、全サイト・全画面で適用されます。
  • Actions・Controllers は、小文字にしたアクション名・コントローラ名と完全一致で比較されます(Context.cs)。"Edit" のように大文字を含めると一致しません。["Edit","New"] ではなく ["edit","new"] と書きます。

本文は Body と ExtensionSettings のどちらに書くか ​

確認したソースでは、読み込み時に ExtensionSettings を種別ごとの設定クラスとして読み込み、そのうえで Body が空でなければ本文を Body で上書きします(ExtensionInitializer.cs)。

ExtensionTypeBody の扱い
ScriptScript(スクリプト本文)になる
StyleStyle(CSS 本文)になる
SqlCommandText(SQL 本文)になる
ServerScriptBody(サーバースクリプト本文)になる
Html表示用の HTML(言語は ExtensionSettings の Language)になる
Fields / NavigationMenu / Plugin使われない。設定はすべて ExtensionSettings に書く

つまり、本文は Body に書くのが基本で、Body を空にすれば ExtensionSettings 内の Script・Style・CommandText などがそのまま使われます。両方に書いた場合は Body が優先されます。また、ExtensionName が空でなければ ExtensionSettings の Name より優先されます。

WARNING

ExtensionSettings が空文字列や不正な JSON だと設定として読み込めず、そのレコードは無視されます。条件が不要な場合も {} を入れてください。また、読み込み時の条件は Disabled だけで、TenantId による絞り込みはしていません(ExtensionInitializer.cs)。確認したソースでは、登録したレコードはテナントに関係なく適用されます。

SQL で直接登録する例 ​

特定サイトに JavaScript を追加 ​

sql
INSERT INTO Extensions (
    TenantId,
    ExtensionType,
    ExtensionName,
    ExtensionSettings,
    Body,
    Description,
    Disabled
) VALUES (
    1,
    'Script',
    'MyCustomScript',
    '{"SiteIdList":[12345,67890],"Actions":["edit","new"]}',
    '$(document).ready(function() { console.log("カスタムスクリプト読み込み完了"); });',
    'サイトID 12345, 67890 の編集・新規画面で実行されるスクリプト',
    0
);
sql
INSERT INTO "Extensions" (
    "TenantId",
    "ExtensionType",
    "ExtensionName",
    "ExtensionSettings",
    "Body",
    "Description",
    "Disabled"
) VALUES (
    1,
    'Script',
    'MyCustomScript',
    '{"SiteIdList":[12345,67890],"Actions":["edit","new"]}',
    '$(document).ready(function() { console.log("カスタムスクリプト読み込み完了"); });',
    'サイトID 12345, 67890 の編集・新規画面で実行されるスクリプト',
    false
);
sql
INSERT INTO Extensions (
    TenantId,
    ExtensionType,
    ExtensionName,
    ExtensionSettings,
    Body,
    Description,
    Disabled
) VALUES (
    1,
    'Script',
    'MyCustomScript',
    '{"SiteIdList":[12345,67890],"Actions":["edit","new"]}',
    '$(document).ready(function() { console.log("カスタムスクリプト読み込み完了"); });',
    'サイトID 12345, 67890 の編集・新規画面で実行されるスクリプト',
    0
);

全サイト共通の CSS ​

sql
INSERT INTO Extensions (
    TenantId,
    ExtensionType,
    ExtensionName,
    ExtensionSettings,
    Body,
    Description,
    Disabled
) VALUES (
    1,
    'Style',
    'GlobalStyle',
    '{}',
    '.field-label { font-weight: bold; color: #333; }',
    '全サイト共通のカスタムスタイル',
    0
);
sql
INSERT INTO "Extensions" (
    "TenantId",
    "ExtensionType",
    "ExtensionName",
    "ExtensionSettings",
    "Body",
    "Description",
    "Disabled"
) VALUES (
    1,
    'Style',
    'GlobalStyle',
    '{}',
    '.field-label { font-weight: bold; color: #333; }',
    '全サイト共通のカスタムスタイル',
    false
);
sql
INSERT INTO Extensions (
    TenantId,
    ExtensionType,
    ExtensionName,
    ExtensionSettings,
    Body,
    Description,
    Disabled
) VALUES (
    1,
    'Style',
    'GlobalStyle',
    '{}',
    '.field-label { font-weight: bold; color: #333; }',
    '全サイト共通のカスタムスタイル',
    0
);

特定ユーザー向けサーバースクリプト ​

sql
INSERT INTO Extensions (
    TenantId,
    ExtensionType,
    ExtensionName,
    ExtensionSettings,
    Body,
    Description,
    Disabled
) VALUES (
    1,
    'ServerScript',
    'AdminOnlyValidation',
    '{"UserIdList":[1,2],"BeforeUpdate":true}',
    'if (model.Status < 200) { model.Status = 200; }',
    '管理者のみ更新前に実行されるバリデーション',
    0
);
sql
INSERT INTO "Extensions" (
    "TenantId",
    "ExtensionType",
    "ExtensionName",
    "ExtensionSettings",
    "Body",
    "Description",
    "Disabled"
) VALUES (
    1,
    'ServerScript',
    'AdminOnlyValidation',
    '{"UserIdList":[1,2],"BeforeUpdate":true}',
    'if (model.Status < 200) { model.Status = 200; }',
    '管理者のみ更新前に実行されるバリデーション',
    false
);
sql
INSERT INTO Extensions (
    TenantId,
    ExtensionType,
    ExtensionName,
    ExtensionSettings,
    Body,
    Description,
    Disabled
) VALUES (
    1,
    'ServerScript',
    'AdminOnlyValidation',
    '{"UserIdList":[1,2],"BeforeUpdate":true}',
    'if (model.Status < 200) { model.Status = 200; }',
    '管理者のみ更新前に実行されるバリデーション',
    0
);

API から実行できる拡張 SQL ​

ExtensionSettings に "Api": true を設定すると、拡張 SQL を API から実行できます。

sql
INSERT INTO Extensions (
    TenantId,
    ExtensionType,
    ExtensionName,
    ExtensionSettings,
    Body,
    Description,
    Disabled
) VALUES (
    1,
    'Sql',
    'GetSiteList',
    '{"Api":true}',
    'SELECT SiteId, Title FROM Sites WHERE TenantId = @_T ORDER BY SiteId',
    'サイト一覧を取得する拡張SQL',
    0
);
sql
INSERT INTO "Extensions" (
    "TenantId",
    "ExtensionType",
    "ExtensionName",
    "ExtensionSettings",
    "Body",
    "Description",
    "Disabled"
) VALUES (
    1,
    'Sql',
    'GetSiteList',
    '{"Api":true}',
    'SELECT "SiteId", "Title" FROM "Sites" WHERE "TenantId" = @ipT ORDER BY "SiteId"',
    'サイト一覧を取得する拡張SQL',
    false
);
sql
INSERT INTO Extensions (
    TenantId,
    ExtensionType,
    ExtensionName,
    ExtensionSettings,
    Body,
    Description,
    Disabled
) VALUES (
    1,
    'Sql',
    'GetSiteList',
    '{"Api":true}',
    'SELECT SiteId, Title FROM Sites WHERE TenantId = @ipT ORDER BY SiteId',
    'サイト一覧を取得する拡張SQL',
    0
);

@_T(PostgreSQL・MySQL では @ipT)はログインユーザー(API キーのユーザー)のテナント ID で、拡張 SQL では常に使えます。接頭辞は DBMS で変わり、Parameter.json の SqlParameterPrefix が空の既定なら SQL Server は @_、PostgreSQL・MySQL は @ip です(Parameter.cs、SqlIo.cs)。PostgreSQL ではテーブル名・列名が二重引用符付きの大文字小文字を区別した名前で作られるため(CreateTable.sql)、"Extensions" のように二重引用符で囲みます(囲まないと小文字の名前として扱われ、テーブルが見つかりません)。Disabled は PostgreSQL では boolean 型なので false を入れます(PostgreSqlDataTypes.cs)。@TenantId というパラメータは渡されないため、@TenantId と書くと動きません。

/api/extended/sql から呼び出します。

json
POST /api/extended/sql
{
    "ApiVersion": 1.1,
    "ApiKey": "your-api-key",
    "Name": "GetSiteList"
}

Extensions API ​

Extensions テーブルのレコードは API で CRUD 操作できます。

前提条件 ​

  1. テナント管理画面で「Extensions API を許可する」を有効にしている(Tenants テーブルの AllowExtensionsApi)
  2. API キーで認証する
  3. 特権ユーザーである
  4. ライセンスの Environment が 1・2 の環境ではない

コントローラは最初に AllowExtensionsApi を見て、無効なら 403 を返します。次に、認証済みでかつ特権ユーザーでなければ 401 を返します(ExtensionsController.cs#L35-L46)。取得・作成・更新・削除の各処理は、さらに Parameters.Environment() が 1 または 2 のとき 403 を返します(ExtensionValidators.cs#L18-L21、Validators.cs#L97-L114)。Environment() はライセンス情報の Environment の値で、ライセンスがない環境は 0、試用ライセンスだけの環境は 3 です(Parameters.cs#L169-L178)。

エンドポイント ​

操作メソッドエンドポイント
全件取得POST/api/extensions/get
個別取得POST/api/extensions/{id}/get
作成POST/api/extensions/create
更新POST/api/extensions/{id}/update
削除POST/api/extensions/{id}/delete

全件取得 ​

テナント内の Extension レコードを取得します。

json
POST /api/extensions/get
{
    "ApiVersion": 1.1,
    "ApiKey": "your-api-key"
}

レスポンス例:

json
{
    "StatusCode": 200,
    "Response": {
        "Data": [
            {
                "ApiVersion": 1.1,
                "ExtensionId": 1,
                "TenantId": 1,
                "Ver": 1,
                "ExtensionType": "Script",
                "ExtensionName": "MyCustomScript",
                "ExtensionSettings": "{\"SiteIdList\":[12345]}",
                "Body": "console.log('Hello');",
                "Description": "カスタムスクリプト",
                "Disabled": false,
                "Creator": 1,
                "Updator": 1,
                "CreatedTime": "2026-02-19T10:00:00",
                "UpdatedTime": "2026-02-19T10:00:00"
            }
        ]
    }
}

View を指定すると、条件に合うレコードだけを取得できます。

json
POST /api/extensions/get
{
    "ApiVersion": 1.1,
    "ApiKey": "your-api-key",
    "View": {
        "ColumnFilterHash": {
            "ExtensionType": "Script"
        }
    }
}

個別取得 ​

URL に ExtensionId を指定して 1 件取得します。

json
POST /api/extensions/1/get
{
    "ApiVersion": 1.1,
    "ApiKey": "your-api-key"
}

作成 ​

作成時は ExtensionSettings を JSON オブジェクトで渡します。

json
POST /api/extensions/create
{
    "ApiVersion": 1.1,
    "ApiKey": "your-api-key",
    "ExtensionType": "Script",
    "ExtensionName": "MyNewScript",
    "ExtensionSettings": {
        "SiteIdList": [12345],
        "Actions": ["edit"]
    },
    "Body": "console.log('Hello from Extensions API');",
    "Description": "API経由で作成したスクリプト",
    "Disabled": false
}

作成・更新では、受け取った ExtensionSettings を ExtensionType に対応する設定クラスとして読めるかを確かめます(ExtensionApiModel.cs#L61-L97)。読めない場合は不正な JSON として扱われ、エラーになります(ExtensionUtilities.cs#L348-L350)。

レスポンス例:

json
{
    "Id": 2,
    "StatusCode": 200,
    "Message": "MyNewScript を作成しました。"
}

更新 ​

URL に ExtensionId を指定します。変更するフィールドだけを指定でき、指定しなかったフィールドは既存の値が維持されます。

json
POST /api/extensions/1/update
{
    "ApiVersion": 1.1,
    "ApiKey": "your-api-key",
    "Body": "console.log('Updated script');",
    "Description": "更新されたスクリプト",
    "Disabled": false
}

レスポンス例:

json
{
    "Id": 1,
    "StatusCode": 200,
    "Message": "MyNewScript を更新しました。"
}

削除 ​

json
POST /api/extensions/1/delete
{
    "ApiVersion": 1.1,
    "ApiKey": "your-api-key"
}

レスポンス例:

json
{
    "Id": 1,
    "StatusCode": 200,
    "Message": "MyNewScript を削除しました。"
}

エラーレスポンス ​

json
{
    "StatusCode": 403,
    "Message": "この操作を行う権限がありません。"
}
StatusCode説明
200成功
400不正なリクエスト(JSON 形式エラー等)
401認証できない、または特権ユーザーでない
403テナントで Extensions API が許可されていない、または Environment が 1・2 の環境
404指定した ExtensionId が存在しない

ファイルベースの設定との関係 ​

Extensions テーブルのレコードと App_Data/Parameters/ExtendedXxx/ のファイルは共存します。

管理方法設定場所登録タイミング
ファイルApp_Data/Parameters/ExtendedXxx/*.jsonアプリ起動時に読込
データベースExtensions テーブルアプリ起動時に読込

両方に設定がある場合は、それぞれ独立してリストに追加されます。同名の拡張があると両方とも適用されるため、意図しない重複に注意してください。

WARNING

Extensions テーブルの変更を反映するには、アプリケーションの再起動またはパラメータリロードが必要です。

再起動せずに反映するには、特権ユーザーでログインした状態で次の URL にアクセスします。成功すると空白のページが表示され、Extensions テーブルを含むパラメータが再読み込みされます(ParametersInitializer.cs)。

text
https://{プリザンターのURL}/admins/reloadparameters

マルチクラスタ環境での比較 ​

観点ファイルベースデータベース(Extensions テーブル)
設定の配布各インスタンスにファイルを配置する必要ありデータベースを共有すれば自動的に同期
設定変更の反映各インスタンスでファイルを更新&再起動DB を更新して各インスタンスを再起動
バージョン管理Git などで管理DB レコードとして管理(履歴は別途必要)
運用自動化デプロイツールとの連携が必要API 経由で設定変更が可能

関連ページ ​

変更履歴

第9版記事の確認版を繰り返す表現を整理する
第8版Extensions テーブルへの登録 SQL を3種類のDBMSに対応
第7版Extensions テーブルのログイン情報パラメータが DB ごとに違うことを明記
第6版サイト設定の変更履歴・拡張 SQL の外部 DB 接続・サイト名の解決・API ラッパー・ApiVersion の解説と、関連する改修・設計メモを追加
第5版「拡張機能」「画面カスタマイズ集」に対応バージョンを表示
第4版本文から元記事や以前の版への言及を除き、正しい動作だけを書く形に整理
第3版「拡張機能」「API」を 1.5.8.1 のソースで検証して修正
第2版記事のファイル名に並び順の番号を付け、元記事リンクを frontmatter の sources に移行
第1版「拡張機能」「API」セクションの記事を追加