サイトの移動抑止・アーカイブ管理・サイト構成一覧
サイト(フォルダやテーブル)の管理に関する小技をまとめます。
- ドラッグ&ドロップによるサイトの誤移動を、スクリプトと拡張ナビゲーションメニューで防ぐ
- 使わなくなったサイトを、CSS とスクリプトで「アーカイブ」のように隠す・切り替える
- サイト構成の一覧を、階層付きで SQL(拡張 SQL の API)から取得する
サイトの移動を抑止する
サイトの管理権限を持つユーザは、ドラッグ&ドロップでサイトの階層移動や並べ替えができます。操作が簡単なぶん、ちょっとした誤操作で意図せず移動させてしまうことがあります。
仕組み
サイトの移動は jQuery UI の Sortable で実現されています。対象の要素に対して Sortable の有効・無効(disabled オプション)を切り替えれば、移動させる・させないを制御できます。
ここでは次の要件で実装します。
- 最初は移動を無効にしておき、必要なときだけ有効にする
- 有効・無効はナビゲーションメニューから切り替える
bodyの中に、現在の状態を保持する hidden のinputを追加します。body内の hidden input にフラグや値を持たせるのはプリザンターでよく使われる方法で、$p.UserIdなどスクリプトで使える値の多くもこの input 群から取得されています。- フラグを反転し、メニューの文言を差し替える関数を用意します。
- 移動できる要素(
.nav-sites.sortable)にポインタが乗ったときに、フラグを見て Sortable を有効・無効に切り替えます。
設定
拡張スクリプト と 拡張ナビゲーションメニュー として配置します。
if(!$('#SiteSortable').length){
$('body').append('<input id="SiteSortable" name="SiteSortable" type="hidden" value="0">');
}
$p.naviMenuExtendDisableSorter = function(){
if($('#SiteSortable').val() == 0){
$('#SiteSortable').val(1);
$('#SettingsMenu_DisableSorter a').text('ソートを無効にする');
$p.closeSideMenu();
}else{
$('#SiteSortable').val(0);
$('#SettingsMenu_DisableSorter a').text('ソートを有効にする');
$p.closeSideMenu();
}
}
$('.nav-sites.sortable').on('mouseenter pointerenter touchstart',function(){
$(this).sortable($('#SiteSortable').val() == 0 ? 'disable' : 'enable');
});{
"TargetId": "SettingsMenu",
"Action": "Append",
"NavigationMenus": [
{
"ContainerId": "ExtendSettingsContainer",
"MenuId": "ExtendSettingsMenu",
"Name": "拡張管理",
"Icon": "ui-icon ui-icon-gear",
"ChildMenus": [
{
"MenuId": "SettingsMenu_DisableSorter",
"Name": "ソートを有効にする",
"Icon": "ui-icon ui-icon-radio-off",
"Url": "javascript:$p.naviMenuExtendDisableSorter();",
"ReferenceTypes": ["Sites"],
"Actions": ["index"]
}
]
}
]
}ReferenceTypes と Actions の指定により、メニューはサイト(フォルダ)の画面を表示しているときだけ表示されます。
サイトをアーカイブのように管理する
あまり使わないサイトや終了したプロジェクトのサイトなど、削除はできないが表示されていると邪魔なサイトを、その場で見えなくする方法です。アーカイブ用フォルダへ移動させる方法もありますが、アクセス権の管理がサイトの階層から離れる、所定の階層外に動かしたくない、といった場合に使えます。
「読取専用の場合は画面に表示しない」オプションとの比較
読取専用の場合は画面に表示しない オプションを使い、管理者など必要最低限のユーザだけに書き込み権限を付与すれば、他のユーザの画面からはサイトが消えます。ただし完全に表示から消えるため、一時的に表示させることはできません。一時的に隠したいだけなら、次のスタイルとスクリプトの方法が向いています。
CSS で隠す
隠したいサイトを含むフォルダのテーブルの管理で、スタイルに次の CSS を設定します(サイト ID が 3482327 の場合)。
.nav-site[data-value="3482327"] {
display:none;
}スクリプトで表示を切り替える
CSS で display:none; にしているだけなので、jQuery で表示できます。
$('.nav-site').show();toggle を使うと、「通常のサイト」と「アーカイブされたサイト」の表示を切り替えられます。「上へ」の要素も nav-site クラスを持っているため、:not(.to-parent) で除外しています。
$('.nav-site:not(.to-parent)').toggle();ナビゲーションメニューに組み込む
拡張ナビゲーションメニュー で切り替えメニューを追加します。メニューの文言も同時に切り替えています。
{
"TargetId": "SettingsMenu",
"Action": "Append",
"NavigationMenus": [
{
"ContainerId": "SiteArchiveContainer",
"MenuId": "SiteArchiveMenu",
"Name": "アーカイブ",
"Icon": "ui-icon ui-icon-gear",
"ChildMenus": [
{
"MenuId": "SiteArchive_ArchiveToggle",
"Name": "アーカイブ表示",
"Icon": "ui-icon ui-icon-arrowrefresh-1-w",
"Url": "javascript:$('.nav-site:not(.to-parent)').toggle();$('#SiteArchive_ArchiveToggle a').text($('#SiteArchive_ArchiveToggle a').text() === 'アーカイブ表示' ? '通常表示' : 'アーカイブ表示');",
"ReferenceTypes": ["Sites"],
"Actions": ["index"]
}
]
}
]
}TIP
非表示のスタイルを当てるときに、アーカイブ対象のサイトの背景色も変えておくと、アーカイブ表示に切り替えたときに見分けやすくなります。
サイト構成の一覧を取得する
Sites テーブルから、サイトの一覧を階層付きで取得するクエリです。再帰 CTE でタイトルを 親 > 子 > 孫 の形に連結しながら取り出します。
INFO
SQL Server の NVARCHAR(長さ指定なしの CAST は 30 文字)では切り詰められ、直前の親のタイトルだけを連結しても階層全体にならないため、NVARCHAR(4000) と親の FullName を使っています。PostgreSQL は text、MySQL は CHAR(4000) に変換します。PostgreSQL と MySQL では WITH RECURSIVE を使います(PostgreSQL、MySQL)。MySQL の例は 8.0 以降を対象とし、階層全体のタイトルが 4,000 文字に収まる前提です。
WITH CTE AS (
SELECT
[SiteId],
[ReferenceType],
[Title],
[ParentId],
CAST([Title] AS NVARCHAR(4000)) AS [FullName]
FROM
[Sites]
WHERE
[ParentId] = 0
UNION ALL
SELECT
C.[SiteId],
C.[ReferenceType],
C.[Title],
C.[ParentId],
CAST((P.[FullName] + ' > ' + C.[Title]) AS NVARCHAR(4000)) AS [FullName]
FROM
[Sites] C
INNER JOIN CTE P
ON P.[SiteId] = C.[ParentId]
)
SELECT
[SiteId],
[ReferenceType],
[FullName],
[ParentId]
FROM
CTE
ORDER BY
[ParentId],
[SiteId]WITH RECURSIVE CTE AS (
SELECT
"SiteId",
"ReferenceType",
"Title",
"ParentId",
CAST("Title" AS text) AS "FullName"
FROM
"Sites"
WHERE
"ParentId" = 0
UNION ALL
SELECT
C."SiteId",
C."ReferenceType",
C."Title",
C."ParentId",
CAST((P."FullName" || ' > ' || C."Title") AS text) AS "FullName"
FROM
"Sites" C
INNER JOIN CTE P
ON P."SiteId" = C."ParentId"
)
SELECT
"SiteId",
"ReferenceType",
"FullName",
"ParentId"
FROM
CTE
ORDER BY
"ParentId",
"SiteId"WITH RECURSIVE CTE AS (
SELECT
`SiteId`,
`ReferenceType`,
`Title`,
`ParentId`,
CAST(`Title` AS CHAR(4000)) AS `FullName`
FROM
`Sites`
WHERE
`ParentId` = 0
UNION ALL
SELECT
C.`SiteId`,
C.`ReferenceType`,
C.`Title`,
C.`ParentId`,
CAST(CONCAT(P.`FullName`, ' > ', C.`Title`) AS CHAR(4000)) AS `FullName`
FROM
`Sites` C
INNER JOIN CTE P
ON P.`SiteId` = C.`ParentId`
)
SELECT
`SiteId`,
`ReferenceType`,
`FullName`,
`ParentId`
FROM
CTE
ORDER BY
`ParentId`,
`SiteId`実行結果の例です。
| SiteId | ReferenceType | FullName | ParentId |
|---|---|---|---|
| 3457307 | Sites | [Sandbox]社内用 | 0 |
| 3526345 | Results | [Sandbox]社内用 > 記録テーブル(子テーブル) | 3457307 |
| 3526346 | Issues | [Sandbox]社内用 > 期限付きテーブル(親テーブル) | 3457307 |
| 3578784 | Results | [Sandbox]社内用 > ユーザ複数選択テスト | 3457307 |
拡張 SQL で API 化する
拡張 SQL の API 化 を使うと、この一覧を API で取得できます。定義ファイルと SQL ファイルを分けて配置します(SQL の中身は上のクエリと同じです)。
{
"Name": "GetSiteList",
"Api": true,
"CommandText": "-- Write an arbitrary SQL statement."
}使用中の DBMS の上記クエリ全文を GetSiteList.json.sql に保存します。
パンくずリストやサイトツリーなど、階層を持ったメニューを作るときに便利です。