private string BuildAddNewOnvifCameraWebPageBody([AllowNull] OnvifCameraSetting cameraSettings)
        {
            TimeSpan DefaultAlarmCancelInterval = TimeSpan.FromSeconds(30);

            string id                        = cameraSettings?.Id ?? Guid.NewGuid().ToString();
            string name                      = cameraSettings?.Name ?? string.Empty;
            string hostName                  = cameraSettings?.CameraHost ?? @"http://";
            string userId                    = cameraSettings?.Login ?? string.Empty;
            string password                  = cameraSettings?.Password ?? string.Empty;
            string alarmCancelInterval       = (cameraSettings?.AlarmCancelInterval ?? DefaultAlarmCancelInterval).TotalSeconds.ToString(CultureInfo.InvariantCulture);
            string snapshotDownloadDirectory = cameraSettings?.SnapshotDownloadDirectory ?? string.Empty;

            string buttonLabel = cameraSettings != null ? "Save" : "Add";
            string header      = cameraSettings != null ? "Edit Camera" : "Add New Camera";

            StringBuilder stb = new StringBuilder();

            stb.Append(PageBuilderAndMenu.clsPageBuilder.FormStart("ftmCameraChange", "IdChange", "Post"));

            stb.Append(@"<div>");
            stb.Append(@"<table class='full_width_table'>");
            stb.Append("<tr height='5'><td></td><td></td></tr>");
            stb.Append(Invariant($"<tr><td class='tableheader' colspan=2>{header}</td></tr>"));
            stb.Append("</td></tr>");
            stb.Append(Invariant($"<tr><td class='tablecell'>Name:</td><td class='tablecell'>"));
            stb.Append(HtmlTextBox(nameof(OnvifCameraSetting.Name), name));
            stb.Append("</td></tr>");
            stb.Append(Invariant($"<tr><td class='tablecell'>Camera:</td><td class='tablecell'>"));
            stb.Append(HtmlTextBox(nameof(OnvifCameraSetting.CameraHost), hostName, size: 75));
            stb.Append("</td></tr>");
            stb.Append(Invariant($"<tr><td class='tablecell'>User:</td><td class='tablecell'>"));
            stb.Append(HtmlTextBox(nameof(OnvifCameraSetting.Login), userId));
            stb.Append("</td></tr>");
            stb.Append(Invariant($"<tr><td class='tablecell'>Password:</td><td class='tablecell'>"));
            stb.Append(HtmlTextBox(nameof(OnvifCameraSetting.Password), password, type: "password"));
            stb.Append("</td></tr>");
            stb.Append(Invariant($"<tr><td class='tablecell'>Alarm Cancel Interval(seconds):</td><td class='tablecell'>"));
            stb.Append(HtmlTextBox(nameof(OnvifCameraSetting.AlarmCancelInterval), alarmCancelInterval));
            stb.Append("</td></tr>");
            stb.Append(Invariant($"<tr><td class='tablecell'>Snapshot download directory:</td><td class='tablecell'>"));
            stb.Append(HtmlTextBox(nameof(OnvifCameraSetting.SnapshotDownloadDirectory), snapshotDownloadDirectory));
            stb.Append("</td></tr>");

            stb.Append(Invariant($"<tr><td colspan=2>{HtmlTextBox(RecordId, id, type: "hidden")}<div id='{SaveErrorDivId}' style='color:Red'></div></td><td></td></tr>"));
            stb.Append(Invariant($"<tr><td colspan=2>{FormPageButton(OnvifSaveCamera, buttonLabel)}"));

            if (cameraSettings != null)
            {
                stb.Append(FormPageButton(OnvifDeleteCamera, "Delete"));
            }

            stb.Append(FormPageButton(OnvifCancelCamera, "Cancel"));
            stb.Append(Invariant($"</td></tr>"));
            stb.Append("<tr height='5'><td colspan=2></td></tr>");
            stb.Append(@"</table>");
            stb.Append(@"</div>");
            stb.Append(PageBuilderAndMenu.clsPageBuilder.FormEnd());

            return(stb.ToString());
        }
        public void AddOnvifCamera(OnvifCameraSetting device)
        {
            using (var scopedLock = configLock.WriterLock())
            {
                onvifCameras[device.Id] = device;

                SetValue(nameof(device.Name), device.Name, device.Id);
                SetValue(nameof(device.CameraHost), device.CameraHost, device.Id);
                SetValue(nameof(device.Login), device.Login, device.Id);
                SetValue(nameof(device.Password), HS.EncryptString(device.Password, nameof(device.Password)), device.Id);
                SetValue(nameof(device.AlarmCancelInterval), (long)device.AlarmCancelInterval.TotalSeconds, device.Id);
                SetValue(nameof(device.SnapshotDownloadDirectory), device.SnapshotDownloadDirectory, device.Id);

                SetValue(OnvifCameraIds, onvifCameras.Keys.Aggregate((x, y) => x + idsSeparator + y));
            }
        }
        private void HandleOnvifCameraPostBack(NameValueCollection parts, string form)
        {
            if (form == NameToIdWithPrefix(OnvifDeleteCamera))
            {
                pluginConfig.RemoveOnvifCamera(parts[RecordId]);
                pluginConfig.FireConfigChanged();
                divToUpdate.Add(SaveErrorDivId, RedirectPageJS(Invariant($"/{pageUrl}?{TabId}=1")));
            }
            else if (form == NameToIdWithPrefix(OnvifCancelCamera))
            {
                divToUpdate.Add(SaveErrorDivId, RedirectPageJS(Invariant($"/{pageUrl}?{TabId}=1")));
            }
            else if (form == NameToIdWithPrefix(OnvifSaveCamera))
            {
                StringBuilder results    = new StringBuilder();
                string        cameraName = parts[nameof(OnvifCameraSetting.Name)];
                if (string.IsNullOrWhiteSpace(cameraName))
                {
                    results.AppendLine("Camera Name is empty.<br>");
                }

                string userId   = parts[nameof(OnvifCameraSetting.Login)];
                string password = parts[nameof(OnvifCameraSetting.Password)];

                string cameraHostString = parts[nameof(OnvifCameraSetting.CameraHost)];
                if (!Uri.TryCreate(cameraHostString, UriKind.Absolute, out var cameraHost))
                {
                    results.AppendLine("Camera address is not valid.<br>");
                }

                var alarmCancelIntervalString = parts[nameof(OnvifCameraSetting.AlarmCancelInterval)];
                if (!long.TryParse(alarmCancelIntervalString, NumberStyles.Any, CultureInfo.InvariantCulture, out var alarmCancelInterval) ||
                    alarmCancelInterval < 0)
                {
                    results.AppendLine("Alarm cancel interval is not valid.<br>");
                }


                var snapshotDirectory = parts[nameof(OnvifCameraSetting.SnapshotDownloadDirectory)];
                if (!Directory.Exists(snapshotDirectory))
                {
                    results.AppendLine("Snapshot directory is not valid.<br>");
                }

                if (results.Length > 0)
                {
                    divToUpdate.Add(SaveErrorDivId, results.ToString());
                }
                else
                {
                    string id = parts[RecordId];

                    if (string.IsNullOrWhiteSpace(id))
                    {
                        id = System.Guid.NewGuid().ToString();
                    }

                    var data = new OnvifCameraSetting(id,
                                                      cameraName,
                                                      cameraHost.AbsoluteUri.ToString(CultureInfo.InvariantCulture),
                                                      userId,
                                                      password,
                                                      TimeSpan.FromSeconds(alarmCancelInterval),
                                                      snapshotDirectory);

                    pluginConfig.AddOnvifCamera(data);
                    pluginConfig.FireConfigChanged();
                    divToUpdate.Add(SaveErrorDivId, RedirectPageJS(Invariant($"/{pageUrl}?{TabId}=1")));
                }
            }
        }