How to update a field in a database via email? - laravel

When a user subscribes to news from the site, he receives a welcome letter in his mail with a question, does he really want to subscribe to news?
Also in the letter there is a button that confirms his consent to the newsletter.
How can I make it so that when I click this button from an email, the values ​​​​in my database are updated?
This is my mail form welcome.blade.php
Welcome, User
<form action="{{route('welcome', $data->hash)}} method="POST">#csrf
<button type="submit">Click me</button>
</form>
Controller
public function welcome($hash) {
\DB::table('config')->where('hash', $hash)->update(['agree' => 1]);
Route
Route::post('welcome', 'WelcomeController#welcome')->name('welcome')

Embedding forms in emails is not allowed/recommended. It is a security risk. Email clients will simply warn the recipients of potential danger and will disable the form.
You need to add a link to your application in the email content.
click me
When a user will click on the URL below route will hit.
Route::get('/add-consent/{token}', 'WelcomeController#welcome')->name('welcome');
In the action identify user based on token and perform the action.
public function welcome($token) {
// Identify user based on token and perform the action...
\DB::table('config')->where('hash', $token)->update(['agree' => 1]);
}

Related

Centralize user role verification in laravel

I'm working on an e-commerce website and i need to display certain data based on the user role. Basically the page can be viewed by three types of people: normal people who enters the page and by a client user or an admin who have their accounts but they see different buttons/data according to their role.
I'm working with jetstream authenticaton system and what i do is simply verify the user role on my views like this:
#if (Auth::user()->role->name == 'admin')
//display button only the admin can see
#else
//display button only a client user can see
#endif
My question is, can i implement something like a gate to centralize the condition?, i mean, in case i need to change it only doing it in one file and avoid to do it in all the lines where i implented it.
Any help is welcome. Thanks!
Yes. Just define gates or use a model policy. Using gates, you can do something like (using a Post model as an example):
Gate::define('edit-post', function (User $user, Post $post) {
return $user->role->name == 'admin' || $user->id == $post->user_id;
});
Gate::define('delete-post', function (User $user, Post $post) {
return $user->role->name == 'admin';
});
And use it in your .blade.php files using the #can blade directives.
#can('edit-post')
// show an edit button
#endcan
#can('delete-post')
// show a delete button
#endcan
Here we are basically creating two controls for editing and deleting a post. To edit a post (or rather see the edit button), you either have to be an admin or the user must have created the post. To delete it, you have to be an admin.
References: Laravel Documentation on Authorization

Model validation with Razor Pages

I have a form that is at the bottom of the page.
When model validation failes on server side return Page() gets me back to the top of the page and that is very inconvenient because the user has to scroll down to the form to see the validation errors.
My questions are:
1. Can I redirect to contact form div ID?
2. The best scenario would be if model validation can be done asynchronously so there would be only partial page refresh. Is that possible? (that was done easily in Web Forms with UpdatePanel).
I appreciate any information you might share.
Jacob
First of all try to validate on the client first to prevent unnecessary postbacks. You can use attributes to set validation:
using System.ComponentModel.DataAnnotations;
public class ForgotPasswordViewModel()
{
[Required]
[EmailAddress]
public string Email { get; set; }
}
In the view check the Modelstate:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
if (ModelState.IsValid)
{
Of course client side isn't enough, so in case the client side validation is skipped the server makes sure that the model validates.
One way to scroll to a position is to use an anchor. The anchor is a client side way to bookmark parts in the document. Normally you would see something like this:
<a href"home#contact">Contact</a>
By clicking the link you would scroll to the contact bookmark in the home document. You can do the same for the post:
<div id="contact">
<form method="post" action="home#contact">
#Html.AntiForgeryToken()
</form>
</div>
In case of an error the page will automatically scroll to the form, since on the client the #contact hash was set. The assumption is that in case of success you redirect to another view. This is afaik the only way to scroll without the use of javascript. And since javascript validation didn't work...
Please note that with .Net Core AntiForgery is added automatically if you use the tag helper. But since I didn't use it I have to add this myself.
There are other options that involve javascript or some server side redirection, but I think this is an easy solution.
I do not know if partial page refresh is the best solution. I think it depends. There are plenty of examples available on how you can post asynchronous. Check this link: https://www.w3schools.com/jquery/ajax_ajax.asp
Maybe remote validation?
"Remote validation is a great feature to use when you need to validate data on the client against data on the server. For example, your app may need to verify whether an email or user name is already in use..."
https://learn.microsoft.com/en-us/aspnet/core/mvc/models/validation
I have found solution that works fine for me:
#if (Model.validationError)
{
<text>
document.getElementById('contact').scrollIntoView();
</text>
}

Not able to know when user is identified as a bot in google invisible captcha on client side

I am following Programmatically invoke challenge process for identifying a user as human or bot.
below is my captcha div in html.
<div class="g-recaptcha"
data-sitekey="<site_key>"
data-callback="onSubmit"
data-size="invisible">
</div>
and i have data callback function in my html script tag:
var onSubmit = function(token){
// do next processing with token
}
data callback gets invoked every time when google identifies a user as human successfully.
it does not get invoked when google identifies a user as a bot.
but i want to know when google identifies user a bot in above similar way on clicking a button?
because when i click on a button, i programmatically invoke challenge by grecaptcha.execute() so this happens:
1) if user is validated as human , data-callback is called and i do my next processing with token.but
2) if user is not validated as human, data-callback is not called and nothing happens on click and i stay on same page.
i expect acknowledgment here in second case so that i can decide further processing.
please help me in this.

View that hides/shows controls

I am in the process of porting a site I wrote from ASP.NET webforms to MVC3 and need some guidance as outlined below. I'm new to MVC3.
In my existing ASP.NET web forms project I have a simple page where the user enters a username, they then click a button which causes a postback, on postback there is some basic code that checks if the entered username exists in a user repository - if it does, a textbox containing the users e-mail is shown and the username textbox is made invisible. This happens with ajax and so when the username is entered, the textbox containing the e-mail along with an "Update" button is shown without a full page refresh.
I created a model such as:
public class ChangeEmailModel
{
[Required]
public string Username { get; set; }
[Required]
public string Email { get; set; }
}
Problem is that when the user first enters the page, they should only see a textbox prompting them to enter a username. Once the username is entered and an update button clicked, only then their e-mail is shown (retrieved from the database). Once the e-mail is shown, they can edit the e-mail and click update, which then will need to post to a controller action that saves the updated e-mail. I'm not yet fully used to thinking in the MVC way, so I'm not sure if I've started on the wrong foot with the model above...
Can someone give me some guidance on how this can be accomplished in MVC3 so I can give it a try?
I will start off by suggesting that you start using JQuery for your javascript/ajax functions. ASP.Net MVC3 supports JQuery nicely. I will ignore validation of the email for now as it will be much easier to get you started without it. A high level overview will be:
Add the JQuery script to your page
Add the JQuery vsdoc script to your page so you have some intellisense
Create a partial view to show the email and submit button
Create a controller action that performs the email lookup you mentioned
Create a div to accept the newly returned Email Update form
Use JQuery to override the submit on your username lookup to perform an ajax update instead (and populate the Email Update form div)
1. Add the JQuery script to your page
This should be pretty easy - just drag it from your scripts folder. I think mvc3 comes with jquery-1.5.1.js. Use the min (minified) version when you release to production.
2. Add the JQuery vsdoc script to your page so you have some intellisense
Not quite as easy here - you will want to use an if statement that always evaluates to false so the script is not actually included in your content. Having it on the page though, will cause VS to use it for intellisense. Put this near the top of your view:
#if (false) { <script src="../../Scripts/jquery-1.5.1-vsdoc.js" type="text/javascript"></script> }
Hopefully you are using Razor. If not, start using it. It seemed a little foreign to me at first, but it requires much less markup.
3. Create a partial view to show the email and submit button
You could use the ViewBag to pass the Email address and UserName (for now as we are ignoring validation), but go ahead and make it strongly typed to your Model from above. Your view may look something like this:
#model ChangeEmailModel
#{using (Html.BeginForm("UpdateEmail", "Home", FormMethod.Post, new { id = "UpdateEmailForm" }))
{
<input type="hidden" name="userName" value="#Model.UserName" />
#Html.EditorFor(m => m.Email)
<button id="submitEmailUpdate" type="submit">Submit</button>
}
}
Note that we have given Ids to the form and the submit button. JQuery will find the form and button based on these ids. (if we need to, which we will if we want to "ajaxify" the action of updating the email. I did not go into that detail here, but it will be the same process to get that working as it is for the original username lookup)
4. Create a controller action that performs the email lookup you mentioned
I won't go into controllers much here (as you are asking about ajax type updates) but it might look like:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult LookupEmail(string userName)
{
//connect to db and lookup email based on passed in username
//create a new instance of your model
var changeEmailModel = new ChangeEmailModel(.....)
//return a partial view
return PartialView("EmailUpdateForm", changeEmailModel);
}
Make sure to return a PartialView here rather than a View.
5. Create a div to accept the newly returned Email Update form
Make sure this div is not contained in your Username lookup form (as you want to hide it). We will be working with two separate forms. This div could be hidden if you prefer (but will start out empty anyway) I am calling it emailFormDiv
6. Use JQuery to override the submit on your username lookup to perform an ajax update instead
JQuery will allow you to attach functions to... well a lot of things, but we will be using it to override the submit button on your username lookup form. Assume that your original username lookup form with an id of "formUserNameLookup" that has a submit button with an id of "submitUserNameLookup". You would then create a script tag that looks something like this:
<script type="text/javascript" language="javascript">
$(document).ready(function () { //The document.ready function will fire when the html document is... ready
$('#submitUserNameLookup').click(function (ev) { //fires when the submit button is clicked
ev.preventDefault(); //prevent the normal action of the button click
$.post($('#formUserNameLookup').attr('action'), //get the url from the form's action attribute. Could be hard coded for simplicity
$('#formUserNameLookup').serialize(), //serialize the data in the form
function (response, status) {
$('#emailFormDiv').html(response); //replace the html of your div with the response
$('#formUserNameLookup').hide(); //hide the original form
}, 'html'); //states that we are expecting html back from the post
});
});
</script>
The code above is attaching a function to be run when the submit button is clicked. It won't run, of course, until the button is actually clicked. Using JQuery/Javascript to attach functions to html elements, rather than embedding them directly inside the element is definitely preferred, and is referred to as unobtrusive javascript. If you continue with ajaxifying more of your page, you will want to look into JQuery's live and/or delegate functions. Note that there are plenty of things that can be changed once you start looking toward performance and/or best practices. The above should get you going though. I hope I haven't made too many assumptions on your current level of familiarity with ASP.Net MVC (like controllers and posting to controllers) but by all means, ask if you need further help.

HttpPost issues using ASP.NET MVC 3 with Facebook Connect in an iFrame

When implementing a Facebook Canvas app using an iframe the iframe does a post to my page. This is fine, but it causes the validators to fire which brings up the page with errors on all of the input fields.
Is there anyway to not have the validators fire on this initial page load through the iframe canvas?
Here is a sample code snippet:
#model My.User
#using (Html.BeginForm())
{
#Html.TextBoxFor(e => e.FirstName)
#Html.ValidationMessageFor(e => e.FirstName)
<input type="submit" />
}
The validators are being fired because Facebook is doing an HTTP POST to the url being loaded into the iFrame. By default, turn off your validators initially. Only once when you get a signed_request POST parameter, from facebook then you turn them on.
In the interest of using the framework and not making a hack, this is how I solved this... I am not set that this in the end all be all because there are some issues when implementing this with larger sets of permissions which are noted below. The main issue is the constraint of the length of the URL.
First, when the user hits the site I am deciding to send them along based on my own set of rules, but still persist the signed request from Facebook via the URL.
if(Liked)
{
return RedirectToAction("SimplyAnswerTheQuestion", new { sr = FBSignedRequest });
} else {
return View(ElitistResponse(BetterThanYouObject));
}
Now the issue with this is that you will run out of space to store the signed request as a query string variable if you have been allowed more information about the FB user (i.e. Likes, Friends, etc.). You could capture the signed request and store it before the redirect and then give the user a token in a cookie to pass along to access the data throughout the rest of the session.

Resources