I'm getting a 403 error when I try to post a form with codeigniter, vuejs and axios. I try many ways os solve this problem around the forum and internet, but nothing work. Here is the code, thank you for your help.
P.D: Code was working with form helper (form_open ) when I have to change it to normal html form I get 403 error...I think the problem is csrf token
Codeigniter config
$config['csrf_protection'] = TRUE;
$config['csrf_token_name'] = 'csrf_token_idic';
$config['csrf_cookie_name'] = 'csrf_cookie_idic';
$config['csrf_expire'] = 7200;
$config['csrf_regenerate'] = TRUE;
$config['csrf_exclude_uris'] = array();
VUEJS and axios
var app = new Vue({
el: '#app',
data: function() {
return {
table: [{
n_muestra: '',
descripcion: '',
lote: '',
cantidad: '',
n_planilla: '',
ot_interna: '',
delete: false
}],
cant_filas: 1,
csrf_token_idic: '<?= $this->security->get_csrf_hash(); ?>',
}
},
methods:{
validateBeforeSubmit(e) {
e.preventDefault();
axios.defaults.withCredentials = true
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = this.csrf_token_idic;
this.$validator.validateAll().then((result) => {
if (result) {
data= {
'csrf_token_idic': this.csrf_token_idic,
'data': this.table
}
axios.post(baseUrl+'create/add_internal_work_orders/'+'<?php echo $this->uri->segment(3)?>' ,data)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
return;
}else{
alert("no");
}
});
}
}
})
BROWSER PREVIEW
caption
You must pass the data in the form of FormData.
For example:
Use in php page:
<?php
$csrf = array(
'name' => $this->security->get_csrf_token_name(),
'hash' => $this->security->get_csrf_hash()
);
?>
new Vue({
el: '#app',
data() {
return {
token_name: '<?= $csrf['name']; ?>',
token_hash: '<?= $csrf['hash']; ?>',
...
methods: {
validateBeforeSubmit(){
var bodyFormData = new FormData();
bodyFormData.append(this.token_name, this.token_hash);
bodyFormData.append('firstname', 'Joe');
bodyFormData.append('lastname', 'Backer');
bodyFormData.append('age', 31);
axios.post(baseUrl+'create/add_internal_work_orders/'+'<?php echo $this->uri->segment(3)?>', bodyFormData)
.then(response => {
console.log(response);
});
}
https://codeigniter.com/userguide3/libraries/security.html?highlight=security
Related
**my rout file and when i type directly posts in URL it shows the posts but with created method in app.js it shows nothing **
Route::get('/posts', function () {
$posts_json = DB::table('posts')
->orderBy('posts.created_at','desc')->take(4)->get();return $posts_json;}
My app.js file
const app = new Vue({
el: '#app',
data: {
msg: 'make post',
content:'',
posts: [],
bUrl: 'http://localhost/pathikhome',
},
ready: function(){
this.created();
},
created(){
axios.get(this.bUrl +'/posts')
.then(response => {
console.log(response);
this.posts = response.data;
})
.catch(function (error) {
console.log(error);
});
},
methods: {
addPost(){
axios.post(this.bUrl +'/addPost', {
content:this.content
})
if not success
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
}});
ready is not supported anymore. That's Vue v1. Your new method is mounted. See https://v2.vuejs.org/v2/guide/instance.html#Lifecycle-Diagram and https://v2.vuejs.org/v2/guide/migration.html#ready-replaced
Also data is a function that returns a data object, so if should look like this:
data: function() {
return {
msg: 'make post',
content: '',
posts: []
}
}
remove this.bUrl in the url of your axios:
created(){
axios.get('/posts')
.then(response => {
EDIT:
try to remove the ready function:
ready: function(){
this.created();
},
your data() should have a return inside:
data() {
return{
msg: 'make post',
content:'',
posts: [],
bUrl: 'http://localhost/pathikhome',
}
},
I am using VUE.js with Laravel to upload file using fetch api. I have added the csrf token to the header of the request, but still getting the 419 unknown status. Any help will be appreciated thanks.
Here is the JS of the component
<script>
export default {
name:'UploadModal',
data(){
return {
image:'',
ext:'',
file:''
};
},
methods: {
onFileChange(e) {
var files = e.target.files || e.dataTransfer.files;
if (!files.length)
return;
this.file = files[0];
this.createImage(files[0]);
},
uploadArtwork: function () {
let formData = new FormData();
formData.append('artwork', this.file);
fetch(this.$parent.itemUrl, {
method:'POST',
body: formData,
headers: {
'Content-Type': 'multipart/form-data',
'X-CSRF-TOKEN' : Laravel.csrfToken
}
})
.then(res => res.json())
.then(res => {
alert(res);
})
.catch(e => console.log(e));
},
createImage(file) {
var image = new Image();
var reader = new FileReader();
var vm = this;
reader.onload = (e) => {
vm.image = e.target.result;
};
reader.readAsDataURL(file);
},
}
}
</script>
I know this is an old question, but I ran into this issue as well when using fetch and the linked answer (Laravel 5.5 ajax call 419 (unknown status)) did not help, since that relates to jQuery's Ajax method.
For those who are facing the same issue, it looks like this is due to the default credentials setting (defaults to "omit"), which essentially omits the csrf header for some reason. You can get around this by changing credentials to "same-origin" or "include" depending on your needs.
Example:
fetch("/leads", {
method: 'POST',
credentials: "same-origin",
headers: csrf_header
}).then(res => res.json())
.then(
(json) => {
this.setState({
isLoaded: true,
items: json.leads.data,
sort: json.sort,
search: json.search,
sort_by: json.sort_by,
filter: json.filter
});
}
);
I'm trying to download an excel file using ajax method in laravel.
Controller function:
$myFile = Excel::create($name, function ($excel) use ($export) {
$excel->sheet('Data', function ($sheet) use ($export) {
$sheet->fromArray($export);
$sheet->cells('A1:N1', function ($cells) {
$cells->setBackground('#dbdbdb');
$cells->setFontColor('#000000');
$cells->setFontWeight('bold');
$cells->setFont(array(
'family' => 'Calibri',
'size' => '9',
));
});
$sheet->setStyle(array(
'font' => array(
'name' => 'Calibri',
'size' => 9,
),
));
});
});
$myFile = $myFile->string('xlsx');
$response = array(
'name' => $name,
'file' => "data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64," . base64_encode($myFile),
);
return response()->json($response);
Ajax function:
$(document).on('click', '.ExportJobs', function() {
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
var ids = [];
$(".InvoiceCheckBox:checked").each(function(e) {
ids.push(this.value);
});
data = {
"ids": ids,
};
$.ajax({
method: "POST",
url: "/exportNew",
data: data,
success: function(response) {
var a = document.createElement("a");
a.href = response.file;
a.download = response.name;
document.body.appendChild(a);
a.click();
a.remove();
}
});
});
But using above controller method is not returning excel formatted file if I change string value from xlsx to csv then csv formatted file is getting downloaded.
How do we make the excel formatted file downloaded? Any suggestions, Please!
I know this is quite late, but posting for others who struggle with same issue like me
I also needed to download excel from using Maatwebsite excel library by using ajax post call.
added a button to fire the ajax call to download excel file
<button onclick="downloadExcel()" id="btn-download-payroll" class="btn btn-dark-success btn-md" style="transform: translateY(50%); top: 50%; font-size: 13px;"><i aria-hidden="true" class="fa fa-cog mr-10"></i>
Download
</button>
Used following js code to post ajax request
function downloadExcel() {
var salaryMonth = $("#dp-salary-month").datepicker("getDate");
var department = $("#cbox-department");
var month = new Date(salaryMonth).getMonth() + 1;
var year = new Date(salaryMonth).getFullYear();
$.ajax({
xhrFields: {
responseType: 'blob',
},
type: 'POST',
url: '/downloadPayroll',
data: {
salaryMonth: month,
salaryYear: year,
is_employee_salary: 1,
department: department.val()
},
success: function(result, status, xhr) {
var disposition = xhr.getResponseHeader('content-disposition');
var matches = /"([^"]*)"/.exec(disposition);
var filename = (matches != null && matches[1] ? matches[1] : 'salary.xlsx');
// The actual download
var blob = new Blob([result], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
});
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
});
}
in routes/web.php file set the reoute for my controller
Route::post('/downloadPayroll', 'Payroll\\Process\\PayrollController#downloadPayroll');
Here I used maatwebsite/excel library to generate excel file with FromQuery approach but due to library update Excel::create has been replaced by Excel::download in "maatwebsite/excel": "^3.1" I used download method in my case here is my HelperClass to generate records according to my requirement
PayrollHelper.php
namespace App\Http\Helpers;
use App\PayrollEmployee;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\Exportable;
class PayrollHelper implements FromQuery
{
use Exportable;
public function forDepartment(int $department)
{
$this->department = $department;
return $this;
}
public function forMonth(string $month)
{
$this->month = $month;
return $this;
}
public function query()
{
// get the salary information for the given month and given department
return PayrollEmployee::query()->where(['salary_month' => $this->month,'department_id'=>$this->department]);
}
}
finally in my controller
class PayrollController extends Controller
{
public function downloadPayroll(Request $request)
{
$file_name = '';
try {
$requestData = $request->all();
$salary_month = $requestData['salaryMonth'];
$salary_year = $requestData['salaryYear'];
$department = $requestData['department'];
$is_employee_salary = boolval($requestData['is_employee_salary']);
$month = Carbon::createFromDate($salary_year, $salary_month);
$month_start = Carbon::parse($month)->startOfMonth();
$formated_month = Carbon::parse($month)->format('F Y');
$file_name = 'Employee_salary_' . $formated_month . '.xlsx';
// to download directly need to return file
return Excel::download((new PayrollHelper)->forMonth($month_start)->forDepartment($department), $file_name, null, [\Maatwebsite\Excel\Excel::XLSX]);
} catch (exception $e) {
}
}
}
After creating excel file return file to get as ajax response as blob
That's all
Just see the xhrFields to set responseType as blob and then see the ajax success part. Hope you everyone find the solution:
<script>
$(document).ready(function(){
$("#ExportData").click(function()
{
dataCaptureExport();
});
});
function dataCaptureExport(){
var FromDate = $('#dateFrom').val();
var ToDate = $('#dateTo').val();
var dataString = { FromDate: FromDate, ToDate:ToDate, _token: '{{csrf_token()}}'};
$.ajax
({
type: "POST",
url: '{{ route('invoice_details_export') }}',
data: dataString,
cache: false,
xhrFields:{
responseType: 'blob'
},
success: function(data)
{
var link = document.createElement('a');
link.href = window.URL.createObjectURL(data);
link.download = `Invoice_details_report.xlsx`;
link.click();
},
fail: function(data) {
alert('Not downloaded');
//console.log('fail', data);
}
});
}
It's late but help for others
You can do this way
In Ajax
$(document).on('click', '#downloadExcel', function () {
$("#downloadExcel").hide();
$("#ExcelDownloadLoader").show();
$.ajax({
url: '{{ route("admin.export_pending_submitted_tasks") }}',
method: "GET",
cache: false,
data: {
search_partner,
search_state,
search_city,
_token,
},
success: function (response) {
var a = document.createElement("a");
a.href = response.file;
a.download = response.name;
document.body.appendChild(a);
a.click();
a.remove();
$("#downloadExcel").show();
$("#ExcelDownloadLoader").hide();
},
error: function (ajaxContext) {
$("#downloadExcel").show();
$("#ExcelDownloadLoader").hide();
alert('Export error: '+ajaxContext.responseText);
}
});
});
In Controller
// Get pending submitted tasks export excel
public function export_pending_submitted_tasks(Request $request){
$input = $request->input();
$pending_submitted_tasks = SubmittedTask::select('id', 'partner', 'se_id', 'description', 'created_at', 'status', 'updated_at');
(isset($input['search_partner'])) ? $pending_submitted_tasks->where('partner_id', $input['search_partner']) : '';
(isset($input['search_partner'])) ? $pending_submitted_tasks->where('state', 'like', '%'.$input['search_state'].'%') : '';
(isset($input['search_partner'])) ? $pending_submitted_tasks->where('city', 'like', '%'.$input['search_city'].'%') : '';
$pendingTaskList = $pending_submitted_tasks->where('status', 'pending')->get();
if($pendingTaskList->count() > 0):
$myFile = Excel::raw(new ExportPendingTaskHelper($pendingTaskList), 'Xlsx');
$response = array(
'name' => "Pending-Task-List.xlsx",
'file' => "data:application/vnd.ms-excel;base64,".base64_encode($myFile)
);
return response()->json($response);
else:
return back()->with('message', 'No Pending tasks available to download!!');
endif;
}
If you are using jquery:
// In controller:
return Excel::download(new SomeExport, 'Some_Report.xlsx', null, [\Maatwebsite\Excel\Excel::XLSX]);
// Ajax:
$.ajax({
type: 'GET',
url: '{{ route("some.route") }}',
data: {
"_token": "{{ csrf_token() }}"
},
xhrFields:{
responseType: 'blob'
},
beforeSend: function() {
//
},
success: function(data) {
var url = window.URL || window.webkitURL;
var objectUrl = url.createObjectURL(data);
window.open(objectUrl);
},
error: function(data) {
//
}
});
I've currently got this:
$.ajax({
url: '/some/url',
dataType: 'json',
type: 'POST',
data: formData,
success: function(data) {
if (window.confirm('Thank you for your message. Can I erase the form?')) {
document.querySelector('.form-input').val('');
}
},
error: function(xhr, status, err) {
console.error(status, err.toString());
alert('There was some problem with sending your message.');
}
});
Instead of it going to a URL, how can I change it to send directly to a specific email address? I am using this contact form with a React app I've created.
So react component, class based.
class Foo extends Component {
popupQuestion() {
// implement method
}
sendEmail() = () => {
axios.post('/some/url', {
subject: 'mail',
to: 'someone#example.com',
body: 'something',
name: 'name'
})
.then(function (response) {
popupQuestion();
})
.catch(function (error) {
console.log(error);
return 'Error occurred. Please refresh page and try again.';
});
}
render() {
return(
<form onSubmit={this.sendEmail}>
// ...
</form>
);
}
}
And php method that will be executed on some/url
public function sendEmailAction(): bool
{
$request = // get request;
$subject = $request->get('subject');
$to = $request->get('to');
$body = $request->get('body');
$name = $request->get('name');
$transport = (new Swift_SmtpTransport('smtp.example.org', 25))
->setUsername('your username')
->setPassword('your password');
$mailer = new Swift_Mailer($transport);
$message = (new Swift_Message($subject))
->setFrom(['mymail#exmaple.com' => 'me'])
->setTo([$to => $name])
->setBody($body);
$sent = $mailer->send($message);
return $sent ? true : false;
}
I'm stuck implementing load next set of posts in WordPress with isotope masonry layout.
It's just not triggered for appended elements and I can't figure out why.
If someone could point me in right direction I would appreciate.
jQuery AJAX function
function load_content( args ) {
$container = $('#container-async');
$status = $('.status');
$.ajax({
url: ajax_url,
data: {
action: 'do_load_posts',
nonce: nonce,
args: args
},
type: 'post',
dataType: 'json',
success: function( data, textStatus, XMLHttpRequest ) {
$items = data.posts;
if ( args.action === 'filter') {
/**
* Replace items
*/
$container.imagesLoaded( function() {
$container.isotope({
itemSelector: '.grid-item',
percentPosition: true,
masonry: {
columnWidth: '.grid-sizer'
}
});
$container.html( $items ).isotope( 'appended', $items, function() {
console.log('inserted');
});
});
}
else {
/**
* Append items
*/
$container.imagesLoaded( function() {
$container.isotope({
itemSelector: '.grid-item',
percentPosition: true,
masonry: {
columnWidth: '.grid-sizer'
}
});
$container.append( $items ).isotope( 'appended', $items, function() {
console.log('appended');
});
});
}
},
error: function( MLHttpRequest, textStatus, errorThrown ) {
$status.toggleClass('active');
}
});
}
WordPress AJAX:
function ajax_do_load_posts() {
// Verify nonce
if( !isset( $_POST['nonce'] ) || !wp_verify_nonce( $_POST['nonce'], 'nonce' ) )
die('Permission denied');
$response = [
'status' => 500,
'posts' => 0,
];
$filter = $_POST['args'];
$type = get_post_type_object( $filter['type'] );
$args = [
'post_type' => sanitize_text_field($filter['type']),
'paged' => intval($filter['page']),
'posts_per_page' => $filter['qty'],
];
$qry = new WP_Query($args);
if ($qry->have_posts()) :
ob_start();
while($qry->have_posts()) : $qry->the_post(); ?>
<div class="grid-item col-xs-12 col-sm-6 col-md-4 col-lg-3">
<?php get_template_part('templates/loop', 'card' ); ?>
</div>
<?php endwhile;
$response = [
'status' => 200,
'posts' => ob_get_clean(),
];
endif;
wp_reset_query();
wp_reset_postdata();
die(json_encode($response));
}
add_action('wp_ajax_do_load_posts', 'ajax_do_load_posts');
add_action('wp_ajax_nopriv_do_load_posts', 'ajax_do_load_posts');
Items are inserted or appended into container but masonry is triggered only on items that are already there on page load.
I have googled around and tried various solutions but none of them work, neither with isotope or masonry jquery plugin.
I was able to fix my issue, and kind of looks like the problem is because I was using jQuery default append/insert methods.
I have switched to isotope methods and now things look fine.
So basically instead of
$container.html( $items ).isotope( 'appended', $items, function() {
console.log('inserted');
});
I am now using:
$grid.isotope('insert', jQuery($items));
It looks logical actually that isotope cannot know items are appended if I don't use isotopes append/insert method. And notice that I also wrapped $items inside jQuery.
Anyhow my working JS for same PHP function is:
function load_content( args ) {
$container = $('#container-async');
$status = $('.status');
$.ajax({
url: ajax_url,
data: {
action: 'do_load_posts',
nonce: nonce,
args: args
},
type: 'post',
dataType: 'json',
success: function( data, textStatus, XMLHttpRequest ) {
$items = data.posts;
var $grid = $('.masonry').imagesLoaded( function() {
$grid.isotope({
itemSelector: '.grid-item',
percentPosition: true,
masonry: {
columnWidth: '.grid-sizer'
}
});
if ( args.action === 'filter') {
$grid.isotope('remove', $('.grid-item'));
}
$grid.isotope('insert', jQuery($items));
});
$grid.on( 'layoutComplete', function( event, laidOutItems ) {
$container.removeClass(data.action);
});
},
error: function( MLHttpRequest, textStatus, errorThrown ) {
$status.toggleClass('active');
}
});
}