本記事は、OSSのノーコード・ローコード開発ツール「プリザンター」 Advent Calendar 2025 の8日目の記事です。
はじめに
プリザンターはオープンソースソフトウェアです。ソースコードが GitHub に公開されているため、誰でも新しい機能を追加して利用できます。今回は試しに、「状況による制御」の特定設定を一時的に 無効化 できる機能を追加してみたいと思います。
「状況による制御」は複雑な設定になることが多く、動作確認のために一度設定を無効化した状態でテストを行いたい、というニーズに応えるための機能です。
ソースコードのリポジトリ
プリザンターのソースコードは、以下のリポジトリで公開されています。この記事の内容を試してみたい方は、こちらからソースコードをクローンしてください。
https://github.com/Implem/Implem.Pleasanter
現状の動作仕様の確認
まずは、機能追加前の「状況による制御」の設定画面を確認してみます。こちらは、状況による制御を 1 つだけ設定した状態の例です。レコードの状況が「完了」になった場合、そのレコードを読み取り専用にするよう設定されています。

この設定の詳細画面を開くと、以下のようになっています。項目の制御では、タイトルが非表示、内容が読み取り専用、開始が入力必須に設定されています。

状況が完了のレコードを開くと、以下のように表示されます。読み取り専用や、各項目の設定が正しく動作しています。

機能追加
それでは、早速、ソースコードに手を入れていきたいと思います。
StatusControl.csは、状況による制御の1件分の設定を保持するためのクラスです。プリザンターのテーブルの管理で行う設定は、ほぼ全て、Implem.Pleasanter.Libraries.Settingsの中にあるクラス群で管理されています。ここに新しいプロパティDisabledを追加して、ユーザによる「無効」の設定を保存できるようにします。
using Implem.Libraries.Utilities;
using Implem.Pleasanter.Interfaces;
using Implem.Pleasanter.Libraries.Requests;
using Implem.Pleasanter.Libraries.Security;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Implem.Pleasanter.Libraries.Settings
{
[Serializable()]
public class StatusControl : ISettingListItem
{
[JsonConverter(typeof(StringEnumConverter))]
public enum ControlConstraintsTypes
{
None,
Required,
ReadOnly,
Hidden
}
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int? Status { get; set; }
public bool? ReadOnly { get; set; }
public Dictionary<string, ControlConstraintsTypes> ColumnHash { get; set; }
public View View { get; set; }
public List<int> Depts { get; set; }
public List<int> Groups { get; set; }
public List<int> Users { get; set; }
public int? Delete { get; set; }
// 【追加】Disabledプロパティを追加。
public bool? Disabled { get; set; }
public StatusControl()
{
}
public StatusControl(
int id,
string name,
string description,
int? status,
bool? readOnly,
Dictionary<string, ControlConstraintsTypes> columnHash,
View view,
List<Permission> permissions,
// 【追加】新しい設定が追加される際に、Disabledプロパティを受け取れるようにします。
bool? disabled)
{
Id = id;
Name = name;
Status = status;
ReadOnly = readOnly;
ColumnHash = columnHash;
Description = description;
View = view;
SetPermissions(permissions: permissions);
// 【追加】プロパティにセットします。
Disabled = disabled;
}
public void Update(
string name,
string description,
int? status,
bool? readOnly,
Dictionary<string, ControlConstraintsTypes> columnHash,
View view,
List<Permission> permissions,
// 【追加】設定が更新される際に、Disabledプロパティを受け取れるようにします。
bool? disabled)
{
if (name != null)
{
Name = name;
}
if (status != null)
{
Status = status;
}
if (readOnly != null)
{
ReadOnly = readOnly;
}
if (columnHash != null)
{
ColumnHash = columnHash;
}
if (description != null)
{
Description = description;
}
if (view != null)
{
View = view;
}
if (permissions != null)
{
SetPermissions(permissions: permissions);
}
// 【追加】プロパティにセットします。
if (disabled != null)
{
Disabled = disabled;
}
}
// ...省略...
public StatusControl GetRecordingData(
Context context,
SiteSettings ss)
{
var statusControl = new StatusControl();
statusControl.Id = Id;
statusControl.Name = Name;
if (!Description.IsNullOrEmpty())
{
statusControl.Description = Description;
}
statusControl.Status = Status;
if (ReadOnly == true)
{
statusControl.ReadOnly = ReadOnly;
}
if (ColumnHash?.Any(o => o.Value != ControlConstraintsTypes.None) == true)
{
statusControl.ColumnHash = ColumnHash
.Where(o => o.Value != ControlConstraintsTypes.None)
.ToDictionary(o => o.Key, o => o.Value);
}
statusControl.View = View?.GetRecordingData(
context: context,
ss: ss);
if (Depts?.Any() == true)
{
statusControl.Depts = Depts;
}
if (Groups?.Any() == true)
{
statusControl.Groups = Groups;
}
if (Users?.Any() == true)
{
statusControl.Users = Users;
}
// 【追加】この設定はJSONとして保存されます。設定がtrueの場合だけ保存されるようにしてJSONが膨大にならないようにします。
if (Disabled == true)
{
statusControl.Disabled = Disabled;
}
return statusControl;
}
// ...省略...
}
}
StatusControlApiSettingModel.csは、APIから要求されたJSONデータを受け取るためのクラスです。Disabledプロパティを受け取れるようコードを追加します。
using Implem.Pleasanter.Libraries.Settings;
using System;
using System.Collections.Generic;
namespace Implem.Pleasanter.Models.ApiSiteSettings
{
[Serializable]
public class StatusControlApiSettingModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int? Status { get; set; }
public bool? ReadOnly { get; set; }
public Dictionary<string, StatusControl.ControlConstraintsTypes> ColumnHash { get; set; }
public View View { get; set; }
public ApiSiteSettingPermission Permission { get; set; }
public int? Delete { get; set; }
// 【追加】Disabledプロパティを追加。
public bool? Disabled { get; set; }
public StatusControlApiSettingModel()
{
}
}
}
SiteUtilities.csは、テーブルの管理画面を生成する機能をもったクラスです。プリザンターはサーバサイドでHTMLレンダリングを行うため、テーブルの管理画面のUIを変更する場合には、このファイルを編集します。「無効」をユーザが設定できるようUIを生成するコードを追加します。
using Implem.DefinitionAccessor;
using Implem.Libraries.Classes;
using Implem.Libraries.DataSources.Interfaces;
using Implem.Libraries.DataSources.SqlServer;
using Implem.Libraries.Utilities;
using Implem.Pleasanter.Libraries.DataSources;
using Implem.Pleasanter.Libraries.DataTypes;
using Implem.Pleasanter.Libraries.Extensions;
using Implem.Pleasanter.Libraries.General;
using Implem.Pleasanter.Libraries.Html;
using Implem.Pleasanter.Libraries.HtmlParts;
using Implem.Pleasanter.Libraries.Models;
using Implem.Pleasanter.Libraries.Requests;
using Implem.Pleasanter.Libraries.Resources;
using Implem.Pleasanter.Libraries.Responses;
using Implem.Pleasanter.Libraries.Security;
using Implem.Pleasanter.Libraries.Server;
using Implem.Pleasanter.Libraries.Settings;
using Implem.Pleasanter.Libraries.Web;
using Implem.Pleasanter.Models.ApiSiteSettings;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Web;
using static Implem.Pleasanter.Libraries.ServerScripts.ServerScriptModel;
namespace Implem.Pleasanter.Models
{
public static class SiteUtilities
{
// ...省略...
// 状況による制御の表のヘッダ部分のHTMLを作る関数です。
private static HtmlBuilder StatusControlsHeader(
this HtmlBuilder hb, Context context, SiteSettings ss, IEnumerable<int> selected)
{
return hb.THead(action: () => hb
.Tr(css: "ui-widget-header", action: () => hb
.Th(action: () => hb
.CheckBox(
controlCss: "select-all",
_checked: ss.StatusControls?.Any() == true && ss.StatusControls?.All(o =>
selected?.Contains(o.Id) == true) == true))
.Th(action: () => hb
.Text(text: Displays.Id(context: context)))
.Th(action: () => hb
.Text(text: Displays.Name(context: context)))
.Th(action: () => hb
.Text(text: Displays.Status(context: context)))
.Th(action: () => hb
.Text(text: Displays.Description(context: context)))
.Th(action: () => hb
.Text(text: Displays.ReadOnly(context: context)))
// 【追加】状況による制御の一覧のヘッダに「無効」のラベルを表示します。
.Th(action: () => hb
.Text(text: Displays.Disabled(context: context)))));
}
// 状況による制御の表の一覧部分のHTMLを作る関数です。
private static HtmlBuilder StatusControlsBody(
this HtmlBuilder hb,
Context context,
SiteSettings ss,
IEnumerable<int> selected)
{
if (ss.StatusControls?.Any() == true)
{
var statusColumn = ss.GetColumn(
context: context,
columnName: "Status");
hb.TBody(action: () =>
{
ss.StatusControls?.ForEach(statusControl =>
{
hb.Tr(
css: "grid-row",
attributes: new HtmlAttributes()
.DataId(statusControl.Id.ToString()),
action: () => hb
.Td(action: () => hb
.CheckBox(
controlCss: "select",
_checked: selected?.Contains(statusControl.Id) == true))
.Td(action: () => hb
.Text(text: statusControl.Id.ToString()))
.Td(action: () => hb
.Text(text: statusControl.Name))
.Td(action: () => hb
.Text(text: statusControl.Status != -1
? statusColumn?.ChoiceHash?.Get(statusControl.Status.ToString())?.Text
: "*"))
.Td(action: () => hb
.Text(text: statusControl.Description))
.Td(action: () => hb
.Span(
css: "ui-icon ui-icon-circle-check",
_using: statusControl.ReadOnly == true))
// 【追加】状況による制御の一覧の内容部分に「無効」の設定状況を表示します。
.Td(action: () => hb
.Span(
css: "ui-icon ui-icon-circle-check",
_using: statusControl.Disabled == true)));
});
});
}
return hb;
}
// 状況による制御の1件分を設定するダイアログのHTMLを作る関数です。
public static HtmlBuilder StatusControlDialog(
Context context,
SiteSettings ss,
string controlId,
StatusControl statusControl)
{
var hb = new HtmlBuilder();
var status = ss.GetColumn(
context: context,
columnName: "Status");
var optionCollection = $"-1,*\n{status.ChoicesText}".SplitReturn()
.Select(o => new Choice(o))
.GroupBy(o => o.Value)
.ToDictionary(
o => o.Key,
o => new ControlData(
text: o.First().Text,
css: o.First().CssClass));
return hb.Form(
attributes: new HtmlAttributes()
.Id("StatusControlForm")
.Action(Locations.ItemAction(
context: context,
id: ss.SiteId)),
action: () => hb
.FieldText(
controlId: "StatusControlId",
controlCss: " always-send",
labelText: Displays.Id(context: context),
text: statusControl.Id.ToString(),
_using: controlId == "EditStatusControl")
.FieldTextBox(
controlId: "StatusControlName",
controlCss: " always-send",
labelText: Displays.Name(context: context),
text: statusControl.Name,
validateRequired: true)
.FieldDropDown(
context: context,
controlId: "StatusControlStatus",
controlCss: " always-send",
labelText: Displays.Status(context: context),
optionCollection: optionCollection,
selectedValue: statusControl.Status.ToString())
.FieldTextBox(
controlId: "StatusControlDescription",
fieldCss: "field-wide",
controlCss: " always-send",
labelText: Displays.Description(context: context),
text: statusControl.Description)
// 【追加】無効化するためのチェックボックスを追加します。
.FieldCheckBox(
fieldId: "StatusControlDisabledField",
controlId: "StatusControlDisabled",
controlCss: " always-send",
labelText: Displays.Disabled(context: context),
_checked: statusControl.Disabled == true)
.Div(
id: "StatusControlTabsContainer",
css: "tab-container",
action: () => hb
.Ul(id: "StatusControlTabs", action: () => hb
.Li(action: () => hb
.A(
href: "#StatusControlGeneralTab",
text: Displays.General(context: context)))
.Li(action: () => hb
.A(
href: "#StatusControlViewFiltersTab",
text: Displays.Condition(context: context)))
.Li(action: () => hb
.A(
href: "#StatusControlAccessControlsTab",
text: Displays.AccessControls(context: context))))
.StatusControlGeneralTab(
context: context,
ss: ss,
statusControl: statusControl)
.StatusControlViewFiltersTab(
context: context,
ss: ss,
statusControl: statusControl)
.StatusControlAccessControlsTab(
context: context,
ss: ss,
statusControl: statusControl))
.P(css: "message-dialog")
.Div(css: "command-center", action: () => hb
.Button(
controlId: "AddStatusControl",
text: Displays.Add(context: context),
controlCss: "button-icon validate button-positive",
onClick: "$p.send($(this));",
icon: "ui-icon-disk",
action: "SetSiteSettings",
method: "post",
_using: controlId == "NewStatusControl")
.Button(
controlId: "UpdateStatusControl",
text: Displays.Change(context: context),
controlCss: "button-icon validate button-positive",
onClick: "$p.send($(this));",
icon: "ui-icon-disk",
action: "SetSiteSettings",
method: "post",
_using: controlId == "EditStatusControl")
.Button(
text: Displays.Cancel(context: context),
controlCss: "button-icon button-neutral",
onClick: "$p.closeDialog($(this));",
icon: "ui-icon-cancel")));
}
// ...省略...
}
}
SiteModel.csは、APIやUIから渡されたデータをStatusControlクラスに受け渡す役割を持ったクラスです。Disabledプロパティを受け渡すためのコードを追加します。
using Implem.DefinitionAccessor;
using Implem.Libraries.Classes;
using Implem.Libraries.DataSources.SqlServer;
using Implem.Libraries.Utilities;
using Implem.Pleasanter.Libraries.DataSources;
using Implem.Pleasanter.Libraries.DataTypes;
using Implem.Pleasanter.Libraries.Extensions;
using Implem.Pleasanter.Libraries.General;
using Implem.Pleasanter.Libraries.Html;
using Implem.Pleasanter.Libraries.HtmlParts;
using Implem.Pleasanter.Libraries.Models;
using Implem.Pleasanter.Libraries.Requests;
using Implem.Pleasanter.Libraries.Responses;
using Implem.Pleasanter.Libraries.Security;
using Implem.Pleasanter.Libraries.Server;
using Implem.Pleasanter.Libraries.ServerScripts;
using Implem.Pleasanter.Libraries.Settings;
using Implem.Pleasanter.Models.ApiSiteSettings;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using static Implem.Pleasanter.Libraries.ServerScripts.ServerScriptModel;
using static Implem.Pleasanter.Libraries.Settings.Column;
using static Implem.Pleasanter.Libraries.Settings.SiteSettings;
namespace Implem.Pleasanter.Models
{
[Serializable]
public class SiteModel : BaseItemModel
{
// ...省略...
// UpsertStatusControlApiから渡されたデータをStatusControlに格納します。
public void UpsertStatusControlByApi(
SiteSettings siteSetting,
List<ApiSiteSettings.StatusControlApiSettingModel> statusControlSettings,
Context context)
{
List<int> deleteSelected = new List<int>();
statusControlSettings.ForEach(statusControlSetting =>
{
var statusControl = siteSetting.StatusControls?.
FirstOrDefault(o => o.Id == statusControlSetting.Id.ToInt());
if (statusControlSetting.Delete.ToInt() == ApiSiteSetting.DeleteFlag.IsDelete.ToInt())
{
deleteSelected.Add(statusControlSetting.Id.ToInt());
}
else if (statusControl != null)
{
statusControl.Update(
name: statusControlSetting.Name,
description: statusControlSetting.Description,
status: statusControlSetting.Status,
readOnly: statusControlSetting.ReadOnly,
view: statusControlSetting.View,
columnHash: statusControlSetting.ColumnHash,
permissions: statusControlSetting.Permission != null ? ParsePermissions(
apiSettingPermission: statusControlSetting.Permission,
ss: siteSetting,
target: statusControl) : null,
// 【追加】APIによる更新時にプロパティへの受け渡しを行います。
disabled: statusControlSetting.Disabled);
}
else
{
SiteSettings.StatusControls.Add(new StatusControl(
id: statusControlSetting.Id,
name: statusControlSetting.Name,
description: statusControlSetting.Description,
status: statusControlSetting.Status,
readOnly: statusControlSetting.ReadOnly,
view: statusControlSetting.View,
columnHash: statusControlSetting.ColumnHash,
permissions: ParsePermissions(
apiSettingPermission: statusControlSetting.Permission,
ss: siteSetting),
// 【追加】APIによる追加時にプロパティへの受け渡しを行います。
disabled: statusControlSetting.Disabled));
}
});
if (deleteSelected.Count() != 0)
{
siteSetting.StatusControls.Delete(deleteSelected);
}
}
// ...省略...
// UIから状況による制御1件を追加する際のメソッドです。
private void AddStatusControl(Context context, ResponseCollection res)
{
SiteSettings.SetChoiceHash(context: context);
var statusControl = new StatusControl(
id: SiteSettings.StatusControls.MaxOrDefault(o => o.Id) + 1,
name: context.Forms.Data("StatusControlName"),
description: context.Forms.Data("StatusControlDescription"),
status: context.Forms.Int("StatusControlStatus"),
readOnly: context.Forms.Bool("StatusControlReadOnly"),
columnHash: StatusControlColumnHash(context: context),
view: new View(
context: context,
ss: SiteSettings,
prefix: "StatusControl"),
permissions: StatusControlPermissions(context: context),
// 【追加】UIによる追加時にプロパティへの受け渡しを行います。
disabled: context.Forms.Bool("StatusControlDisabled"));
SiteSettings.StatusControls.Add(statusControl);
res
.ReplaceAll("#EditStatusControlWrap", new HtmlBuilder()
.EditStatusControl(
context: context,
ss: SiteSettings))
.CloseDialog();
}
// UIから状況による制御1件を更新する際のメソッドです。
private void UpdateStatusControl(Context context, ResponseCollection res)
{
var statusControl = SiteSettings.StatusControls.Get(context.Forms.Int("StatusControlId"));
if (statusControl == null)
{
res.Message(Messages.NotFound(context: context));
}
else
{
SiteSettings.SetChoiceHash(context: context);
var view = statusControl.View ?? new View();
view.SetByForm(
context: context,
ss: SiteSettings,
prefix: "StatusControl");
statusControl.Update(
name: context.Forms.Data("StatusControlName"),
description: context.Forms.Data("StatusControlDescription"),
status: context.Forms.Int("StatusControlStatus"),
readOnly: context.Forms.Bool("StatusControlReadOnly"),
columnHash: StatusControlColumnHash(context: context),
view: view,
permissions: StatusControlPermissions(context: context),
// 【追加】UIによる更新時にプロパティへの受け渡しを行います。
disabled: context.Forms.Bool("StatusControlDisabled"));
res
.ReplaceAll("#EditStatusControlWrap", new HtmlBuilder()
.EditStatusControl(
context: context,
ss: SiteSettings))
.CloseDialog();
}
}
// ...省略...
}
}
SettingsJsonConverter.csはサイト設定をビジュアル化するためのPleasanter Site Visualizerで使用するクラスです。「無効」プロパティが受け渡しできるようコードを追加します。
using Implem.DefinitionAccessor;
using Implem.Libraries.DataSources.SqlServer;
using Implem.Libraries.Utilities;
using Implem.Pleasanter.Libraries.DataSources;
using Implem.Pleasanter.Libraries.HtmlParts;
using Implem.Pleasanter.Libraries.Requests;
using Implem.Pleasanter.Libraries.Responses;
using Implem.Pleasanter.Libraries.Security;
using Implem.Pleasanter.Libraries.Server;
using Implem.Pleasanter.Libraries.Settings;
using Implem.Pleasanter.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace Implem.Pleasanter.Libraries.SiteManagement
{
public partial class SettingsJsonConverter
{
// ...省略...
public class StatusControlsSettingsModel : SettingsModelBase
{
// ...省略...
public class StatusControlsTable : List3TableBase<StatusControlsTable.ColumnModel>
{
// ...省略...
public class ColumnModel
{
public int Id;
public string Name;
public string Status;
public string Description;
public bool? ReadOnly;
public List<string> ColumnHash;
public bool? Incomplete;
public bool? Own;
public bool? NearCompletionTime;
public bool? Delay;
public bool? Overdue;
public string Search;
public List<ViewFiltersColumn.FilterConditionItem> ColumnFilterHash;
public List<string> Permissions;
public List<string> ChangedColumns = new();
// 【追加】無効プロパティを追加します。
public bool? Disabled;
public static void StatusControlBasePanel(
ColumnModel dst,
Context context,
StatusControl statusControl,
SiteModel siteModel,
SiteSettings ss)
{
var status = ss.GetColumn(
context: context,
columnName: "Status");
var optionCollection = $"-1,*\n{status.ChoicesText}".SplitReturn()
.Select(o => new Choice(o))
.GroupBy(o => o.Value)
.ToDictionary(
o => o.Key,
o => new ControlData(
text: o.First().Text,
css: o.First().CssClass));
dst.Id = statusControl.Id;
dst.Name = statusControl.Name;
dst.Status = optionCollection
.Where(kv => kv.Key == statusControl.Status.ToString())
.Select(kv => kv.Value.Text)
.FirstOrDefault(string.Empty);
dst.Description = statusControl.Description;
// 【追加】無効プロパティへの受け渡しを行います。
dst.Disabled = statusControl.Disabled;
}
}
// ...省略...
}
}
}
}
}
IssueModel.cs は期限付きテーブルのクラスです。無効化されたデータによる制御が動作しないようにするため、LINQ の Where を使用して、動作対象から除外するコードを追加します。同様に記録テーブルである ResultModel.cs にも同じメソッドがあるため、同様のコードを追加します。
using Implem.DefinitionAccessor;
using Implem.Libraries.Classes;
using Implem.Libraries.DataSources.SqlServer;
using Implem.Libraries.Utilities;
using Implem.Pleasanter.Libraries.DataSources;
using Implem.Pleasanter.Libraries.DataTypes;
using Implem.Pleasanter.Libraries.Extensions;
using Implem.Pleasanter.Libraries.General;
using Implem.Pleasanter.Libraries.Html;
using Implem.Pleasanter.Libraries.HtmlParts;
using Implem.Pleasanter.Libraries.Models;
using Implem.Pleasanter.Libraries.Requests;
using Implem.Pleasanter.Libraries.Responses;
using Implem.Pleasanter.Libraries.Security;
using Implem.Pleasanter.Libraries.Server;
using Implem.Pleasanter.Libraries.ServerScripts;
using Implem.Pleasanter.Libraries.Settings;
using Implem.Pleasanter.Models.ApiSiteSettings;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using static Implem.Pleasanter.Libraries.ServerScripts.ServerScriptModel;
namespace Implem.Pleasanter.Models
{
[Serializable]
public class IssueModel : BaseItemModel
{
// ...省略...
private void SetByStatusControls(
Context context,
SiteSettings ss,
bool force = false)
{
if (StatusControlHash == null || force)
{
StatusControlHash = new Dictionary<string, StatusControl.ControlConstraintsTypes>();
ss.StatusControls?
// 【追加】Disabledプロパティがtrueの場合は動作しないように除外します。
.Where(statusControl => statusControl.Disabled != true)
.Where(statusControl => statusControl.Status == -1
|| statusControl.Status == Status.Value)
.Where(statusControl => statusControl.Accessable(context: context))
.Where(statusControl => statusControl.View == null
|| Matched(
context: context,
ss: ss,
view: statusControl.View))
.ForEach(statusControl =>
{
ReadOnly |= statusControl.ReadOnly == true;
statusControl.ColumnHash?.ForEach(data =>
StatusControlHash.AddIfNotConainsKey(data.Key, data.Value));
});
}
}
// ...省略...
}
}
機能追加後の動作確認
これで、プリザンターの状況による制御を無効にする機能が追加できました。動作確認をしてみましょう。
設定ダイアログに「無効」のチェックボックスが表示されるようになりました。チェックをオンにして、「変更」ボタンをクリックします。

状況による制御の一覧にも無効のチェックが表示されるようになりました。画面下部の「更新」ボタンをクリックします。

さきほどと同様の「完了」になっているレコードを開いてみます。状況による制御が無効化されているため、読み取り専用や項目の設定が機能していないことが確認できました。

おわりに
今回、プリザンターに追加したコードはわずか22行でした。UI を含め、C# のみの編集で完結しており、JavaScript は一切書いていません。このように、ちょっとした機能であれば簡単に追加できる拡張性を備えていますので、もしほしい機能があれば、ぜひ実際にコードを書いて試してみていただければ幸いです。
プリザンターは今後も業務に役立つノーコード・ローコード開発ツールとして進化を続けてまいります。よかったら応援してください。皆さん良いクリスマスをお過ごしください。
https://pleasanter.org

GitHubにスターつけてもらえたら嬉しいです。
https://github.com/Implem/Implem.Pleasanter