The MTOA Web Service Framework makes it easier to bootstrap a Web Service Project that:
Uses MTOA for API KEY authentication and authorization
Has out-of-the-box integration with MTOA Web Service Capabilities
Supports both .NET Framework and .NET Core
Initialization
The MTOA Web Service Framework needs to be initialized as follows
In Global.asax.cs
protected void Application_Start(){ var container = UnityConfig.Container; int myServiceId = 11; // Register your service ServiceFramework.Initialize(container, myServiceId); AreaRegistration.RegisterAllAreas(); GlobalConfiguration.Configuration.Filters.Add(new WebServiceExceptionFilter()); }
In UnityConfig.cs
public static void RegisterTypes(IUnityContainer container){ // NOTE: To load from web.config uncomment the line below // Make sure to add a Unity.Configuration to the using statements. // container.LoadConfiguration(); // TODO: Register your type's mappings here. // container.RegisterType<IProductRepository, ProductRepository>(); ServiceFramework.RegisterTypes(container); }
Configure the API Key in the Web.Config file
<appSettings> <add key="MtoaWebServiceUrl" value="https://wwwappstest.tc.gc.ca/Saf-Sec-Sur/13/MTAPI-PLATFORM-TEST/api/" /> <add key="MtoaWebServiceApiKey" value="--- YOUR API KEY ---" /> <add key="MtoaWebServiceJwt" value="--- YOUR JWT ---" /> </appSettings>
API Key Authorization
By default, the MTOA Web Service Framework authorizes API keys that are bound to the same service specified during the Web Service initialization.
For example, if the MTOA Web Service Framework has been initialized with the following call, it means that the caller application needs to be bound to the “RTMR” service for the call to be successful.
ServiceFramework.Initialize(container, rtmrServiceId);
If a Web Service needs to interact with an application that isn’t bound to that specific service (in this example rtmrServiceId), the WEBSERVICE_FRAMEWORK_ALLOWED_SERVICES Service Setting can be used to specify the service name to authorize.
By specifying this Service Setting (using the MTOA ServiceSettingAPI), the Web Service will accepts both the MTOA API KEY and the RTMR API KEY (even if our Web Service was associated with the MTOA service).
Using myTC Account Nuget Packages
If you don’t have access to TC NuGet, go to:
Tools > Options > NuGet Package Manager > Package Sources and add a reference to http://nuget.tc.gc.ca/nuget.
To install the myTC Account NuGet packages, right click on the project and select Manage NuGet Packages.
Once this is done, ensure that the Package source on the top right corner is set to “TC Nuget“.
Make sure Browse is selected and search for “MTOA” and install all of the packages.
Layouts
Colored Cards
Below is a sample HTML for the cards.
<div class="container" id="HomeContainer"> <div class="row mrgn-tp-lg"> <div class="col-md-4"> <a href="#add_your_link"> <section class="panel panel-height"> <header class="panel-heading modes-1"> <h3><img src="your_image.png" class="iconImgMode" alt="">My Services</h3> </header> <div class="panel-body"> <p>To view which services are available, click on My Services.</p> </div> </section> </a> </div> <div class="col-md-4"> <a href="#add_your_link"> <section class="panel panel-height"> <header class="panel-heading modes-2"> <h3><img src="your_image.png" class="iconImgMode" alt="">My Requests</h3> </header> <div class="panel-body"> <p>To view which requests you are working on, or have submitted, click on My Requests.</p> </div> </section> </a> </div> <div class="col-md-4"> <a href="#add_your_link"> <section class="panel panel-height"> <header class="panel-heading modes-3"> <h3><img src="your_image.png" class="iconImgMode" alt="">Account Settings</h3> </header> <div class="panel-body"> <p>To change your account settings, click on Account Settings.</p> </div> </section> </a> </div> </div> </div>
CSS used for the cards
.panel-body header { padding:25px 0 16px 20px } .panel-body header h1 { border-bottom:none; margin-top:0; font-size:26px } .panel-body header h2 { border-bottom:none; margin-top:10px; font-size:20px } .panel-body { padding:5px 15px 10px 15px } .panel-body .form-group { margin-bottom:20px } .panel-primary { border:1px solid #335075; border-radius:4px; margin:15px; padding:0 } .modes-cards { border-top-left-radius:15px; border-top-right-radius:15px } .modes-text { padding:15px; border:1px solid #000 } .modes-1 { background-color:#335075; color:#fff; -webkit-border-top-left-radius:10px; -webkit-border-top-right-radius:10px; -moz-border-radius-topleft:10px; -moz-border-radius-topright:10px; border-top-left-radius:10px; border-top-right-radius:10px } .modes-2 { background-color:#e06425; color:#fff; -webkit-border-top-left-radius:10px; -webkit-border-top-right-radius:10px; -moz-border-radius-topleft:10px; -moz-border-radius-topright:10px; border-top-left-radius:10px; border-top-right-radius:10px } .modes-3 { background-color:#fdb53a; color:#fff; -webkit-border-top-left-radius:10px; -webkit-border-top-right-radius:10px; -moz-border-radius-topleft:10px; -moz-border-radius-topright:10px; border-top-left-radius:10px; border-top-right-radius:10px } section.panel-height { min-height:250px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px; -webkit-box-shadow:0 3px 0 0 rgba(222,222,222,.5); -moz-box-shadow:0 3px 0 0 rgba(222,222,222,.5); box-shadow:0 3px 0 0 rgba(222,222,222,.5) } header.panel-heading h1 { margin-top:10px!important; font-weight:lighter } div.container p { color:#000; font-size:14px } footer.panel-footer { margin-top:25px } div#HomeContainer a,div#HomeContainer a:visited { color:transparent!important } div#HomeContainer a h3:hover { text-decoration:none } div#HomeContainer a p:hover { text-decoration:underline } div#HomeContainer img.iconImgMode { margin-right:10px; margin-bottom:10px }
1.1.1.1 Service Tiles
Below is a sample HTML
<div class="row"> <div class="col-md-3"> <a style="text-decoration:none;" href="#your_link"> <div class="box"> <div> <img src="your_image.svg" class="iconImg" alt="alt text" /> <div class="tile-title">MTOA Account Verification</div> </div> </div> </a> </div> </div>
CSS
.tile-title { font-size: 18px; color: #707070; margin: 12px 0 0 0; } .iconImg { height: 50px; width: 50px; margin-bottom: -0.5em; }
1.1.1.1 My Requests Cards
Below is a sample HTML
<div class="row"> <a style="text-decoration:none;" href="#your_link"> <div class="row box" id="request-row"> <div class="col-md-1 justify-content-center align-self-center info-col"> <img src=”your_image.svg” class="iconImg" alt=”alt text” /> </div> <div class="col-md-2 justify-content-center align-self-center info-col"> <p class="service-request-header">Name</p> <p class="request-row-line-height" >The Name</p> </div> <div class="col-md-2 justify-content-center align-self-center info-col"> <p class="service-request-header">Request Id</p> <p class="request-row-line-height">999</p> </div> <div class="col-md-2 justify-content-center align-self-center info-col"> <p class="service-request-header">Service</p> <p class="request-row-line-height">A Service</p> </div> <div class="col-md-2 justify-content-center align-self-center info-col"> <p class="service-request-header">Last Updated</p> <p class="request-row-line-height">28/06/2019 1:38:00 PM</p> </div> <div class="col-md-2 justify-content-center align-self-center info-col"> <p class="service-request-header">Status</p> <p class="request-row-line-height" >In progress</p> </div> <div class="col-md-1"></div> <div class="col-md-1 justify-content-center align-self-center info-col"> <img src="your_image.png" class="chev-right" alt="alt text" /> </div> </div> </a> </div>
CSS
.box { background-color: #fff; padding: 0px 0px; border: 1px solid #DEDEDE; margin-bottom: 1em; transition: .3s; } .box:hover { background-color: #F7F7F7; cursor: pointer; } .box:hover .chev-right { transform: translate(3px); } .chev-right { margin-top: 10px; transition: .2s; } .iconImg { height: 35px; width: 35px; } .info-col { margin: 10px 0; overflow-wrap: break-word; word-break: break-word; } p.request-row-line-height { line-height: 1.3; } .service-request-header { display: block; font-size: 16px; color: #707070; margin-top: 0px; margin-bottom: 5px; font-family: Helvetica,Arial,sans-serif; font-weight: 700; line-height: 1.1; }
Main navigation bar
In order to use the navigation bar you need create a sub folder called MainNavigationBar under the Views folder and create a file called Index.cshtml. In your Controllers folder add a new controller called MainNavigationBarController.cs.
Note: The following section is only applicable if you’re not using the myTC Account Services Template.
Steps to add the Main Navigation Bar
Add View
Create a sub folder called MainNavigationBar and add a view called Index.cshtml. You will need to update your resource files to include the required text.
In the Index.cshtml add the following HTML:
@model WebCommon.ViewModels.MainNavigationBarViewModel @{ Layout = null; } <!DOCTYPE html> <html lang="en"> <head></head> <body> <!-- Application templates - Sample menu --> <!-- DataAjaxFragmentStart --> <div class="pnl-strt container nvbar"> <h2 class="wb-inv">Main navigation menu</h2> <div class="row"> <ul class="list-inline menu" role="menubar"> <li><a class="item" href="@Model.HomePageUrl">@Resource.Home</a></li> <li><a class="item" href="@Model.ServicePageUrl">@Resource.MyServices</a></li> <li><a class="item" href="@Model.ServiceRequestsPageUrl">@Resource.MyRequests</a></li> </ul> </div> </div> <!-- DataAjaxFragmentEnd --> </body> </html>
Add Controller
Add MainNavigationController.cs
Create an action called Index and add this line at the top to have access to the view model (using WebCommon.ViewModels;) and populate the view model (see below).
public ActionResult Index() { var vm = new MainNavigationBarViewModel { HomePageUrl = GetHomePageUrl().ToString(), ServicePageUrl = GetServicesPageUrl().ToString(), ServiceRequestsPageUrl = GetServiceRequestPageUrl().ToString() }; return View(vm); }
Set CustomSiteMenuUrl
In your BaseController override the PopulateViewBag function and this line:
WebTemplateCore.CustomSiteMenuURL = Url.Content($"~/{(WebTemplateCore.TwoLetterCultureLanguage.Equals("en") ? "eng" : "fra")}/MainNavigationBar/Index");
Adding the ServiceTitleBar
In the View folders in the Shared sub folder create a file called _ServiceTitleBar.cshtml.
Add the following to the partial view:
@model RTMR.Web.Portal.Models.ServiceTitleBarViewModel <header> <h2 class="app-name-submenu"><img src="~/Content/Images/file.svg" class="iconImg" alt="" /> @Model.Title</h2> </header> @if (Model.TitleBarButtons!=null &&Model.TitleBarButtons.Any()) { <hr class="home-hr" /> <div class="row"> @foreach (var menuBar in Model.TitleBarButtons) { <div class="col-md-4"> <a href="@menuBar.Link" class="@Html.Raw(menuBar.IsPrimary ? "btn btn-primary" : "btn btn-default active") btn-lg btn-block">@menuBar.Text</a> </div> } </div> }
Add to view
Inside your view add the following line:
In your view model add the following property:
public ServiceTitleBarViewModel ServiceTitleBar { get; set; }
Inside your controller you can set the view models ServiceTitleBar property to set the Title, TitleBarButtons with associated text and links.
Wizard Control
The wizard control is used to visually display the status of each steps when filling out a form.
Steps to add the Wizard
Adding the Wizard
In the View folders in the Shared sub folder create a file called Wizard.cshtml.
Add to view
Inside your view add the following line:
Figure 3.4‑13 - Add to view
In your view model add the following property:
public WizardViewModel Wizard
Add WizardStepState
In your Models folder add an Enum called WizardStepState.
public enum WizardStepState { NotAccessed, Completed, Incomplete, InProgress, Inapplicable, Summary }
Add WizardStepViewModel
In your Models folder add a new view model called WizardStepViewModel.
public class WizardStepViewModel { public string Title { get; set; } public string Url { get; set; } public WizardStepState State { get; set; } public string GetUrl() { return State == WizardStepState.Inapplicable || State == WizardStepState.NotAccessed ? "#" : Url; } }
Add WizardViewModel
In your Models folder add a new view model called WizardViewModel.
public class WizardViewModel { public WizardViewModel() { WizardSteps = new List<WizardStepViewModel>(); } public ICollection<WizardStepViewModel> WizardSteps { get; set; } public int Count => WizardSteps.Count; public int? LastCompletedIndex => WizardSteps.Reverse() .Select((x,i) => new {State = x.State, Index = Count - i - 1}) .FirstOrDefault(x=>x.State == WizardStepState.Completed) ?.Index; public int? InProgressIndex => WizardSteps.Reverse() .Select((x, i) => new { State = x.State, Index = Count - i - 1}) .FirstOrDefault(x => x.State == WizardStepState.InProgress) ?.Index; }
Add WizardController
Create a WizardController in the Controllers folder, or add the following code in your controller.
public ActionResult Index(int steps = 8, int current = 5, int completed = 4, int incomplete = 3, int inapplicable = 6, int summary = 7) { current = Clamp(current, steps); completed = Clamp(completed, steps); incomplete = Clamp(incomplete, steps); inapplicable = Clamp(inapplicable, steps); summary = Clamp(summary, steps); var vm = GenerateWizardViewModel(steps, current, completed, incomplete, inapplicable, summary) return View(vm); } protected WizardViewModel GenerateWizardViewModel(int count, int current = 0, int completed = 0, int incomplete = 0, int inapplicable = 0, int summary = 0) { var steps = Enumerable.Range(1, count).Select(x => new WizardStepViewModel() { Title = "Your Title", Url = Url.Action("Action", "Controller"), State = x == 1 ? WizardStepState.InProgress : WizardStepState.NotAccessed, }).ToList(); if (completed > 0) { for (int i = 0; i < completed; i++) { steps[i].State = WizardStepState.Completed; } } if (current != 0) { steps[current - 1].State = WizardStepState.InProgress; } if (incomplete != 0) { steps[incomplete - 1].State = WizardStepState.Incomplete; } if (inapplicable != 0) { steps[inapplicable - 1].State = WizardStepState.Inapplicable; } if (summary != 0) { steps[summary - 1].State = WizardStepState.Summary; } return new WizardViewModel() { WizardSteps = steps }; } private int Clamp(int val, int max) => val < max ? val : max;
1.1.2 Survey Feedback
The survey lets you select a rating from 1 to 5 stars (this is a required field) and also add optional comments. The survey is anonymous, which means that the user’s id will not be saved in the database.
You can view an example of the survey feedback on this site: http://ncras545.tc.gc.ca:8084/eng/feedback
Steps to create a survey feedback.
Rendering 5 Star Rating Bar in your Survey Feedback involves following tasks:
Create a partial-view "_rating.cshtml" to render 5 Star Rating Bar in your Survey Feedback
@model string[] @*We expect at least two elements in model model[0] to be used as "name" and model[1] as the value*@ @{ var name = Model[0]; var value = Model[1]; } <div class="ratingContainer"> <fieldset aria-required="true" class="toggle-list-items-container" data-val="true" id="Rating"> <legend class="star-rating__title"><span class="field-name">@Resource.Rating</span></legend> <div class="star-rating__stars"> <input type="radio" class="star-rating__input" name="@name" value="1" id="@name-1" @Html.Raw("1".Equals(value) ? "checked" : "")><label class="star-rating__label" role="img" for="@name-1" title="@Resource.OneStar" aria-label="@Resource.OneStar"></label> @for (var i = 2; i <= 5; i++) { <input type="radio" class="star-rating__input" name="@name" value="@i" id="@name-@i" @Html.Raw(i.ToString().Equals(value) ? "checked" : "")><label class="star-rating__label" role="img" for="@name-@i" title="@string.Format(Resource.Stars, i)" aria-label="@string.Format(Resource.Stars, i)"></label> } </div> </fieldset> </div>
a) In above code "@model string[]" accepts string array – this partial view expects at least two values; the first element in array must be the “name” attribute for “radio” buttons that partial view uses for “stars”
b) Above partial view renders 5 Star Rating Bar in your Survey Feedback, as shown below:
The default state | Mouse Over State |
In your controller create a POST action
[HttpPost] [UrlRouteEng(Path = "index")] [UrlRouteFra(Path = "index")] public async Task<ActionResult> Index(FeedbackViewModel feedbackViewModel) { var feedback = new Feedback { Comment = feedbackViewModel.Comment, Rating = feedbackViewModel.Rating, ServiceId = RtmrServiceId }; if (!ModelState.IsValid) { return View(feedbackViewModel); } Logger.Info("[HttpPost] - New feedback survey created."); await _feedbackManager.Add(feedback); return RedirectToAction("Index"); }
Add “Resource” strings used in tooltip (meets an accessibility of 5 Star Rating Bar in your Survey Feedback)
At present partial-view (_ratings.cshtml) mentioned above, expects a “Resource” file, however as per your need you may copy following resource strings to different CSS file, in that case make sure to change above mentioned partial-view to use resource file of your choice.
Resource.resx |
... Stars {0} Stars OneStar 1 Star ... |
French Translation for tooltip:
Resource. fr-CA.resx |
... Stars {0} Étoiles OneStar 1 Étoile ... |
In case you want to get the feedback in a middle of your view you only need following
...
@Html.Partial("_rating", new string[] {"rating", Model.Rating.ToString()})
...
If you want a feedback in a Dialog then inside your view add the following
@* feedback Overlay *@ <section id="feedback-screen" class="wb-overlay modal-content overlay-def wb-popup-mid feedbackCont"> <header class="modal-header"> <h2 class="modal-title">@Html.Raw(Resource.TCFeedbackSurvey)</h2> </header> <div class="modal-body"> @Html.Raw(Resource.TellUsAbout) @using (Html.BeginForm("Index", "Feedback", FormMethod.Post, new { id = "feedbackForm" })) { <div class="form-group" id="feedback"> @Html.ValidationMessageFor(m => m.Rating) <div class="row"> <div class="col-md-12"> @Html.Partial("_rating", new string[] {"rating", Model.Rating.ToString()}) </div> </div> </div> <div> @Html.Raw(Resource.ProvideComments) </div> <div class="form-group"> @Html.LabelFor(model => model.Comment, htmlAttributes: new { @for = "meetingroom-comment" })<br /> @Html.EditorFor(model => model.Comment, new { htmlAttributes = new { id = "meetingroom-comment", @class = "form-control" } }) </div> <div class="form-group top-md"> <input id="saveAndContinueButton" disabled type="button" name="saveAndContinueButton" value="@Resource.Submit" class="btn btn-primary right-md" /> <button class="btn btn-default overlay-close" type="button">@Resource.CancelButton</button> </div> } </div> </section>
Copy styles in a separate css file (in this case css file is “rating.css”)
.toggle-list-items-container { display: flex; margin: 5px 10px; } .toggle-list-items-container > Legend { display: contents; font-family: Helvetica,Arial,sans-serif; font-size: 16px; font-weight: 700; } .toggle-list-items-container > Legend > span { margin: auto; } .ratingContainer { display: flex; float: left; border: 1px solid #ccc; border-radius: 5px; padding: 10px; } .ratingContainerFocus { box-shadow: 0 0 8px rgba(82,168,236,.6); } .star-rating__stars { position: relative; height: 4rem; width: 25rem; background-size: 5rem 5rem; margin: 0px 0px 0px 20px; } .star-rating__title { position: relative; } .star-rating__label { position: absolute; height: 100%; background-size: 5rem 5rem; } .star-rating__input { margin: 0; position: absolute; height: 1px; width: 1px; overflow: hidden; clip: rect(1px, 1px, 1px, 1px); } .star-rating__stars .star-rating__label:nth-of-type(1) { z-index: 5; width: 20%; } .star-rating__stars .star-rating__label:nth-of-type(2) { z-index: 4; width: 40%; } .star-rating__stars .star-rating__label:nth-of-type(3) { z-index: 3; width: 60%; } .star-rating__stars .star-rating__label:nth-of-type(4) { z-index: 2; width: 80%; } .star-rating__stars .star-rating__label:nth-of-type(5) { z-index: 1; width: 100%; background-image: url(../images/off.svg); } .star-rating__stars .star-rating__label:nth-of-type(5):hover { /*Handles a bug*/ background-image: url(../images/on.svg); } .star-rating__input:checked + .star-rating__label, .star-rating__input:focus + .star-rating__label, .star-rating__label:hover { background-image: url(../images/on.svg); } .star-rating__label:hover ~ .star-rating__label { background-image: url(../images/off.svg); }
Note: Above stylesheet uses “on.svg” and “off.svg” to draw solid and hollow star
To draw stars, copy following svg files and if needed change relative path in above CSS
Content of “on.svg”
<svg xmlns="http://www.w3.org/2000/svg" width="80" height="80"> <path transform="translate(15, 7.5)" fill="#fc0" stroke="#fc0" d="m25,1 6,17h18l-14,11 5,17-15-10-15,10 5-17-14-11h18z"/> </svg>
Content of “off.svg”
<svg xmlns="http://www.w3.org/2000/svg" width="80" height="80"> <path transform="translate(15, 7.5)" fill="#fff" stroke="#fc0" stroke-width="6%" d="m25,1 6,17h18l-14,11 5,17-15-10-15,10 5-17-14-11h18z"/> </svg>
Either copy above styles in a view or reference a separate css file
In this case we reference above rating.css file in a view where rating bar need to be displayed
@section Styles { ... <link href="~/Content/css/rating.css" rel="stylesheet" /> ... }
If rating bar is used in a Dialog then inside your view copy following script
@section scripts { <script> 'use strict'; $(document).ready(function () { // following section use to highlight border surronding to star container $('.star-rating__input').focus(function () { $(this).closest('.ratingContainer').addClass('ratingContainerFocus'); }); $('.star-rating__input').blur(function () { $(this).closest('.ratingContainer').removeClass('ratingContainerFocus'); }); // following section only needed if rating bar used in a Dialog // and form in a dialog need to submit asynchronously without refreshing page var feedbackDialog = $('#feedback-screen'); var feedbackForm = $('#feedbackForm'); var checkFeedback = function () { $('#saveAndContinueButton').prop('disabled', true); var values = feedbackForm.serializeArray(); if (values && values.length > 0) { values.forEach(function (v, i) { if (v.value && v.value.length > 0) { $('#saveAndContinueButton').removeAttr('disabled'); return false; } }); } } $(".feedbackForm > input[type=text], input[type=radio], textarea").on('change keyup paste', checkFeedback); $("#saveAndContinueButton").click(function (e) { e.preventDefault(); $.ajax({ type: "POST", url: feedbackForm.attr('action'), data: feedbackForm.serialize() }); feedbackDialog.trigger('close'); }); feedbackDialog.trigger('open.wb-overlay'); }); </script> }
Above Javascript performs following tasks:
a) "feedbackDialog.trigger('open.wb-overlay');" opens a dialog defined (assumes you want a feedback in a dialog) with id “#feedback-screen”
b) Monitors the input within each input in a form defined with id “#feedbackForm” (you may need to change it to suite your need)
c) Enables “submit” button with id "saveAndContinueButton" when there is at least one input value has non-null input or if one of 5 Star rating selected
d) Submits the content of a form defined with an id “feedbackForm” async post to whatever the “Action” defined on “feedbackForm”
e) Closes the dialog immediately after submitting the “feedbackForm”
If rating bar is used in a regular form (not in a Dialog) along with other input elements then inside your view copy following script
@section scripts { <script> 'use strict'; $(document).ready(function () { // following section use to highlight border surronding to star container $('.star-rating__input').focus(function () { $(this).closest('.ratingContainer').addClass('ratingContainerFocus'); }); $('.star-rating__input').blur(function () { $(this).closest('.ratingContainer').removeClass('ratingContainerFocus'); }); }); </script> }
1.1.1 Adding Outage Notices
Under Views -> MainNavigation, open Index.cshtml.
Add a new link item with a link for notices.
<div class="pnl-strt container nvbar"> <h2 class="wb-inv">Main navigation menu</h2> <div class="row"> <ul class="list-inline menu" role="menubar"> <li><a class="item" href="@Model.HomePageUrl">@Resource.Home</a></li> <li><a class="item" href="@Model.ServicePageUrl">@Resource.MyServices</a></li> <li><a class="item" href="@Model.ServiceRequestsPageUrl">@Resource.MyRequests</a></li> <li><a class="item" href="@Model.NoticesPageUrl">@Resource.Notices</a></li> </ul> </div> </div>
Update your resource files with “Notices” from English and “Avis” for French.
MainNavigationBarController
Under Controllers, open MainNavigationBarController.cs
In the view model add the NoticesPageUrl property and call the GetNoticesPageUrl function.
public ActionResult Index() { var vm = new MainNavigationBarViewModel { HomePageUrl = GetHomePageUrl().ToString(), ServicePageUrl = GetServicesPageUrl().ToString(), ServiceRequestsPageUrl = GetServiceRequestPageUrl().ToString(), NoticesPageUrl = GetNoticesPageUrl().ToString() }; return View(vm); }
Outage Notice View Models
Add the two view models
public class OutageNoticeViewModel { public string Header { get; set; } public string Text { get; set; } public IEnumerable<string> AffectedServices { get; set; } } public class OutageNoticesViewModel { public List<OutageNoticeViewModel> OutageNotices { get; set; } public ServiceMenuBarViewModel ServiceTitleBar { get; set; } }
Create a new view.
@model Models.OutageNoticesViewModel @section ServiceTitleBar { @Html.Partial("_ServiceTitleBar", Model.ServiceTitleBar) } <div class="container"> <div class="row"> <h2>@Html.Raw(Resource.UpcomingOutages)</h2> <p>@Html.Raw(Resource.TechnicalSupport)</p> <hr style="border: #AF3C43 1px solid;" /> @if (Model.OutageNotices.Any()) { <p>@Html.Raw(Resource.PlannedOutages)</p> foreach (var m in Model.OutageNotices) { <section class="alert alert-warning"> <div> <h3>@Html.Raw(m.Header)</h3> <p>@Html.Raw(m.Body)</p> <h4>@Resource.AffectedServices</h4> <ul> @foreach (var service in m.AffectedServices) { <li>@service</li> } </ul> </div> </section> } } else { <p>@Html.Raw(Resource.NoOutages)</p> } </div> </div>
OutageNoticeController
Create a new controller called OutageNoticeController.
Add the following code.
[UrlRouteController(Name = "OutageNoticeControllerRoute")] public class OutageNoticeController : PortalBaseController { private readonly IOutageNoticeManager _outageNoticeManager; private readonly IServiceCatalog _serviceCatalog; // GET: Notices public OutageNoticeController(IUserManager userManager, ILogger<OutageNoticeController> logger, ICache cache, IServiceSettingManager serviceSettingManager, IOutageNoticeManager outageNoticeManager, IServiceCatalog serviceCatalog) : base(userManager, logger, cache, serviceSettingManager) { _outageNoticeManager = outageNoticeManager; _serviceCatalog = serviceCatalog; } [UrlRouteEng(Path = "Notices")] [UrlRouteFra(Path = "Avis")] public async Task<ActionResult> Index() { var userOutageNotices = await _outageNoticeManager.GetByUserId(CurrentUser.Id); var services = await _serviceCatalog.GetAll(); var outageNotices = userOutageNotices.Select(outageNotice => new { ServiceNames = services.Where(serviceInfo => outageNotice.Services.Any(serviceId => serviceId == serviceInfo.Id)), Header = Utility.IsEnglish() ? outageNotice.EnglishHeaderText : outageNotice.FrenchHeaderText, Body = Utility.IsEnglish() ? outageNotice.EnglishBodyText : outageNotice.FrenchBodyText }) .Select(service => new OutageNoticeViewModel { Body = service.Body, Header = service.Header, AffectedServices = service.ServiceNames.Select(serviceInfo => Utility.IsEnglish() ? serviceInfo.FriendlyNameEn : serviceInfo.FriendlyNameFr) }); var viewModel = new OutageNoticesViewModel { ServiceTitleBar = CreateServiceTitleButtons(Resources.Resource.Notices, "bell"), OutageNotices = outageNotices }; return View(viewModel); } }
1.1.1 Application user properties
To access user who’s currently authenticated you can use the CurrentUser property which is accessible in the BaseController.
1.1.2 Application Session Timeout
In you web.config you can add Session Timeouts by modifying the following section:
In this example (highlighted in yellow) after 17 minutes if inactivity a prompt will be displayed with 3 minutes of reaction to either continue or end the sessions. A logout URL. The default value for a timeout is 20 minutes.
For more information you can visit the following site:
· https://wet-boew.github.io/v4.0-ci/docs/ref/session-timeout/session-timeout-en.html
· https://gccode.ssc-spc.gc.ca/iitb-dgiit/sds/GOCWebTemplates/DotNetTemplates/wikis/Documentation/configurations (Under Session Timeout header)
<GoC.WebTemplate version="v4_0_30" environment="Akamai" loadJQueryFromGoogle="false" showPreContent="true" showPostContent="true" showFeedbackLink="true" feedbackLinkUrl="" showLanguageLink="true" showSharePageLink="true" showSearch="true" showFeatures="true" staticFilesLocation="~\Views\GoC.WebTemplate\StaticFallbackFiles" xmlns="http://tempuri.org/WebTemplateMVC.xsd"> <sessionTimeOut enabled="true" inactivity="1020000" reactionTime="180000" logouturl="/secure/PIU/SAML/Logout" /> <leavingSecureSiteWarning enabled="false" displayModalWindow="true" redirectURL="" excludedDomains="" /> </GoC.WebTemplate>
1.1.3 Invoices
1.1.3.1 Business Object
Invoice
Property | Description |
public int Id { get; set; } | Invoice id. |
public int ServiceRequestId { get; set; } | Service request id associated to the invoice. |
public string Number { set; get; } | Number associated to the invoice. |
public string PaymentReferenceNumber { get; set; } | Payment reference number associated to the invoice. |
public decimal? Amount { get; set; } | Amount for the invoice. |
public DateTime? Date { get; set; } | Date of the invoice. |
1.1.3.2 Manager
InvoiceManager
Property | Description |
Task<Invoice> Add(Invoice invoice); | Adds a new invoice. |
Task<Invoice> Update(Invoice invoice); | Updates an existing invoice. |
Task<ICollection<Invoice>> GetByServiceRequest(int serviceRequestId); | Searches invoices based on a service request id. |
Task<Invoice> GetByNumber(string number); | Searches for an invoice based on a number. |
Task<Invoice> GetByPaymentReference(string reference); | Searches for an invoice based on payment reference number. |
1.1.3.3 InvoiceManager.Add(Invoice) Method
Creates a new invoice.
public async Task<Invoice> Add(Invoice invoice)
In this example a new invoice is created with the values provided. The id isn’t required, as it gets generated automatically.
//generate an invoice id, and a new invoice. var invoiceNumber = “abc-123”; var paymentReferenceNumber = “xyz-jkl-123”; var newInvoice = new Invoice { Number = invoiceNumber, ServiceRequestId = ServiceRequestId, Date = DateTime.Now, Amount = Amount, PaymentReferenceNumber = paymentReferenceNumber }; //add a new invoice. await _invoiceManager.Add(newInvoice);
1.1.3.4 Parameters
Invoice (Invoice)
Business object used to get/set the values.
1.1.3.5 Returns
Task<Invoice> (Invoice)
An Invoice with all of the created invoice information.
1.1.3.6 Exceptions
MtoaException
If the add can’t be completed.
If the Service Request id is deleted or doesn’t exist.
1.1.3.7 InvoiceManager.Update(Invoice) Method
Updates an existing invoice.
public async Task<Invoice> Update(Invoice invoice)
In this example an existing invoice value get updated. A valid id must be provided, or no changes will be made. An existing invoice can be retrieved using the GetByNumber() or GetPaymentReference() functions.
//get the invoice by number. var invoice = await _invoiceManager.GetByNumber(invoiceNumber); // or, get the new invoice by payment reference. var invoice = await _invoiceManager.GetByPaymentReference(paymentReferenceNumber); //update invoice amount of an existing one. invoice.Amount = 499.99M; await _invoiceManager.Update(invoice);
1.1.3.8 Parameters
Invoice (Invoice)
Business object used to get/set the values.
1.1.3.9 Returns
Task<Invoice> (Invoice)
An Invoice with all of the updated invoice information.
1.1.3.10 Exceptions
MtoaException
If the update can’t be completed.
If the Service Request id is deleted or doesn’t exist.
1.1.3.11 InvoiceManager.GetByNumber(string) Method
Get an invoice by its number.
public async Task<Invoice> GetByNumber(string number)
In this example an invoice is retrieved by using a number. The number must be a valid.
//get the invoice by number. var invoice = await _invoiceManager.GetByNumber(invoiceNumber);
1.1.3.12 Parameters
Number (string)
The invoice number.
1.1.3.13 Returns
Task<Invoice> (Invoice)
An invoice with all of the information.
1.1.3.14 Exceptions
MtoaException
Invoice is not found.
ArgumentException
If the id is equal to zero.
1.1.3.15 InvoiceManager.GetByPaymentReference(string) Method
Get an invoice by its payment reference number.
public async Task<Invoice> GetByPaymentReference(string reference)
In this example an invoice is retrieved by using a number. The reference number be valid.
//get the new invoice. var invoice = await MtoaApi.InvoiceApi.GetByPaymentReference(paymentReferenceNumber);
1.1.3.16 Parameters
Reference (string)
The invoice payment reference number.
1.1.3.17 Returns
Task<Invoice> (Invoice)
An invoice with all of the information.
1.1.3.18 Exceptions
MtoaException
Invoice is not found.
ArgumentException
If the id is equal to zero.
1.1.3.19 InvoiceManager.GetByServiceRequest(string) Method
Gets a list of invoices by service request id.
public async Task<ICollection<Invoice>> GetByServiceRequest(int serviceRequestId)
In this example a list of invoices are returned given a valid service request id.
//get invoice by service requests. var invoices = await MtoaApi.InvoiceApi.GetByServiceRequest(ServiceRequestId);
1.1.3.20 Parameters
ServiceRequestId (int)
The service request id.
1.1.3.21 Returns
Task<ICollection<Invoice>> (Invoice)
A collection of invoices with all of the invoice information.
Null is returned if no invoices are found.
1.1.3.22 Exceptions
ArgumentException
If the id is equal to zero.
1.1.3.23 Code sample
Add this view model to use with the controller and view.
Add this view to either create or update an invoice.
Add this controller for invoices.
1.1.4 File Attachments Upload
1.1.4.1 Domain Object
FileAttachment
Used to get or set the values of a file attachment.
Property | Description |
public int Id { get; set; } | File attachment id. |
public int ServiceRequestId { get; set; } | Service request id associated to the attachment. |
public string Name { get; set; } | File attachment name. |
public string Content Type { get; set; } | File content type (example “image/jpeg”). |
public int? Size { get; set; } | File attachment size. |
Public byte[] Data {get; set;} | File data. |
1.1.4.2 FileAttachmentManager.Upload(FileAttachment) Method
Uploads a new file attachment to the database.
public async Task<FileAttachment> Upload(FileAttachment fileAttachment)
How to create a new file attachment. The file attachment id isn’t required, since it gets generated automatically.
const int serviceRequestId = 41;
const string contentType = "image/jpeg";
byte[] bytes = byte array
const string filename = "images.jpg";
var fileAttachment = new FileAttachment
{
ContentType = contentType,
Data = bytes,
Name = filename,
ServiceRequestId = serviceRequestId,
Size = bytes.Length
};
var newAttachment = await _attachmentManager.Upload(fileAttachment);
1.1.4.3 Parameters
FileAttachment (FileAttachment)
Business object used to get/set the values.
1.1.4.4 Returns
Task<FileAttachment> (FileAttachment)
A FileAttachment with all of the uploaded attachment information.
1.1.4.5 Exceptions
MtoaException
If the upload can’t be completed.
If the Service Request id is deleted or doesn’t exist.
MtoaVirusScanException
If a virus is detected or any issues found during the scanning process.
1.1.4.6 FileAttachment.GetById(int) Method.
Return a file attachment by its id.
public async Task<FileAttachment> GetById(int id)
In this example a file attachment is retrieved by using its id.
var attachment = await _fileAttachmentManager.GetById(100);
1.1.4.7 Parameters
Id (int)
The file attachment id.
1.1.4.8 Returns
Task<FileAttachment> (FileAttachment)
A FileAttachment with all of the uploaded attachment information.
1.1.4.9 Exceptions
MtoaException
File attachment is not found.
ArgumentException
If the id is equal to zero.
1.1.4.10 FileAttachmentManager.GetByServiceRequest(int) Method
Returns a list of file attachments by service request id.
public async Task<ICollection<FileAttachment>> GetByServiceRequest(int id)
In this example a list of file attachments are retrieved by using a service request id. The id must be a valid.
Task<ICollection<ServiceRegulatedEntity>> Add(int serviceRequestId, ICollection<ServiceRegulatedEntity> serviceRegulatedEntities)
1.1.4.11 Parameters
Id (int)
The service request id.
1.1.4.12 Returns
Task<ICollection<FileAttachment>> (FileAttachment)
A collection of file attachments with all of the file attachment information.
Null is returned if no file attachments are found.
1.1.4.13 Exceptions
ArgumentException
If the id is equal to zero.
1.1.4.14 FileAttachmentManager.Remove(int) Method
Removes a file attachment by its id.
public async Task Remove(int id)
In this example a file attachment is removed by using its id. The id must be valid.
await MtoaApi.FileAttachmentApi.Remove(newAttachment.Id);
1.1.4.15 Parameters
Id (int)
The service request id.
1.1.4.16 Exceptions
ArgumentException
If the id is equal to zero.
1.1.4.17 Large file upload configurations (.NET CORE)
By default, ASP.NET Core allows you to upload files up to 28 MB (approximately) in size.
If your application doesn’t have a web.config file. You’ll need to add one. Below is how the web.config should look like.
maxAllowedContentLength is measured in bytes. In this example the value is set to 201MB.
1.1.4.18 File names and special characters
The file name must start with a letter or a number, and can have the following special characters:
· A space
· ,
· -
· _
· .
File extension must at least have two letters for the file extension.
This is the regular expression used to validate the file names: ^[a-zA-Z0-9][a-zA-Z0-9, \-,_\.]*\.[A-Za-z]{3,}$
1.1.4.19 Virus Scan
All files are scanned for viruses using the OSWAT MetaDefender version 4. For more information see this document.
1.1.4.20 Creating a new file attachment using model, view and controller (.NET CORE)
1.1.4.21 Adding a new file attachment view model
Create a new view model.
1.1.4.22 Adding a file attachment controller.
Create a new file attachment controller.
1.1.4.23 Adding a file attachment view (index).
Create a new file attachment view called Index to add a new file attachment List is used to view a file attachment per service requests.