Backporting PIA code
This commit is contained in:
Родитель
fe525fb624
Коммит
14be55f3d8
|
@ -681,6 +681,8 @@ namespace WebApplication.Models
|
|||
.HasMaxLength(255)
|
||||
.IsUnicode(false);
|
||||
|
||||
entity.Property(e => e.Discoverability).HasDefaultValueSql("((0))");
|
||||
|
||||
entity.Property(e => e.SubjectAreaName)
|
||||
.HasMaxLength(255)
|
||||
.IsUnicode(false);
|
||||
|
@ -688,6 +690,10 @@ namespace WebApplication.Models
|
|||
entity.Property(e => e.UpdatedBy)
|
||||
.HasMaxLength(255)
|
||||
.IsUnicode(false);
|
||||
|
||||
entity.Property(e => e.ValidFrom).HasColumnType("datetime2(0)");
|
||||
|
||||
entity.Property(e => e.ValidTo).HasColumnType("datetime2(0)");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<SubjectAreaForm>(entity =>
|
||||
|
@ -739,7 +745,9 @@ namespace WebApplication.Models
|
|||
.HasMaxLength(255)
|
||||
.IsUnicode(false);
|
||||
|
||||
|
||||
entity.Property(e => e.ValidFrom).HasColumnType("datetime2(0)");
|
||||
|
||||
entity.Property(e => e.ValidTo).HasColumnType("datetime2(0)");
|
||||
});
|
||||
|
||||
OnModelCreatingPartial(modelBuilder);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace WebApplication.Models
|
||||
|
@ -119,5 +120,16 @@ namespace WebApplication.Models
|
|||
});
|
||||
|
||||
}
|
||||
|
||||
public IQueryable<SubjectAreaRoleMap> SubjectAreaRoleMapsFor(Guid[] assignedAdGroups, string[] applicationRoles)
|
||||
{
|
||||
return
|
||||
from r in this.SubjectAreaRoleMap
|
||||
where assignedAdGroups.Contains(r.AadGroupUid)
|
||||
&& applicationRoles.Contains(r.ApplicationRoleName)
|
||||
&& r.ExpiryDate > DateTimeOffset.Now
|
||||
&& r.ActiveYn
|
||||
select r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,10 @@ namespace WebApplication.Models
|
|||
[Display(Name = "Subject Area Name")]
|
||||
[Required(AllowEmptyStrings = false, ErrorMessage = "Please input valid Area Name")]
|
||||
public string SubjectAreaName { get; set; }
|
||||
[Display(Name = "Is Active")]
|
||||
|
||||
[StringLength(10)]
|
||||
[RegularExpression("^[a-zA-Z0-9]{3,10}$", ErrorMessage = "Short Code must be between 3-10 alphanumeric characters.")]
|
||||
public string ShortCode { get; set; }
|
||||
public bool ActiveYn { get; set; }
|
||||
[Display(Name = "Subject Area Form")]
|
||||
public int? SubjectAreaFormId { get; set; }
|
||||
|
@ -21,5 +24,6 @@ namespace WebApplication.Models
|
|||
public DateTime ValidFrom { get; set; }
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
||||
public DateTime ValidTo { get; set; }
|
||||
public byte? Discoverability { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace WebApplication.Models
|
||||
|
@ -14,5 +15,17 @@ namespace WebApplication.Models
|
|||
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
||||
public DateTime ValidTo { get; set; }
|
||||
public byte Revision { get; set; }
|
||||
|
||||
[Display(Name="PIA Status")]
|
||||
public string FormStatusDisplay
|
||||
{
|
||||
get
|
||||
{
|
||||
var fs = (SubjectAreaFormStatus?) FormStatus ?? SubjectAreaFormStatus.Incomplete;
|
||||
string status = fs < SubjectAreaFormStatus.Complete ? "Incomplete" : "Complete";
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
namespace WebApplication.Models
|
||||
{
|
||||
public enum SubjectAreaFormStatus : byte
|
||||
{
|
||||
Unknown = 0,
|
||||
Incomplete = 1,
|
||||
Complete = 2
|
||||
}
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace WebApplication.Models
|
||||
{
|
||||
|
@ -10,5 +12,9 @@ namespace WebApplication.Models
|
|||
public DateTime ExpiryDate { get; set; }
|
||||
public bool ActiveYn { get; set; }
|
||||
public string UpdatedBy { get; set; }
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
||||
public DateTime ValidFrom { get; set; }
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
||||
public DateTime ValidTo { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,5 +10,6 @@
|
|||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
@ -7,17 +7,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Dynamic.Core;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using WebApplication.Models;
|
||||
using Dapper;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using WebApplication.Controllers.Customisations;
|
||||
using WebApplication.Services;
|
||||
|
||||
namespace WebApplication.Controllers
|
||||
|
|
|
@ -11,6 +11,9 @@ using WebApplication.Framework;
|
|||
using WebApplication.Models;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using WebApplication.Forms;
|
||||
using WebApplication.Models.Forms;
|
||||
using WebApplication.Models.Wizards;
|
||||
|
||||
namespace WebApplication.Controllers
|
||||
{
|
||||
|
@ -56,6 +59,11 @@ namespace WebApplication.Controllers
|
|||
// GET: SubjectArea/Create
|
||||
public IActionResult Create()
|
||||
{
|
||||
if (!CanPerformCurrentActionGlobally())
|
||||
{
|
||||
return Forbid();
|
||||
}
|
||||
|
||||
ViewData["SubjectAreaFormId"] = new SelectList(_context.SubjectAreaForm.OrderBy(x=>x.SubjectAreaFormId), "SubjectAreaFormId", "SubjectAreaFormId");
|
||||
SubjectArea subjectArea = new SubjectArea();
|
||||
subjectArea.ActiveYn = true;
|
||||
|
@ -68,21 +76,67 @@ namespace WebApplication.Controllers
|
|||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
[ChecksUserAccess]
|
||||
public async Task<IActionResult> Create([Bind("SubjectAreaId,SubjectAreaName,ActiveYn,SubjectAreaFormId,DefaultTargetSchema,UpdatedBy")] SubjectArea subjectArea)
|
||||
public async Task<IActionResult> Create(
|
||||
[Bind("SubjectAreaId,SubjectAreaName,ShortCode, ActiveYn,SubjectAreaFormId,DefaultTargetSchema,UpdatedBy")]
|
||||
SubjectArea subjectArea)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
subjectArea.UpdatedBy = User.Identity.Name;
|
||||
subjectArea.ShortCode = subjectArea.ShortCode?.ToLower();
|
||||
|
||||
_context.Add(subjectArea);
|
||||
if (!await CanPerformCurrentActionOnRecord(subjectArea))
|
||||
{
|
||||
return new ForbidResult();
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
return RedirectToAction(nameof(IndexDataTable));
|
||||
try
|
||||
{
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
catch (DbUpdateException e)
|
||||
{
|
||||
ModelState.AddModelError("SubjectAreaName", e.InnerException.Message);
|
||||
return View(subjectArea);
|
||||
}
|
||||
|
||||
if (subjectArea.SubjectAreaFormId == null)
|
||||
{
|
||||
await CreateEmptySubjectAreaFormAndPIAAsync(subjectArea);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
ViewData["SubjectAreaFormId"] = new SelectList(_context.SubjectAreaForm.OrderBy(x=>x.SubjectAreaFormId), "SubjectAreaFormId", "SubjectAreaFormId", subjectArea.SubjectAreaFormId);
|
||||
return View(subjectArea);
|
||||
return RedirectToAction(nameof(IndexDataTable));
|
||||
}
|
||||
|
||||
private async Task CreateEmptySubjectAreaFormAndPIAAsync(SubjectArea subjectArea)
|
||||
{
|
||||
// TODO: Replace this zone with a configurable item that is more generic that PHN
|
||||
//await _sharedZoneService.GetZone();
|
||||
var currentPhnZone = new Site();
|
||||
|
||||
PIAWizardViewModel pia = new PIAWizardViewModel()
|
||||
{
|
||||
BelongingDataset = subjectArea.SubjectAreaName,
|
||||
BelongingDatasetCode = subjectArea.ShortCode,
|
||||
Site = currentPhnZone
|
||||
};
|
||||
|
||||
var form = new SubjectAreaForm()
|
||||
{
|
||||
FormJson = JsonConvert.SerializeObject(pia.ToForm(), PIAForm.JsonSettings),
|
||||
FormStatus = default,
|
||||
Revision = 1,
|
||||
UpdatedBy = User.Identity.Name
|
||||
};
|
||||
|
||||
subjectArea.SubjectAreaForm = form;
|
||||
|
||||
_context.Add(form);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// GET: SubjectArea/Edit/5
|
||||
|
@ -94,7 +148,9 @@ namespace WebApplication.Controllers
|
|||
return NotFound();
|
||||
}
|
||||
|
||||
var subjectArea = await _context.SubjectArea.FindAsync(id);
|
||||
var subjectArea = await _context.SubjectArea
|
||||
.Include(x => x.SubjectAreaForm)
|
||||
.FirstOrDefaultAsync(x => x.SubjectAreaId == id);
|
||||
if (subjectArea == null)
|
||||
return NotFound();
|
||||
|
||||
|
@ -112,26 +168,44 @@ namespace WebApplication.Controllers
|
|||
[ChecksUserAccess]
|
||||
public async Task<IActionResult> Edit(int id, [Bind("SubjectAreaId,SubjectAreaName,ActiveYn,SubjectAreaFormId,DefaultTargetSchema,UpdatedBy")] SubjectArea subjectArea)
|
||||
{
|
||||
if (id != subjectArea.SubjectAreaId)
|
||||
{
|
||||
var subjectAreaRecord = await _context.SubjectArea
|
||||
.Include(x => x.SubjectAreaForm)
|
||||
.FirstOrDefaultAsync(x => x.SubjectAreaId == id);
|
||||
|
||||
if (subjectAreaRecord == null)
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var hasGlobalPermission = await CanPerformCurrentActionOnRecord(subjectAreaRecord);
|
||||
var hasSpecialPermission = await this.CanEditSubjectArea(subjectAreaRecord);
|
||||
if (!hasGlobalPermission && !hasSpecialPermission)
|
||||
return Forbid();
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
try
|
||||
{
|
||||
subjectArea.UpdatedBy = User.Identity.Name;
|
||||
_context.Update(subjectArea);
|
||||
//explicit property setting because we now have additional fields that are *NOT* set by edit
|
||||
subjectAreaRecord.SubjectAreaName = subjectArea.SubjectAreaName;
|
||||
subjectAreaRecord.ActiveYn = subjectArea.ActiveYn;
|
||||
subjectAreaRecord.SubjectAreaFormId = subjectArea.SubjectAreaFormId;
|
||||
subjectAreaRecord.DefaultTargetSchema = subjectArea.DefaultTargetSchema;
|
||||
subjectAreaRecord.UpdatedBy = User.GetUserName();
|
||||
|
||||
if (!await CanPerformCurrentActionOnRecord(subjectArea))
|
||||
if (subjectAreaRecord.SubjectAreaFormId == null)
|
||||
{
|
||||
await CreateEmptySubjectAreaFormAndPIAAsync(subjectAreaRecord);
|
||||
}
|
||||
|
||||
_context.Update(subjectAreaRecord);
|
||||
|
||||
if (!await CanPerformCurrentActionOnRecord(subjectAreaRecord))
|
||||
return new ForbidResult();
|
||||
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
{
|
||||
if (!SubjectAreaExists(subjectArea.SubjectAreaId))
|
||||
if (!SubjectAreaExists(subjectAreaRecord.SubjectAreaId))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
@ -146,6 +220,35 @@ namespace WebApplication.Controllers
|
|||
return View(subjectArea);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[ChecksUserAccess]
|
||||
public async Task<IActionResult> EditPIA(int? id)
|
||||
{
|
||||
if (id == null)
|
||||
{
|
||||
return View("Error", new ErrorViewModel() { Message = "Subject area was not found." });
|
||||
}
|
||||
|
||||
var subjectAreaRecord = await _context.SubjectArea
|
||||
.Include(x => x.SubjectAreaForm)
|
||||
.FirstOrDefaultAsync(x => x.SubjectAreaId == id);
|
||||
|
||||
if (subjectAreaRecord == null)
|
||||
return NotFound();
|
||||
|
||||
var hasGlobalPermission = await CanPerformCurrentActionOnRecord(subjectAreaRecord);
|
||||
var hasSpecialPermission = await this.CanEditSubjectArea(subjectAreaRecord);
|
||||
if (!hasGlobalPermission && !hasSpecialPermission)
|
||||
return Forbid();
|
||||
|
||||
if (subjectAreaRecord.SubjectAreaFormId == null)
|
||||
{
|
||||
await CreateEmptySubjectAreaFormAndPIAAsync(subjectAreaRecord);
|
||||
}
|
||||
|
||||
return RedirectToAction("PIAWizard", "Wizards", new { id = subjectAreaRecord.SubjectAreaId });
|
||||
}
|
||||
|
||||
// GET: SubjectArea/Delete/5
|
||||
[ChecksUserAccess]
|
||||
public async Task<IActionResult> Delete(int? id)
|
||||
|
@ -199,13 +302,14 @@ namespace WebApplication.Controllers
|
|||
JObject GridOptions = new JObject();
|
||||
|
||||
JArray cols = new JArray();
|
||||
cols.Add(JObject.Parse("{ 'data':'ShortCode', 'name':'Code', 'autoWidth':true }"));
|
||||
cols.Add(JObject.Parse("{ 'data':'SubjectAreaId', 'name':'Id', 'autoWidth':true }"));
|
||||
cols.Add(JObject.Parse("{ 'data':'SubjectAreaName', 'name':'Name', 'autoWidth':true }"));
|
||||
cols.Add(JObject.Parse("{ 'data':'SubjectAreaFormId', 'name':'Form', 'autoWidth':true }"));
|
||||
cols.Add(JObject.Parse("{ 'data':'DefaultTargetSchema', 'name':'Default Target Schema', 'autoWidth':true }"));
|
||||
cols.Add(JObject.Parse("{ 'data':'UpdatedBy', 'name':'Updated By', 'autoWidth':true }"));
|
||||
cols.Add(JObject.Parse("{ 'data':'ActiveYn', 'name':'Is Active', 'autoWidth':true, 'ads_format':'bool'}"));
|
||||
|
||||
cols.Add(JObject.Parse("{ 'data':'Discoverability', 'name':'Discoverability', 'autoWidth':true, 'ads_format':'discoverability'}"));
|
||||
HumanizeColumns(cols);
|
||||
|
||||
JArray pkeycols = new JArray();
|
||||
|
@ -213,9 +317,11 @@ namespace WebApplication.Controllers
|
|||
|
||||
JArray Navigations = new JArray();
|
||||
Navigations.Add(JObject.Parse("{'Url':'/TaskGroup/IndexDataTable?SubjectAreaId=','Description':'View Task Groups', 'Icon':'object-group','ButtonClass':'btn-primary'}"));
|
||||
Navigations.Add(JObject.Parse("{'Url':'/TaskGroup/IndexDataTable?SubjectAreaId=','Description':'View System Mappings', 'Icon':'bullseye','ButtonClass':'btn-primary'}"));
|
||||
Navigations.Add(JObject.Parse("{'Url':'/TaskGroup/IndexDataTable?SubjectAreaId=','Description':'View Role Mappings', 'Icon':'user-tag','ButtonClass':'btn-primary'}"));
|
||||
Navigations.Add(JObject.Parse("{'Url':'/TaskGroup/IndexDataTable?SubjectAreaId=','Description':'Auto Create Target Schema & AD Groups', 'Icon':'cogs','ButtonClass':'btn-danger'}"));
|
||||
//Navigations.Add(JObject.Parse("{'Url':'/TaskGroup/IndexDataTable?SubjectAreaId=','Description':'View System Mappings', 'Icon':'bullseye','ButtonClass':'btn-primary'}"));
|
||||
//Navigations.Add(JObject.Parse("{'Url':'/TaskGroup/IndexDataTable?SubjectAreaId=','Description':'View Role Mappings', 'Icon':'user-tag','ButtonClass':'btn-primary'}"));
|
||||
Navigations.Add(JObject.Parse("{'Url':'/SubjectArea/Provision?Id=','Description':'Auto Create Target Schema & AD Groups', 'Icon':'cogs','ButtonClass':'btn-danger'}"));
|
||||
Navigations.Add(JObject.Parse("{'Url':'/SubjectArea/EditPIA/','Description':'Edit Privacy Impact Assessment', 'Icon':'pencil-alt','ButtonClass':'btn-primary'}"));
|
||||
Navigations.Add(JObject.Parse("{'Url':'/Wizards/PIASummary/','Description':'View PIA Details', 'Icon':'list-alt','ButtonClass':'btn-primary'}"));
|
||||
|
||||
GridOptions["GridColumns"] = cols;
|
||||
GridOptions["ModelName"] = "SubjectArea";
|
||||
|
@ -259,19 +365,25 @@ namespace WebApplication.Controllers
|
|||
//filter the list by permitted roles
|
||||
if (!CanPerformCurrentActionGlobally())
|
||||
{
|
||||
//TODO: Ensure that this works properly with the switch from Roles to Groups
|
||||
var permittedRoles = GetPermittedGroupsForCurrentAction();
|
||||
var identity = User.Identity.Name;
|
||||
var identity = User.GetUserName();
|
||||
|
||||
var myIncompleteForms = _context
|
||||
.SubjectArea
|
||||
.Include(x => x.SubjectAreaForm)
|
||||
.Where(x => x.SubjectAreaForm == null || x.SubjectAreaForm.FormStatus < (int)SubjectAreaFormStatus.Complete)
|
||||
.Where(x => x.UpdatedBy == identity || (x.SubjectAreaForm != null && x.SubjectAreaForm.UpdatedBy == identity))
|
||||
;
|
||||
|
||||
var myRoleMaps = _context.SubjectAreaRoleMapsFor(GetUserAdGroupUids(), permittedRoles);
|
||||
|
||||
modelDataAll =
|
||||
(from md in modelDataAll
|
||||
join rm in _context.SubjectAreaRoleMap
|
||||
on md.SubjectAreaId equals rm.SubjectAreaId
|
||||
where
|
||||
GetUserAdGroupUids().Contains(rm.AadGroupUid)
|
||||
&& permittedRoles.Contains(rm.ApplicationRoleName)
|
||||
&& rm.ExpiryDate > DateTimeOffset.Now
|
||||
&& rm.ActiveYn
|
||||
select md).Distinct();
|
||||
from md in modelDataAll
|
||||
where
|
||||
myIncompleteForms.Any(x => x.SubjectAreaId == md.SubjectAreaId)
|
||||
|| myRoleMaps.Any(x => x.SubjectAreaId == md.SubjectAreaId)
|
||||
select md;
|
||||
}
|
||||
|
||||
//Sorting
|
||||
|
@ -282,7 +394,7 @@ namespace WebApplication.Controllers
|
|||
//Search
|
||||
if (!string.IsNullOrEmpty(searchValue))
|
||||
{
|
||||
modelDataAll = modelDataAll.Where(m => m.SubjectAreaName == searchValue);
|
||||
modelDataAll = modelDataAll.Where(m => m.SubjectAreaName.Contains(searchValue));
|
||||
}
|
||||
|
||||
//total number of rows count
|
||||
|
@ -299,5 +411,92 @@ namespace WebApplication.Controllers
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[ChecksUserAccess]
|
||||
public async Task<IActionResult> Publish(int? id)
|
||||
{
|
||||
var subjectArea = await _context.SubjectArea.FindAsync(id);
|
||||
if (subjectArea == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (!await CanPerformCurrentActionOnRecord(subjectArea))
|
||||
{
|
||||
return Forbid();
|
||||
}
|
||||
|
||||
subjectArea.TaskGroups = await _context.TaskGroup.Where(x => x.SubjectAreaId == subjectArea.SubjectAreaId).ToListAsync();
|
||||
if (subjectArea.SubjectAreaFormId != null && subjectArea.SubjectAreaFormId != 0)
|
||||
subjectArea.SubjectAreaForm = _context.SubjectAreaForm.First(x => x.SubjectAreaFormId == subjectArea.SubjectAreaFormId);
|
||||
|
||||
ViewBag.Roles = _context.SubjectAreaRoleMap.Where(x => x.SubjectAreaId == subjectArea.SubjectAreaId);
|
||||
return View(subjectArea);
|
||||
}
|
||||
|
||||
|
||||
[ChecksUserAccess]
|
||||
public async Task<IActionResult> Provision(int id)
|
||||
{
|
||||
var subjectArea = await _context.SubjectArea.FindAsync(id);
|
||||
if (subjectArea == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
// TODO: Implement the processing functionality
|
||||
//await _processingClient.QueueSubjectAreaProvisioning(id);
|
||||
return View(subjectArea);
|
||||
}
|
||||
|
||||
|
||||
[ChecksUserAccess]
|
||||
public async Task<IActionResult> Revise(int? id)
|
||||
{
|
||||
if (id == null)
|
||||
{
|
||||
return RedirectToAction(nameof(IndexDataTable));
|
||||
}
|
||||
|
||||
// get the subject area
|
||||
var subjectArea = await _context.SubjectArea.FindAsync(id);
|
||||
if (subjectArea == null)
|
||||
{
|
||||
return View("Error", new ErrorViewModel() { Message = "Subject area not found." });
|
||||
}
|
||||
|
||||
if (!await base.CanPerformCurrentActionOnRecord(subjectArea))
|
||||
return Forbid();
|
||||
|
||||
var saForm = await _context.SubjectAreaForm.FindAsync(subjectArea.SubjectAreaFormId);
|
||||
|
||||
// Deserialize model so we can reset some values
|
||||
PIAWizardViewModel PIA = PIAWizardViewModel.FromForm(JsonConvert.DeserializeObject<PIAForm>(saForm.FormJson, PIAForm.JsonSettings));
|
||||
|
||||
// Set DbId within the json after getting id of new SubjectAreaForm
|
||||
PIA.SubjectAreaId = subjectArea.SubjectAreaId;
|
||||
PIA.MaxStep = 0;
|
||||
PIA.Step = 0;
|
||||
|
||||
// Create new SubjectAreaForm with copied values
|
||||
// Increment revision num and set form status back to 1
|
||||
var revisedSubjectAreaForm = new SubjectAreaForm
|
||||
{
|
||||
FormStatus = (byte)SubjectAreaFormStatus.Incomplete,
|
||||
FormJson = JsonConvert.SerializeObject(PIA.ToForm(), PIAForm.JsonSettings),
|
||||
SubjectAreas = saForm.SubjectAreas,
|
||||
Revision = (byte)(saForm.Revision + 1)
|
||||
};
|
||||
|
||||
// Add to db
|
||||
_context.SubjectAreaForm.Add(revisedSubjectAreaForm);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
subjectArea.SubjectAreaFormId = revisedSubjectAreaForm.SubjectAreaFormId;
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
// Now we can redireect to the wizard with our new SubjectAreaForm
|
||||
return RedirectToAction("PIAWizard", "Wizards", new { id = subjectArea.SubjectAreaId });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
using System.Linq;
|
||||
using System.Linq.Dynamic.Core;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using WebApplication.Services;
|
||||
using WebApplication.Framework;
|
||||
using WebApplication.Models;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using WebApplication.Forms;
|
||||
using WebApplication.Models.Forms;
|
||||
using WebApplication.Models.Wizards;
|
||||
|
||||
namespace WebApplication.Controllers
|
||||
{
|
||||
|
@ -91,6 +98,21 @@ namespace WebApplication.Controllers
|
|||
return View(subjectAreaForm);
|
||||
}
|
||||
|
||||
public async Task<IActionResult> EditInWizard(int? id)
|
||||
{
|
||||
if (id == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var subjectAreaForm = await _context.SubjectAreaForm.FindAsync(id);
|
||||
if (subjectAreaForm == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
return RedirectToAction("PIAWizardResume", "Wizards", new { id = id });
|
||||
}
|
||||
|
||||
// POST: SubjectAreaForm/Edit/5
|
||||
// To protect from overposting attacks, enable the specific properties you want to bind to, for
|
||||
// more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
|
||||
|
@ -171,10 +193,186 @@ namespace WebApplication.Controllers
|
|||
{
|
||||
return _context.SubjectAreaForm.Any(e => e.SubjectAreaFormId == id);
|
||||
}
|
||||
[ChecksUserAccess]
|
||||
public IActionResult IndexDataTable()
|
||||
{
|
||||
//note - this is just here so that we don't ahve to re-scaffold
|
||||
return RedirectToAction(nameof(Index));
|
||||
return View();
|
||||
}
|
||||
|
||||
public JObject GridCols()
|
||||
{
|
||||
JObject GridOptions = new JObject();
|
||||
|
||||
JArray cols = new JArray();
|
||||
cols.Add(JObject.Parse("{ 'data':'SubjectAreaFormId', 'name':'Id' }"));
|
||||
cols.Add(JObject.Parse("{ 'data':'FormStatus', 'name':'Status', }"));
|
||||
cols.Add(JObject.Parse("{ 'data':'UpdatedBy', 'name':'Updated By'}"));
|
||||
cols.Add(JObject.Parse("{ 'data':'ValidFrom', 'name':'Valid From'}"));
|
||||
cols.Add(JObject.Parse("{ 'data':'ValidTo', 'name':'Valid To' }"));
|
||||
cols.Add(JObject.Parse("{ 'data':'Revision', 'name':'Target' }"));
|
||||
|
||||
HumanizeColumns(cols);
|
||||
|
||||
JArray pkeycols = new JArray();
|
||||
pkeycols.Add("SubjectAreaFormId");
|
||||
|
||||
JArray Navigations = new JArray();
|
||||
|
||||
GridOptions["GridColumns"] = cols;
|
||||
GridOptions["ModelName"] = nameof(SubjectAreaForm);
|
||||
GridOptions["PrimaryKeyColumns"] = pkeycols;
|
||||
GridOptions["Navigations"] = Navigations;
|
||||
GridOptions["AutoWidth"] = false;
|
||||
//todo: put the button settings on the row!!
|
||||
//GridOptions["CrudButtons"] = GetSecurityFilteredActions("Create,Edit,Details,Delete");
|
||||
GridOptions["CrudButtons"] = new JArray("Create", "Edit", "Details", "Delete");
|
||||
|
||||
return GridOptions;
|
||||
}
|
||||
|
||||
|
||||
[ChecksUserAccess]
|
||||
public ActionResult GetGridOptions()
|
||||
{
|
||||
return new OkObjectResult(JsonConvert.SerializeObject(GridCols()));
|
||||
}
|
||||
|
||||
[ChecksUserAccess]
|
||||
public async Task<ActionResult> GetGridData()
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
string draw = Request.Form["draw"];
|
||||
string start = Request.Form["start"];
|
||||
string length = Request.Form["length"];
|
||||
string sortColumn = Request.Form["columns[" + Request.Form["order[0][column]"] + "][data]"];
|
||||
string sortColumnDir = Request.Form["order[0][dir]"];
|
||||
|
||||
string searchValue = Request.Form["search[value]"];
|
||||
|
||||
//Paging Size (10,20,50,100)
|
||||
int pageSize = length != null ? Convert.ToInt32(length) : 0;
|
||||
int skip = start != null ? Convert.ToInt32(start) : 0;
|
||||
int recordsTotal = 0;
|
||||
|
||||
// Getting all Customer data
|
||||
var modelDataAll = (from temptable in _context.SubjectAreaForm
|
||||
select temptable);
|
||||
|
||||
//filter the list by permitted roles
|
||||
if (!CanPerformCurrentActionGlobally())
|
||||
{
|
||||
//TODO: Make sure this works with the Groups instead of ROles
|
||||
var permittedRoles = GetPermittedGroupsForCurrentAction();
|
||||
|
||||
modelDataAll =
|
||||
(from md in modelDataAll
|
||||
join sa in _context.SubjectArea
|
||||
on md.SubjectAreaFormId equals sa.SubjectAreaFormId
|
||||
join rm in _context.SubjectAreaRoleMapsFor(GetUserAdGroupUids(), permittedRoles)
|
||||
on sa.SubjectAreaId equals rm.SubjectAreaId
|
||||
select md).Distinct();
|
||||
}
|
||||
|
||||
//Sorting
|
||||
if (!(string.IsNullOrEmpty(sortColumn) && string.IsNullOrEmpty(sortColumnDir)))
|
||||
{
|
||||
modelDataAll = modelDataAll.OrderBy(sortColumn + " " + sortColumnDir);
|
||||
}
|
||||
//Search
|
||||
if (!string.IsNullOrEmpty(searchValue))
|
||||
{
|
||||
modelDataAll = modelDataAll.Where(m => m.FormJson.Contains(searchValue));
|
||||
}
|
||||
|
||||
//Filter based on querystring params
|
||||
if (!(string.IsNullOrEmpty(Request.Form["QueryParams[FormStatus]"])))
|
||||
{
|
||||
var formStatusFilter = System.Convert.ToInt64(Request.Form["QueryParams[FormStatus]"]);
|
||||
modelDataAll = modelDataAll.Where(t => t.FormStatus == formStatusFilter);
|
||||
}
|
||||
|
||||
if (!(string.IsNullOrEmpty(Request.Form["QueryParams[UpdatedBy]"])))
|
||||
{
|
||||
var filter = Request.Form["QueryParams[UpdatedBy]"];
|
||||
modelDataAll = modelDataAll.Where(t => t.UpdatedBy == filter);
|
||||
}
|
||||
|
||||
//Custom Includes
|
||||
//modelDataAll = modelDataAll
|
||||
// .Include(t => t).AsNoTracking();
|
||||
|
||||
|
||||
//total number of rows count
|
||||
recordsTotal = await modelDataAll.CountAsync();
|
||||
//Paging
|
||||
var data = await modelDataAll.Skip(skip).Take(pageSize).ToListAsync();
|
||||
|
||||
|
||||
//Returning Json Data
|
||||
var jserl = new JsonSerializerSettings
|
||||
{
|
||||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
|
||||
Converters = { new Newtonsoft.Json.Converters.StringEnumConverter() }
|
||||
};
|
||||
|
||||
return new OkObjectResult(JsonConvert.SerializeObject(new { draw = draw, recordsFiltered = recordsTotal, recordsTotal = recordsTotal, data = data }, jserl));
|
||||
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// GET: SubjectAreaForm/Revise/5
|
||||
[ChecksUserAccess]
|
||||
public async Task<IActionResult> Revise(int? id)
|
||||
{
|
||||
if (id == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var subjectAreaForm = await _context.SubjectAreaForm.FindAsync(id);
|
||||
if (subjectAreaForm == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
if (!await base.CanPerformCurrentActionOnRecord(subjectAreaForm))
|
||||
return Forbid();
|
||||
|
||||
// Create new SubjectAreaForm with copied values
|
||||
// Increment revision num and set form status back to 1
|
||||
var revisedSubjectAreaForm = new SubjectAreaForm
|
||||
{
|
||||
FormStatus = (byte)SubjectAreaFormStatus.Complete,
|
||||
FormJson = subjectAreaForm.FormJson,
|
||||
SubjectAreas = subjectAreaForm.SubjectAreas,
|
||||
Revision = (byte)(subjectAreaForm.Revision + 1)
|
||||
};
|
||||
|
||||
// Add to db
|
||||
_context.SubjectAreaForm.Add(revisedSubjectAreaForm);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
// Deserialize model so we can reset some values
|
||||
var PIA = PIAWizardViewModel.FromForm(JsonConvert.DeserializeObject<PIAForm>(subjectAreaForm.FormJson, PIAForm.JsonSettings));
|
||||
|
||||
// Set SubjectAreaId within the json after getting id of new SubjectAreaForm
|
||||
PIA.SubjectAreaId = revisedSubjectAreaForm.SubjectAreaFormId;
|
||||
PIA.MaxStep = 0;
|
||||
PIA.Step = 0;
|
||||
|
||||
// Re-serialize and update db again
|
||||
revisedSubjectAreaForm.FormJson = JsonConvert.SerializeObject(PIA.ToForm());
|
||||
_context.Update(revisedSubjectAreaForm);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
// Now we can redireect to the wizard with our new SubjectAreaForm
|
||||
return RedirectToAction("PIAWizardResume", "Wizards", new { id = revisedSubjectAreaForm.SubjectAreaFormId });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -249,6 +249,27 @@ namespace WebApplication.Controllers
|
|||
// Getting all Customer data
|
||||
var modelDataAll = from dep in _context.TaskGroupDependency select dep;
|
||||
|
||||
//filter the list by permitted roles
|
||||
if (!CanPerformCurrentActionGlobally())
|
||||
{
|
||||
var requiredRoles = GetPermittedRolesForCurrentAction();
|
||||
|
||||
var filteredTaskGroups =
|
||||
from tg in _context.TaskGroup
|
||||
join rm in _context.SubjectAreaRoleMapsFor(GetUserAdGroupUids(), requiredRoles)
|
||||
on tg.SubjectAreaId equals rm.SubjectAreaId
|
||||
select tg;
|
||||
|
||||
//cross join + distinct to get all possiblities without a GroupJoin (which doesnt' work in EFCore)
|
||||
modelDataAll =
|
||||
(from md in modelDataAll
|
||||
from tg in filteredTaskGroups
|
||||
where
|
||||
tg.TaskGroupId == md.AncestorTaskGroupId
|
||||
|| tg.TaskGroupId == md.DescendantTaskGroupId
|
||||
select md).Distinct();
|
||||
}
|
||||
|
||||
modelDataAll = modelDataAll
|
||||
.Include(t => t.AncestorTaskGroup)
|
||||
.Include(t => t.DescendantTaskGroup).AsNoTracking();
|
||||
|
@ -291,13 +312,15 @@ namespace WebApplication.Controllers
|
|||
public async Task<IActionResult> DetailsPlus([FromQuery] long AncestorTaskGroupId, [FromQuery] long DescendantTaskGroupId)
|
||||
{
|
||||
var taskGroupDependency = await _context.TaskGroupDependency
|
||||
.Include(m => m.AncestorTaskGroup)
|
||||
.Include(m => m.DescendantTaskGroup)
|
||||
.FirstOrDefaultAsync(m => m.AncestorTaskGroupId == AncestorTaskGroupId && m.DescendantTaskGroupId == DescendantTaskGroupId);
|
||||
if (taskGroupDependency == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return View("Details", taskGroupDependency);
|
||||
return View("DetailsPlus", taskGroupDependency);
|
||||
}
|
||||
|
||||
// GET: TaskGroupDependency/Edit/5
|
||||
|
@ -308,7 +331,7 @@ namespace WebApplication.Controllers
|
|||
{
|
||||
return NotFound();
|
||||
}
|
||||
return View("Edit", taskGroupDependency);
|
||||
return View("EditPlus", taskGroupDependency);
|
||||
}
|
||||
|
||||
// POST: TaskGroupDependency/Edit/5
|
||||
|
|
|
@ -378,7 +378,7 @@ namespace WebApplication.Controllers
|
|||
ti.NumberOfRetries = 0;
|
||||
if (Status != "InProgress") { ti.TaskRunnerId = null; }
|
||||
}
|
||||
_context.SaveChanges();
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
//TODO: Add Error Handling
|
||||
return new OkObjectResult(new { });
|
||||
|
|
|
@ -403,6 +403,8 @@ namespace WebApplication.Controllers
|
|||
|
||||
tm.TaskMasterId = 0;
|
||||
tm.TaskMasterName = tm.TaskMasterName + " Copy";
|
||||
tm.ActiveYn = false;
|
||||
|
||||
_context.Add(tm);
|
||||
}
|
||||
await _context.SaveChangesAsync();
|
||||
|
|
|
@ -218,7 +218,7 @@ namespace WebApplication.Controllers
|
|||
cols.Add(JObject.Parse("{ 'data':'TaskMasterWaterMarkDateTime', 'name':'Watermark Date', 'autoWidth':true }"));
|
||||
cols.Add(JObject.Parse("{ 'data':'TaskMasterWaterMarkBigInt', 'name':'Watermark Value', 'autoWidth':true }"));
|
||||
cols.Add(JObject.Parse("{ 'data':'TaskWaterMarkJson', 'name':'Json', 'autoWidth':true }"));
|
||||
cols.Add(JObject.Parse("{ 'data':'ActiveYn', 'name':'Active', 'autoWidth':true, 'ads_format': 'bool' }"));
|
||||
cols.Add(JObject.Parse("{ 'data':'ActiveYn', 'name':'Is Active', 'autoWidth':true, 'ads_format': 'bool' }"));
|
||||
|
||||
HumanizeColumns(cols);
|
||||
|
||||
|
|
|
@ -248,7 +248,7 @@ namespace WebApplication.Controllers
|
|||
//Search
|
||||
if (!string.IsNullOrEmpty(searchValue))
|
||||
{
|
||||
modelDataAll = modelDataAll.Where(m => m.TaskTypeName == searchValue);
|
||||
modelDataAll = modelDataAll.Where(m => m.TaskTypeName.Contains(searchValue));
|
||||
}
|
||||
|
||||
//total number of rows count
|
||||
|
|
|
@ -257,8 +257,7 @@ namespace WebApplication.Controllers
|
|||
//Search
|
||||
if (!string.IsNullOrEmpty(searchValue))
|
||||
{
|
||||
//TODO: Implement search
|
||||
//modelDataAll = modelDataAll.Where(m => m.TaskMasterName.Contains(searchValue));
|
||||
modelDataAll = modelDataAll.Where(m => m.MappingName.Contains(searchValue));
|
||||
}
|
||||
|
||||
//Filter based on querystring params
|
||||
|
|
|
@ -1,33 +1,353 @@
|
|||
using System.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Newtonsoft.Json;
|
||||
using WebApplication.Forms;
|
||||
using WebApplication.Forms.PIAWizard;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using WebApplication.Framework;
|
||||
using WebApplication.Models;
|
||||
using WebApplication.Models.Wizards;
|
||||
using WebApplication.Services;
|
||||
using Microsoft.Extensions.Options;
|
||||
using WebApplication.Models.Forms;
|
||||
using WebApplication.Models.Options;
|
||||
using SubjectArea = WebApplication.Models.SubjectArea;
|
||||
|
||||
|
||||
namespace WebApplication.Controllers.Customisations
|
||||
namespace WebApplication.Controllers
|
||||
{
|
||||
public class WizardsController : Controller
|
||||
public class WizardsController : BaseController
|
||||
{
|
||||
private readonly AdsGoFastContext _context;
|
||||
public WizardsController(AdsGoFastContext context)
|
||||
private readonly Services.IMicrosoftGraphService _graphService;
|
||||
private readonly IEmailService _emailService;
|
||||
private readonly ProcessingFunctionClient _processingClient;
|
||||
|
||||
public WizardsController(AdsGoFastContext context,
|
||||
Services.ISecurityAccessProvider securityProvider,
|
||||
Services.IEntityRoleProvider roleProvider,
|
||||
Services.IMicrosoftGraphService graphService,
|
||||
Services.IEmailService emailService,
|
||||
ProcessingFunctionClient processingClient)
|
||||
: base(securityProvider, roleProvider)
|
||||
{
|
||||
_context = context;
|
||||
_graphService = graphService;
|
||||
_emailService = emailService;
|
||||
_processingClient = processingClient;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> ExternalFileUpload()
|
||||
{
|
||||
|
||||
ViewData["UploadSystemId"] = new SelectList(await _context.SourceAndTargetSystems.Where(t=> t.SystemType.ToLower() == "azure blob").OrderBy(t => t.SystemName).ToListAsync(), "SystemId", "SystemName");
|
||||
ViewData["UploadSystemId"] = new SelectList(await _context.SourceAndTargetSystems.Where(t => t.SystemType.ToLower() == "azure blob").OrderBy(t => t.SystemName).ToListAsync(), "SystemId", "SystemName");
|
||||
ViewData["EmailSystemId"] = new SelectList(await _context.SourceAndTargetSystems.Where(t => t.SystemType.ToLower() == "sendgrid").OrderBy(t => t.SystemName).ToListAsync(), "SystemId", "SystemName");
|
||||
ViewData["TargetSystemId"] = new SelectList(await _context.SourceAndTargetSystems.Where(t => t.SystemType.ToLower() == "azure blob" || t.SystemType.ToLower() == "adls").OrderBy(t => t.SystemName).ToListAsync(), "SystemId", "SystemName");
|
||||
ViewData["TaskGroupId"] = new SelectList(await _context.TaskGroup.OrderBy(t => t.TaskGroupName).ToListAsync(), "TaskGroupId", "TaskGroupName");
|
||||
ViewData["ScheduleMasterId"] = new SelectList(await _context.ScheduleMaster.OrderBy(t => t.ScheduleDesciption).ToListAsync(), "ScheduleMasterId", "ScheduleDesciption");
|
||||
ViewData["ExternalParties"] = "";
|
||||
|
||||
ViewData["DataFactoryId"] = new SelectList(await _context.DataFactory.OrderBy(t => t.Name).ToListAsync(), "Id", "Name");
|
||||
|
||||
return View();
|
||||
}
|
||||
|
||||
private async Task SetViewBagDataForPIA()
|
||||
{
|
||||
ViewBag.SourceSystems = new SelectList(_context.SourceAndTargetSystems
|
||||
.OrderBy(x => x.SystemName), "SystemId", "SystemName");
|
||||
|
||||
//TODO: Refactor this out
|
||||
//var phnZones = await _sharedZoneService.GetAllZones();
|
||||
//ViewBag.PhnZones = new SelectList(new List<PhnZone>(phnZones)
|
||||
// .OrderBy(x => x.Name), "Id", "Name");
|
||||
|
||||
var users = await GetPHNUsers();
|
||||
ViewBag.PHNUsers = new SelectList(users.OrderBy(x => x.DisplayName),
|
||||
"UserId", "DisplayName");
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[ChecksUserAccess]
|
||||
[Route("Wizards/PIAWizard/{subjectAreaId}")]
|
||||
public async Task<IActionResult> PIAWizard([FromRoute] int subjectAreaId)
|
||||
{
|
||||
await SetViewBagDataForPIA();
|
||||
|
||||
var subjectAreaRecord = await _context.SubjectArea
|
||||
.Include(x => x.SubjectAreaForm)
|
||||
.FirstOrDefaultAsync(x => x.SubjectAreaId == subjectAreaId);
|
||||
|
||||
if (subjectAreaRecord == null)
|
||||
return View("Error", new ErrorViewModel { Message = "Subject area not found." });
|
||||
|
||||
var hasGlobalPermission = await CanPerformCurrentActionOnRecord(subjectAreaRecord);
|
||||
var hasSpecialPermission = await this.CanEditSubjectArea(subjectAreaRecord);
|
||||
if (!hasGlobalPermission && !hasSpecialPermission)
|
||||
return Forbid();
|
||||
|
||||
if ((SubjectAreaFormStatus?)subjectAreaRecord.SubjectAreaForm.FormStatus == SubjectAreaFormStatus.Complete)
|
||||
{
|
||||
return RedirectToAction("Revise", "SubjectArea", new {id = subjectAreaId});
|
||||
}
|
||||
|
||||
PIAWizardViewModel PIA = PIAWizardViewModel.FromForm(JsonConvert.DeserializeObject<PIAForm>(subjectAreaRecord.SubjectAreaForm.FormJson, PIAForm.JsonSettings));
|
||||
|
||||
if (!await base.CanPerformCurrentActionOnRecord(subjectAreaRecord.SubjectAreaForm))
|
||||
return Forbid();
|
||||
|
||||
// Ensure SubjectAreaId is in hidden field
|
||||
PIA.SubjectAreaId = subjectAreaId;
|
||||
|
||||
return View("PIAWizard", PIA);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ChecksUserAccess]
|
||||
[Route("Wizards/PIAWizard/{subjectAreaId}")]
|
||||
public async Task<IActionResult> PIAWizardPost([FromRoute] int subjectAreaId, [FromForm] PIAWizardViewModel PIA)
|
||||
{
|
||||
if (subjectAreaId == 0)
|
||||
return RedirectToAction("IndexDataTable", "SubjectArea");
|
||||
|
||||
var subjectAreaRecord = await _context.SubjectArea
|
||||
.Include(x => x.SubjectAreaForm)
|
||||
.FirstOrDefaultAsync(x => x.SubjectAreaId == subjectAreaId);
|
||||
|
||||
if (subjectAreaRecord == null)
|
||||
return View("Error", new ErrorViewModel { Message = "Subject area not found." });
|
||||
|
||||
var hasGlobalPermission = await CanPerformCurrentActionOnRecord(subjectAreaRecord);
|
||||
var hasSpecialPermission = await this.CanEditSubjectArea(subjectAreaRecord);
|
||||
if (!hasGlobalPermission && !hasSpecialPermission)
|
||||
return Forbid();
|
||||
|
||||
await SetViewBagDataForPIA();
|
||||
|
||||
// not sure if this will break anything but surely we do want the user to be able to save the form on step 0?
|
||||
if (PIA.Step >= 0 || PIA.SubmissionMethod == 1) // If wizard has been started or submitting page 0
|
||||
{
|
||||
SubjectAreaForm subjectAreaForm;
|
||||
|
||||
if (PIA.SubjectAreaId == 0) // If database entry has not been created
|
||||
{
|
||||
ModelState.Remove("Step"); // Remove so we can manually set
|
||||
PIA.Step++;
|
||||
subjectAreaForm = CreatePIADataEntry(PIA);
|
||||
|
||||
_context.Add(subjectAreaForm);
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
ModelState.Remove("SubjectAreaId"); // remove so we can manually set
|
||||
PIA.SubjectAreaId = subjectAreaId; // Set dbid to keep track for next time
|
||||
|
||||
// Reserialize json now that we have our auto generated id
|
||||
subjectAreaForm.FormJson = JsonConvert.SerializeObject(PIA.ToForm());
|
||||
_context.Update(subjectAreaForm);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
else // Database entry exists
|
||||
{
|
||||
subjectAreaForm = await UpdatePIAData(PIA);
|
||||
|
||||
_context.Update(subjectAreaForm);
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
// If submitted form
|
||||
if (subjectAreaForm.FormStatus == (int)SubjectAreaFormStatus.Complete)
|
||||
{
|
||||
// notify all phn users whose roles are set throgh the form by email
|
||||
string[] stewards = PIA.DataStewards.Select(ds => ds.UserId).ToArray();
|
||||
await _emailService.SendNotificationToPHNUsers(PIA.DataOwner.UserId, PIA.DataCustodian.UserId, stewards);
|
||||
await _processingClient.QueueSubjectAreaProvisioning(PIA.SubjectAreaId);
|
||||
// Redirect out of wizard and
|
||||
return RedirectToAction("Details", "SubjectArea", new { id = PIA.SubjectAreaId });
|
||||
}
|
||||
else
|
||||
{
|
||||
IncrementStep(PIA);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ModelState.Remove("Step"); // Remove so we can manually set
|
||||
PIA.Step = 0;
|
||||
|
||||
IncrementStep(PIA);
|
||||
}
|
||||
|
||||
return View(nameof(PIAWizard), PIA);
|
||||
}
|
||||
|
||||
private async Task<SubjectAreaForm> UpdatePIAData(PIAWizardViewModel PIA)
|
||||
{
|
||||
var subjectArea = await _context.SubjectArea.FindAsync(PIA.SubjectAreaId);
|
||||
var subjectAreaForm = await _context.SubjectAreaForm.FindAsync(subjectArea.SubjectAreaFormId);
|
||||
if (PIA.Step == 8 && PIA.SubmissionMethod == 1) // only formally submits on summary page
|
||||
{
|
||||
if (ModelState.IsValid) // server side validation performed on last step
|
||||
{
|
||||
subjectAreaForm.FormStatus = (int)SubjectAreaFormStatus.Complete; // Set status to submitted
|
||||
}
|
||||
}
|
||||
|
||||
subjectAreaForm.FormJson = JsonConvert.SerializeObject(PIA.ToForm()); // Update json
|
||||
return subjectAreaForm;
|
||||
}
|
||||
|
||||
private SubjectAreaForm CreatePIADataEntry(PIAWizardViewModel PIA)
|
||||
{
|
||||
// Construct Base Database entry
|
||||
SubjectAreaForm subjectAreaForm = new SubjectAreaForm();
|
||||
subjectAreaForm.Revision = 1;
|
||||
subjectAreaForm.UpdatedBy = User.Identity.Name;
|
||||
subjectAreaForm.FormStatus = (int)SubjectAreaFormStatus.Incomplete;
|
||||
subjectAreaForm.FormJson = JsonConvert.SerializeObject(PIA.ToForm());
|
||||
return subjectAreaForm;
|
||||
}
|
||||
|
||||
private void IncrementStep(PIAWizardViewModel PIA)
|
||||
{
|
||||
// If pressed next, increment step
|
||||
if (PIA.SubmissionMethod == 1)
|
||||
{
|
||||
PIA.Step++;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> AddDataSteward([Bind("DataStewards")] PIAWizardViewModel pia)
|
||||
{
|
||||
var testData = await GetPHNUsers();
|
||||
|
||||
ViewBag.PHNUsers = new SelectList(testData,
|
||||
"UserId", "DisplayName");
|
||||
|
||||
var dataSteward = new UserReference();
|
||||
|
||||
pia.DataStewards.Add(dataSteward);
|
||||
|
||||
return PartialView("_DataStewards", pia);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> RemoveDataSteward([Bind("DataStewards")] PIAWizardViewModel pia)
|
||||
{
|
||||
var PHNUsers = await GetPHNUsers();
|
||||
|
||||
ViewBag.PHNUsers = new SelectList(PHNUsers,
|
||||
"UserId", "DisplayName"); // find better way to do this
|
||||
|
||||
if (pia.DataStewards.Count > 1)
|
||||
{
|
||||
pia.DataStewards.RemoveAt(pia.DataStewards.Count - 1);
|
||||
}
|
||||
|
||||
return PartialView("_DataStewards", pia);
|
||||
}
|
||||
|
||||
private async Task<List<UserReference>> GetPHNUsers()
|
||||
{
|
||||
var members = await _graphService.GetMembers();
|
||||
|
||||
return members.OrderBy(m => m.DisplayName).ToList();
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> PIASummary(int? id)
|
||||
{
|
||||
if (id == null)
|
||||
{
|
||||
return RedirectToAction("IndexDataTable", "SubjectArea");
|
||||
}
|
||||
|
||||
var subjectArea = await _context.SubjectArea.FindAsync(id);
|
||||
|
||||
var saform = await _context.SubjectAreaForm.FindAsync(subjectArea.SubjectAreaFormId);
|
||||
|
||||
PIAWizardViewModel PIA = PIAWizardViewModel.FromForm(JsonConvert.DeserializeObject<PIAForm>(saform.FormJson, PIAForm.JsonSettings));
|
||||
|
||||
return View(PIA);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ChecksUserAccess]
|
||||
public async Task<IActionResult> PostEFN(ExternalFileUpload upload)
|
||||
{
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(nameof(ExternalFileUpload), upload);
|
||||
}
|
||||
|
||||
TaskMaster taskMaster = new TaskMaster()
|
||||
{
|
||||
TaskMasterName = upload.TaskMasterName,
|
||||
TaskTypeId = (int)upload.TaskTypeId,
|
||||
TaskGroupId = upload.TaskGroupId,
|
||||
ScheduleMasterId = upload.ScheduleMasterId,
|
||||
SourceSystemId = upload.EmailSystemId,
|
||||
TargetSystemId = upload.TargetSystemId,
|
||||
AllowMultipleActiveInstances = upload.AllowMultipleActiveInstances,
|
||||
TaskDatafactoryIr = upload.TaskDatafactoryIr,
|
||||
// data on external parties is not being stored currently
|
||||
TaskMasterJson = upload.ExternalParties,
|
||||
ActiveYn = upload.IsActive,
|
||||
DependencyChainTag = upload.DependencyChainTag,
|
||||
DataFactoryId = upload.DataFactoryId,
|
||||
DegreeOfCopyParallelism = upload.DegreeOfCopyParallelism,
|
||||
};
|
||||
|
||||
taskMaster.ScheduleMaster = await _context.ScheduleMaster.FindAsync(taskMaster.ScheduleMasterId);
|
||||
taskMaster.SourceSystem = await _context.SourceAndTargetSystems.FindAsync(taskMaster.SourceSystemId);
|
||||
taskMaster.TargetSystem = await _context.SourceAndTargetSystems.FindAsync(taskMaster.TargetSystemId);
|
||||
taskMaster.TaskGroup = await _context.TaskGroup.FindAsync(taskMaster.TaskGroupId);
|
||||
var tt = await _context.TaskType
|
||||
.Where(t => string.Equals(t.TaskTypeName.ToLower(), "generate and send sas uri file upload link"))
|
||||
.ToListAsync();
|
||||
|
||||
taskMaster.TaskType = tt.FirstOrDefault();
|
||||
taskMaster.TaskTypeId = taskMaster.TaskType?.TaskTypeId ?? 0;
|
||||
|
||||
var masterJson = new
|
||||
{
|
||||
Source = new
|
||||
{
|
||||
Type = "SASUri",
|
||||
SasURIDaysValid = 7,
|
||||
RelativePath = upload.RelativePath,
|
||||
DataFileName = upload.UploadFileName,
|
||||
TargetSystem = upload.TargetSystemId,
|
||||
PHIWideTransientInZoneIDForPHNX = upload.TargetSystemId,
|
||||
FileUploaderWebAppURL = upload.FileUploaderWebAppURL,
|
||||
TargetSystemUidInPHI = upload.TargetSystemUidInPHI // target system guid user inputs?
|
||||
},
|
||||
Target = new
|
||||
{
|
||||
Type = "Email",
|
||||
EmailSubject = upload.EmailSubject,
|
||||
EmailRecipient = upload.OperatorEmail,
|
||||
EmailRecipientName = upload.OperatorName,
|
||||
EmailTemplateFileName = "PipQISasUriEmail"
|
||||
}
|
||||
};
|
||||
|
||||
taskMaster.TaskMasterJson = JsonConvert.SerializeObject(masterJson);
|
||||
|
||||
await _context.TaskMaster.AddAsync(taskMaster);
|
||||
if (!await CanPerformCurrentActionOnRecord(taskMaster))
|
||||
{
|
||||
return new ForbidResult();
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return RedirectToAction("Details", "TaskMaster", new { id = taskMaster.TaskMasterId });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using WebApplication.Models;
|
||||
|
||||
namespace WebApplication.Extensions
|
||||
{
|
||||
public static class OptionsExtensions
|
||||
{
|
||||
public static InputSelectionOptionsFor<T> AsInputSelectionOptions<T>(this IEnumerable<T> selected) where T : Enum => new InputSelectionOptionsFor<T>(selected);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
using System.Threading.Tasks;
|
||||
using WebApplication.Framework;
|
||||
using WebApplication.Models;
|
||||
|
||||
namespace WebApplication.Controllers
|
||||
{
|
||||
public static class SubjectAreaControllerExtensions
|
||||
{
|
||||
public static async Task<bool> CanEditSubjectArea(this BaseController controller, SubjectArea sa)
|
||||
{
|
||||
var isPartiallyCompleted = (SubjectAreaFormStatus?)sa.SubjectAreaForm?.FormStatus < SubjectAreaFormStatus.Complete;
|
||||
var isMine = sa.UpdatedBy == controller.User.GetUserName() || sa.SubjectAreaForm?.UpdatedBy == controller.User.GetUserName();
|
||||
|
||||
return (isPartiallyCompleted && isMine);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
|
||||
namespace WebApplication.Framework
|
||||
{
|
||||
|
@ -31,5 +32,10 @@ namespace WebApplication.Framework
|
|||
public static IDictionary<TKey, TValue> ToDictionary<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> collection)
|
||||
=> new Dictionary<TKey, TValue>(collection);
|
||||
|
||||
public static SelectList ToSelectList<T>(this IEnumerable<T> collection, string dataValueField, string dataTextField)
|
||||
=> new SelectList(collection, dataValueField, dataTextField);
|
||||
|
||||
public static SelectList ToSelectList<T>(this IEnumerable<T> collection, string dataValueField, string dataTextField, object selectedValue)
|
||||
=> new SelectList(collection, dataValueField, dataTextField, selectedValue);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
## External File Upload
|
||||
## External File Upload Wizard
|
||||
|
||||
This wizard allows you to configure write-only, <br/>key-based access to your transient input storage locations.
|
||||
|
||||
This wizard facilitates the creation of a task master for the task type which involves generating and sending a SAS URI file upload link to an external user so they can create a Privacy Impact Assessment for an externally uploaded data set.
|
|
@ -5,24 +5,6 @@ namespace WebApplication.Models.Wizards
|
|||
{
|
||||
public partial class ExternalFileUpload
|
||||
{
|
||||
public Int64 UploadSystemId { get; set; }
|
||||
public Int64 EmailSystemId { get; set; }
|
||||
public Int64 TargetSystemId { get; set; }
|
||||
public Int64 ScheduleMasterId { get; set; }
|
||||
|
||||
public Int64 TaskGroupId { get; set; }
|
||||
|
||||
public string ExternalParties { get; set; }
|
||||
public string UploadFileName { get; set; }
|
||||
|
||||
|
||||
[Display(Name = "Email Address")]
|
||||
[Required(ErrorMessage = "Email is required.")]
|
||||
[EmailAddress(ErrorMessage = "Invalid Email Address.")]
|
||||
public string Email { get; set; }
|
||||
[Display(Name = "Email Address")]
|
||||
[Required(ErrorMessage = "Email is required.")]
|
||||
[EmailAddress(ErrorMessage = "Invalid Email Address.")]
|
||||
public string OperatorEmail { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using WebApplication.Forms.PIAWizard;
|
||||
|
||||
namespace WebApplication.Models.Forms
|
||||
{
|
||||
public class PIAForm
|
||||
{
|
||||
public int SubjectAreaId { get; set; } // Database row id
|
||||
public int Step { get; set; } // Last submitted step
|
||||
public int MaxStep { get; set; } // Furthest page reached in wizard
|
||||
public int SubmissionMethod { get; set; } // 0: Save-only, 1: Next
|
||||
public PublicPrivateDataSet DataSetPrivacyStatus { get; set; }
|
||||
|
||||
public bool AuthorisedByDataSubjects { get; set; }
|
||||
public bool DataAgreementsSigned { get; set; }
|
||||
public string DataAgreementsLocation { get; set; }
|
||||
public bool UnderstandMetadata { get; set; }
|
||||
public bool AcceptResponsibility { get; set; }
|
||||
|
||||
public string BelongingDatasetCode { get; set; }
|
||||
public string BelongingDataset { get; set; } // name of associated subject area code
|
||||
|
||||
public Guid? PhnId { get; set; }
|
||||
public string DataSourceLocation { get; set; }
|
||||
public int? OwningSystem { get; set; }
|
||||
public string SystemDataLocation { get; set; }
|
||||
public string SystemPurposeVersion { get; set; }
|
||||
public string PhnName { get; set; }
|
||||
public string SourceSystemName { get; set; }
|
||||
public string SystemPurposeVersionNumber { get; set; }
|
||||
|
||||
public string DataSetDescription { get; set; }
|
||||
public string SupportingDocumentationLocation { get; set; }
|
||||
public PersonalDeIdentified DataPersonalOrDeIdentified { get; set; }
|
||||
public SuppressionRuleOptions SuppressionRuleOne { get; set; }
|
||||
public string SuppressionRuleOneExpl { get; set; }
|
||||
public SuppressionRuleOptions SuppressionRuleTwo { get; set; }
|
||||
public string SuppressionRuleTwoExpl { get; set; }
|
||||
public SuppressionRuleOptions SuppressionRuleThree { get; set; }
|
||||
public string SuppressionRuleThreeExpl { get; set; }
|
||||
public SuppressionRuleOptions SuppressionRuleFour { get; set; }
|
||||
public string SuppressionRuleFourExpl { get; set; }
|
||||
public bool IdentifiedDataCustodianManagedRisk { get; set; }
|
||||
public string DataRiskManagementExplanation { get; set; }
|
||||
|
||||
public FormOfData DataForm { get; set; }
|
||||
public InformationLevelData InfoLevelOfData { get; set; }
|
||||
public InformationLevelVariable InfoLevelOfVariable { get; set; }
|
||||
public IList<Properties> TopThreePropsOfData { get; set; } = new List<Properties>();
|
||||
|
||||
public bool IsDataQualityLevelHigh { get; set; }
|
||||
public string DataQualityExplanation { get; set; }
|
||||
public bool IsDataAboutVulnerablePopulation { get; set; }
|
||||
public string PopulationVulnerabilityExplanation { get; set; }
|
||||
public DateTime? WhenDataWasCollected { get; set; }
|
||||
public string DataTimeCollectedExplanation { get; set; }
|
||||
public bool IsDataHierarchical { get; set; }
|
||||
public string DataHierarchicalExplanation { get; set; }
|
||||
public bool IsDataTimeStamped { get; set; }
|
||||
public string DataTimestampedExplanation { get; set; }
|
||||
|
||||
public UserReference DataOwner { get; set; }
|
||||
public UserReference DataCustodian { get; set; }
|
||||
public IList<UserReference> DataStewards { get; set; } = new List<UserReference>();
|
||||
public IList<UserReference> ExternalUsers { get; set; } = new List<UserReference>();
|
||||
|
||||
public BinaryOrNa RespectDataAssurances { get; set; }
|
||||
public string DataAssuranceExplanation { get; set; }
|
||||
public BinaryOrNa HasProvidedTransparency { get; set; }
|
||||
public string ProvidedTransparencyExplanation { get; set; }
|
||||
public BinaryOrNa HasObtainedSubjectsViews { get; set; }
|
||||
public string SubjectsViewsExplanation { get; set; }
|
||||
|
||||
public bool HasInitialSpecification { get; set; }
|
||||
public IList<Specifications> TopLevelAssessment { get; set; } = new List<Specifications>();
|
||||
public string DataComplexityExpl { get; set; }
|
||||
public string SensitiveVariableExpl { get; set; }
|
||||
public string DetailedVariableExpl { get; set; }
|
||||
public string AnalysisExplanation { get; set; }
|
||||
public IList<Properties> DataAnalyticalApproaches { get; set; } = new List<Properties>();
|
||||
public bool PreparationTesting { get; set; }
|
||||
public string PenetrationTestingExplanation { get; set; }
|
||||
|
||||
public IList<EnvironmentReconfigurations> ReconfigureEnvironment { get; set; } = new List<EnvironmentReconfigurations>();
|
||||
public string ExplanationReconfigureEnvirontment { get; set; }
|
||||
public IList<DataModification> ModifyingData { get; set; } = new List<DataModification>();
|
||||
public string ExplanationModifyingDataOptions { get; set; }
|
||||
|
||||
public IList<DataBreachImpactManagement> ImpactManagementReductionOptions { get; set; } = new List<DataBreachImpactManagement>();
|
||||
public string ImpactManagementExplanation { get; set; }
|
||||
public string ImpactManagementOtherOption { get; set; } // remove
|
||||
public IList<StakeholderAssuranceProtection> StakeholdersAssurance { get; set; } = new List<StakeholderAssuranceProtection>();
|
||||
public string StakeholderAssuranceExplanation { get; set; }
|
||||
public IList<DataBreachPlan> PlanForDataBreachProcess { get; set; } = new List<DataBreachPlan>();
|
||||
public string DataBreachPlanExplanation { get; set; }
|
||||
|
||||
|
||||
public static readonly JsonSerializerSettings JsonSettings = new JsonSerializerSettings
|
||||
{
|
||||
ObjectCreationHandling = ObjectCreationHandling.Replace,
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
MissingMemberHandling = MissingMemberHandling.Ignore,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace WebApplication.Forms.PIAWizard
|
||||
{
|
||||
public enum BinaryOrNa
|
||||
{
|
||||
NA = 0,
|
||||
Yes = 1,
|
||||
No = 2
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace WebApplication.Forms.PIAWizard
|
||||
{
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum DataBreachImpactManagement
|
||||
{
|
||||
[Display(Name = "Additional Access Control")]
|
||||
AdditionalAccessControl = 0,
|
||||
[Display(Name = "Removed Identifiable Data")]
|
||||
RemoveIdentifiableData = 1,
|
||||
[Display(Name = "Formal DSA on Data Usage")]
|
||||
DSAOnDataUsage = 2,
|
||||
[Display(Name = "Data Suppression Used")]
|
||||
DataSuppressionUsed = 3,
|
||||
[Display(Name = "Other")]
|
||||
Other = 4
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace WebApplication.Forms.PIAWizard
|
||||
{
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum DataBreachPlan
|
||||
{
|
||||
[Display(Name = "Robust Audit Trail")]
|
||||
RobustAuditTrail = 0,
|
||||
[Display(Name = "Crisis Management Policy")]
|
||||
CrisisManagementPolicy = 1,
|
||||
[Display(Name = "Notify Of Data Breach Process")]
|
||||
NotifyDataBreachProcess = 2,
|
||||
[Display(Name = "Trained Staff")]
|
||||
TrainedStaff = 3,
|
||||
[Display(Name = "Other")]
|
||||
Other = 4
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace WebApplication.Forms.PIAWizard
|
||||
{
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum DataModification
|
||||
{
|
||||
[Display(Name = "De-Identify")]
|
||||
DeIdentify = 0,
|
||||
Suppress = 1,
|
||||
Anonymise = 2,
|
||||
Aggregate = 3,
|
||||
[Display(Name = "Delete Certain Fields")]
|
||||
DeleteCertainFields = 4
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace WebApplication.Forms.PIAWizard
|
||||
{
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum EnvironmentReconfigurations
|
||||
{
|
||||
[Display(Name = "Specify People Access")]
|
||||
SpecifyPeopleAccess = 0,
|
||||
[Display(Name = "Specify Requisite Security Level")]
|
||||
SpecifyRequisiteSecurityLevel = 1,
|
||||
[Display(Name = "Allow Access Only Within Own Secure Environment")]
|
||||
AllowAccessOnlyWithinOwnSecureEnvironment = 2,
|
||||
[Display(Name = "Specify All Analytics Be Checked Before Publishing")]
|
||||
SpecifyAllAnalyticsBeCheckedBeforePublish = 3,
|
||||
[Display(Name = "Make Use of Data Agreements")]
|
||||
MakeUseOfDataAgreements = 4
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace WebApplication.Forms.PIAWizard
|
||||
{
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum FormOfData
|
||||
{
|
||||
[Display(Name = "Flat-file")]
|
||||
FlatFile = 0,
|
||||
[Display(Name = "Survey Data")]
|
||||
Text = 1,
|
||||
Database = 2
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace WebApplication.Forms.PIAWizard
|
||||
{
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum InformationLevelData
|
||||
{
|
||||
[Display(Name = "Individual Level")]
|
||||
IndividualLevel = 0,
|
||||
Aggregated = 1,
|
||||
Locality = 2
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace WebApplication.Forms.PIAWizard
|
||||
{
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum InformationLevelVariable
|
||||
{
|
||||
[Display(Name = "Direct Identifiers")]
|
||||
DirectIdentifier = 0,
|
||||
[Display(Name = "Indirect Identifiers")]
|
||||
IndirectIdentifiers = 1,
|
||||
[Display(Name = "Aggregated Cohort Grouping")]
|
||||
AggregatedCohortGrouping = 2
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace WebApplication.Forms.PIAWizard
|
||||
{
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum PersonalDeIdentified
|
||||
{
|
||||
DeIdentified = 0,
|
||||
Personal = 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace WebApplication.Forms.PIAWizard
|
||||
{
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum Properties
|
||||
{
|
||||
Age = 0,
|
||||
Gender = 1,
|
||||
Quality = 2,
|
||||
[Display(Name = "Indigenous Status")]
|
||||
IndigenousStatus = 3,
|
||||
Location = 4,
|
||||
Clinical = 5
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace WebApplication.Forms.PIAWizard
|
||||
{
|
||||
public enum PublicPrivateDataSet
|
||||
{
|
||||
Private = 0,
|
||||
Public = 1
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace WebApplication.Forms.PIAWizard
|
||||
{
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum Specifications
|
||||
{
|
||||
[Display(Name = "Reduce Data Complexity")]
|
||||
ReduceDataComplexity = 0,
|
||||
[Display(Name = "Exclude Sensitive Variables")]
|
||||
ExcludeSensitiveVariable = 1,
|
||||
[Display(Name = "Exclude Detailed Variables")]
|
||||
ExcludeDetailedVariables = 2
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace WebApplication.Forms.PIAWizard
|
||||
{
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum StakeholderAssuranceProtection
|
||||
{
|
||||
[Display(Name = "DSA")]
|
||||
DSA = 0,
|
||||
[Display(Name = "Contact When Risk Profile Changes")]
|
||||
ContactWhenRiskProfileChanges = 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace WebApplication.Forms.PIAWizard
|
||||
{
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum SuppressionRuleOptions
|
||||
{
|
||||
[Display(Name = "Not Applicable")]
|
||||
NA = 0,
|
||||
[Display(Name = "I have applied this suppression rule")]
|
||||
Applied = 1,
|
||||
[Display(Name = "I have not applied this suppression rule")]
|
||||
NotApplied = 2,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace WebApplication.Forms.PIAWizard
|
||||
{
|
||||
public class InputOption<U> where U : System.Enum
|
||||
{
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public U Option { get; set; }
|
||||
public bool IsSelected { get; set; }
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using WebApplication.Forms.PIAWizard;
|
||||
|
||||
namespace WebApplication.Models
|
||||
{
|
||||
|
||||
public class InputSelectionOptionsFor<T> where T : System.Enum
|
||||
{
|
||||
public List<InputOption<T>> Options { get; set; } = new List<InputOption<T>>();
|
||||
|
||||
public InputSelectionOptionsFor(IEnumerable<T> selected = null)
|
||||
{
|
||||
var selectedItems = selected?.ToArray() ?? new T[0];
|
||||
|
||||
foreach (T option in Enum.GetValues(typeof(T)))
|
||||
{
|
||||
Options.Add(new InputOption<T>
|
||||
{
|
||||
Option = option,
|
||||
IsSelected = selectedItems.Contains(option)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public string TemplateName
|
||||
{
|
||||
get
|
||||
{
|
||||
var genericType = typeof(T).Name;
|
||||
return $"EditorFor{genericType}";
|
||||
}
|
||||
}
|
||||
|
||||
public IList<T> GetSelections() => this.Options.Where(x => x.IsSelected).Select(x => x.Option).ToList();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WebApplication.Models
|
||||
{
|
||||
public class Site
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace WebApplication.Models.Forms
|
||||
{
|
||||
public class UserReference
|
||||
{
|
||||
public string DisplayName { get; set; }
|
||||
[Required(ErrorMessage = "You must select a user")]
|
||||
public string UserId { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
[Obsolete("Property has been renamed to UserId and now contains either the UPN or the ObjectId depending on available claims", true)]
|
||||
public string UserPrincipalName { get => UserId; set => UserId = value; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
|
||||
namespace WebApplication.Models.ValidationAttributes
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
|
||||
public class MustBeTrue : ValidationAttribute
|
||||
{
|
||||
public override string FormatErrorMessage(string name)
|
||||
{
|
||||
return "You cannot publish a dataset without answering this question with a 'yes'";
|
||||
}
|
||||
|
||||
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
|
||||
}
|
||||
|
||||
var boolValue = value as bool?;
|
||||
if (boolValue != null && boolValue == true)
|
||||
{
|
||||
return ValidationResult.Success;
|
||||
}
|
||||
|
||||
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.DataAnnotations;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace WebApplication.Models.ValidationAttributes
|
||||
{
|
||||
public class MustBeTrueAdapter : AttributeAdapterBase<MustBeTrue>
|
||||
{
|
||||
public MustBeTrueAdapter(MustBeTrue attribute, IStringLocalizer stringLocalizer)
|
||||
: base(attribute, stringLocalizer)
|
||||
{
|
||||
}
|
||||
|
||||
public override void AddValidation(ClientModelValidationContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
MergeAttribute(context.Attributes, "data-val", "true");
|
||||
MergeAttribute(context.Attributes, "data-val-must-be-true", GetErrorMessage(context));
|
||||
}
|
||||
|
||||
public override string GetErrorMessage(ModelValidationContextBase validationContext)
|
||||
{
|
||||
if (validationContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(validationContext));
|
||||
}
|
||||
|
||||
return GetErrorMessage(validationContext.ModelMetadata, validationContext.ModelMetadata.GetDisplayName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.DataAnnotations;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace WebApplication.Models.ValidationAttributes
|
||||
{
|
||||
public class ValidationAdapterProvider : IValidationAttributeAdapterProvider
|
||||
{
|
||||
private readonly IValidationAttributeAdapterProvider _baseProvider = new ValidationAttributeAdapterProvider();
|
||||
|
||||
public IAttributeAdapter GetAttributeAdapter(ValidationAttribute attribute, IStringLocalizer stringLocalizer)
|
||||
{
|
||||
if (attribute is MustBeTrue)
|
||||
{
|
||||
return new MustBeTrueAdapter(attribute as MustBeTrue, stringLocalizer);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _baseProvider.GetAttributeAdapter(attribute, stringLocalizer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace WebApplication.Models.Wizards
|
||||
{
|
||||
public partial class ExternalFileUpload
|
||||
{
|
||||
public Int64 UploadSystemId { get; set; }
|
||||
public Int64 EmailSystemId { get; set; }
|
||||
public Int64 TargetSystemId { get; set; }
|
||||
public Int64 ScheduleMasterId { get; set; }
|
||||
public Int64 DataFactoryId { get; set; }
|
||||
public Int64 TaskGroupId { get; set; }
|
||||
public Int64 TaskTypeId { get; set; }
|
||||
|
||||
public string UploadFileName { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public bool AllowMultipleActiveInstances { get; set; }
|
||||
|
||||
public string ExternalParties { get; set; } // is this info relevant or should be stored?
|
||||
|
||||
[Required(ErrorMessage = "Name is required")]
|
||||
public string TaskMasterName { get; set; }
|
||||
|
||||
[Required]
|
||||
public string TaskDatafactoryIr { get; set; }
|
||||
public string DependencyChainTag { get; set; }
|
||||
public int DegreeOfCopyParallelism { get; set; }
|
||||
|
||||
public string EmailTemplateFileName { get; set; }
|
||||
[Display(Name = "Email Address")]
|
||||
[Required(ErrorMessage = "Email is required.")]
|
||||
[EmailAddress(ErrorMessage = "Invalid Email Address")]
|
||||
public string Email { get; set; }
|
||||
[Display(Name = "Email Address")]
|
||||
[Required(ErrorMessage = "Email is required.")]
|
||||
[EmailAddress(ErrorMessage = "Invalid Email Address")]
|
||||
public string OperatorEmail { get; set; }
|
||||
public string OperatorName { get; set; }
|
||||
public string RelativePath { get; set; }
|
||||
public string FileUploaderWebAppURL { get; set; }
|
||||
public string EmailSubject { get; set; }
|
||||
public string TargetSystemUidInPHI { get; set; }
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,467 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using WebApplication.Extensions;
|
||||
using WebApplication.Forms;
|
||||
using WebApplication.Forms.PIAWizard;
|
||||
using WebApplication.Framework;
|
||||
using WebApplication.Models.Forms;
|
||||
using WebApplication.Models.ValidationAttributes;
|
||||
|
||||
namespace WebApplication.Models.Wizards
|
||||
{
|
||||
public partial class PIAWizardViewModel : IValidatableObject
|
||||
{
|
||||
public int SubjectAreaId { get; set; } // Database row id
|
||||
public int Step { get; set; } // Last submitted step
|
||||
public int MaxStep { get; set; } // Furthest page reached in wizard
|
||||
public int SubmissionMethod { get; set; } // 0: Save-only, 1: Next
|
||||
public string BelongingDataset { get; set; }
|
||||
public string BelongingDatasetCode { get; set; } // subject area code
|
||||
|
||||
[Display(Name = "Is this data set private or public?")]
|
||||
[Required]
|
||||
public PublicPrivateDataSet DataSetPrivacyStatus { get; set; }
|
||||
|
||||
#region Introduction
|
||||
|
||||
[Display(Name = "1. Are you authorised to use this data?")]
|
||||
[Required]
|
||||
[MustBeTrue]
|
||||
public bool AuthorisedByDataSubjects { get; set; }
|
||||
|
||||
[Display(Name = "2. Have the appropriate data agreements been signed?")]
|
||||
[Required]
|
||||
[MustBeTrue]
|
||||
public bool DataAgreementsSigned { get; set; }
|
||||
|
||||
[Display(Name = "2a. Where are these data agreements stored? Provide a location (i.e. file path)")]
|
||||
[Required]
|
||||
public string DataAgreementsLocation { get; set; }
|
||||
|
||||
[Display(Name = "3. Do you understand that the metadata associated with this data set will be searchable?")]
|
||||
[Required]
|
||||
[MustBeTrue]
|
||||
public bool UnderstandMetadata { get; set; }
|
||||
|
||||
[Display(Name = "4. Do you accept responsibility for this PIA process and the truthfulness of the responses?")]
|
||||
[Required]
|
||||
[MustBeTrue]
|
||||
public bool AcceptResponsibility { get; set; }
|
||||
|
||||
[Display(Name = "5. Provide a description of the data set being referred to")]
|
||||
[Required(ErrorMessage = "This field is required")]
|
||||
public string DataSetDescription { get; set; }
|
||||
|
||||
// dropdown for all phns
|
||||
[Display(Name = "6. To which PHN does the Dataset belong?")]
|
||||
public Guid? PhnId { get; set; }
|
||||
|
||||
[Display(Name = "6a. Please provide the source of the data from where it was captured?")]
|
||||
[Required(ErrorMessage = "This field is required")]
|
||||
public string DataSourceLocation { get; set; }
|
||||
|
||||
// dropdown of all extraction tools
|
||||
[Display(Name = "7. Under what storage system is the data set being taken from?")]
|
||||
[Required(ErrorMessage = "This field is required")]
|
||||
public int? OwningSystem { get; set; } // source system
|
||||
|
||||
[Display(Name = "7a. What is the specific location from within the system where the data is obtained?")]
|
||||
[Required(ErrorMessage = "This field is required")]
|
||||
public string SystemDataLocation { get; set; }
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 4 fields - first 2 are auto generated based on previous 2 (5. and 6.)
|
||||
/// When parsing data from model, combine the names of 5. + 6. + 7. + version number
|
||||
/// joined together using an underscore as a separator, to a single value "System purpose version"
|
||||
/// </summary>
|
||||
[Display(Name = "8. PHN_System_Purpose_Version E.g. WAPHA_PATCAT_GP_V0.3")]
|
||||
public string SystemPurposeVersion { get; set; }
|
||||
public string PhnName { get; set; }
|
||||
[Required(ErrorMessage = "This field is required")]
|
||||
public string SourceSystemName { get; set; }
|
||||
[Required(ErrorMessage = "This field is required")]
|
||||
public string SystemPurposeVersionNumber { get; set; }
|
||||
|
||||
public Site Site { get; set; } = new Site();
|
||||
public string SystemPurposeVersionFullName => $"{ Site.Name }_{ SourceSystemName }_{ SystemPurposeVersion }_{ SystemPurposeVersionNumber }";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Data subjects
|
||||
|
||||
[Display(Name = "9. Data Owner")]
|
||||
[Required]
|
||||
public UserReference DataOwner { get; set; } = new UserReference();
|
||||
|
||||
[Display(Name = "10. Data Custodian")] // only 1
|
||||
[Required]
|
||||
public UserReference DataCustodian { get; set; } = new UserReference();
|
||||
|
||||
[Display(Name = "11. Data Stewards")] // can have multiple
|
||||
public IList<UserReference> DataStewards { get; set; } = new List<UserReference>() { new UserReference() };
|
||||
|
||||
[Display(Name = "External Users with Reader Access")]
|
||||
public IList<UserReference> ExternalUsers { get; set; } = new List<UserReference>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Understand your legal responsibility
|
||||
|
||||
[Display(Name = "12. Provide the location where additional documentation can be found for this data such as a link to a document library or a reference to a location in a document management system")]
|
||||
[Required(ErrorMessage = "This field is required")]
|
||||
public string SupportingDocumentationLocation { get; set; }
|
||||
|
||||
|
||||
[Display(Name = "13. Is the data personal information or de-identified data?")]
|
||||
[Required(ErrorMessage = "This field is required")]
|
||||
public PersonalDeIdentified DataPersonalOrDeIdentified { get; set; }
|
||||
|
||||
[Display(Name = "14. Suppress both numerator and denominator if either or both of these in a row contain a number greater than 10 (to prevent back calculation), but don’t suppress the proportion")]
|
||||
public SuppressionRuleOptions SuppressionRuleOne { get; set; }
|
||||
|
||||
[Display(Name = "Explanation:")]
|
||||
[StringLength(400)]
|
||||
public string SuppressionRuleOneExpl { get; set; }
|
||||
|
||||
[Display(Name = "15. If and when the count of a particular cell is less than 10, is the suppression only applied to that row?")]
|
||||
public SuppressionRuleOptions SuppressionRuleTwo { get; set; }
|
||||
|
||||
[Display(Name = "Explanation:")]
|
||||
[StringLength(400)]
|
||||
public string SuppressionRuleTwoExpl { get; set; }
|
||||
|
||||
[Display(Name = "16. Are precautionary measures taken for at-risk populations by aggregating age groups (i.e. age groupings 0 – 4 and or 5 -14 are aggregated into < 15 years) and gender – merging (Indeterminate/Intersex/Unspecified/Not stated/Inadequately described) to Sex X?")]
|
||||
public SuppressionRuleOptions SuppressionRuleThree { get; set; }
|
||||
|
||||
[Display(Name = "Explanation:")]
|
||||
[StringLength(400)]
|
||||
public string SuppressionRuleThreeExpl { get; set; }
|
||||
|
||||
[Display(Name = "17. If you find zeros for Sex X (Indeterminate/Intersex/Unspecified/Not stated/Inadequately described) for any QIM, please merge all age groups and Indigenous status for Sex X to see if there is any valid number (again rule 1 and 2 applies)")]
|
||||
public SuppressionRuleOptions SuppressionRuleFour { get; set; }
|
||||
|
||||
[Display(Name = "Explanation:")]
|
||||
[StringLength(400)]
|
||||
public string SuppressionRuleFourExpl { get; set; }
|
||||
|
||||
[Display(Name = "18. Do privacy obligations still apply to de-identified data? Have you identified and documented how the data custodian should manage such a risk, while still using de-identified data in the ways that it is legally permitted to use it?")]
|
||||
public bool IdentifiedDataCustodianManagedRisk { get; set; }
|
||||
[Display(Name = "18a. How will risks be managed?")]
|
||||
[StringLength(400)]
|
||||
public string DataRiskManagementExplanation { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Data Metadata
|
||||
|
||||
[Display(Name = "19. What form is your data in?")]
|
||||
[Required(ErrorMessage = "This field is required")]
|
||||
public FormOfData DataForm { get; set; }
|
||||
|
||||
[Display(Name = "20. What level of information about the population does the data provide?")]
|
||||
[Required(ErrorMessage = "This field is required")]
|
||||
public InformationLevelData InfoLevelOfData { get; set; }
|
||||
|
||||
[Display(Name = "21. What level of information about the population does the data provide?")]
|
||||
[Required(ErrorMessage = "This field is required")]
|
||||
public InformationLevelVariable InfoLevelOfVariable { get; set; }
|
||||
|
||||
[Display(Name = "22. For the fields listed, what are the main subject areas the data set contains (Select all the apply)?")] // change validation
|
||||
public InputSelectionOptionsFor<Properties> TopThreePropsOfData { get; set; } = new InputSelectionOptionsFor<Properties>();
|
||||
|
||||
[Display(Name = "23. Is the data quality high?")]
|
||||
[Required(ErrorMessage = "This field is required")]
|
||||
public bool IsDataQualityLevelHigh { get; set; }
|
||||
[Display(Name = "Explanation")]
|
||||
[StringLength(400)]
|
||||
public string DataQualityExplanation { get; set; }
|
||||
|
||||
[Display(Name = "24. Is the data about a vulnerable population?")]
|
||||
[Required(ErrorMessage = "This field is required")]
|
||||
public bool IsDataAboutVulnerablePopulation { get; set; }
|
||||
[Display(Name = "Explanation")]
|
||||
[StringLength(400)]
|
||||
public string PopulationVulnerabilityExplanation { get; set; }
|
||||
|
||||
[Display(Name = "25. When was the data collected?")]
|
||||
[DataType(DataType.Date)]
|
||||
[Required(ErrorMessage = "This field is required")]
|
||||
public DateTime? WhenDataWasCollected { get; set; }
|
||||
[Display(Name = "Explanation")]
|
||||
[StringLength(400)]
|
||||
public string DataTimeCollectedExplanation { get; set; }
|
||||
|
||||
[Display(Name = "26. Is the data hierarchical in nature?")]
|
||||
[Required(ErrorMessage = "This field is required")]
|
||||
public bool IsDataHierarchical { get; set; }
|
||||
[Display(Name = "Explanation")]
|
||||
[StringLength(400)]
|
||||
public string DataHierarchicalExplanation { get; set; }
|
||||
|
||||
[Display(Name = "27. Is the data time-stamped?")]
|
||||
[Required(ErrorMessage = "This field is required")]
|
||||
public bool IsDataTimeStamped { get; set; }
|
||||
|
||||
[Display(Name = "Explanation")]
|
||||
[StringLength(400)]
|
||||
public string DataTimestampedExplanation { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Meet your ethical obligations
|
||||
|
||||
[Display(Name = "28. Has the appropriate consent been obtained for the data set keeping in mind its intended purpose once it has been de-identified?")]
|
||||
[Required(ErrorMessage = "This field is required")]
|
||||
public BinaryOrNa RespectDataAssurances { get; set; }
|
||||
[Display(Name = "28a. Provide an explanation")]
|
||||
[StringLength(400)]
|
||||
public string DataAssuranceExplanation { get; set; }
|
||||
[Display(Name = "29. Has the appropriate level of transparency been provided on how the data can be reused?")]
|
||||
[Required(ErrorMessage = "This field is required")]
|
||||
public BinaryOrNa HasProvidedTransparency { get; set; }
|
||||
[Display(Name = "29a. Give a description of your rationale")]
|
||||
[StringLength(400)]
|
||||
public string ProvidedTransparencyExplanation { get; set; }
|
||||
[Display(Name = "30. Have the appropriate data sharing agreements or release activies been considered?")]
|
||||
[Required(ErrorMessage = "This field is required")]
|
||||
public BinaryOrNa HasObtainedSubjectsViews { get; set; }
|
||||
[Display(Name = "30a. Have all concerns been adequately addressed?")]
|
||||
[StringLength(400)]
|
||||
public string SubjectsViewsExplanation { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Identify the processes you will need to go through
|
||||
|
||||
[Display(Name = "31. Have you assessed the data set to an inital risk specification?")]
|
||||
public bool HasInitialSpecification { get; set; }
|
||||
[Display(Name = "32. If yes, what actions have you undertaken to reduce the level of risk? Select all that apply and provide an explanation")]
|
||||
public InputSelectionOptionsFor<Specifications> TopLevelAssessment { get; set; } = new InputSelectionOptionsFor<Specifications>();
|
||||
|
||||
[Display(Name = "32a. Explanation")]
|
||||
[StringLength(400)]
|
||||
public string DataComplexityExpl { get; set; }
|
||||
[Display(Name = "32b. Explanation")]
|
||||
[StringLength(400)]
|
||||
public string SensitiveVariableExpl { get; set; }
|
||||
[Display(Name = "32c. Explanation")]
|
||||
[StringLength(400)]
|
||||
public string DetailedVariableExpl { get; set; }
|
||||
|
||||
[Display(Name = "33. Review the data set and establish relevant plausible scenarios for your data situation. In mitigating a potential data breach situation consider the who, how, and why in your scenario planning. Explain? ")]
|
||||
public string AnalysisExplanation { get; set; }
|
||||
|
||||
[Display(Name = "34. Data Analytical approaches considered:")]
|
||||
public InputSelectionOptionsFor<Properties> DataAnalyticalApproaches { get; set; } = new InputSelectionOptionsFor<Properties>();
|
||||
#endregion
|
||||
|
||||
#region Identify the disclosure control processes that are relevant to your data situation
|
||||
|
||||
[Display(Name = "35. If you need to reconfigure the environment, how would you do that?")]
|
||||
public InputSelectionOptionsFor<EnvironmentReconfigurations> ReconfigureEnvirontment { get; set; } = new InputSelectionOptionsFor<EnvironmentReconfigurations>();
|
||||
|
||||
[Display(Name = "Explanation:")]
|
||||
[StringLength(400)]
|
||||
public string ExplanationReconfigureEnvirontment { get; set; }
|
||||
|
||||
[Display(Name = "36. If you need to modify the data, how would you do that?")]
|
||||
public InputSelectionOptionsFor<DataModification> ModifyingData { get; set; } = new InputSelectionOptionsFor<DataModification>();
|
||||
|
||||
[Display(Name = "Explanation:")]
|
||||
[StringLength(400)]
|
||||
public string ExplanationModifyingDataOptions { get; set; }
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Impact management puts in place a plan for reducing the impact of such an event should it happen
|
||||
|
||||
[Display(Name = "37. How would you manage the impact of a disclosure (related to Notifiable Data Breach) if you and your stakeholders have developed a good working relationship. What have you done to reduce the likelihood to manage this?")]
|
||||
public InputSelectionOptionsFor<DataBreachImpactManagement> ImpactManagementReductionOptions { get; set; } = new InputSelectionOptionsFor<DataBreachImpactManagement>();
|
||||
|
||||
[Display(Name = "Explanation")]
|
||||
public string ImpactManagementExplanation { get; set; }
|
||||
|
||||
[Display(Name = "38. Identify who your stakeholders are and what assurances you provided them with in terms of the use and protection of the data")]
|
||||
public InputSelectionOptionsFor<StakeholderAssuranceProtection> StakeholdersAssurance { get; set; } = new InputSelectionOptionsFor<StakeholderAssuranceProtection>();
|
||||
|
||||
[Display(Name = "Explanation")]
|
||||
public string StakeholderAssuranceExplanation { get; set; }
|
||||
|
||||
[Display(Name = "39. Document the process you would follow in the event of a possible breach.")]
|
||||
public InputSelectionOptionsFor<DataBreachPlan> PlanForDataBreachProcess { get; set; } = new InputSelectionOptionsFor<DataBreachPlan>();
|
||||
|
||||
[Display(Name = "Explanation")]
|
||||
[StringLength(400)]
|
||||
public string DataBreachPlanExplanation { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
public PIAForm ToForm()
|
||||
{
|
||||
return new PIAForm()
|
||||
{
|
||||
AcceptResponsibility = this.AcceptResponsibility,
|
||||
AnalysisExplanation = this.AnalysisExplanation,
|
||||
AuthorisedByDataSubjects = this.AuthorisedByDataSubjects,
|
||||
BelongingDataset = this.BelongingDataset,
|
||||
BelongingDatasetCode = this.BelongingDatasetCode,
|
||||
DataAgreementsSigned = this.DataAgreementsSigned,
|
||||
DataAgreementsLocation = this.DataAgreementsLocation,
|
||||
DataAnalyticalApproaches = this.DataAnalyticalApproaches.GetSelections(),
|
||||
DataAssuranceExplanation = this.DataAssuranceExplanation,
|
||||
DataBreachPlanExplanation = this.DataBreachPlanExplanation,
|
||||
DataComplexityExpl = this.DataComplexityExpl,
|
||||
DataCustodian = this.DataCustodian,
|
||||
DataForm = this.DataForm,
|
||||
DataHierarchicalExplanation = this.DataHierarchicalExplanation,
|
||||
DataOwner = this.DataOwner,
|
||||
DataPersonalOrDeIdentified = this.DataPersonalOrDeIdentified,
|
||||
DataQualityExplanation = this.DataQualityExplanation,
|
||||
DataRiskManagementExplanation = this.DataRiskManagementExplanation,
|
||||
DataSetDescription = this.DataSetDescription,
|
||||
DataSetPrivacyStatus = this.DataSetPrivacyStatus,
|
||||
DataSourceLocation = this.DataSourceLocation,
|
||||
DataStewards = this.DataStewards.ToList(),
|
||||
DataTimeCollectedExplanation = this.DataTimeCollectedExplanation,
|
||||
DataTimestampedExplanation = this.DataTimestampedExplanation,
|
||||
DetailedVariableExpl = this.DetailedVariableExpl,
|
||||
ExplanationModifyingDataOptions = this.ExplanationModifyingDataOptions,
|
||||
ExplanationReconfigureEnvirontment = this.ExplanationReconfigureEnvirontment,
|
||||
ExternalUsers = this.ExternalUsers.ToList(),
|
||||
HasInitialSpecification = this.HasInitialSpecification,
|
||||
HasObtainedSubjectsViews = this.HasObtainedSubjectsViews,
|
||||
HasProvidedTransparency = this.HasProvidedTransparency,
|
||||
IdentifiedDataCustodianManagedRisk = this.IdentifiedDataCustodianManagedRisk,
|
||||
ImpactManagementExplanation = this.ImpactManagementExplanation,
|
||||
ImpactManagementReductionOptions = ImpactManagementReductionOptions.GetSelections(),
|
||||
InfoLevelOfData = InfoLevelOfData,
|
||||
InfoLevelOfVariable = InfoLevelOfVariable,
|
||||
IsDataAboutVulnerablePopulation = IsDataAboutVulnerablePopulation,
|
||||
WhenDataWasCollected = WhenDataWasCollected,
|
||||
IsDataHierarchical = IsDataHierarchical,
|
||||
IsDataQualityLevelHigh = IsDataQualityLevelHigh,
|
||||
IsDataTimeStamped = IsDataTimeStamped,
|
||||
MaxStep = MaxStep,
|
||||
ModifyingData = ModifyingData.GetSelections(),
|
||||
OwningSystem = OwningSystem,
|
||||
PlanForDataBreachProcess = PlanForDataBreachProcess.GetSelections(),
|
||||
ReconfigureEnvironment = ReconfigureEnvirontment.GetSelections(),
|
||||
RespectDataAssurances = RespectDataAssurances,
|
||||
SensitiveVariableExpl = this.SensitiveVariableExpl,
|
||||
StakeholdersAssurance = StakeholdersAssurance.GetSelections(),
|
||||
Step = Step,
|
||||
SubjectAreaId = SubjectAreaId,
|
||||
PhnId = this.Site.Id,
|
||||
PhnName = this.Site.Name,
|
||||
ProvidedTransparencyExplanation = this.ProvidedTransparencyExplanation,
|
||||
PopulationVulnerabilityExplanation = this.PopulationVulnerabilityExplanation,
|
||||
StakeholderAssuranceExplanation = this.StakeholderAssuranceExplanation,
|
||||
SubmissionMethod = SubmissionMethod,
|
||||
SubjectsViewsExplanation = this.SubjectsViewsExplanation,
|
||||
SupportingDocumentationLocation = this.SupportingDocumentationLocation,
|
||||
SuppressionRuleFour = SuppressionRuleFour,
|
||||
SuppressionRuleFourExpl = SuppressionRuleFourExpl,
|
||||
SuppressionRuleOne = SuppressionRuleOne,
|
||||
SuppressionRuleOneExpl = SuppressionRuleOneExpl,
|
||||
SuppressionRuleThree = SuppressionRuleThree,
|
||||
SuppressionRuleThreeExpl = SuppressionRuleThreeExpl,
|
||||
SuppressionRuleTwo = SuppressionRuleTwo,
|
||||
SuppressionRuleTwoExpl = SuppressionRuleTwoExpl,
|
||||
SystemDataLocation = this.SystemDataLocation,
|
||||
SystemPurposeVersion = SystemPurposeVersion,
|
||||
SystemPurposeVersionNumber = SystemPurposeVersionNumber,
|
||||
SourceSystemName = SourceSystemName,
|
||||
TopLevelAssessment = TopLevelAssessment.GetSelections(),
|
||||
TopThreePropsOfData = TopThreePropsOfData.GetSelections(),
|
||||
UnderstandMetadata = UnderstandMetadata,
|
||||
};
|
||||
}
|
||||
|
||||
public static PIAWizardViewModel FromForm(PIAForm form)
|
||||
{
|
||||
return new PIAWizardViewModel()
|
||||
{
|
||||
AcceptResponsibility = form.AcceptResponsibility,
|
||||
AnalysisExplanation = form.AnalysisExplanation,
|
||||
AuthorisedByDataSubjects = form.AuthorisedByDataSubjects,
|
||||
BelongingDataset = form.BelongingDataset,
|
||||
BelongingDatasetCode = form.BelongingDatasetCode,
|
||||
DataAgreementsSigned = form.DataAgreementsSigned,
|
||||
DataAgreementsLocation = form.DataAgreementsLocation,
|
||||
DataAnalyticalApproaches = form.DataAnalyticalApproaches.AsInputSelectionOptions(),
|
||||
DataAssuranceExplanation = form.DataAssuranceExplanation,
|
||||
DataBreachPlanExplanation = form.DataBreachPlanExplanation,
|
||||
DataComplexityExpl = form.DataComplexityExpl,
|
||||
DataCustodian = form.DataCustodian,
|
||||
DataForm = form.DataForm,
|
||||
DataHierarchicalExplanation = form.DataHierarchicalExplanation,
|
||||
DataOwner = form.DataOwner,
|
||||
DataPersonalOrDeIdentified = form.DataPersonalOrDeIdentified,
|
||||
DataQualityExplanation = form.DataQualityExplanation,
|
||||
DataRiskManagementExplanation = form.DataRiskManagementExplanation,
|
||||
DataSetDescription = form.DataSetDescription,
|
||||
DataSetPrivacyStatus = form.DataSetPrivacyStatus,
|
||||
DataSourceLocation = form.DataSourceLocation,
|
||||
DataStewards = form.DataStewards.ToList(),
|
||||
DataTimeCollectedExplanation = form.DataTimeCollectedExplanation,
|
||||
DataTimestampedExplanation = form.DataTimestampedExplanation,
|
||||
DetailedVariableExpl = form.DetailedVariableExpl,
|
||||
ExplanationModifyingDataOptions = form.ExplanationModifyingDataOptions,
|
||||
ExplanationReconfigureEnvirontment = form.ExplanationReconfigureEnvirontment,
|
||||
ExternalUsers = form.ExternalUsers.ToList(),
|
||||
HasInitialSpecification = form.HasInitialSpecification,
|
||||
HasObtainedSubjectsViews = form.HasObtainedSubjectsViews,
|
||||
HasProvidedTransparency = form.HasProvidedTransparency,
|
||||
IdentifiedDataCustodianManagedRisk = form.IdentifiedDataCustodianManagedRisk,
|
||||
ImpactManagementExplanation = form.ImpactManagementExplanation,
|
||||
ImpactManagementReductionOptions = form.ImpactManagementReductionOptions.AsInputSelectionOptions(),
|
||||
InfoLevelOfData = form.InfoLevelOfData,
|
||||
InfoLevelOfVariable = form.InfoLevelOfVariable,
|
||||
IsDataAboutVulnerablePopulation = form.IsDataAboutVulnerablePopulation,
|
||||
WhenDataWasCollected = form.WhenDataWasCollected,
|
||||
IsDataHierarchical = form.IsDataHierarchical,
|
||||
IsDataQualityLevelHigh = form.IsDataQualityLevelHigh,
|
||||
IsDataTimeStamped = form.IsDataTimeStamped,
|
||||
MaxStep = form.MaxStep,
|
||||
ModifyingData = form.ModifyingData.AsInputSelectionOptions(),
|
||||
OwningSystem = form.OwningSystem,
|
||||
PlanForDataBreachProcess = form.PlanForDataBreachProcess.AsInputSelectionOptions(),
|
||||
ReconfigureEnvirontment = form.ReconfigureEnvironment.AsInputSelectionOptions(),
|
||||
RespectDataAssurances = form.RespectDataAssurances,
|
||||
StakeholdersAssurance = form.StakeholdersAssurance.AsInputSelectionOptions(),
|
||||
Step = form.Step,
|
||||
SubjectAreaId = form.SubjectAreaId,
|
||||
PhnId = form.PhnId,
|
||||
PhnName = form.PhnName,
|
||||
Site = new Site() {Id = form.PhnId.GetValueOrDefault(), Name = form.PhnName},
|
||||
ProvidedTransparencyExplanation = form.ProvidedTransparencyExplanation,
|
||||
PopulationVulnerabilityExplanation = form.PopulationVulnerabilityExplanation,
|
||||
SensitiveVariableExpl = form.SensitiveVariableExpl,
|
||||
StakeholderAssuranceExplanation = form.StakeholderAssuranceExplanation,
|
||||
SubmissionMethod = form.SubmissionMethod,
|
||||
SubjectsViewsExplanation = form.SubjectsViewsExplanation,
|
||||
SupportingDocumentationLocation = form.SupportingDocumentationLocation,
|
||||
SuppressionRuleFour = form.SuppressionRuleFour,
|
||||
SuppressionRuleFourExpl = form.SuppressionRuleFourExpl,
|
||||
SuppressionRuleOne = form.SuppressionRuleOne,
|
||||
SuppressionRuleOneExpl = form.SuppressionRuleOneExpl,
|
||||
SuppressionRuleThree = form.SuppressionRuleThree,
|
||||
SuppressionRuleThreeExpl = form.SuppressionRuleThreeExpl,
|
||||
SuppressionRuleTwo = form.SuppressionRuleTwo,
|
||||
SuppressionRuleTwoExpl = form.SuppressionRuleTwoExpl,
|
||||
SystemDataLocation = form.SystemDataLocation,
|
||||
SystemPurposeVersion = form.SystemPurposeVersion,
|
||||
SystemPurposeVersionNumber = form.SystemPurposeVersionNumber,
|
||||
SourceSystemName = form.SourceSystemName,
|
||||
TopLevelAssessment = form.TopLevelAssessment.AsInputSelectionOptions(),
|
||||
TopThreePropsOfData = form.TopThreePropsOfData.AsInputSelectionOptions(),
|
||||
UnderstandMetadata = form.UnderstandMetadata,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WebApplication.Models.Wizards
|
||||
{
|
||||
public partial class PIAWizardViewModel : IValidatableObject
|
||||
{
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
if (!IsCompliantWithDataAgreements())
|
||||
{
|
||||
yield return new ValidationResult("You must answer questions 1 to 4 with \"yes\"");
|
||||
}
|
||||
|
||||
if (DataOwner.UserId == DataCustodian.UserId)
|
||||
{
|
||||
yield return new ValidationResult("Question 9. Data Owner and Custodian should be different people");
|
||||
}
|
||||
|
||||
if (StewardIsAlreadyOwnerOrCustodian())
|
||||
{
|
||||
yield return new ValidationResult("Question 10. A Data Steward cannot already be the owner or custodian");
|
||||
}
|
||||
|
||||
if (DataStewards.Count >= 2)
|
||||
{
|
||||
if (HasDuplicatedStewardNames())
|
||||
{
|
||||
yield return new ValidationResult("Question 11. Each steward should be unique");
|
||||
}
|
||||
}
|
||||
|
||||
yield return ValidationResult.Success;
|
||||
}
|
||||
|
||||
private bool HasDuplicatedStewardNames()
|
||||
{
|
||||
return DataStewards
|
||||
.GroupBy(ds => ds.UserId)
|
||||
.Any(g => g.Count() > 1);
|
||||
}
|
||||
|
||||
private bool StewardIsAlreadyOwnerOrCustodian()
|
||||
{
|
||||
return DataStewards
|
||||
.Any(ds => ds.UserId == DataOwner.UserId ||
|
||||
ds.UserId == DataCustodian.UserId);
|
||||
}
|
||||
|
||||
private bool IsCompliantWithDataAgreements()
|
||||
{
|
||||
return AuthorisedByDataSubjects && DataAgreementsSigned && UnderstandMetadata
|
||||
&& AcceptResponsibility;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using SharedZone.Client.Model;
|
||||
|
||||
namespace WebApplication.Services
|
||||
{
|
||||
public class EmailService : IEmailService
|
||||
{
|
||||
private readonly IMicrosoftGraphService _graphService;
|
||||
private readonly ISharedZoneService _sharedZoneService;
|
||||
|
||||
public EmailService(IMicrosoftGraphService graphService, ISharedZoneService sharedZoneService)
|
||||
{
|
||||
_graphService = graphService;
|
||||
_sharedZoneService = sharedZoneService;
|
||||
}
|
||||
|
||||
public async Task SendDatasetAccessApprovalNotification(string requestor, SubjectArea subjectArea)
|
||||
{
|
||||
var sendToEmail = await _graphService.GetUserMailIdentity(requestor);
|
||||
await _sharedZoneService.PostEmailToQueue(Guid.NewGuid().ToString(), default, sendToEmail, "DataSetAccessRequestAccepted", new
|
||||
{
|
||||
RequestorName = sendToEmail.name,
|
||||
SubjectAreaName = subjectArea.SubjectAreaName,
|
||||
});
|
||||
}
|
||||
|
||||
public async Task SendDatasetAccessRejectionNotification(string requestor, SubjectArea subjectArea)
|
||||
{
|
||||
var sendToEmail = await _graphService.GetUserMailIdentity(requestor);
|
||||
await _sharedZoneService.PostEmailToQueue(Guid.NewGuid().ToString(), default, sendToEmail, "DataSetAccessRequestRejected", new
|
||||
{
|
||||
RequestorName = sendToEmail.name,
|
||||
SubjectAreaName = subjectArea.SubjectAreaName,
|
||||
});
|
||||
}
|
||||
|
||||
public async Task SendDatasetAccessRequestNotification(string recipient, string requestor, SubjectArea subjectArea)
|
||||
{
|
||||
var sendToEmail = await _graphService.GetUserMailIdentity(recipient);
|
||||
var requestingUser = await _graphService.GetUserMailIdentity(requestor);
|
||||
|
||||
await _sharedZoneService.PostEmailToQueue(Guid.NewGuid().ToString(), default, sendToEmail, "DataSetAccessRequestCreated", new
|
||||
{
|
||||
RequestorName = requestingUser.name,
|
||||
RecipientName = sendToEmail.name,
|
||||
dataSetName = subjectArea.SubjectAreaName
|
||||
});
|
||||
}
|
||||
|
||||
public async Task SendNotificationToPHNUsers(string owner, string custodian, string[] stewards)
|
||||
{
|
||||
//todo: this doesnt' do anything, but where it's being called doesn't actually calculate the delta
|
||||
//we should change this to send out the email when syncing teh AD groups - so if you are added to the AD group you
|
||||
//get an email.
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -28,8 +28,8 @@ namespace WebApplication.Services
|
|||
return await LoadSubjectAreaRoles(x.SubjectAreaId, groups);
|
||||
case SubjectAreaForm x:
|
||||
return await LoadSubjectAreaFormRoles(x.SubjectAreaFormId, groups);
|
||||
case TaskGroup x:
|
||||
return await LoadSubjectAreaRoles(x.SubjectAreaId, groups);
|
||||
case TaskGroup x when x.SubjectAreaId.HasValue:
|
||||
return await LoadSubjectAreaRoles(x.SubjectAreaId.Value, groups);
|
||||
case TaskInstance x:
|
||||
return await LoadTaskInstanceRoles(x.TaskInstanceId, groups);
|
||||
case TaskInstanceExecution x:
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
using System.Threading.Tasks;
|
||||
using SharedZone.Client.Model;
|
||||
|
||||
namespace WebApplication.Services
|
||||
{
|
||||
public interface IEmailService
|
||||
{
|
||||
Task SendDatasetAccessRejectionNotification(string requestor, SubjectArea subjectArea);
|
||||
Task SendDatasetAccessApprovalNotification(string requestor, SubjectArea subjectArea);
|
||||
Task SendDatasetAccessRequestNotification(string recipient, string requestor, SubjectArea subjectArea);
|
||||
Task SendNotificationToPHNUsers(string owner, string custodian, string[] stewards);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using WebApplication.Forms.PIAWizard;
|
||||
using WebApplication.Models;
|
||||
using WebApplication.Models.Forms;
|
||||
|
||||
namespace WebApplication.Services
|
||||
{
|
||||
public interface IMicrosoftGraphService
|
||||
{
|
||||
Task<IEnumerable<UserReference>> GetMembers();
|
||||
Task<(string name, string email)> GetUserMailIdentity(string upn);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Graph;
|
||||
using Microsoft.Graph.Auth;
|
||||
using Microsoft.Identity.Client;
|
||||
using Microsoft.Identity.Web;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using WebApplication.Forms.PIAWizard;
|
||||
using WebApplication.Models;
|
||||
using WebApplication.Models.Options;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace WebApplication.Services
|
||||
{
|
||||
public class MicrosoftGraphService : IMicrosoftGraphService
|
||||
{
|
||||
private readonly IOptions<MicrosoftIdentityOptions> _azureOptions;
|
||||
private readonly IOptions<SecurityModelOptions> _securityOptions;
|
||||
private GraphServiceClient _graphServiceClient;
|
||||
|
||||
public MicrosoftGraphService(IOptions<MicrosoftIdentityOptions> azureOptions, IOptions<SecurityModelOptions> securityOptions)
|
||||
{
|
||||
_azureOptions = azureOptions;
|
||||
_securityOptions = securityOptions;
|
||||
|
||||
IConfidentialClientApplication confidentialClient = ConfidentialClientApplicationBuilder
|
||||
.Create(_azureOptions.Value.ClientId)
|
||||
.WithTenantId(_azureOptions.Value.TenantId)
|
||||
.WithClientSecret(_azureOptions.Value.ClientSecret)
|
||||
.Build();
|
||||
|
||||
ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClient);
|
||||
_graphServiceClient = new GraphServiceClient(authProvider);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<UserReference>> GetMembers()
|
||||
{
|
||||
List<UserReference> users = new List<UserReference>();
|
||||
|
||||
Dictionary<string, User> members = new Dictionary<string, User>();
|
||||
|
||||
foreach (var secRole in _securityOptions.Value.SecurityRoles)
|
||||
{
|
||||
if (_securityOptions.Value.SecurityRoles.TryGetValue(secRole.Key, out var role) && !string.IsNullOrEmpty(role.SecurityGroupId))
|
||||
{
|
||||
var memberData = await _graphServiceClient.Groups[$"{ role.SecurityGroupId }"].Members?
|
||||
.Request()
|
||||
.GetAsync();
|
||||
|
||||
foreach (var member in memberData)
|
||||
{
|
||||
if (!members.ContainsKey(member.Id))
|
||||
{
|
||||
members.Add(member.Id, (User)member);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Out.WriteLine($"{ secRole.Key } security group missing from config");
|
||||
}
|
||||
}
|
||||
|
||||
users = members
|
||||
.Select(u => new UserReference
|
||||
{
|
||||
DisplayName = u.Value.DisplayName,
|
||||
UserId = u.Value.Id
|
||||
})
|
||||
.ToList();
|
||||
|
||||
return users;
|
||||
}
|
||||
|
||||
public async Task<(string name, string email)> GetUserMailIdentity(string upn)
|
||||
{
|
||||
var userData = await _graphServiceClient.Users[$"{ upn }"].Request().GetAsync();
|
||||
|
||||
if (userData == null)
|
||||
throw new Exception("User not found");
|
||||
|
||||
return (userData.DisplayName, userData.Mail);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Azure.Storage.Queues;
|
||||
using Azure.Storage.Queues.Specialized;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Identity.Client;
|
||||
using WebApplication.Models.Options;
|
||||
|
||||
namespace WebApplication.Services
|
||||
{
|
||||
public class ProcessingFunctionClient
|
||||
{
|
||||
private readonly IConfiguration _config;
|
||||
private readonly string QueueName = "subjectareaprocessing";
|
||||
|
||||
public ProcessingFunctionClient(HttpClient httpClient, IConfiguration config)
|
||||
{
|
||||
_config = config;
|
||||
|
||||
}
|
||||
|
||||
public async Task QueueSubjectAreaProvisioning(int subjectAreaId)
|
||||
{
|
||||
var connectionString = GetConnectionStringOrSetting(_config, "AzureWebJobsStorage");
|
||||
QueueClient queue = new QueueClient(connectionString, QueueName, new QueueClientOptions()
|
||||
{
|
||||
MessageEncoding = QueueMessageEncoding.Base64,
|
||||
});
|
||||
|
||||
await queue.CreateIfNotExistsAsync();
|
||||
await queue.SendMessageAsync(subjectAreaId.ToString());
|
||||
}
|
||||
|
||||
|
||||
//todo: figure out where this acctually lives it should be an extension method...
|
||||
string GetConnectionStringOrSetting(IConfiguration configuration, string connectionName) =>
|
||||
configuration.GetConnectionString(connectionName) ?? configuration[connectionName];
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -110,6 +110,10 @@ namespace WebApplication.Services
|
|||
//Next Get Role based on App Roles
|
||||
roles = GetUserRoles(identity);
|
||||
}
|
||||
|
||||
if (roles.Any() && !roles.Contains("Reader"))
|
||||
roles.Add("Reader");
|
||||
|
||||
return roles;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,11 @@
|
|||
<input asp-for="SubjectAreaName" class="form-control" />
|
||||
<span asp-validation-for="SubjectAreaName" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="ShortCode" class="control-label"></label>
|
||||
<input asp-for="ShortCode" class="form-control" />
|
||||
<span asp-validation-for="ShortCode" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group form-check">
|
||||
<label class="form-check-label">
|
||||
<input class="form-check-input" asp-for="ActiveYn" /> @Html.DisplayNameFor(model => model.ActiveYn)
|
||||
|
|
|
@ -12,41 +12,52 @@
|
|||
<h4> @("SubjectArea".Humanize(LetterCasing.Title))</h4>
|
||||
<hr />
|
||||
<dl class="row">
|
||||
<dt class = "col-sm-2">
|
||||
@Html.DisplayNameFor(model => model.SubjectAreaForm)
|
||||
</dt>
|
||||
<dd class = "col-sm-10">
|
||||
@Html.DisplayFor(model => model.SubjectAreaForm.SubjectAreaFormId)
|
||||
</dd>
|
||||
<dt class = "col-sm-2">
|
||||
@Html.DisplayNameFor(model => model.SubjectAreaName)
|
||||
</dt>
|
||||
<dd class = "col-sm-10">
|
||||
@Html.DisplayFor(model => model.SubjectAreaName)
|
||||
</dd>
|
||||
<dt class = "col-sm-2">
|
||||
@Html.DisplayNameFor(model => model.ActiveYn)
|
||||
</dt>
|
||||
<dd class = "col-sm-10">
|
||||
@Html.DisplayFor(model => model.ActiveYn)
|
||||
</dd>
|
||||
<dt class = "col-sm-2">
|
||||
@Html.DisplayNameFor(model => model.DefaultTargetSchema)
|
||||
</dt>
|
||||
<dd class = "col-sm-10">
|
||||
@Html.DisplayFor(model => model.DefaultTargetSchema)
|
||||
</dd>
|
||||
<dt class = "col-sm-2">
|
||||
@Html.DisplayNameFor(model => model.UpdatedBy)
|
||||
</dt>
|
||||
<dd class = "col-sm-10">
|
||||
@Html.DisplayFor(model => model.UpdatedBy)
|
||||
</dd>
|
||||
<dt class="col-sm-2">
|
||||
@Html.DisplayNameFor(model => model.SubjectAreaName)
|
||||
</dt>
|
||||
<dd class="col-sm-10">
|
||||
@Html.DisplayFor(model => model.SubjectAreaName)
|
||||
</dd>
|
||||
<dt class="col-sm-2">
|
||||
@Html.DisplayNameFor(model => model.ShortCode)
|
||||
</dt>
|
||||
<dd class="col-sm-10">
|
||||
@Html.DisplayFor(model => model.ShortCode)
|
||||
</dd>
|
||||
<dt class="col-sm-2">
|
||||
@Html.DisplayNameFor(model => model.SubjectAreaForm.FormStatusDisplay)
|
||||
</dt>
|
||||
<dd class="col-sm-10">
|
||||
@Html.DisplayFor(model => model.SubjectAreaForm.FormStatusDisplay)
|
||||
</dd>
|
||||
<dt class="col-sm-2">
|
||||
@Html.DisplayNameFor(model => model.ActiveYn)
|
||||
</dt>
|
||||
<dd class="col-sm-10">
|
||||
@Html.DisplayFor(model => model.ActiveYn)
|
||||
</dd>
|
||||
<dt class="col-sm-2">
|
||||
@Html.DisplayNameFor(model => model.DefaultTargetSchema)
|
||||
</dt>
|
||||
<dd class="col-sm-10">
|
||||
@Html.DisplayFor(model => model.DefaultTargetSchema)
|
||||
</dd>
|
||||
<dt class="col-sm-2">
|
||||
@Html.DisplayNameFor(model => model.UpdatedBy)
|
||||
</dt>
|
||||
<dd class="col-sm-10">
|
||||
@Html.DisplayFor(model => model.UpdatedBy)
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div>
|
||||
<a href="javascript:history.go(-1)" title="Back to List" class="btn btn-secondary"><i class="fa fa-arrow-circle-left"></i> Back</a>
|
||||
<a asp-action="Edit" asp-route-id="@Model.SubjectAreaId" class="btn btn-warning details" title="Edit"><i class="fa fa-pencil-square-o"></i> Edit</a>
|
||||
<a asp-action="Edit" asp-route-id="@Model.SubjectAreaId" class="btn btn-warning details" title="Edit"><i class="fa fa-pencil-square-o"></i> Edit</a>
|
||||
|
||||
<hr />
|
||||
|
||||
<a href="@Url.ActionLink("PIAWizard", "Wizards", new { subjectAreaId = Model.SubjectAreaId })" title="Edit Privacy Impact Assessment" class="btn btn-outline-primary">Edit Privacy Impact Assessment</a>
|
||||
<a href="@Url.ActionLink("PIASummary", "Wizards", new { id = Model.SubjectAreaId })" title="View PIA Summary" class="btn btn-info">Privacy Impact Assessment Summary</a>
|
||||
</div>
|
||||
@section Scripts
|
||||
{
|
||||
|
|
|
@ -25,11 +25,6 @@
|
|||
<input class="form-check-input" asp-for="ActiveYn" /> @Html.DisplayNameFor(model => model.ActiveYn)
|
||||
</label>
|
||||
</div>
|
||||
@*<div class="form-group">
|
||||
<label asp-for="SubjectAreaFormId" class="control-label"></label>
|
||||
<input asp-for="SubjectAreaFormId" class="form-control" />
|
||||
<span asp-validation-for="SubjectAreaFormId" class="text-danger"></span>
|
||||
</div>*@
|
||||
<div class="form-group">
|
||||
<label asp-for="DefaultTargetSchema" class="control-label"></label>
|
||||
<input asp-for="DefaultTargetSchema" class="form-control" />
|
||||
|
@ -42,6 +37,7 @@
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<a href="javascript:history.go(-1)" title="Back to List" class="btn btn-secondary" ><i class="fa fa-arrow-circle-left"></i> Back</a>
|
||||
<a href="@Url.ActionLink(nameof(WizardsController.PIAWizard), "Wizards", new { subjectAreaId = Model.SubjectAreaId })" title="Edit Privacy Impact Assessment" class="btn btn-outline-primary">Edit Privacy Impact Assessment</a>
|
||||
<input type="submit" value="Save" class="btn btn-danger" />
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
@using Humanizer;
|
||||
@model IEnumerable<WebApplication.Models.TaskGroup>
|
||||
@model IEnumerable<WebApplication.Models.SubjectArea>
|
||||
|
||||
@{
|
||||
ViewData["Title"] = typeof(SubjectArea).Name.Pluralize().Humanize(LetterCasing.Title);
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
@section Scripts
|
||||
{
|
||||
<script type="text/javascript">
|
||||
var ModelName = "SubjectArea";
|
||||
var ModelName = "SubjectAreaForm";
|
||||
$(document).ready(function () {
|
||||
DataTablesGridPrep();
|
||||
});
|
||||
|
|
|
@ -13,18 +13,18 @@
|
|||
<hr />
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">
|
||||
@Html.DisplayNameFor(model => model.AncestorTaskGroupId)
|
||||
@Html.DisplayNameFor(model => model.AncestorTaskGroup)
|
||||
</dt>
|
||||
<dd class="col-sm-10">
|
||||
@Html.DisplayFor(model => model.AncestorTaskGroupId)
|
||||
@Html.DisplayFor(model => model.AncestorTaskGroup.TaskGroupName)
|
||||
</dd>
|
||||
</dl>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">
|
||||
@Html.DisplayNameFor(model => model.DescendantTaskGroupId)
|
||||
@Html.DisplayNameFor(model => model.DescendantTaskGroup)
|
||||
</dt>
|
||||
<dd class="col-sm-10">
|
||||
@Html.DisplayFor(model => model.DescendantTaskGroupId)
|
||||
@Html.DisplayFor(model => model.DescendantTaskGroup.TaskGroupName)
|
||||
</dd>
|
||||
</dl>
|
||||
<dl class="row">
|
||||
|
@ -37,7 +37,7 @@
|
|||
</dl>
|
||||
</div>
|
||||
<div>
|
||||
@Html.ActionLink("Edit", "Edit", new { /* id = Model.PrimaryKey */ }) |
|
||||
@Html.ActionLink("Edit", "EditPlus", new { AncestorTaskGroupId = Model.AncestorTaskGroupId, DescendantTaskGroupId = Model.DescendantTaskGroupId }) |
|
||||
<a asp-action="IndexDataTable" title="Back to List" class="btn-sm btn-secondary"><i class="fa fa-arrow-circle-left"></i> Back</a>
|
||||
</div>
|
||||
@section Scripts
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<hr />
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<form asp-action="Edit">
|
||||
<form asp-action="EditPlus">
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label asp-for="AncestorTaskGroupId" class="control-label"></label>
|
||||
|
|
|
@ -49,6 +49,11 @@
|
|||
<input class="form-check-input" asp-for="ActiveYn" /> @Html.DisplayNameFor(model => model.ActiveYn)
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="UpdatedOn" class="control-label"></label>
|
||||
<input asp-for="UpdatedOn" class="form-control" />
|
||||
<span asp-validation-for="UpdatedOn" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<a href="javascript:history.go(-1)" title="Back to List" class="btn btn-secondary"><i class="fa fa-arrow-circle-left"></i> Back</a>
|
||||
|
|
|
@ -46,6 +46,11 @@
|
|||
<input class="form-check-input" asp-for="ActiveYn" /> @Html.DisplayNameFor(model => model.ActiveYn)
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="UpdatedOn" class="control-label"></label>
|
||||
<input asp-for="UpdatedOn" class="form-control" />
|
||||
<span asp-validation-for="UpdatedOn" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<a href="javascript:history.go(-1)" title="Back to List" class="btn btn-secondary" ><i class="fa fa-arrow-circle-left"></i> Back</a>
|
||||
<input type="submit" value="Save" class="btn btn-danger" />
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
@model WebApplication.Models.Wizards.ExternalFileUpload
|
||||
@using Humanizer;
|
||||
@model WebApplication.Models.Wizards.ExternalFileUpload
|
||||
@{
|
||||
ViewData["Title"] = "ExternalFileUpload";
|
||||
ViewData["Title"] = "ExternalFileUpload".Humanize(LetterCasing.Title);
|
||||
Layout = "~/Views/Shared/_Layout.cshtml";
|
||||
}
|
||||
|
||||
<h1>ExternalFileUpload</h1>
|
||||
<h1>External File Upload</h1>
|
||||
<!-- MultiStep Form -->
|
||||
<div id="smartwizard">
|
||||
<ul class="nav">
|
||||
<li>
|
||||
<a class="nav-link" href="#step-1">
|
||||
<strong>Step 1</strong>
|
||||
<br /> Select Storage Account for Upload
|
||||
<br /> Select Source and Target Account for Upload
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -23,7 +24,7 @@
|
|||
<li>
|
||||
<a class="nav-link" href="#step-3">
|
||||
<strong>Step 3</strong>
|
||||
<br /> Select Lockbox Location
|
||||
<br /> Task Master Properties
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -35,39 +36,44 @@
|
|||
<li>
|
||||
<a class="nav-link" href="#step-5">
|
||||
<strong>Step 5</strong>
|
||||
<br /> Input Third Party Details
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="nav-link" href="#step-6">
|
||||
<strong>Step 6</strong>
|
||||
<br /> Input Upload FileName
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="nav-link" href="#step-7">
|
||||
<strong>Step 7</strong>
|
||||
<br /> Input Operator Email Address
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="nav-link" href="#step-8">
|
||||
<strong>Step 8</strong>
|
||||
<br /> Select Task Group
|
||||
<br /> Data Factory Settings
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form asp-action="Create">
|
||||
<form asp-action="PostEFN">
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||
<div class="tab-content">
|
||||
<div id="step-1" class="tab-pane" role="tabpanel">
|
||||
<br />
|
||||
<div class="form-group">
|
||||
<label> Data File Name </label>
|
||||
<br />
|
||||
<input asp-for="UploadFileName" class="form-control" />
|
||||
<span asp-validation-for="UploadFileName" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label> Describe the details of the third parties uploading this data</label>
|
||||
<br />
|
||||
<textarea asp-for="ExternalParties" class="form-control"></textarea>
|
||||
<span asp-validation-for="ExternalParties" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label> Select the Azure storage location into which you would like your external user to upload data. This should be in your "transient-in" zone.</label>
|
||||
<br />
|
||||
<select asp-for="UploadSystemId" class="form-control" asp-items="ViewBag.UploadSystemId"></select>
|
||||
<span asp-validation-for="UploadSystemId" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label> Select the storage account (Lockbox) into which you would like the file copied after it is uploaded</label>
|
||||
<br />
|
||||
<select asp-for="TargetSystemId" class="form-control" asp-items="ViewBag.TargetSystemId"></select>
|
||||
<span asp-validation-for="TargetSystemId" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label"> The ID that will be used by the uploader app to find the Transient In zone''s base URL</label>
|
||||
<input asp-for="TargetSystemUidInPHI" class="form-control" />
|
||||
<span asp-validation-for="TargetSystemUidInPHI" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="step-2" class="tab-pane" role="tabpanel">
|
||||
<br />
|
||||
|
@ -77,14 +83,53 @@
|
|||
<select asp-for="EmailSystemId" class="form-control" asp-items="ViewBag.EmailSystemId"></select>
|
||||
<span asp-validation-for="EmailSystemId" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label"> Input your email address.</label>
|
||||
<input asp-for="Email" class="form-control" />
|
||||
<span asp-validation-for="Email" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label"> Input the email address of the administrator who you would like to be notified once the file is uploaded.</label>
|
||||
<input asp-for="OperatorEmail" class="form-control" />
|
||||
<span asp-validation-for="OperatorEmail" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label"> Input the name of the person to whom the email address belongs</label>
|
||||
<input asp-for="OperatorName" class="form-control" />
|
||||
<span asp-validation-for="OperatorName" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label"> Input the File Uploader WebApp URL</label>
|
||||
<input asp-for="FileUploaderWebAppURL" class="form-control" />
|
||||
<span asp-validation-for="FileUploaderWebAppURL" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label"> Email Subject</label>
|
||||
<input asp-for="EmailSubject" class="form-control" />
|
||||
<span asp-validation-for="EmailSubject" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label"> Relative path of the storage target e.g. YourName/PipQi/{yyyy}/{MM}/{dd}/{hh}/{mm}/</label>
|
||||
<input asp-for="RelativePath" class="form-control" />
|
||||
<span asp-validation-for="RelativePath" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="step-3" class="tab-pane" role="tabpanel">
|
||||
<br />
|
||||
<div class="form-group">
|
||||
<label> Select the storage account into which you would like the file copied after it is uploaded</label>
|
||||
<label> Task Master Name </label>
|
||||
<br />
|
||||
<select asp-for="TargetSystemId" class="form-control" asp-items="ViewBag.TargetSystemId"></select>
|
||||
<span asp-validation-for="TargetSystemId" class="text-danger"></span>
|
||||
<input asp-for="TaskMasterName" class="form-control" />
|
||||
<span asp-validation-for="TaskMasterName" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label> Select the Task Group into which you want your new tasks created</label>
|
||||
<br />
|
||||
<select id="efnTgSelector" asp-for="TaskGroupId" class="form-control" asp-items="ViewBag.TaskGroupId"></select>
|
||||
<span asp-validation-for="TaskGroupId" class="text-danger"></span>
|
||||
</div>
|
||||
<div id="taskGroupDetails">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div id="step-4" class="tab-pane" role="tabpanel">
|
||||
|
@ -95,40 +140,48 @@
|
|||
<select asp-for="ScheduleMasterId" class="form-control" asp-items="ViewBag.ScheduleMasterId"></select>
|
||||
<span asp-validation-for="ScheduleMasterId" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label> Active State </label>
|
||||
<br />
|
||||
<input asp-for="IsActive" class="form-control" />
|
||||
<span asp-validation-for="IsActive" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label> Allow Multiple Active Instances </label>
|
||||
<br />
|
||||
<input asp-for="AllowMultipleActiveInstances" class="form-control" />
|
||||
<span asp-validation-for="AllowMultipleActiveInstances" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="step-5" class="tab-pane" role="tabpanel">
|
||||
<br />
|
||||
<div class="form-group">
|
||||
<label> Select the storage account into which you would like the file copied after it is uploaded</label>
|
||||
<label> Data Factory </label>
|
||||
<br />
|
||||
<textarea asp-for="ExternalParties" class="form-control" asp-items="ViewBag.TargetSystemId"></textarea>
|
||||
<span asp-validation-for="TargetSystemId" class="text-danger"></span>
|
||||
<select asp-for="DataFactoryId" class="form-control" asp-items="ViewBag.DataFactoryId"></select>
|
||||
<span asp-validation-for="DataFactoryId" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="step-6" class="tab-pane" role="tabpanel">
|
||||
<br />
|
||||
<div class="form-group">
|
||||
<label class="control-label"> Input the name of the file to be uploaded.</label>
|
||||
<input asp-for="UploadFileName" class="form-control" />
|
||||
<span asp-validation-for="UploadFileName" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="step-7" class="tab-pane" role="tabpanel">
|
||||
<br />
|
||||
<div class="form-group">
|
||||
<label class="control-label"> Input the email address of the administrator who you would like to be notified once the file is uploaded.</label>
|
||||
<input asp-for="OperatorEmail" class="form-control" />
|
||||
<span asp-validation-for="OperatorEmail" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="step-8" class="tab-pane" role="tabpanel">
|
||||
<br />
|
||||
<div class="form-group">
|
||||
<label> Select the Task Group into which you want your new tasks created</label>
|
||||
<label> Data Factory Integration Runtime </label>
|
||||
<br />
|
||||
<select asp-for="TaskGroupId" class="form-control" asp-items="ViewBag.TaskGroupId"></select>
|
||||
<span asp-validation-for="TaskGroupId" class="text-danger"></span>
|
||||
<select asp-for="TaskDatafactoryIr" asp-items="DataFactoryRuntimes.Runtimes" class="form-control"></select>
|
||||
<span asp-validation-for="TaskDatafactoryIr" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label> Data Factory Dependency Chain Tag </label>
|
||||
<br />
|
||||
<input asp-for="DependencyChainTag" class="form-control" />
|
||||
<span asp-validation-for="DependencyChainTag" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label> Data Factory Degree of Copy Parallelism </label>
|
||||
<br />
|
||||
<input asp-for="DegreeOfCopyParallelism" class="form-control" />
|
||||
<span asp-validation-for="DegreeOfCopyParallelism" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<input id="submitBtn" class="align-content-center btn btn-danger" type="submit" value="Submit Data" />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -139,16 +192,7 @@
|
|||
@section Scripts
|
||||
{
|
||||
<link rel="stylesheet" href="~/lib//smartwizard/dist/css/smart_wizard_all.min.css" />
|
||||
<script src="~/lib/showdown/showdown.min.js" asp-append-version="true"></script>
|
||||
<script src="~/lib/smartwizard/dist/js/jquery.smartWizard.min.js" asp-append-version="true"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
// SmartWizard initialize
|
||||
$('#smartwizard').smartWizard({ theme: 'dots'});
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<script src="~/js/efnWizard.js" asp-append-version="true"></script>
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
@model WebApplication.Models.Wizards.PIAWizardViewModel
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<a id="PIASummaryBackBtn" href="javascript:history.go(-1)" title="Back to List" class="btn btn-secondary">
|
||||
<i class="fa fa-arrow-circle-left"></i> Back
|
||||
</a>
|
||||
<a class="btn btn-info " id="PIASUmmaryPrintBtn" title="Print Summary">
|
||||
<i class="fa fa-print"></i> Print Summary
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
@await Html.PartialAsync("~/Views/Wizards/_PIAWizardSummaryStep9.cshtml")
|
||||
|
||||
@section Scripts
|
||||
{
|
||||
<script src="~/js/readonlyPIA.js" type="text/javascript"></script>
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
@model WebApplication.Models.Wizards.PIAWizardViewModel
|
||||
@{
|
||||
ViewData["Title"] = "Privacy Impact Assessment";
|
||||
Layout = "~/Views/Shared/_Layout.cshtml";
|
||||
}
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="PIAProcessModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="exampleModalLabel">Data Set Privacy Impact Assessment Process</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<h3>Is Your Data Set Publicly Available?</h3>
|
||||
<hr />
|
||||
<br />
|
||||
<p>
|
||||
The Office of the Australian Information Commissioner (OAIC) and Commonwealth Scientific and Industrial Research Organisation (CSIRO)'s Data61 (Office of the Australian Information Commissioner, 2017) released the De-identification Decision-Making Framework (DDF) to assist organisations
|
||||
to de-identify their data effectively. The DDF is a practical and accessible guide for Australian organisations that handle personal information and are considering sharing or releasing it to meet their ethical responsibilities and legal obligations, such as those under the Privacy Act 1988.
|
||||
OAIC have released the DDF as a freely available open source book as they feel that they have an important message that they want to ensure is disseminated as widely as possible.
|
||||
</p>
|
||||
<p>
|
||||
We have used that guide extensively to develop our process for assessing the privacy impacts at a data set level, to closely align with an approach recommended by the Information Commissioner. Being able to demonstrate the close alignment with the advice provided by the OAIC that we
|
||||
have adopted provides us with a high level of confidence of implementing suitable privacy controls as well as being able to defend the approach we have taken.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* todo: make this look better *@
|
||||
<h1>Privacy Impact Assessment - Subject Area: @Model.BelongingDataset</h1>
|
||||
<!-- MultiStep Form -->
|
||||
<div id="smartwizard">
|
||||
<ul class="nav">
|
||||
<li>
|
||||
<a class="nav-link" href="#step-1">
|
||||
<strong>Step 1</strong>
|
||||
<br />
|
||||
Introduction
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="nav-link" href="#step-2">
|
||||
<strong>Step 2</strong>
|
||||
<br />
|
||||
Data Set Authorities
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="nav-link" href="#step-3">
|
||||
<strong>Step 3</strong>
|
||||
<br />
|
||||
Legal Responsibility
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="nav-link" href="#step-4">
|
||||
<strong>Step 4</strong>
|
||||
<br />
|
||||
Know Your Data
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="nav-link" href="#step-5">
|
||||
<strong>Step 5</strong>
|
||||
<br />
|
||||
Ethical Obligations
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="nav-link" href="#step-6">
|
||||
<strong>Step 6</strong>
|
||||
<br />
|
||||
Risk Processes
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="nav-link" href="#step-7">
|
||||
<strong>Step 7</strong>
|
||||
<br />
|
||||
Control Process
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="nav-link" href="#step-8">
|
||||
<strong>Step 8</strong>
|
||||
<br />
|
||||
Impact Management
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="nav-link" href="#step-9">
|
||||
<strong>Step 9</strong>
|
||||
<br />
|
||||
Summary
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
@using (Html.BeginForm("PIAWizard", "Wizards", new { subjectAreaId = Model.SubjectAreaId }, FormMethod.Post, true, new { @id = "form" }))
|
||||
{
|
||||
@Html.HiddenFor(m => m.Step)
|
||||
@Html.HiddenFor(m => m.MaxStep)
|
||||
@Html.HiddenFor(m => m.SubjectAreaId)
|
||||
@Html.HiddenFor(m => m.SubmissionMethod)
|
||||
@Html.HiddenFor(m => m.BelongingDataset)
|
||||
@Html.HiddenFor(m => m.BelongingDatasetCode)
|
||||
|
||||
<div class="tab-content">
|
||||
<div id="step-1" class="tab-pane" role="tabpanel">
|
||||
@await Html.PartialAsync("_PIAWizardStep1.cshtml")
|
||||
</div>
|
||||
<div id="step-2" class="tab-pane" role="tabpanel">
|
||||
@await Html.PartialAsync("_PIAWizardStep2.cshtml")
|
||||
</div>
|
||||
<div id="step-3" class="tab-pane" role="tabpanel">
|
||||
@await Html.PartialAsync("_PIAWizardStep3.cshtml")
|
||||
</div>
|
||||
<div id="step-4" class="tab-pane" role="tabpanel">
|
||||
@await Html.PartialAsync("_PIAWizardStep4.cshtml")
|
||||
</div>
|
||||
<div id="step-5" class="tab-pane" role="tabpanel">
|
||||
@await Html.PartialAsync("_PIAWizardStep5.cshtml")
|
||||
</div>
|
||||
<div id="step-6" class="tab-pane privateDataSetStep" role="tabpanel">
|
||||
<div class="card-body text-danger text-center privateDataSetWarning">
|
||||
<strong>This step is only required for private data sets. You may skip it if your data set is public</strong>
|
||||
<a class="btn btn-warning editBtn" href="#step-9"><i class="fas fa-edit"></i>Skip to Summary</a>
|
||||
</div>
|
||||
@await Html.PartialAsync("_PIAWizardStep6.cshtml")
|
||||
</div>
|
||||
<div id="step-7" class="tab-pane privateDataSetStep" role="tabpanel">
|
||||
<div class="card-body text-danger text-center privateDataSetWarning">
|
||||
<strong>This step is only required for private data sets. You may skip it if your data set is public</strong>
|
||||
<a class="btn btn-warning editBtn" href="#step-9"><i class="fas fa-edit"></i>Skip to Summary</a>
|
||||
</div>
|
||||
@await Html.PartialAsync("_PIAWizardStep7.cshtml")
|
||||
</div>
|
||||
<div id="step-8" class="tab-pane privateDataSetStep" role="tabpanel">
|
||||
<div class="card-body text-danger text-center privateDataSetWarning">
|
||||
<strong>This step is only required for private data sets. You may skip it if your data set is public</strong>
|
||||
<a class="btn btn-warning editBtn" href="#step-9"><i class="fas fa-edit"></i>Skip to Summary</a>
|
||||
</div>
|
||||
@await Html.PartialAsync("_PIAWizardStep8.cshtml")
|
||||
</div>
|
||||
<div id="step-9" class="tab-pane" role="tabpanel">
|
||||
<br />
|
||||
<div class="form-group">
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body text-danger text-center">
|
||||
<strong>Please review your selections and make sure all data is accurate and true. To formalise the submission of this Privacy Impact Assessment, click the submit button at the bottom of this page.</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@await Html.PartialAsync("_PIAWizardSummaryStep9.cshtml")
|
||||
<input id="submitBtn" class="btn-block btn-danger" type="submit" value="Submit Data" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@section Scripts
|
||||
{
|
||||
<script src="~/js/piaWizardToolTips.js"></script>
|
||||
<link rel="stylesheet" href="~/lib//smartwizard/dist/css/smart_wizard_all.min.css" />
|
||||
<script src="~/lib/smartwizard/dist/js/jquery.smartWizard.min.js" asp-append-version="true"></script>
|
||||
<script src="~/lib/showdown/showdown.min.js" asp-append-version="true"></script>
|
||||
<script src="~/js/setDisplayNameDatasetUsers.js" asp-append-version="true"></script>
|
||||
|
||||
<script src="~/js/piaWizard.js" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
getPIAWizardModelData(@Html.Raw(Json.Serialize(Model))); // allows external js file to access model data
|
||||
</script>
|
||||
<script src="~/js/customValidators.js" asp-append-version="true"></script>
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
@using WebApplication.Forms.PIAWizard
|
||||
@model WebApplication.Models.Wizards.PIAWizardViewModel
|
||||
<br />
|
||||
|
||||
<div class="row">
|
||||
<h3>
|
||||
Introduction
|
||||
</h3>
|
||||
</div>
|
||||
<br />
|
||||
@*<div class="form-group row">
|
||||
|
||||
|
||||
This data set is
|
||||
<p>
|
||||
@Html.DisplayFor(model => model.DataSetPrivacyStatus)
|
||||
</p>.
|
||||
Click
|
||||
<span id="changePrivacyStatus">
|
||||
<i class="fa fa-exclamation-circle"></i>
|
||||
</span>
|
||||
to change it.
|
||||
|
||||
|
||||
</div>*@
|
||||
|
||||
<div class="form-group row col-12 wizardSelector">
|
||||
<div class="col-6">
|
||||
<label asp-for="DataSetPrivacyStatus" class="control-label"></label>
|
||||
|
||||
<span id="privacyModalBtn">
|
||||
<i class="fa fa-exclamation-circle"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
@Html.DropDownListFor(model => model.DataSetPrivacyStatus, Html.GetEnumSelectList<PublicPrivateDataSet>(), "Select Dataset Privacy Status", new { @class = "form-control"})
|
||||
<span asp-validation-for="DataSetPrivacyStatus" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row col-12 wizardSelector">
|
||||
<div class="col-6">
|
||||
<label asp-for="AuthorisedByDataSubjects" class="control-label"></label>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
@Html.RadioButtonFor(model => model.AuthorisedByDataSubjects, "true") Yes
|
||||
@Html.RadioButtonFor(model => model.AuthorisedByDataSubjects, "false") No
|
||||
<span asp-validation-for="AuthorisedByDataSubjects" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row col-12 wizardSelector">
|
||||
<div class="col-6">
|
||||
<label asp-for="DataAgreementsSigned" class="control-label"></label>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
@Html.RadioButtonFor(model => model.DataAgreementsSigned, "true") Yes
|
||||
@Html.RadioButtonFor(model => model.DataAgreementsSigned, "false") No
|
||||
<span asp-validation-for="DataAgreementsSigned" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row col-12 wizardSelector">
|
||||
<div class="col-6">
|
||||
<label asp-for="DataAgreementsLocation" class="control-label"></label>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
@Html.TextBoxFor(model => model.DataAgreementsLocation, new { @class = "form-control" })
|
||||
<span asp-validation-for="DataAgreementsLocation" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row col-12 wizardSelector">
|
||||
<div class="col-6">
|
||||
<label asp-for="UnderstandMetadata" class="control-label"></label>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
@Html.RadioButtonFor(model => model.UnderstandMetadata, "true") Yes
|
||||
@Html.RadioButtonFor(model => model.UnderstandMetadata, "false") No
|
||||
<span asp-validation-for="UnderstandMetadata" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row col-12 wizardSelector">
|
||||
<div class="col-6">
|
||||
<label asp-for="AcceptResponsibility" class="control-label"></label>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
@Html.RadioButtonFor(model => model.AcceptResponsibility, "true") Yes
|
||||
@Html.RadioButtonFor(model => model.AcceptResponsibility, "false") No
|
||||
<span asp-validation-for="AcceptResponsibility" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row col-12 wizardSelector">
|
||||
<div class="col-6">
|
||||
<label asp-for="DataSetDescription" class="control-label"></label>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<textarea asp-for="DataSetDescription" class="form-control"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row col-12 wizardSelector">
|
||||
<div class="col-6">
|
||||
<label asp-for="PhnId" class="control-label"></label>
|
||||
</div>
|
||||
<div class="formSelect col-6">
|
||||
@Html.DropDownListFor(model => model.PhnZone.Id, ViewBag.PhnZones as SelectList, "Select Your PHN Zone", new { @class = "form-control", @id = "dsSelection" })
|
||||
@Html.HiddenFor(model => model.PhnZone.Name, new { @id = "hiddenPhnName"})
|
||||
<span asp-validation-for="PhnId" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row col-12 wizardSelector">
|
||||
<div class="col-6">
|
||||
<label asp-for="DataSourceLocation" class="control-label"></label>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
@Html.TextBoxFor(model => model.DataSourceLocation, new { @class = "form-control" })
|
||||
<span asp-validation-for="DataSourceLocation" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row col-12 wizardSelector">
|
||||
<div class="col-6">
|
||||
<label asp-for="OwningSystem" class="control-label"></label>
|
||||
</div>
|
||||
<div class="formSelect col-6">
|
||||
@Html.DropDownListFor(model => model.OwningSystem, ViewBag.SourceSystems as SelectList, "Select Your Source System", new { @class = "form-control", @id = "tgSelection" })
|
||||
<span asp-validation-for="OwningSystem" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row col-12 wizardSelector">
|
||||
<div class="col-6">
|
||||
<label asp-for="SystemDataLocation" class="control-label"></label>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
@Html.TextBoxFor(model => model.SystemDataLocation, new { @class = "form-control" })
|
||||
<span asp-validation-for="SystemDataLocation" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-12 row wizardSelector">
|
||||
<div class="col-6">
|
||||
<label asp-for="SystemPurposeVersion" style="width:90%" class="control-label"></label>
|
||||
</div>
|
||||
<div class="col-6" id="sysVersionInput">
|
||||
@Html.TextBoxFor(model => model.PhnName, new { @size = 3, id = "spvPhnName", @readonly = "readonly" })
|
||||
_
|
||||
@Html.TextBoxFor(model => model.SourceSystemName, new { @size = 3, id = "spdSourceSystemName", @readonly = "readonly" })
|
||||
_
|
||||
@Html.TextBoxFor(model => model.SystemPurposeVersion, new { @size = 10 })
|
||||
_
|
||||
@Html.TextBoxFor(model => model.SystemPurposeVersionNumber, new { @size = 2 })
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,73 @@
|
|||
@model WebApplication.Models.Wizards.PIAWizardViewModel;
|
||||
<br />
|
||||
|
||||
<h3>Data Set Authorities</h3>
|
||||
<br />
|
||||
<div class="form-group row col-12">
|
||||
<p>
|
||||
<strong>
|
||||
Identify the name of the data owner/sponsor, custodian and steward as it relates to the PHI platform. The owners, custodians and stewards will be responsible for how this data set is governed and managed
|
||||
</strong>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group row col-12">
|
||||
<div class="col-6">
|
||||
|
||||
</div>
|
||||
<div class="col-6 ">
|
||||
<label>Users</label>
|
||||
</div>
|
||||
|
||||
<div class="col-6">
|
||||
<label class="control-label">
|
||||
@Html.DisplayNameFor(model => model.DataOwner)
|
||||
<i class="fas fa-info-circle" id="dataOwnerTT"></i>
|
||||
<span asp-validation-for="DataOwner.UserId" class="text-danger"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
@Html.DropDownListFor(model => model.DataOwner.UserId, ViewBag.PHNUsers as SelectList, "Select User", new { @class = "form-control", @id = "nameDroplist", @onchange = "setOwnerDisplayName(this)" })
|
||||
@Html.HiddenFor(model => model.DataOwner.DisplayName, new { @id = "ownerName"})
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row col-12">
|
||||
<div class="col-6">
|
||||
<label class="control-label">
|
||||
@Html.DisplayNameFor(model => model.DataCustodian)
|
||||
<i class="fas fa-info-circle" id="dataCustodianTT"></i>
|
||||
<span asp-validation-for="DataCustodian.UserId" class="text-danger"></span> @* Validation for user id field. May need to change this for more complex validations *@
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
@Html.DropDownListFor(model => model.DataCustodian.UserId, ViewBag.PHNUsers as SelectList, "Select User", new { @class = "form-control", @id = "nameDroplist", @onchange = "setOwnerDisplayName(this)" })
|
||||
@Html.HiddenFor(model => model.DataCustodian.DisplayName, new { @id = "custodianName"})
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row col-12">
|
||||
<div class="col-6">
|
||||
<label class="control-label">
|
||||
@Html.DisplayNameFor(model => model.DataStewards)
|
||||
<i class="fas fa-info-circle" id="dataStewardTT"></i>
|
||||
<span asp-validation-for="DataStewards" class="text-danger"></span>
|
||||
</label>
|
||||
|
||||
<div id="btnsAddRemoveSteward">
|
||||
<button class="btn btn-light" id="addSteward" type="button"><i class="fas fa-plus"></i> Add More</button>
|
||||
<button class="btn btn-light" id="removeSteward" type="button"><i class="fas fa-minus"></i> Remove Last</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-6 form-group" id="stewardsContainer">
|
||||
@Html.EditorFor(model => model.DataStewards, new { @class = "form-control" })
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* External users granted data access (hidden) *@
|
||||
<div id="externalUsers">
|
||||
@Html.EditorFor(model => model.ExternalUsers, "ExternalUsers")
|
||||
</div>
|
||||
|
||||
<br />
|
|
@ -0,0 +1,159 @@
|
|||
@model WebApplication.Models.Wizards.PIAWizardViewModel
|
||||
<br />
|
||||
|
||||
<h3>
|
||||
Understanding Your Legal Responsibility
|
||||
</h3>
|
||||
<br />
|
||||
|
||||
<div class="form-group row col-12">
|
||||
<p>
|
||||
<strong>
|
||||
Before you proceed, please read and confirm the following disclaimer statements, by checking the appropriate boxes or updating with input from the dropdown options:
|
||||
</strong>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group row col-12">
|
||||
<div class="col-6">
|
||||
<label asp-for="SupportingDocumentationLocation" class="control-label"></label>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
@Html.TextBoxFor(model => model.SupportingDocumentationLocation, new { @class = "form-control" })
|
||||
<span asp-validation-for="SupportingDocumentationLocation" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row col-12">
|
||||
<div class="col-6">
|
||||
<label class="control-label">
|
||||
@Html.DisplayNameFor(model => model.DataPersonalOrDeIdentified)
|
||||
<i class="fas fa-info-circle" id="personalInfoTT"></i>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
@Html.RadioButtonFor(model => model.DataPersonalOrDeIdentified, "deidentified") De-Identified
|
||||
@Html.RadioButtonFor(model => model.DataPersonalOrDeIdentified, "personal") Personal
|
||||
<span asp-validation-for="DataPersonalOrDeIdentified" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="suppressionRuleQuestions">
|
||||
<div class="form-group row col-12">
|
||||
<div class="col-6">
|
||||
|
||||
<h4>
|
||||
Data Cleansing - If it is de-identified data, what controls need to be in place to maintain this status?
|
||||
</h4>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row col-12">
|
||||
<div class="col-6">
|
||||
<label class="control-label">
|
||||
@Html.DisplayNameFor(model => model.SuppressionRuleOne)
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
@Html.RadioButtonFor(model => model.SuppressionRuleOne, "na") Not Applicable
|
||||
<br />
|
||||
@Html.RadioButtonFor(model => model.SuppressionRuleOne, "applied") I have applied this suppression rule
|
||||
<br />
|
||||
@Html.RadioButtonFor(model => model.SuppressionRuleOne, "notApplied") I have not applied this suppression rule
|
||||
<span asp-validation-for="SuppressionRuleOne" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<label asp-for="SuppressionRuleOneExpl"></label>
|
||||
<br />
|
||||
<textarea asp-for="SuppressionRuleOneExpl" rows="4" cols="50"></textarea>
|
||||
<span asp-validation-for="SuppressionRuleOneExpl" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row col-12">
|
||||
<div class="col-6">
|
||||
<label class="control-label">
|
||||
@Html.DisplayNameFor(model => model.SuppressionRuleTwo)
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
@Html.RadioButtonFor(model => model.SuppressionRuleTwo, "na") Not Applicable
|
||||
<br />
|
||||
@Html.RadioButtonFor(model => model.SuppressionRuleTwo, "applied") I have applied this suppression rule
|
||||
<br />
|
||||
@Html.RadioButtonFor(model => model.SuppressionRuleTwo, "notApplied") I have not applied this suppression rule
|
||||
<span asp-validation-for="SuppressionRuleTwo" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<label asp-for="SuppressionRuleTwoExpl"></label>
|
||||
<br />
|
||||
<textarea asp-for="SuppressionRuleTwoExpl" rows="4" cols="50"></textarea>
|
||||
<span asp-validation-for="SuppressionRuleTwoExpl" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row col-12">
|
||||
<div class="col-6">
|
||||
<label class="control-label">
|
||||
@Html.DisplayNameFor(model => model.SuppressionRuleThree)
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
@Html.RadioButtonFor(model => model.SuppressionRuleThree, "na") Not Applicable
|
||||
<br />
|
||||
@Html.RadioButtonFor(model => model.SuppressionRuleThree, "applied") I have applied this suppression rule
|
||||
<br />
|
||||
@Html.RadioButtonFor(model => model.SuppressionRuleThree, "notApplied") I have not applied this suppression rule
|
||||
<span asp-validation-for="SuppressionRuleThree" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<label asp-for="SuppressionRuleThreeExpl"></label>
|
||||
<br />
|
||||
<textarea asp-for="SuppressionRuleThreeExpl" rows="4" cols="50"></textarea>
|
||||
<span asp-validation-for="SuppressionRuleThreeExpl" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row col-12">
|
||||
<div class="col-6">
|
||||
<label class="control-label">
|
||||
@Html.DisplayNameFor(model => model.SuppressionRuleFour)
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
@Html.RadioButtonFor(model => model.SuppressionRuleFour, "na") Not Applicable
|
||||
<br />
|
||||
@Html.RadioButtonFor(model => model.SuppressionRuleFour, "applied") I have applied this suppression rule
|
||||
<br />
|
||||
@Html.RadioButtonFor(model => model.SuppressionRuleFour, "notApplied") I have not applied this suppression rule
|
||||
<span asp-validation-for="SuppressionRuleFour" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<label asp-for="SuppressionRuleFourExpl"></label>
|
||||
<br />
|
||||
<textarea asp-for="SuppressionRuleFourExpl" rows="4" cols="50"></textarea>
|
||||
<span asp-validation-for="SuppressionRuleFourExpl" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row col-12">
|
||||
<div class="col-6">
|
||||
<label class="control-label">
|
||||
@Html.DisplayNameFor(model => model.IdentifiedDataCustodianManagedRisk)
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
@Html.RadioButtonFor(model => model.IdentifiedDataCustodianManagedRisk, "true") Yes
|
||||
@Html.RadioButtonFor(model => model.IdentifiedDataCustodianManagedRisk, "false") No
|
||||
<span asp-validation-for="IdentifiedDataCustodianManagedRisk" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row col-12">
|
||||
<div class="col row">
|
||||
<label asp-for="DataRiskManagementExplanation"></label>
|
||||
</div>
|
||||
<div class="col row">
|
||||
<textarea asp-for="DataRiskManagementExplanation" class="form-control" type="text" placeholder=""></textarea>
|
||||
<span asp-validation-for="DataRiskManagementExplanation" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
|
@ -0,0 +1,197 @@
|
|||
@model WebApplication.Models.Wizards.PIAWizardViewModel
|
||||
<br />
|
||||
|
||||
<h3>
|
||||
Metadata
|
||||
</h3>
|
||||
<br />
|
||||
|
||||
<div class="form-group row col-12">
|
||||
<p>
|
||||
<strong>
|
||||
Conduct a high-level examination of your data, focusing on the data type, features, and properties. This involves:
|
||||
</strong>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group row col-12 ">
|
||||
<div class="col-2">
|
||||
<strong>Data Type</strong>
|
||||
</div>
|
||||
<div class="col-10">
|
||||
<div class="row wizardSelector">
|
||||
<div class="col-5">
|
||||
<label asp-for="DataForm" class="control-label"></label>
|
||||
</div>
|
||||
<div class="col-5">
|
||||
<div>
|
||||
@Html.RadioButtonFor(model => model.DataForm, "FlatFile", false) Flat-file
|
||||
</div>
|
||||
<div>
|
||||
@Html.RadioButtonFor(model => model.DataForm, "Text") Text (Survey Data)
|
||||
</div>
|
||||
<div>
|
||||
@Html.RadioButtonFor(model => model.DataForm, "Database") Database
|
||||
</div>
|
||||
</div>
|
||||
<span asp-validation-for="DataForm" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="row wizardSelector">
|
||||
<div class="col-5">
|
||||
<label asp-for="InfoLevelOfData" class="control-label"></label>
|
||||
</div>
|
||||
<div class="col-5">
|
||||
<div>
|
||||
@Html.RadioButtonFor(model => model.InfoLevelOfData, "IndividualLevel") Individual Level <i class="fas fa-info-circle" id="microdataTT"></i>
|
||||
</div>
|
||||
<div>
|
||||
@Html.RadioButtonFor(model => model.InfoLevelOfData, "Aggregated") Aggregated
|
||||
</div>
|
||||
<div>
|
||||
@Html.RadioButtonFor(model => model.InfoLevelOfData, "Locality") Locality
|
||||
</div>
|
||||
</div>
|
||||
<span asp-validation-for="InfoLevelOfData" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row col-12">
|
||||
<div class="col-2">
|
||||
<strong>Variable Type</strong>
|
||||
</div>
|
||||
<div class="col-10">
|
||||
<div class="row wizardSelector">
|
||||
<div class="col-5">
|
||||
<label asp-for="InfoLevelOfVariable" class="control-label"></label>
|
||||
</div>
|
||||
<div class="col-5">
|
||||
<div>
|
||||
@Html.RadioButtonFor(model => model.InfoLevelOfVariable, "DirectIdentifier") Direct Identifiers <i class="fas fa-info-circle" id="directIdTT"></i>
|
||||
</div>
|
||||
<div>
|
||||
@Html.RadioButtonFor(model => model.InfoLevelOfVariable, "IndirectIdentifiers") Indirect Identifiers <i class="fas fa-info-circle" id="indirectIdTT"></i>
|
||||
</div>
|
||||
<div>
|
||||
@Html.RadioButtonFor(model => model.InfoLevelOfVariable, "AggregatedCohortGrouping") Aggregated Cohort Grouping
|
||||
</div>
|
||||
</div>
|
||||
<span asp-validation-for="InfoLevelOfVariable" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row col-12">
|
||||
<div class="col-2">
|
||||
<strong>Data Set Properties</strong>
|
||||
</div>
|
||||
<div class="col-10 row">
|
||||
<div class="col-5">
|
||||
<label asp-for="TopThreePropsOfData" class="control-label"></label>
|
||||
</div>
|
||||
<div class="col-5">
|
||||
@Html.EditorFor(m => m.TopThreePropsOfData, Model.TopThreePropsOfData.TemplateName, new { @class = "form-control" })
|
||||
</div>
|
||||
<span asp-validation-for="TopThreePropsOfData" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row col-12">
|
||||
<div class="col-2">
|
||||
<strong>The data itself can have properties that make the data situation more or less sensitive. Please answer the following:</strong>
|
||||
<i class="fas fa-info-circle" id="sensitiveInfoTT"></i>
|
||||
</div>
|
||||
<div class="col-10">
|
||||
<div class="row wizardSelector">
|
||||
<div class="col-5">
|
||||
<label class="control-label">
|
||||
@Html.DisplayNameFor(model => model.IsDataQualityLevelHigh)
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
@Html.RadioButtonFor(model => model.IsDataQualityLevelHigh, "true") Yes
|
||||
@Html.RadioButtonFor(model => model.IsDataQualityLevelHigh, "false") No
|
||||
</div>
|
||||
<span asp-validation-for="IsDataQualityLevelHigh" class="text-danger"></span>
|
||||
<div class="col-4">
|
||||
<label asp-for="DataQualityExplanation" class="control-label"></label>
|
||||
<br />
|
||||
<textarea asp-for="DataQualityExplanation" class="form-control" type="text" placeholder=""></textarea>
|
||||
</div>
|
||||
<span asp-validation-for="DataQualityExplanation" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<div class="row wizardSelector">
|
||||
<div class="col-5">
|
||||
<label class="control-label">
|
||||
@Html.DisplayNameFor(model => model.IsDataAboutVulnerablePopulation)
|
||||
<i class="fas fa-info-circle" id="vulnerableDataTT"></i>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
@Html.RadioButtonFor(model => model.IsDataAboutVulnerablePopulation, "true") Yes
|
||||
@Html.RadioButtonFor(model => model.IsDataAboutVulnerablePopulation, "false") No
|
||||
</div>
|
||||
<span asp-validation-for="IsDataAboutVulnerablePopulation" class="text-danger"></span>
|
||||
<div class="col-4">
|
||||
<label asp-for="PopulationVulnerabilityExplanation" class="control-label"></label>
|
||||
<br />
|
||||
<textarea asp-for="PopulationVulnerabilityExplanation" class="form-control" type="text" placeholder=""></textarea>
|
||||
</div>
|
||||
<span asp-validation-for="PopulationVulnerabilityExplanation" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="row wizardSelector">
|
||||
<div class="col-5">
|
||||
<label class="control-label">
|
||||
@Html.DisplayNameFor(model => model.WhenDataWasCollected)
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<input type="date" id="piaDatePicker" class="form-control" asp-for="WhenDataWasCollected" />
|
||||
<span asp-validation-for="WhenDataWasCollected" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<label asp-for="DataTimeCollectedExplanation" class="control-label"></label>
|
||||
<br />
|
||||
<textarea asp-for="DataTimeCollectedExplanation" class="form-control" type="text" placeholder=""></textarea>
|
||||
</div>
|
||||
<span asp-validation-for="DataTimeCollectedExplanation" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="row wizardSelector">
|
||||
<div class="col-5">
|
||||
<label class="control-label">
|
||||
@Html.DisplayNameFor(model => model.IsDataHierarchical)
|
||||
<i class="fas fa-info-circle" id="hierarchicalTT"></i>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
@Html.RadioButtonFor(model => model.IsDataHierarchical, "true") Yes
|
||||
@Html.RadioButtonFor(model => model.IsDataHierarchical, "false") No
|
||||
</div>
|
||||
<span asp-validation-for="IsDataHierarchical" class="text-danger"></span>
|
||||
<div class="col-4">
|
||||
<label asp-for="DataHierarchicalExplanation" class="control-label"></label>
|
||||
<br />
|
||||
<textarea asp-for="DataHierarchicalExplanation" class="form-control" type="text" placeholder=""></textarea>
|
||||
</div>
|
||||
<span asp-validation-for="DataHierarchicalExplanation" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="row wizardSelector">
|
||||
<div class="col-5">
|
||||
<label class="control-label">
|
||||
@Html.DisplayNameFor(model => model.IsDataTimeStamped)
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
@Html.RadioButtonFor(model => model.IsDataTimeStamped, "true") Yes
|
||||
@Html.RadioButtonFor(model => model.IsDataTimeStamped, "false") No
|
||||
</div>
|
||||
<span asp-validation-for="IsDataTimeStamped" class="text-danger"></span>
|
||||
<div class="col-4">
|
||||
<label asp-for="DataTimestampedExplanation" class="control-label"></label>
|
||||
<br />
|
||||
<textarea asp-for="DataTimestampedExplanation" class="form-control" type="text" placeholder=""></textarea>
|
||||
</div>
|
||||
<span asp-validation-for="DataTimestampedExplanation" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
|
@ -0,0 +1,63 @@
|
|||
@model WebApplication.Models.Wizards.PIAWizardViewModel;
|
||||
<br />
|
||||
<div class="form-group">
|
||||
|
||||
<h3>Meet Your Ethical Obligations</h3>
|
||||
|
||||
<div class="control-label">
|
||||
<p><b>Considerations here include: consent, transparency, stakeholder engagement, and governance. You should document how you are going to achieve each of those aspects.</b></p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group wizardSelector">
|
||||
<label asp-for="RespectDataAssurances" class="control-label" style="width: 75%"></label>
|
||||
<span style="padding-left: 50px;">
|
||||
@Html.RadioButtonFor(model => model.RespectDataAssurances, "na") NA
|
||||
@Html.RadioButtonFor(model => model.RespectDataAssurances, "yes") Yes
|
||||
@Html.RadioButtonFor(model => model.RespectDataAssurances, "no") No
|
||||
</span>
|
||||
<span asp-validation-for="RespectDataAssurances" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="DataAssuranceExplanation" class="control-label" style="width: 75%"></label>
|
||||
<span style="padding-left: 50px;">
|
||||
<textarea asp-for="DataAssuranceExplanation" class="form-control" type="text" placeholder=""></textarea>
|
||||
</span>
|
||||
<span asp-validation-for="DataAssuranceExplanation" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<div class="form-group wizardSelector">
|
||||
<label asp-for="HasProvidedTransparency" class="control-label" style="width: 75%"></label>
|
||||
<span style="padding-left: 50px;">
|
||||
@Html.RadioButtonFor(model => model.HasProvidedTransparency, "na") NA
|
||||
@Html.RadioButtonFor(model => model.HasProvidedTransparency, "yes") Yes
|
||||
@Html.RadioButtonFor(model => model.HasProvidedTransparency, "no") No
|
||||
</span>
|
||||
<span asp-validation-for="HasProvidedTransparency" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="ProvidedTransparencyExplanation" class="control-label" style="width: 75%"></label>
|
||||
<span style="padding-left: 50px;">
|
||||
<textarea asp-for="ProvidedTransparencyExplanation" class="form-control" type="text" placeholder=""></textarea>
|
||||
</span>
|
||||
<span asp-validation-for="ProvidedTransparencyExplanation" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<div class="form-group wizardSelector">
|
||||
<label asp-for="HasObtainedSubjectsViews" class="control-label" style="width: 75%"></label>
|
||||
<span style="padding-left: 50px;">
|
||||
@Html.RadioButtonFor(model => model.HasObtainedSubjectsViews, "na") NA
|
||||
@Html.RadioButtonFor(model => model.HasObtainedSubjectsViews, "yes") Yes
|
||||
@Html.RadioButtonFor(model => model.HasObtainedSubjectsViews, "no") No
|
||||
</span>
|
||||
<span asp-validation-for="HasObtainedSubjectsViews" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="SubjectsViewsExplanation" class="control-label" style="width: 75%"></label>
|
||||
<span style="padding-left: 50px;">
|
||||
<textarea asp-for="SubjectsViewsExplanation" class="form-control" type="text" placeholder=""></textarea>
|
||||
</span>
|
||||
<span asp-validation-for="SubjectsViewsExplanation" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
</div>
|
|
@ -0,0 +1,62 @@
|
|||
@model WebApplication.Models.Wizards.PIAWizardViewModel;
|
||||
<br />
|
||||
<div class="form-group">
|
||||
<h3>Identify The Processes You Will Need To Go Through To Assess Disclosure Risk</h3>
|
||||
<p><b>Risk assessment is a crucial step in the process of producing safe useful data, helping you to:</b></p>
|
||||
<ul>
|
||||
<li><b>Determine whether your data should be shared or released at all</b></li>
|
||||
<li><b>Determine how much disclosure control should be applied</b></li>
|
||||
<li><b>Think about the optimum means for sharing or releasing your data</b></li>
|
||||
</ul>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<label asp-for="HasInitialSpecification" class="control-label"></label>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
@Html.RadioButtonFor(model => model.HasInitialSpecification, "true") Yes
|
||||
@Html.RadioButtonFor(model => model.HasInitialSpecification, "false") No
|
||||
</div>
|
||||
<span asp-validation-for="HasInitialSpecification" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<label asp-for="TopLevelAssessment" class="control-label"></label>
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
@Html.EditorFor(model => model.TopLevelAssessment, Model.TopLevelAssessment.TemplateName, additionalViewData: new { @class = "form-control" })
|
||||
<span asp-validation-for="TopLevelAssessment" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<div class="topLevelAssessmentText">
|
||||
<textarea asp-for="DataComplexityExpl" class="form-control" type="text" placeholder=""></textarea>
|
||||
<span asp-validation-for="DataComplexityExpl" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="topLevelAssessmentText">
|
||||
<textarea asp-for="SensitiveVariableExpl" class="form-control" type="text" placeholder=""></textarea>
|
||||
<span asp-validation-for="SensitiveVariableExpl" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="topLevelAssessmentText">
|
||||
<textarea asp-for="DetailedVariableExpl" class="form-control" type="text" placeholder=""></textarea>
|
||||
<span asp-validation-for="DetailedVariableExpl" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
<label asp-for="AnalysisExplanation" class="control-label"></label>
|
||||
<br />
|
||||
<textarea asp-for="AnalysisExplanation" class="form-control" type="text" placeholder=""></textarea>
|
||||
<span asp-validation-for="AnalysisExplanation" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
<div>
|
||||
<label asp-for="DataAnalyticalApproaches" class="control-label"></label><span asp-validation-for="DataAnalyticalApproaches" class="text-danger"></span>
|
||||
@Html.EditorFor(model => model.DataAnalyticalApproaches, Model.DataAnalyticalApproaches.TemplateName, additionalViewData: new { @class = "form-control" })
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
|
@ -0,0 +1,23 @@
|
|||
@model WebApplication.Models.Wizards.PIAWizardViewModel;
|
||||
<br />
|
||||
<div class="form-group">
|
||||
<h3>Identify The Disclosure Control Processes That Are Relevant To Your Data Situation</h3>
|
||||
<p><b>Disclosure control processes essentially attend to either or both of the two elements of your data situation: the data and i's environment. If your risk analysis in Component 6 suggests that you need stronger controls, then you have two (non-exclusive) choices:</b></p>
|
||||
<ol>
|
||||
<li><b>Reconfigure the data environment</b></li>
|
||||
<li><b>Modify the data, including possibly reducing the amount of data under consideration</b></li>
|
||||
</ol>
|
||||
|
||||
<label asp-for="ReconfigureEnvirontment" class="control-label"></label><span asp-validation-for="ReconfigureEnvirontment" class="text-danger"></span>
|
||||
@Html.EditorFor(model => model.ReconfigureEnvirontment, Model.ReconfigureEnvirontment.TemplateName, additionalViewData: new { @class = "form-control" })
|
||||
<label asp-for="ExplanationReconfigureEnvirontment" class="control-label"></label><span asp-validation-for="ExplanationReconfigureEnvirontment" class="text-danger"></span>
|
||||
<br />
|
||||
<textarea asp-for="ExplanationReconfigureEnvirontment" class="form-control" type="text" placeholder=""></textarea>
|
||||
|
||||
<label asp-for="ModifyingData" class="control-label"></label><span asp-validation-for="ModifyingData" class="text-danger"></span>
|
||||
@Html.EditorFor(model => model.ModifyingData, Model.ModifyingData.TemplateName, additionalViewData: new { @class = "form-control" })
|
||||
<label asp-for="ExplanationModifyingDataOptions" class="control-label"></label><span asp-validation-for="ExplanationModifyingDataOptions" class="text-danger"></span>
|
||||
<br />
|
||||
<textarea asp-for="ExplanationModifyingDataOptions" class="form-control" type="text" placeholder=""></textarea>
|
||||
|
||||
</div>
|
|
@ -0,0 +1,30 @@
|
|||
@model WebApplication.Models.Wizards.PIAWizardViewModel;
|
||||
<br />
|
||||
<div class="form-group">
|
||||
<h3>Impact Management Puts In Place A Plan For Reducing The Impact Of Such An Event Should It Happen</h3>
|
||||
<p><b>Identify your stakeholders and plan how you will communicate with them</b></p>
|
||||
<label asp-for="ImpactManagementReductionOptions" class="control-label"></label>
|
||||
@Html.EditorFor(model => model.ImpactManagementReductionOptions, Model.ImpactManagementReductionOptions.TemplateName, new { @class = "form-control" })
|
||||
<span asp-validation-for="ImpactManagementReductionOptions" class="text-danger"></span>
|
||||
<label asp-for="ImpactManagementExplanation" class="control-label"></label> <span asp-validation-for="ImpactManagementExplanation" class="text-danger"></span>
|
||||
<textarea asp-for="ImpactManagementExplanation" class="form-control" type="text" placeholder=""></textarea>
|
||||
|
||||
<br />
|
||||
|
||||
<h3>Plan What Happens After You Have Shared Or Released The Data</h3>
|
||||
<label asp-for="StakeholdersAssurance" class="control-label"></label>
|
||||
@Html.EditorFor(model => model.StakeholdersAssurance, Model.StakeholdersAssurance.TemplateName, additionalViewData: new { @class = "form-control" })
|
||||
<span asp-validation-for="StakeholdersAssurance" class="text-danger"></span>
|
||||
<label asp-for="StakeholderAssuranceExplanation" class="control-label"></label> <span asp-validation-for="StakeholderAssuranceExplanation" class="text-danger"></span>
|
||||
<textarea asp-for="StakeholderAssuranceExplanation" class="form-control" type="text" placeholder=""></textarea>
|
||||
|
||||
<br />
|
||||
|
||||
<label asp-for="PlanForDataBreachProcess" class="control-label"></label>
|
||||
@Html.EditorFor(model => model.PlanForDataBreachProcess, Model.PlanForDataBreachProcess.TemplateName, additionalViewData: new { @class = "form-control" })
|
||||
<span asp-validation-for="PlanForDataBreachProcess" class="text-danger"></span>
|
||||
<label asp-for="DataBreachPlanExplanation" class="control-label"></label><span asp-validation-for="DataBreachPlanExplanation" class="text-danger"></span>
|
||||
<textarea asp-for="DataBreachPlanExplanation" class="form-control" type="text" placeholder=""></textarea>
|
||||
</div>
|
||||
|
||||
|
|
@ -0,0 +1,637 @@
|
|||
@model WebApplication.Models.Wizards.PIAWizardViewModel;
|
||||
|
||||
<style>
|
||||
.title-table { padding: 0; padding-top:0.5rem;}
|
||||
|
||||
body {
|
||||
font-family: 'Helvetica Neue', Arial, sans-serif;
|
||||
color: #212529;
|
||||
font-size: 12pt;
|
||||
font-weight: normal;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="form-group summarySection" id="summaryBody">
|
||||
<h2>Privacy Impact Assessment Summary</h2>
|
||||
<br />
|
||||
|
||||
<div class="sa-pia-summary">
|
||||
<table class="w-50">
|
||||
<colgroup>
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Subject Area Name</th>
|
||||
<td>@Model.BelongingDataset</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Subject Area Short Code</th>
|
||||
<td>@Model.BelongingDatasetCode</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Data Set Privacy</th>
|
||||
<td id="privacyStatusValue">@Html.DisplayFor(model => model.DataSetPrivacyStatus)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="stepSection">
|
||||
|
||||
@* Step 1 *@
|
||||
<div class="step-heading">
|
||||
<h3>
|
||||
Introduction
|
||||
</h3>
|
||||
|
||||
<a class="btn btn-info editBtn" href="#step-1"><i class="fas fa-edit"></i>Edit</a>
|
||||
</div>
|
||||
|
||||
<div class="sectionBody">
|
||||
|
||||
<table class="col-12">
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.AuthorisedByDataSubjects)</p>
|
||||
<p class="pl-3 mb-0">@(@Model.AuthorisedByDataSubjects ? "Yes" : "No")</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.DataAgreementsSigned)</p>
|
||||
<p class="pl-3 mb-0">@(@Model.DataAgreementsSigned ? "Yes" : "No")</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.DataAgreementsLocation)</p>
|
||||
<p class="pl-3 mb-0">@Html.DisplayFor(model => model.DataAgreementsLocation)</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.UnderstandMetadata)</p>
|
||||
<p class="pl-3 mb-0">@(@Model.UnderstandMetadata ? "Yes" : "No")</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.AcceptResponsibility)</p>
|
||||
<p class="pl-3 mb-0">@(@Model.AcceptResponsibility ? "Yes" : "No")</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.DataSetDescription)</p>
|
||||
<p class="pl-3 mb-0">@Html.DisplayFor(model => model.DataSetDescription)</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.DataSourceLocation)</p>
|
||||
<p class="pl-3 mb-0">@Html.DisplayFor(model => model.DataSourceLocation)</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.SystemDataLocation)</p>
|
||||
<p class="pl-3 mb-0">@Html.DisplayFor(model => model.SystemDataLocation)</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.PhnId)</p>
|
||||
<p class="pl-3 mb-0">@Html.DisplayFor(model => model.PhnName)</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.OwningSystem)</p>
|
||||
<p class="pl-3 mb-0">@Html.DisplayFor(model => model.SourceSystemName)</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.SystemPurposeVersion)</p>
|
||||
<p class="pl-3 mb-0">@Html.DisplayFor(model => model.SystemPurposeVersionFullName)</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@* Step 2 *@
|
||||
<div class="stepSection stepsAfterIntro">
|
||||
|
||||
<div class="step-heading">
|
||||
<h3>
|
||||
Data Set Authorities
|
||||
</h3>
|
||||
|
||||
<a class="btn btn-info editBtn" href="#step-2"><i class="fas fa-edit"></i>Edit</a>
|
||||
</div>
|
||||
|
||||
<div class="sectionBody">
|
||||
|
||||
<table class="col-12">
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.DataOwner)</p>
|
||||
<p class="pl-3 mb-0">@Html.DisplayFor(model => model.DataOwner.DisplayName)</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.DataCustodian)</p>
|
||||
<p class="pl-3 mb-0">@Html.DisplayFor(model => model.DataCustodian.DisplayName)</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.DataStewards)</p>
|
||||
@for (int i = 0; i < Model.DataStewards.Count; i++)
|
||||
{
|
||||
<p class="pl-3 mb-0">@Html.DisplayFor(model => model.DataStewards[i].DisplayName)</p>
|
||||
}
|
||||
</th>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* Step 3 *@
|
||||
<div class="stepSection stepsAfterIntro">
|
||||
|
||||
<div class="step-heading">
|
||||
<h3>
|
||||
Understanding Your Legal Responsibility
|
||||
</h3>
|
||||
|
||||
<a class="btn btn-info editBtn" href="#step-3"><i class="fas fa-edit"></i>Edit</a>
|
||||
</div>
|
||||
|
||||
<div class="sectionBody">
|
||||
|
||||
<table class="col-12">
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.SupportingDocumentationLocation)</p>
|
||||
<p class="pl-3">@Html.DisplayFor(model => model.SupportingDocumentationLocation)</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.DataPersonalOrDeIdentified)</p>
|
||||
<p class="pl-3">@Html.DisplayFor(model => model.DataPersonalOrDeIdentified)</p>
|
||||
</th>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div>
|
||||
<p>
|
||||
<h4> Data Suppression Rules</h4>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<table class="col-12">
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.SuppressionRuleOne)</p>
|
||||
<p class="pl-3 mb-2">@Html.DisplayFor(model => model.SuppressionRuleOne).</p>
|
||||
<p class="pl-3">@Html.DisplayFor(model => model.SuppressionRuleOneExpl)</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.SuppressionRuleTwo)</p>
|
||||
<p class="pl-3 mb-2">@Html.DisplayFor(model => model.SuppressionRuleTwo).</p>
|
||||
<p class="pl-3">@Html.DisplayFor(model => model.SuppressionRuleTwoExpl)</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.SuppressionRuleThree)</p>
|
||||
<p class="pl-3 mb-2">@Html.DisplayFor(model => model.SuppressionRuleThree).</p>
|
||||
<p class="pl-3">@Html.DisplayFor(model => model.SuppressionRuleThreeExpl)</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.SuppressionRuleFour)</p>
|
||||
<p class="pl-3 mb-2">@Html.DisplayFor(model => model.SuppressionRuleFour).</p>
|
||||
<p class="pl-3">@Html.DisplayFor(model => model.SuppressionRuleFourExpl)</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.IdentifiedDataCustodianManagedRisk)</p>
|
||||
<p class="pl-3">@(@Model.IdentifiedDataCustodianManagedRisk ? "Yes" : "No")</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.DataRiskManagementExplanation)</p>
|
||||
<p class="pl-3 mb-2">
|
||||
@Html.DisplayFor(model => model.DataRiskManagementExplanation)
|
||||
</p>
|
||||
</th>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* Step 4 *@
|
||||
<div class="stepSection stepsAfterIntro">
|
||||
|
||||
<div class="step-heading">
|
||||
<h3>
|
||||
Metadata
|
||||
</h3>
|
||||
|
||||
<a class="btn btn-info editBtn" href="#step-4"><i class="fas fa-edit"></i>Edit</a>
|
||||
</div>
|
||||
|
||||
<div class="sectionBody">
|
||||
|
||||
<table class="col-12">
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.DataForm)</p>
|
||||
<p class="pl-3 mb-0">@Html.DisplayFor(model => model.DataForm)</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.InfoLevelOfData)</p>
|
||||
<p class="pl-3 mb-0">@Html.DisplayFor(model => model.InfoLevelOfData)</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.InfoLevelOfVariable)</p>
|
||||
<p class="pl-3 mb-0">@Html.DisplayFor(model => model.InfoLevelOfVariable)</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12 row-multiselect-options">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.TopThreePropsOfData)</p>
|
||||
|
||||
@foreach (var opt in Model.TopThreePropsOfData.Options)
|
||||
{
|
||||
<div class="col-6">
|
||||
@Html.DisplayFor(m => opt.Option)
|
||||
@Html.DisplayFor(m => opt.IsSelected)
|
||||
<br />
|
||||
</div>
|
||||
}
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.IsDataQualityLevelHigh)</p>
|
||||
<p class="pl-3 mb-2">
|
||||
@(@Model.IsDataQualityLevelHigh ? "Yes" : "No")
|
||||
</p>
|
||||
<p class="pl-3 mb-2">
|
||||
@Html.DisplayFor(model => model.DataQualityExplanation)
|
||||
</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.IsDataAboutVulnerablePopulation)</p>
|
||||
<p class="pl-3 mb-2">
|
||||
@(@Model.IsDataAboutVulnerablePopulation ? "Yes" : "No")
|
||||
</p>
|
||||
<p class="pl-3 mb-2">
|
||||
@Html.DisplayFor(model => model.PopulationVulnerabilityExplanation)
|
||||
</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.WhenDataWasCollected)</p>
|
||||
<p class="pl-3 mb-2">
|
||||
@Html.DisplayFor(model => model.WhenDataWasCollected)
|
||||
</p>
|
||||
<p class="pl-3 mb-2">
|
||||
@Html.DisplayFor(model => model.DataTimeCollectedExplanation)
|
||||
</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.IsDataHierarchical)</p>
|
||||
<p class="pl-3 mb-2">
|
||||
@(@Model.IsDataHierarchical ? "Yes" : "No")
|
||||
</p>
|
||||
<p class="pl-3 mb-2">
|
||||
@Html.DisplayFor(model => model.DataHierarchicalExplanation)
|
||||
</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.IsDataTimeStamped)</p>
|
||||
<p class="pl-3 mb-2">
|
||||
@(@Model.IsDataTimeStamped ? "Yes" : "No")
|
||||
</p>
|
||||
<p class="pl-3 mb-2">
|
||||
@Html.DisplayFor(model => model.DataTimestampedExplanation)
|
||||
</p>
|
||||
</th>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@* Step 5 *@
|
||||
<div class="stepSection stepsAfterIntro">
|
||||
|
||||
<div class="step-heading">
|
||||
<h3>
|
||||
Meet Your Ethical Obligations
|
||||
</h3>
|
||||
|
||||
<a class="btn btn-info editBtn" href="#step-5"><i class="fas fa-edit"></i>Edit</a>
|
||||
</div>
|
||||
|
||||
<div class="sectionBody">
|
||||
|
||||
<table class="col-12">
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.RespectDataAssurances)</p>
|
||||
<p class="pl-3 mb-2">
|
||||
@Html.DisplayFor(model => model.RespectDataAssurances)
|
||||
</p>
|
||||
<p class="pl-3 mb-2">
|
||||
@Html.DisplayFor(model => model.DataAssuranceExplanation)
|
||||
</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.HasProvidedTransparency)</p>
|
||||
<p class="pl-3 mb-2">
|
||||
@Html.DisplayFor(model => model.HasProvidedTransparency)
|
||||
</p>
|
||||
<p class="pl-3 mb-2">
|
||||
@Html.DisplayFor(model => model.ProvidedTransparencyExplanation)
|
||||
</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.HasObtainedSubjectsViews)</p>
|
||||
<p class="pl-3 mb-2">
|
||||
@Html.DisplayFor(model => model.HasObtainedSubjectsViews)
|
||||
</p>
|
||||
<p class="pl-3 mb-2">
|
||||
@Html.DisplayFor(model => model.SubjectsViewsExplanation)
|
||||
</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@* Step 6 *@
|
||||
<div class="stepSection stepsAfterIntro privateSectionSummary">
|
||||
|
||||
<div class="step-heading">
|
||||
<h3>
|
||||
Identify The Processes You Will Need To Go Through
|
||||
</h3>
|
||||
|
||||
<a class="btn btn-info editBtn" href="#step-6"><i class="fas fa-edit"></i>Edit</a>
|
||||
</div>
|
||||
|
||||
<div class="sectionBody">
|
||||
|
||||
<table class="col-12">
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.HasInitialSpecification)</p>
|
||||
<p class="pl-3 mb-0">@Html.DisplayFor(model => model.HasInitialSpecification)</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12 row-multiselect-options">
|
||||
<div class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.TopLevelAssessment)</p>
|
||||
</div>
|
||||
@for (int i = 0; i < Model.TopLevelAssessment.Options.Count; i++)
|
||||
{
|
||||
<div class="p-3 col-4 mb-2">
|
||||
@Html.DisplayFor(m => Model.TopLevelAssessment.Options[i].Option)
|
||||
@Html.DisplayFor(m => Model.TopLevelAssessment.Options[i].IsSelected)
|
||||
</div>
|
||||
<div class="p-3 col-8 pl-3">
|
||||
@if (i == 0)
|
||||
{
|
||||
@Html.DisplayFor(model => model.DataComplexityExpl)
|
||||
}
|
||||
@if (i == 1)
|
||||
{
|
||||
@Html.DisplayFor(model => model.SensitiveVariableExpl)
|
||||
}
|
||||
@if (i == 2)
|
||||
{
|
||||
@Html.DisplayFor(model => model.DetailedVariableExpl)
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12">
|
||||
<p class="mb-2">@Html.DisplayNameFor(model => model.AnalysisExplanation)</p>
|
||||
<p class="p-2 pl-3 mb-2">
|
||||
@Html.DisplayFor(model => model.AnalysisExplanation)
|
||||
</p>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12 row-multiselect-options">
|
||||
<div class="mb-2 col-12">@Html.DisplayNameFor(model => model.DataAnalyticalApproaches)</div>
|
||||
@foreach (var opt in Model.DataAnalyticalApproaches.Options)
|
||||
{
|
||||
<div class="col-6">
|
||||
@Html.DisplayFor(m => opt.Option)
|
||||
@Html.DisplayFor(m => opt.IsSelected)
|
||||
<br />
|
||||
</div>
|
||||
}
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* Step 7 *@
|
||||
<div class="stepSection stepsAfterIntro privateSectionSummary">
|
||||
<div class="step-heading">
|
||||
<h3>
|
||||
Identify The Disclosure Control Processes That Are Relevant To Your Data Situation
|
||||
</h3>
|
||||
|
||||
<a class="btn btn-info editBtn" href="#step-7"><i class="fas fa-edit"></i>Edit</a>
|
||||
</div>
|
||||
|
||||
<div class="sectionBody">
|
||||
|
||||
<table>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12 row-multiselect-options">
|
||||
<div class="mb-2 col-12">@Html.DisplayNameFor(model => model.ReconfigureEnvirontment)</div>
|
||||
@foreach (var opt in Model.ReconfigureEnvirontment.Options)
|
||||
{
|
||||
<div class="col-6">
|
||||
@Html.DisplayFor(m => opt.Option)
|
||||
@Html.DisplayFor(m => opt.IsSelected)
|
||||
<br/>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="p-3 mb-2 pl-3 col-12">@Html.DisplayFor(model => model.ExplanationReconfigureEnvirontment)</div>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12 row-multiselect-options">
|
||||
<div class="mb-2 col-12">@Html.DisplayNameFor(model => model.ModifyingData)</div>
|
||||
@foreach (var opt in Model.ModifyingData.Options)
|
||||
{
|
||||
<div class="col-6">
|
||||
@Html.DisplayFor(m => opt.Option)
|
||||
@Html.DisplayFor(m => opt.IsSelected)
|
||||
<br />
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="p-3 mb-2 pl-3 col-12">@Html.DisplayFor(model => model.ExplanationModifyingDataOptions)</div>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* Step 8 *@
|
||||
<div class="stepSection stepsAfterIntro privateSectionSummary">
|
||||
<div class="step-heading">
|
||||
<h3>
|
||||
Impact Management Puts In Place A Plan For Reducing The Impact Of Such An Event Should It Happen
|
||||
</h3>
|
||||
|
||||
<a class="btn btn-info editBtn" href="#step-8"><i class="fas fa-edit"></i>Edit</a>
|
||||
</div>
|
||||
|
||||
<div class="sectionBody">
|
||||
|
||||
<table>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12 row-multiselect-options">
|
||||
<div class="mb-2 col-12">@Html.DisplayNameFor(model => model.ImpactManagementReductionOptions)</div>
|
||||
@foreach (var opt in Model.ImpactManagementReductionOptions.Options)
|
||||
{
|
||||
<div class="col-6">
|
||||
@Html.DisplayFor(m => opt.Option)
|
||||
@Html.DisplayFor(m => opt.IsSelected)
|
||||
<br/>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="p-3 mb-2 pl-3 col-12">@Html.DisplayFor(model => model.ImpactManagementExplanation)</div>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12 row-multiselect-options">
|
||||
<div class="mb-2 col-12">@Html.DisplayNameFor(model => model.StakeholdersAssurance)</div>
|
||||
@foreach (var opt in Model.StakeholdersAssurance.Options)
|
||||
{
|
||||
<div class="col-6">
|
||||
@Html.DisplayFor(m => opt.Option)
|
||||
@Html.DisplayFor(m => opt.IsSelected)
|
||||
<br />
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="p-3 mb-2 pl-3 col-12">@Html.DisplayFor(model => model.StakeholderAssuranceExplanation)</div>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr class="row col-12">
|
||||
<th class="col-12 row-multiselect-options">
|
||||
<div class="mb-2 col-12">@Html.DisplayNameFor(model => model.PlanForDataBreachProcess)</div>
|
||||
@foreach (var opt in Model.PlanForDataBreachProcess.Options)
|
||||
{
|
||||
<div class="col-6">
|
||||
@Html.DisplayFor(m => opt.Option)
|
||||
@Html.DisplayFor(m => opt.IsSelected)
|
||||
<br />
|
||||
</div>
|
||||
}
|
||||
<br />
|
||||
<div class="p-3 mb-2 pl-3 col-12">@Html.DisplayFor(model => model.DataBreachPlanExplanation)</div>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
|
@ -8,13 +8,21 @@
|
|||
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Components\**" />
|
||||
<Compile Remove="Controllers\Enums\**" />
|
||||
<Compile Remove="Models\Stats\**" />
|
||||
<Compile Remove="Templates\**" />
|
||||
<Content Remove="Components\**" />
|
||||
<Content Remove="Controllers\Enums\**" />
|
||||
<Content Remove="Models\Stats\**" />
|
||||
<Content Remove="Templates\**" />
|
||||
<EmbeddedResource Remove="Components\**" />
|
||||
<EmbeddedResource Remove="Controllers\Enums\**" />
|
||||
<EmbeddedResource Remove="Models\Stats\**" />
|
||||
<EmbeddedResource Remove="Templates\**" />
|
||||
<None Remove="Components\**" />
|
||||
<None Remove="Controllers\Enums\**" />
|
||||
<None Remove="Models\Stats\**" />
|
||||
<None Remove="Templates\**" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -60,11 +68,8 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Controllers\Enums\" />
|
||||
<Folder Include="wwwroot\js\bswindow\" />
|
||||
<Folder Include="Models\Stats\" />
|
||||
<Folder Include="wwwroot\fonts\" />
|
||||
<Folder Include="Components\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -273,8 +273,4 @@ var bsWindow = {
|
|||
]
|
||||
});
|
||||
} // prompt();
|
||||
<<<<<<< HEAD
|
||||
};
|
||||
=======
|
||||
};
|
||||
>>>>>>> main
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
$.validator.addMethod('must-be-true', function (value, element, params) {
|
||||
return element.checked;
|
||||
});
|
||||
|
||||
$.validator.unobtrusive.adapters.add('must-be-true', [], function (options) {
|
||||
options.rules['must-be-true'] = {};
|
||||
options.messages['must-be-true'] = options.message;
|
||||
});
|
|
@ -0,0 +1,53 @@
|
|||
$(document).ready(function () {
|
||||
|
||||
// SmartWizard initialize
|
||||
$('#smartwizard').smartWizard({
|
||||
theme: 'dots',
|
||||
keyboardSettings: {
|
||||
keyNavigation: false // set this to true for seamless dev exp
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$('#smartwizard').smartWizard("reset");
|
||||
|
||||
|
||||
|
||||
|
||||
// display preview of task group details
|
||||
$('#efnTgSelector').change(function () {
|
||||
var optionSelected = $("option:selected", this);
|
||||
var valueSelected = this.value;
|
||||
|
||||
|
||||
$.ajax({
|
||||
async: true,
|
||||
type: "GET",
|
||||
url: "/TaskGroup/GetTaskGroupDetails/" + valueSelected,
|
||||
success: function (partialView) {
|
||||
$('#taskGroupDetails').html(partialView);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("#smartwizard").on("stepContent",
|
||||
function (e, anchorObject, stepIndex, stepDirection) {
|
||||
var requestedStep = "EFNStep" + (stepIndex + 1);
|
||||
var converter = new showdown.Converter();
|
||||
|
||||
$.ajax({
|
||||
async: true,
|
||||
data: { fileName: requestedStep },
|
||||
type: "GET",
|
||||
url: "/Help/GetHelp/",
|
||||
success: function (content) {
|
||||
content = converter.makeHtml(content);
|
||||
$('#helpContent').html(content);
|
||||
},
|
||||
error: function () {
|
||||
var error = "Content was not found.";
|
||||
$('#helpContent').html(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,310 @@
|
|||
|
||||
var globalModelData;
|
||||
|
||||
function getPIAWizardModelData(userObj) {
|
||||
globalModelData = userObj;
|
||||
}
|
||||
|
||||
var piaDatePicker = document.getElementById("piaDatePicker");
|
||||
var dateNow = moment().format("YYYY-MM-DD");
|
||||
|
||||
piaDatePicker.max = dateNow;
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
// SmartWizard initialize
|
||||
$('#smartwizard').smartWizard({
|
||||
theme: 'dots',
|
||||
autoAdjustHeight: true,
|
||||
justified: true,
|
||||
toolbarSettings: {
|
||||
toolbarPosition: 'both', // none, top, bottom, both
|
||||
showNextButton: false,
|
||||
toolbarExtraButtons: [
|
||||
// Save & Next Button
|
||||
// Used to progress to the next page while also posting form
|
||||
$('<button></button>').text('Save & Next')
|
||||
.addClass('btn btn-info')
|
||||
.attr('id', 'saveNext')
|
||||
.on('click', function () {
|
||||
submit(1);
|
||||
}),
|
||||
|
||||
// Save Button
|
||||
// Used to save the current page without moving
|
||||
$('<button></button>').text('Save')
|
||||
.addClass('btn btn-info')
|
||||
.attr('id', 'piaSave')
|
||||
.on('click', function () {
|
||||
submit(0);
|
||||
}),
|
||||
]
|
||||
},
|
||||
keyboardSettings: {
|
||||
keyNavigation: false, // set this to true for seamless dev exp
|
||||
}
|
||||
});
|
||||
|
||||
$('#smartwizard').smartWizard("reset");
|
||||
|
||||
$("#smartwizard").on("stepContent", function (e, anchorObject, stepIndex, stepDirection) {
|
||||
if (stepIndex === 8) {
|
||||
$("#saveNext").prop("disabled", true);
|
||||
$("#piaSave").prop("disabled", true);
|
||||
$(".toolbar-bottom").hide();
|
||||
} else {
|
||||
$("#saveNext").prop("disabled", false);
|
||||
$("#piaSave").prop("disabled", false);
|
||||
$(".toolbar-bottom").show();
|
||||
initValidatorStep(stepIndex); // todo: this could be better implemented
|
||||
}
|
||||
});
|
||||
|
||||
$('#submitBtn').on('click', function () {
|
||||
submit(1);
|
||||
});
|
||||
|
||||
window.onbeforeunload = setOnBeforeUnload;
|
||||
|
||||
function setOnBeforeUnload() {
|
||||
return "Leaving this page will reset the data in the wizard";
|
||||
};
|
||||
|
||||
|
||||
function submit(submissionMode) {
|
||||
window.onbeforeunload = null;
|
||||
// Set Step Value before submitting - so we know which page to move on from
|
||||
$("#Step").val($('#smartwizard').smartWizard("getStepIndex"));
|
||||
// Set submission method to next so we move after submitting
|
||||
$("#SubmissionMethod").val(submissionMode);
|
||||
// Post form - will refresh the page to the same step
|
||||
$("#smartwizard > form").submit();
|
||||
}
|
||||
|
||||
// If current step is greater than the max step
|
||||
// This will happen after using the save & next button
|
||||
if (globalModelData.step > globalModelData.maxStep) {
|
||||
// Update max step
|
||||
$("#MaxStep").val(globalModelData.step);
|
||||
}
|
||||
|
||||
// Iterate over steps we've seen and set them to active
|
||||
// If we don't do this then all steps but 1 will be disabled
|
||||
for (let i = 0; i < eval($("#MaxStep").val()); i++) {
|
||||
$('#smartwizard').smartWizard("goToStep", i);
|
||||
}
|
||||
|
||||
// Initialize the leaveStep event
|
||||
$("#smartwizard").on("leaveStep", function (e, anchorObject, currentStepIndex, nextStepIndex, stepDirection) {
|
||||
// When going forward
|
||||
if (stepDirection == "forward") {
|
||||
// If moving to a page we've never seen before
|
||||
if (nextStepIndex + 1 > $("#MaxStep").val()) {
|
||||
// Update the max step counter
|
||||
$("#MaxStep").val(nextStepIndex + 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$('#addSteward').click(function () {
|
||||
window.onbeforeunload = null;
|
||||
$.ajax({
|
||||
async: true,
|
||||
data: $('#form').serialize(),
|
||||
type: "POST",
|
||||
url: "/Wizards/AddDataSteward",
|
||||
success: function (partialView) {
|
||||
$('#stewardsContainer').html(partialView);
|
||||
window.onbeforeunload = setOnBeforeUnload;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#removeSteward').click(function () {
|
||||
window.onbeforeunload = null;
|
||||
$.ajax({
|
||||
async: true,
|
||||
data: $('#form').serialize(),
|
||||
type: "POST",
|
||||
url: "/Wizards/RemoveDataSteward",
|
||||
success: function (partialView) {
|
||||
$('#stewardsContainer').html(partialView);
|
||||
window.onbeforeunload = setOnBeforeUnload;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// After initialization jump to the step we want (on refresh, start at the beginning)
|
||||
$('#smartwizard').smartWizard("goToStep",
|
||||
Math.max(0, Math.min(globalModelData.step, $("#smartwizard > ul")[0].children.length - 1)));
|
||||
|
||||
$("#smartwizard").on("stepContent",
|
||||
function (e, anchorObject, stepIndex, stepDirection) {
|
||||
var requestedStep = "PIAStep" + (stepIndex + 1);
|
||||
var converter = new showdown.Converter();
|
||||
|
||||
$.ajax({
|
||||
async: true,
|
||||
data: { fileName: requestedStep },
|
||||
type: "GET",
|
||||
url: "/Help/GetHelp/",
|
||||
success: function (content) {
|
||||
content = converter.makeHtml(content);
|
||||
$('#helpContent').html(content);
|
||||
},
|
||||
error: function () {
|
||||
var error = "Content was not found.";
|
||||
$('#helpContent').html(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// if maxstep is 0/start then display modal
|
||||
var maxStepVal = $("#MaxStep")[0].value;
|
||||
if (maxStepVal == 0) {
|
||||
$('#PIAProcessModal').modal('show');
|
||||
}
|
||||
|
||||
// enable other option input if clicked
|
||||
var otherOption = $("#ImpactManagementReductionOptions_Options_4__IsSelected")[0];
|
||||
var option = $("#ImpactManagementOtherOption");
|
||||
|
||||
if (otherOption.checked === false) {
|
||||
option.prop("readonly", true);
|
||||
}
|
||||
|
||||
otherOption.addEventListener("change", function () {
|
||||
if (this.checked) {
|
||||
option.prop("readonly", false);
|
||||
} else {
|
||||
option.val("");
|
||||
option.prop("readonly", true);
|
||||
}
|
||||
});
|
||||
|
||||
// enable suppression questions if clicked
|
||||
var deIdentifiedDataBox = $(":radio[value=deidentified]")[0];
|
||||
var personalDataBox = $(":radio[value=personal]")[0];
|
||||
|
||||
deIdentifiedDataBox.addEventListener("change", function () {
|
||||
$("#suppressionRuleQuestions").each(function () {
|
||||
var input = $(this).find(':input');
|
||||
|
||||
input.prop("disabled", false);
|
||||
});
|
||||
});
|
||||
|
||||
personalDataBox.addEventListener("change", function () {
|
||||
$("#suppressionRuleQuestions").each(function () {
|
||||
var input = $(this).find(':input').val("");
|
||||
|
||||
input.prop("checked", false);
|
||||
input.prop("disabled", true);
|
||||
});
|
||||
});
|
||||
|
||||
if (personalDataBox.checked) {
|
||||
// Create a new 'change' event and dispatch it
|
||||
var event = new Event('change');
|
||||
personalDataBox.dispatchEvent(event);
|
||||
}
|
||||
|
||||
// sets phn system purpose version name text inputs
|
||||
// todo: rewrite this, make it cleaner
|
||||
var tgSelection = document.getElementById("tgSelection");
|
||||
var dsSelection = document.getElementById("dsSelection");
|
||||
|
||||
function insertText(event) {
|
||||
var source = event.target || event.srcElement;
|
||||
|
||||
var dropdownText = source.options[source.selectedIndex].text;
|
||||
|
||||
if (dropdownText === "Select Your Source System" || dropdownText === "Select Your PHN Zone") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (source.id === "dsSelection") {
|
||||
var datasetField = document.getElementById("spvPhnName");
|
||||
var phnNameField = document.getElementById("hiddenPhnName");
|
||||
|
||||
datasetField.value = dropdownText; // set the hidden field to make form save properly
|
||||
phnNameField.value = dropdownText;
|
||||
} else {
|
||||
var taskGroupField = document.getElementById("spdSourceSystemName");
|
||||
taskGroupField.value = dropdownText;
|
||||
}
|
||||
}
|
||||
|
||||
tgSelection.addEventListener("change", insertText);
|
||||
dsSelection.addEventListener("change", insertText);
|
||||
|
||||
|
||||
function incrementMaxStepIfZero() {
|
||||
var maxStepVal = $("#MaxStep")[0].value;
|
||||
|
||||
var newStepVal = maxStepVal == 0 ? 1 : maxStepVal;
|
||||
|
||||
$("#MaxStep")[0].value = newStepVal;
|
||||
}
|
||||
|
||||
function TogglePrivateDataSetStepsActive(privacyStatus) {
|
||||
if (privacyStatus == "Public") {
|
||||
disablePrivateSteps();
|
||||
} else {
|
||||
$('#privacyStatus').val("Private");
|
||||
$('.privateDataSetWarning').hide();
|
||||
$(".privateSectionSummary").show();
|
||||
$('.privateDataSetStep :input').show();
|
||||
}
|
||||
}
|
||||
|
||||
// ON LOAD - disable summary steps/questions which are exclusive to private data sets
|
||||
var privacyStatus = $("#DataSetPrivacyStatus option:selected").text();
|
||||
|
||||
if (privacyStatus == "Public") {
|
||||
disablePrivateSteps();
|
||||
}
|
||||
|
||||
function disablePrivateSteps() {
|
||||
$('.privateDataSetWarning').show();
|
||||
$(".privateSectionSummary").hide();
|
||||
}
|
||||
|
||||
var privacyDropDown = document.getElementById("DataSetPrivacyStatus");
|
||||
function showSkipButton() {
|
||||
$('.privateDataSetWarning').show();
|
||||
$('.skipToSummary').show();
|
||||
}
|
||||
privacyDropDown.onchange = showSkipButton;
|
||||
|
||||
var privacyStatusIcon = document.getElementById("privacyModalBtn");
|
||||
function showPIAModal() {
|
||||
$('#PIAProcessModal').modal('show');
|
||||
}
|
||||
privacyStatusIcon.onclick = showPIAModal;
|
||||
|
||||
function initValidatorStep(stepIndex) {
|
||||
var stepNumber = `#step-${ ++stepIndex }`;
|
||||
|
||||
initValidator(stepNumber);
|
||||
}
|
||||
|
||||
function initValidator(element) {
|
||||
var validator = $(element).validate();
|
||||
|
||||
$(element).find("input[type=text], input[type=date], :radio, textarea, select").on("change", function () {
|
||||
_validate(element, validator);
|
||||
});
|
||||
}
|
||||
function _validate(element, validator) {
|
||||
var invalidCount = 0;
|
||||
|
||||
$(element).find("input[type=text], input[type=date], :radio, textarea, select").not(":hidden").each(function () {
|
||||
var x = !validator.check(this);
|
||||
if (x)
|
||||
invalidCount++;
|
||||
});
|
||||
|
||||
return invalidCount === 0;
|
||||
}
|
||||
});
|
|
@ -0,0 +1,72 @@
|
|||
|
||||
tippy('#dataStewardTT',
|
||||
{
|
||||
appendTo: "parent",
|
||||
content: "A position designated with overall accountability and responsibility for decision making in relation to the data set, data collection and / or application allocated and the ongoing capture, compliance, development, management, care and maintenance of data to support business needs."
|
||||
});
|
||||
|
||||
tippy("#dataOwnerTT",
|
||||
{
|
||||
appendTo: "parent",
|
||||
content: "The responsible officer who is identified as having the authority and accountability under legislation, regulation\r\nand/or policy, for the collection and management of data and information as an asset."
|
||||
});
|
||||
|
||||
tippy('#dataCustodianTT',
|
||||
{
|
||||
appendTo: "parent",
|
||||
content: "An entity which handles data. For the purposes of this document, we assume that data custodians are handling data that contains (or is derived from) personal information. This definition may have different interpretations depending on which State or jurisdiction within Australia a PHN is in."
|
||||
});
|
||||
|
||||
tippy('#penetrationTestTT',
|
||||
{
|
||||
appendTo: "parent",
|
||||
content: "A component of disclosure risk assessment involving replicating what a plausible motivated intruder might do (and the auxiliary information and resources they might have) to execute a re-identification and/or disclosure attack on some data. Also known as intruder test."
|
||||
});
|
||||
|
||||
tippy('#personalInfoTT',
|
||||
{
|
||||
appendTo: "parent",
|
||||
content: "A term defined in Section 6(1) of the Privacy Act, which ‘means any information or an opinion about an identified individual, or an individual who is reasonably identifiable."
|
||||
});
|
||||
|
||||
tippy('#sensitiveInfoTT',
|
||||
{
|
||||
appendTo: "parent",
|
||||
content: "A defined category of personal information under the Privacy Act, this includes information or opinion about a person’s racial or ethnic origin, political opinion, religious or philosophical beliefs, sexual orientation, criminal record and health, genetic and/or biometric information. "
|
||||
});
|
||||
|
||||
tippy('#microdataTT',
|
||||
{
|
||||
appendTo: "parent",
|
||||
content: "A microdata set consists of a set of records containing information on individual data subjects. Each record may contain hundreds or even thousands of pieces of information."
|
||||
});
|
||||
|
||||
tippy('#intruderTT',
|
||||
{
|
||||
appendTo: "parent",
|
||||
content: "A data user who attempts to disclose information about a data subject through identification and/or attribute disclosure."
|
||||
});
|
||||
|
||||
tippy('#indirectIdTT',
|
||||
{
|
||||
appendTo: "parent",
|
||||
content: "A variable that can be used to identify an individual with a high probability, either alone or together with other indirect identifiers, and in combination with auxiliary information. Almost any variable can be an indirect identifier, depending on the auxiliary information available to the intruder."
|
||||
});
|
||||
|
||||
tippy('#hierarchicalTT',
|
||||
{
|
||||
appendTo: "parent",
|
||||
content: "This is data that contains information for members of a group (members of a household, or data linked to locations) who are linked with one another.The data is considered riskier because they provide(more) information that might make a data subject unique in a dataset and as such potentially identifiable."
|
||||
});
|
||||
|
||||
tippy('#vulnerableDataTT',
|
||||
{
|
||||
appendTo: "parent",
|
||||
content: "A vulnerable group is one where its members lack capacity (partial or full) to make informed decisions on their own behalf.Examples of vulnerable groups include children, adults with mental impairments or subpopulations constructed out of a category that would itself be considered sensitive – for example a minority ethnic group."
|
||||
});
|
||||
|
||||
tippy('#directIdTT',
|
||||
{
|
||||
appendTo: "parent",
|
||||
content: "A variable that can be used to uniquely identify an individual, either alone or together with other direct identifiers, and often in combination with other readily available information."
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
$(document).ready(function () {
|
||||
|
||||
$('.editBtn').hide();
|
||||
|
||||
// disable summary steps which are exclusive to private data sets
|
||||
if ($('#privacyStatusValue')[0].textContent.trim() == "Public") {
|
||||
$(".privateSectionSummary").hide();
|
||||
}
|
||||
|
||||
$('#PIASUmmaryPrintBtn').on('click',
|
||||
function() {
|
||||
window.print();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
function setDisplayName(_this) {
|
||||
|
||||
var numbers = _this.name.match(/\d+/g).map(Number);
|
||||
|
||||
// get the index from the name attribute of selector
|
||||
const index = numbers[0];
|
||||
|
||||
if (index !== null) {
|
||||
const dNameFieldId = (`DataStewards_${index}__DisplayName`);
|
||||
|
||||
// get the display name of the selected object
|
||||
const dName = $(_this).find(":selected").text();
|
||||
|
||||
$(`#${dNameFieldId}`)[0].value = dName;
|
||||
}
|
||||
}
|
||||
|
||||
function setOwnerDisplayName(_this) {
|
||||
|
||||
let dNameField = "";
|
||||
const dName = $(_this).find(":selected").text();
|
||||
|
||||
if (_this.name.startsWith("DataOwner")) {
|
||||
dNameField = $("#ownerName")[0];
|
||||
} else if (_this.name.startsWith("DataCustodian")) {
|
||||
dNameField = $("#custodianName")[0];
|
||||
}
|
||||
|
||||
dNameField.value = dName;
|
||||
}
|
|
@ -292,6 +292,23 @@ function DataTablesGridStart(GridOptions, PrepOptions) {
|
|||
return item;
|
||||
};
|
||||
}
|
||||
|
||||
if (value.ads_format.toLowerCase() === "discoverability") {
|
||||
GridOptions.GridColumns[index].render = function (data, type, row, meta) {
|
||||
|
||||
if (data === 0) {
|
||||
return "None";
|
||||
}
|
||||
|
||||
if (data === 1) {
|
||||
return "My PHN";
|
||||
}
|
||||
|
||||
if (data === 3) {
|
||||
return "Global";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
var options = {
|
||||
mode: 'code'
|
||||
};
|
||||
|
||||
var container = document.getElementById("jsoneditor");
|
||||
var editor = new JSONEditor(container, options);
|
||||
|
||||
function setJSON() {
|
||||
var json = editor.get();
|
||||
var input = document.getElementById("jsonInput");
|
||||
input.value = json;
|
||||
|
||||
}
|
||||
|
||||
$('#tmSubmit').on('click', function () {
|
||||
event.preventDefault();
|
||||
mapJsonInputToForm();
|
||||
});
|
||||
|
||||
function mapJsonInputToForm() {
|
||||
var json = JSON.stringify(editor.get());
|
||||
|
||||
$('#jsonInput')[0].value = json;
|
||||
|
||||
|
||||
$('#tmForm').submit();
|
||||
}
|
Загрузка…
Ссылка в новой задаче