Delete with confirmation and redirect on success - asp.net-mvc

Here's the behavior I'm looking for:
User selects a record from an index view, which redirects to that record's detail view.
User clicks a link to delete the currently displayed record.
A modal dialog asks for confirmation of the delete.
User presses the Confirm button.
The associated controller action method is executed, which is supposed to delete the record.
User is returned to the index view, where the deleted record is no longer displayed.
I am using ASP.NET MVC 1.0 as the primary framework, the dialog component from the jQuery UI, and LINQ-to-SQL to handle database interaction. Steps one through four execute just fine. Step five, however, retrieves the record, but doesn't delete it. Step six executes, but the record is still displayed in the list.
Here is the code for the delete link:
<% if (Model.CanDelete())
{ %>
<%= Html.ActionLink("Delete", "Delete", new { id = Model.Package_ID },
new { onclick = string.Format("deletePackage({0}); return false;", Model.Package_ID) })%> |
<% } %>
Here is the code for the onclick handler:
function deletePackage(packageID) {
createDialogContent(); // The HTML for the dialog content is set in this method
$.getJSON('/Spectrum/Package/DetailsJSON/' + packageID, function(json) {
$('p.message').html('Delete <strong>' + json.Description + '</strong>?');
});
$('div.confirm').attr('title', 'Delete Package');
$('div.confirm').dialog({
draggable: false,
modal: true,
overlay: {
backgroundColor: '#000',
opacity: 0.5
},
resizable: false,
buttons: {
'Confirm': function() {
$(this).dialog('destroy').remove();
$.post('/Spectrum/Package/Delete/' + packageID);
// The next line used to be: $.get('/Spectrum/Package/Index');
window.location.href = '/Spectrum/Package/Index';
},
Cancel: function() {
$(this).dialog('destroy').remove();
}
}
});
}
Here is the code for the delete controller method:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Delete(int id)
{
Package package = RepositoryManager.Package().GetPackage(id);
PackageDeleteViewModel viewModel = new PackageDeleteViewModel
{
Package_ID = package.Package_ID
};
RepositoryManager.Package().PackageDelete(viewModel); // see code below
return new EmptyResult();
}
Finally, here is the repository method for the delete:
public void PackageDelete(PackageDeleteViewModel data)
{
Package package = RepositoryManager.Package().GetPackage(data.Package_ID);
if (package.BrokerageOrderPackages.Count == 0
&& package.ManagementOrderPackages.Count == 0
&& package.Seeds.Count == 0)
{
db.Packages.DeleteOnSubmit(package); // This branch is being executed
db.SubmitChanges();
}
else
{
throw new RulesException("Package cannot be deleted.", "PackageDelete");
}
}
I don't think I'm pushing the envelope here or getting too fancy. One area of concern: In the Confirm button handler of the onclick method, the first version would delete the record successfully, but not redirect to the Index view. With the current version, the delete fails silently, but the redirection occurs. Firefox/Firebug reported 200's for the Index GET in both situations. The behavior is similar in both Firefox and IE.

$.post is an async ajax request, if you reload the page the call gets cancelled. You should reload the page on its callback function parameter:
$.post('/Spectrum/Package/Delete/' + packageID,
null,
function(json){
if (json.success) {
window.location.href = '/Spectrum/Package/Index';
} else {
// jquery dialog call or
alert(json.errorMessage);
}
},
'json');
And the controller code:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Delete(int id)
{
try {
// Validation and deletion code
} catch exception ex {
return Json(new {errorMessage = ex.Message, success = false});
}
return Json(new {success = true});
}

Related

Using Javascript Bootbox with Ajax.BeginForm In ASP.NET

In view page:
#using (Ajax.BeginForm("UpdateAccount", "AccountInfo", new AjaxOptions {
HttpMethod = "POST",
AllowCache = false,
OnSuccess = "accountUpdateRequestSuccess",
OnFailure = "formSubmissionFailed",
OnBegin = "validateAccountInformation"
}))
validateAccountInformation function is stated below:
function validateAccountInformation() {
if (error.length > 0) {
showErrorNotification(errorListHtml);
return false;
}
else {
if ( Some Conditions ) {
bootbox.confirm({
title: "Confirm",
size: "medium",
message: "Some Message",
callback: function (result) {
if (result) {
Some Code
}
else {
Some Code
}
}
});
}
}
When Save button is clicked, at first validateAccountInformation function is called. If there's no error it will raise a Bootbox. If Bootbox OK button is clicked it'll submit the form with certain values or if Bootbox CANCEL button is clicked it'll submit the form with certain values different than before.
Now, the problem I'm facing is, when user clicks SAVE button it goes to validateAccountInformation function. And if there is no errors it calls Bootbox.Confirm. What I've expected that form submission will be held until some OK/CANCEL button is clicked in Bootbox. But it does not wait for Bootbox. Meaning Bootbox callback function is never called. It might have something to do with AJAX being asynchronous.
I've used javascript confirm method. And this method does hold form submission.
function validateAccountInformation() {
if (error.length > 0) {
showErrorNotification(errorListHtml);
return false;
}
else {
if ( Some Conditions ) {
if (confirm("check")) {
some code
}
}
}
}
Is there some way where I can use Bootbox Confirmation box with Ajax Begin Form as per scenario described above?
I've changed the flow and have been able to integrate Bootbox.
What I've done is, I've omitted the onBegin function. So my view page looks like this
#using (Ajax.BeginForm("UpdateAccount", "AccountInfo", new AjaxOptions {
HttpMethod = "POST",
AllowCache = false,
OnSuccess = "accountUpdateRequestSuccess",
OnFailure = "formSubmissionFailed",
}))
Now I've added a function which is fired by onclick event of this forms summit button. And integrated validateAccountInformation function and Bootbox within this function. Now the important part here is, this function always returns false. By returning false I've managed to hold on the form submission. Now the onClick event function looks like
$(document).on('click', '#btn-edit-account-save', function () {
var submit = validateAccountInformation();
some code
if (submit == true) {
if (some condition)
{
$('Form').submit();
}
else
{
bootbox.confirm(
{
title: "Confirm",
size: "medium",
message: "Some Message",
callback: function (result)
{
if (result)
{
$('Form').submit();
}
else
{
some code
}
}
});
}
}
return false;
});

MVC - Load [HttpPost] view into Jquery/Ajax modal window

The way my code works currently is when a button is pressed variables are passed and Jquery unhides a modal window and makes an AJAX call to get the [HttpGet] portion of my Controller Action. This loads a form in the modal window. The modal has a save and cancel button, when the save button is pressed the form is submitted with ajaxSubmit. On successful save something is saved to the database, then a url is created and passed back to jquery and user is redirected with window.location.href.
What I'd like to have happen is when the save button is pressed the [HttpPost] part of controller action will make some logic choices, and if a certain condition is met within the action then return PartialView(model) is returned back to the modal window with an error message or pop up so that the user doesn't have to retype their information and understands why the save didn't go through and can edit quickly and try save again.
The Jquery:
var createNewClaimSetup = function () {
$(selectors.btnCreateNewClaim).live('click', function () {
var serialNumber = $(this).attr(attributes.serialNumber);
var spaRegistrationId = $(this).attr(attributes.spaRegistrationId);
var customerNumber = $(this).attr(attributes.customerNumber);
var customerStatementName = $(this).attr(attributes.customerStatementName);
$(selectors.serialNumberHidden).val(serialNumber);
$(selectors.spaRegistationIdHidden).val(spaRegistrationId);
$(selectors.customerNumberHidden).val(customerNumber);
$(selectors.customerStatementName).val(customerStatementName);
$(selectors.dialog).attr(attributes.title, 'Create New Claim');
$.ajax ({
url: inputs.warrantyClaimCreateUrl,
type: "GET",
data: {
serialNumber: serialNumber,
customerNumber: customerNumber
},
cache: false,
success: function (result) {
$(selectors.dialog).html(result.toString());
$(selectors.dialog).dialog ({
resizable: false,
height: 600,
width: 850,
modal: true,
buttons: {
'Save': function () {
// check to see if a value has been entered for problem description before submitting form
//
if(!$('#AddProblemModel_ProblemDesc').val().length==0 && (!$('#ClaimPrefix').val().length==0 || $('#chkManualClaimNum').is(':checked')))
$(selectors.createNewClaimForm).ajaxSubmit({
cache: false,
success: function (data) {
if (data.length == 0) {
location.reload();
$(selectors.dialog).dialog('close');
} else {
$(selectors.dialog).dialog('close');
window.location.href = data.redirectToUrl;
}
}
});
else{
return alert('You must select a prefix and enter a description for the problem before saving');
}
},
Cancel: function () {
$(this).dialog('close');
}
}
});
}
});
});
}
Controller Action GET (edited for brevity):
public virtual ActionResult WarrantyClaimCreateNew(string serialNumber, string customerNumber, string customerStatementName)
{
WarrantyClaimModel model = wcs.CreateNewClaimInfo(serialNumber, customerNumber, false);
ViewBag.InvalidPrefix = false;
ViewBag.DupeWaNum = false;
return View(model);
}
Controller Action POST (edited for brevity):
[HttpPost]
public virtual ActionResult WarrantyClaimCreateNew(WarrantyClaimCreateNewModel model)
{
if (ModelState.IsValid)
{
WaNumberDupeModel WAdupe = new WaNumberDupeModel(model.WarrantyClaimNumber);
// Rebuild WarrantyClaimModel object to pass back to view
WarrantyClaimModel model2 = wcs.CreateNewClaimInfo(model.SerialNumber, model.CustomerNumber, false);
// check if prefix is valid
if (WAdupe.DupeType == WaDupeType.InvalidPrefix)
{
ViewBag.InvalidPrefix = true;
return PartialView(model2);
//return Json(new { Url = Url.Action("WarrantyClaimCreateNew", model2) });
}
// check if wa number already exists
if (WAdupe.DupeExists)
{
ViewBag.DupeWaNum = true;
return PartialView(model2);
}
// create/save the new claim
if (wcs.CreateNewWarrantyClaim(model))
{
var redirectString = "WarrantyClaimDetail?warrantyClaimNumber=" + model.WarrantyClaimNumber;
if (Request.IsAjaxRequest())
{
return Json(new { redirectToUrl = redirectString }, JsonRequestBehavior.AllowGet);
}
return RedirectToAction(Actions.WarrantyClaimDetail(newClaimNumber));
}
return RedirectToAction(Actions.WarrantyClaimSaveFail());
}
else
{
return View(model);
}
}
Alerts In View If Conditions In Controller Action Met:
<%if (ViewBag.InvalidPrefix == true) { %>
alert("Please choose a valid prefix for your warranty claim number.");
<% } %>
<%if (ViewBag.DupeWaNum == true) { %>
alert("Warranty Claim Number already exists, please choose another manual WA number or try again.");
<% } %>
I haven't even been able to reload the view refreshed in the modal at all, let alone with changed to the bound model intact.
I know that the first part of the AJAX call (type: "GET") uses the URL to go into the controller action GET and gets back the return View(model) then converts it to HTML and sets the modal window HTML to it. I've been assuming that the ajaxSubmit goes back to the controller and gets the POST version of the action, and that the data part of success: function (data) should be the view return if condition is met and the built JSON URL to redirect to if it's not.
I've tried setting the modal html to data and making sure the controller invalid prefix condition is met, however the content in the modal window never seems to refresh. I'll also need to figure out how to logically tell in JQUERY if the controller returned a view or a URL through JSON but I don't think that will be hard to figure out.
I've read through a dozen or so mvc jquery ajax modal similar questions and they've helped but not
Thanks for any help shedding some light on how I might accomplish what a want here.
Sort of a partial answer to what I was looking for. I realize now my main stumbling block was to have my test alerts before building the modal window as "return alert(whatever)" which was obviously returning the alert and not running the rest of the code, dumb.
The solution I have working now is a pretty bad hack. I have it rebuild the modal and attach the POST view data to it. However this isn't looped so if you save and fail again I've set it to alert the user save failed again and to retry, then the modal closes and they can start again from scratch.
Tomorrow I'll see about turning this into a looping rather than nested structure.
New "working" JQuery code:
var createNewClaimSetup = function () {
$(selectors.btnCreateNewClaim).live('click', function () {
var serialNumber = $(this).attr(attributes.serialNumber);
var spaRegistrationId = $(this).attr(attributes.spaRegistrationId);
var customerNumber = $(this).attr(attributes.customerNumber);
var customerStatementName = $(this).attr(attributes.customerStatementName);
$(selectors.serialNumberHidden).val(serialNumber);
$(selectors.spaRegistationIdHidden).val(spaRegistrationId);
$(selectors.customerNumberHidden).val(customerNumber);
$(selectors.customerStatementName).val(customerStatementName);
$(selectors.dialog).attr(attributes.title, 'Create New Claim');
$.ajax ({
url: inputs.warrantyClaimCreateUrl,
type: "GET",
data: {
serialNumber: serialNumber,
customerNumber: customerNumber
},
cache: false,
success: function (result) {
$(selectors.dialog).html(result.toString());
$(selectors.dialog).dialog ({
resizable: false,
height: 600,
width: 850,
modal: true,
buttons: {
'Save': function () {
// check to see if a value has been entered for problem description before submitting form
//
if(!$('#AddProblemModel_ProblemDesc').val().length==0 && (!$('#ClaimPrefix').val().length==0 || $('#chkManualClaimNum').is(':checked')))
$(selectors.createNewClaimForm).ajaxSubmit({
cache: false,
success: function (data) {
if (data.length == 0) {
location.reload();
$(selectors.dialog).dialog('close');
} else if(data.redirectToUrl) {
$(selectors.dialog).dialog('close');
window.location.href = data.redirectToUrl;
} else {
$(selectors.dialog).html(data.toString());
$('#chkManualClaimNum').attr('checked','checked');
$('#manualClaimInfo').toggle();
$(selectors.dialog).dialog ({
resizable: false,
height: 600,
width: 850,
modal: true,
buttons: {
'Save': function () {
if(!$('#AddProblemModel_ProblemDesc').val().length==0 && (!$('#ClaimPrefix').val().length==0 || $('#chkManualClaimNum').is(':checked')))
$(selectors.createNewClaimForm).ajaxSubmit({
cache: false,
success: function (data) {
if (data.length == 0) {
location.reload();
$(selectors.dialog).dialog('close');
} else if(data.redirectToUrl) {
$(selectors.dialog).dialog('close');
window.location.href = data.redirectToUrl;
} else {
alert('Save failed again. Please start over.');
$(selectors.dialog).dialog('close');
}
}
});
},
Cancel: function () {
$(this).dialog('close');
}
}
});
}
}
});
else{
return alert('You must select a prefix and enter a description for the problem before saving');
}
},
Cancel: function () {
$(this).dialog('close');
}
}
});
}
});
});
}
This does at least show how to pass the POST view back to the modal window.

MVC 3 / Jquery AJAX / Session Expires / C# - Handling session timeout durng ajax call

I have an ajax call to MVC which returns a partialview. This is all fine until the session ends or the cookie expires. When I make the ajax call it displays the content inside a div that was meant to be for the partialview. How can I detect that my session has expired during the ajax call and redirect properly to a full screen/page
I would recommend encapsulating all your requests into a wrapper element:
public class JsonResponse<T>
{
public JsonResponse()
{
}
public JsonResponse(T Data)
{
this.Data = Data;
}
public T Data { get; set; }
public bool IsValid { get; set; }
public string RedirectTo { get; set; }
}
What model you want to send to your client is in Data.
To get the RedirectTo populated, I use a GlobalAuthorize attribute in the Global.Asax and added a handle for HandleUnauthorizedRequests.
public sealed class GlobalAuthorize : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest
(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.Result = new JsonResult
{
Data = new JsonResponse<bool>
{
IsValid = false,
//RedirectTo = FormsAuthentication.LoginUrl
RedirectTo = "/"
},
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
else
{
base.HandleUnauthorizedRequest(filterContext);
}
}
Additionally, I've encapsulated all my Ajax requests into a single function which checks for the RedirectTo.
function global_getJsonResult(Controller, View, data, successCallback, completeCallback, methodType)
{
if (IsString(Controller)
&& IsString(View)
&& !IsUndefinedOrNull(data))
{
var ajaxData;
var ajaxType;
if (typeof (data) == "string")
{
ajaxData = data;
ajaxType = "application/x-www-form-urlencoded"
}
else
{
ajaxData = JSON.stringify(data);
ajaxType = "application/json; charset=utf-8";
}
var method = 'POST';
if (!IsUndefinedOrNull(methodType))
{
method = methodType;
}
var jqXHR = $.ajax({
url: '/' + Controller + '/' + View,
data: ajaxData,
type: method,
contentType: ajaxType,
success: function(jsonResult)
{
if (!IsUndefinedOrNull(jsonResult)
&& jsonResult.hasOwnProperty("RedirectTo")
&& !IsUndefinedOrNull(jsonResult.RedirectTo)
&& jsonResult.RedirectTo.length > 0)
{
$.fn.notify('error', 'Login Expired', 'You have been inactive for a prolonged period of time, and have been logged out of the system.');
window.setTimeout(function() { window.location = jsonResult.RedirectTo }, 5000);
}
else if (IsFunction(successCallback))
{
successCallback(jsonResult, Controller + '/' + View);
}
},
error: function(jqXHR, textStatus, errorThrown)
{
if (errorThrown != 'abort')
{
$.fn.notify('error', 'AJAX Connection Error', textStatus + ': ' + errorThrown);
}
},
complete: function(jqXHR, textStatus)
{
if (IsFunction(completeCallback))
{
completeCallback(jqXHR, textStatus, Controller + '/' + View);
}
}
});
return jqXHR;
}
}
You could create a timer on the client with javascript that will show a dialog to the user when the session has timed out. You would just set the timer's value to whatever your session time out. Then on ajax request, it will reset the count down as well.
var g_sessionTimer = null;
function uiSessionInit() {
id = "uiTimeout";
timeout = 3600000 * 24; // 1 day timeout
uiSessionSchedulePrompt(id, timeout);
$('body').ajaxStart(function () {
// reset timer on ajax request
uiSessionSchedulePrompt(id, timeout);
});
}
function uiSessionSchedulePrompt(id, timeout) {
if (g_sessionTimer)
clearTimeout(g_sessionTimer);
g_sessionTimer = setTimeout(function () { uiSessionExpiring(id); }, timeout);
}
function uiSessionExpiring(id) {
// create a dialog div and use this to show to the user
var dialog = $('<div id="uiTimeout"></div>').text("Your session with has timed out. Please login again.");
$('body').append(dialog);
$('#uiTimeout').dialog({
autoOpen: true,
modal: true,
title: 'Expired Session',
buttons: {
"OK": function(){
$(this).dialog('close');
}
},
close: uiSessionDialogClose
});
}
function uiSessionDialogClose(){
// take user to sign in location
location = 'http://www.mypage.com';
}
It's been 8 years but I've just encountered a similar problem whereby an Ajax call to load a partial view into a modal, made after the user's authentication cookie had expired, would result in the full login page being loaded into the modal. This is how I got around it.
Firstly, I added a hidden control on my login page. The value isn't strictly required but it makes for more readable markup, I think:
<input id="isLoginPage" type="hidden" value="true" />
Then I modified the Ajax call like this. I'm using jQuery but the same principle will work in plain old Javascript too:
$.ajax({
url: `/mypartialview`,
type: "GET",
success: function (data) {
var $modal = $("#myModal");
$modal.find(".modal-content").html(data);
if ($modal.find("#isLoginPage").length > 0) {
window.location.replace("/");
}
$modal.modal("show");
}
});
This works on the basis that if the user's authentication has expired MVC's default behaviour is to return to the login page, so I "sniff" the view returned by the Ajax call for the isLoginPage hidden input and, if it's found, I know that the login page has been returned so I just redirect the user to the root of the application, which MVC will then redirect to the main login page.
If your application's root allows anonymous access you could replace window.location.replace("/") with the path to your application's login page, e.g. window.location.replace("/account/login").
With this working, I encapsulated the solution in a function to simplify things and avoid repeating myself:
function handleAjaxData($container, data) {
$container.html(data);
if ($container.find("#isLoginPage").length > 0) {
window.location.replace("/");
}
}
Which I can then use in my Ajax call like this:
$.ajax({
url: `/mypartialview`,
type: "GET",
success: function (data) {
var $modal = $("#myModal");
handleAjaxData($modal.find(".modal-content"), data);
$modal.modal("show");
}
});
You could obviously go further and include the Ajax call itself in the function, but in my case that would have involved some complicated callbacks so I didn't bother.

How to manage MVC AJAX responses when in a jQuery dialog

Here is my problem:
Inside a jQuery dialog I have the following code:
<%:Ajax.ActionLink("Yes", "SendClaim", "Claim", new { id = Model.ExpenseId }, new AjaxOptions { UpdateTargetId = "dialog" }, new { #class = "button" })%>
When stuff fails in the controller based on roles I return a partial view that replaces the existing dialog (see UpdateTargetId = "dialog").
When everything works I want to do a redirect to another page (an index of all claims) to stop the user performing additional actions but this entire page is rendered inside the jQuery dialog due to it being an ajax request with an update id.
What is the correct way to approach the problem?
I'm a bit of a novice, but I find I have more control with the following approach instead of using Ajax.ActionLink. Hopefully it helps and I have understood what you want to do correctly.
Claim Controller:
[AcceptVerbs(HttpVerbs.Post)]
public Json Send(int expenseId)
{
// Check user stuff
if(valid)
// do stuff
return new Json(true, JsonRequestBehavior.AllowGet);
else
return new Json(false, JsonRequestBehavior.AllowGet);
}
jQuery
function submitClaim() {
$.ajax({
url: "/Claim/Send",
type: "POST",
dataType: "json",
data: { 'expenseId': <%=Model.ExpenseId> },
success: function (data) {
if(data) { // if successful, redirect
document.location = "Claim/Index";
}
else { //load your partial view into your dialog
$("#idOfYourDialog").load('Claim/Error/');
}
},
error: function (xhr) { }
});
}
html
Submit
Returned an 'All OK' dialog and had the following javascript when the user clicks the ok button:
function redirect() {
document.location = "<%:(String)ViewBag.Redirect %>";
}
$(document).ready(function() {
$(".ui-dialog-titlebar-close").click(function() {
redirect();
});
});
Seems unavoidable - you can't seem to do an RedirectToAction when the controller action has been called from Ajax.ActionLink as the response will be stuck into the updatetargetid.

How to return back a message after a submit

I have the following code which is not working as expected. I want to have a retrun from the controller and using alert display the value returned from the controller.
$('#change').dialog({
autoOpen: false,
width: 380,
buttons: {
"Close": function() {
$(this).dialog("close");
},
"Accept": function() {
var test = $("#ChangePasswordForm").submit();
alert(test);
}
}
});
In my controller I want to return a string
[AcceptVerbs(HttpVerbs.Post)]
public string ChangePassword(string Name)
{
var msg = "Cool!";
if (name != null)
return msg;
}
How can I do that?
Your controller needs to return a type that derives from an ActionResult.
If you want to display a simple confirmation message you can add it to the ViewData bag like this:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult ChangePassword(string name)
{
if (!string.IsNullOrEmpty(name))
{
ViewData["msg"] = "Cool";
}
return View();
}
Then, in your view, check for the presence of the value, and display it if it's there:
<% if(ViewData["msg"] != null) { %>
<script type="text/javascript">alert('<%= ViewData["msg"].ToString() %>')</script>
<%} %>
First of all, im assuming you are using an ajax form for this. I also assume you have a or something for putting your text into. All you have to do is set the UpdateTargetId to point at the id of the element you want to update with the text
<%using (Ajax.Form("ChangePasswordForm", new AjaxOptions { UpdateTargetId = "result" })) %>
.
[HttpPost]
public ContentResult ChangePassword(string s)
{
var msg = "Cool!";
if ( s != null ? return Content(msg, "text/plain") : return Content("An error has occured", "text/plain") );
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult ChangePassword(string Name)
{
var msg = "Cool!";
if (name != null)
{
return Content(msg, "text/plain");
}
else
{
return Content("Error...", "text/plain");
}
}
Don't submit the form as that will perform a postback and cause the dialog to be removed.
Instead perform an AJAX post to the Controller Action and return a JsonResult containing the data.
Hook into the success callback from the Ajax request, and call alert passing the data from the Json object.
You'll probably wan't to use a loading mask after clicking submit so the user knows something is going on.

Resources