It's been 2 days now and still I can't get this problem to work.
So basically I am trying to call a php function through jquery ajax
but nothing is working, I am not sure what is the problem is...
here are my codes
the ajax
$(document).ready(function() {
$("#idCheck1").click(function(){
$.ajax({
url:'../../../../Controller/PostsController.php',
data: {action: 'update_checkbox'},
success:function(result){
// $("#dsa").html(result);
}
});
});
});
the view
<?php echo $this->Form->create("Posts", array("action" => "update_checkbox", "id" => "checkingBox")) ?>
<td>
<?php
echo $this->Form->input('Post.' . $i . '.id', array("type" => "hidden", "label" => false, "value" => $sum['posts']['id']))
?>
<?php
echo $this->Form->input('Post.' . $i . '.done', array("type" => "checkbox", "label" => false, "value" => "1", "id" => "idCheck1"))
?>
</td>
</tr>
<?php
$i++;
}
?>
</table>
<?php echo $this->Form->end(); ?>
the controller
public function update_checkbox() {
// debug($this->data);
$var = $this->Post->saveCheckBox($this->data);
$this->set("result", $var);
}
the model
public function saveCheckBox($checkbox) {
debug($checkbox);
$this->saveAll($checkbox['Post']);
}
The url
url:'../../../../Controller/PostsController.php'
looks wrong. Since this is an AJAX request that goes through the browser you can't use relative paths that try to go upwards in the folder hierarchy, as browsers url's don't work that way. You should be making that request so that it is passed through a web server, i.e. the url should look like one of the following:
url:'http://localhost/Controller/PostsController.php'
or
url:'/Controller/PostsController.php'
The first option is an absolute url, but this also makes the code a bit less flexible (suppose you change the domain from localhost to something else). The second option is a relative URL, but one that is relative to the domain root of your web server (i.e. in the example it would still resolve to localhost/Controller...).
In both cases based on what you've posted, your PHP file should live in a Controller/ folder in the document root of your site. The structure of your code however suggests that you are using a framework of some kind (e.g. Zend, Symfony or CodeIgniter)? If that is the case it would be helpful if you post information on what framework you're using as well as that might change the answer.
UPDATE
In the case of cakePHP, you should access the controller through the front controller, meaning the URL should look like this:
url:'/posts'
UPDATE 2
For the jquery side, a complete ajax request example could look like this:
$(".idCheck").click(
function(){
var idVal = $(this).val();
$.ajax({
url:'/posts/update_checkbox',
data: {id: idVal },
type: 'POST',
success:function(result){
$("#dsa").html(result);
}
});
});
Note that the URL already contains the update_checkbox action (i.e. the complete url to the action you want to execute), and the data object contains the value of the clicked element that you want to send to the server. If you want to send a complete form you could also use $('#myformselector').serialize() to convert all inputs in the form to a object suitable for the data property of the ajax request.
Related
I'm trying to return a query made in a controller to the view file so I can use that data in my form. But I am unable to successfully return the data without errors. I know the function is working because it returns the right data but has an error.
Here is my CustomersController fill function which is running the sql query.
public function fill(){
$layout = 'ajax';
$this->autoRender = false;
if ($this->request->is('post')) {
$id = $this->request->data['id'];
$query = $this->Customers->find()
->where([
'id' => $id
])->first();
echo json_encode($query);
}
}
and here is my blah.ctp which is the view file.
<?php use Cake\Routing\Router; ?>
<?= $this->Form->create(Null, ['type' => 'POST']) ?>
<?php
echo $this->Form->input('customer_id', ['options' => $customers, 'empty' => true,'id'=>'customers']);
?>
<?= $this->Form->end() ?>
<script>
document.getElementById('customers').addEventListener('change',function(){
var id = this.value;
var csrfToken = $('[name=_csrfToken]').val();
$.ajax({
type: "POST",
url: '<?php echo Router::url(array("controller" => "Customers", "action" => "fill")); ?>',
data: {'id' : id},
beforeSend: function(xhr){
xhr.setRequestHeader('X-CSRF-Token', csrfToken);
},
success: function(data){
alert(data);
}
});
});
</script>
Currently this is what happens when I select a customer in my drop down box which triggers the script in the view.
As you can see it returns the array of data I need but also has the error cannot emit headers. I have tried solving this error following other questions on stack overflow but can't solve it.
I've tried using $this->set instead of echo json_encode but it always returns nothing. I'm not sure what other way to do this.
First of all, If you're selecting a single record by a unique ID, you can call
->get($id) on the table object directly, instead of building a query chain with ->find().
CakePHP 3.x should automatically handle converting your view to JSON by using the RequestHandlerComponent. Typically, you must enable it if your scaffolding or installation didn't.
1) Enable request handler component. (If not already enabled) https://book.cakephp.org/3.0/en/controllers/components/request-handling.html
2) Remove the echo json_encode($query) line; you should not echo anything as this will break the request/response cycle. This is the cause of your error.
Instead, you should serialize your data to the view. Assuming you have the fetched data in $data: $this->set(compact('data')). Then, make sure you add $this->set('_serialize', ['data']) (again, assuming the data is stored in variable name 'data').
3) Reference this doc for information on how you can request the json. You can use a file extension (.json, .xml).
Also, make sure you add the 'Accept: application/json' header to your request.(https://book.cakephp.org/3.0/en/development/routing.html#Cake\Routing\Router::extensions).
I apologize for the fuzzy response. There are many ways to achieve this with CakePHP3. Please see this doc page for more information: https://book.cakephp.org/3.0/en/views/json-and-xml-views.html
I am trying to save the value of multiple checkboxes in WordPress, So if a guest checks a checkbox (front-end) without clicking any submit buttons, this checked value will be stored in the database.
Next time anyone loads this page, this particular checkbox should be pre-checked.
So far I have got this code in the template file that is responsible for the content of the page I am trying to add these checkboxes to.
(This is no regular WP post/page template but a "collection page" generated by a WooCommerce collection plugin):
//submit code
if(isset($_POST['cadeau']) ){
$data=serialize($_POST['cadeau']);
update_post_meta($post_id, 'cadeau', $data);
}
//edit code
$data=get_post_meta($post_id, 'cadeau');
//$data=unserialize($data[0]);
$data=$data[0];
print_r($data);?>
<input type="checkbox" name="cadeau[]" value="<?php echo $product_id;?>" <?php if(in_array($product_id,$data) ){echo "checked";} ?> >
This is not working however.. Am I on the right track? Or am I doing it completely wrong?
Basically, to make such things in WordPress (and not only) you have to make a POST request to the server. Either by submitting a form or doing an AJAX request. Here is an example of how to make it to work. If you don't want to make AJAX request just skip step 1 and jump directly to step 3 and have a look at the function. I am sure it will help you go on the right track.
1. Create a JS script which sends an AJAX request to admin-ajax.php file:
<script>
jQuery("#submit-button").on("click", function(){
var checkboxValue = jQuery("#your-checkbox").is(":checked") ? 1 : 0;
jQuery.ajax({
url: "http://www.yourdomain.com/admin-ajax.php",
method: 'POST',
data: {action: "save_checkbox", checked: checkboxValue}
}).done(function(response){
console.log("Response: " + response);
//NOTE that 'action' MUST be the same as PHP function name you want to fire
//you can do whatever you want here with your response
}).fail(function(error){
console.log(error);
});
})
</script>
2. Create new file e.x. myajax.php and include it to functions.php
3. Now write a function you want to fire on click, e.x.:
function save_checkbox(){
$checkboxValue = $_REQUEST['checked'];
update_post_meta($coll_id, 'cadeau', $checkboxValue );
$response = array(
"status" => "ok",
"message" => "Post meta saved",
);
$response = json_encode($response);
echo $response;
die();
}
add_action( 'wp_ajax_save_checkbox', 'save_checkbox' );
That's all. As I said note that actionhas to be the same as PHP function. If you want to have some response from your PHP function just echo it and this is what you will get as a response. If you'll get 0 as a response it means that function you want to fire does not exists or you didn't call die() at the end of your function. I hope I could help.
Please, explain to me how to run snippet CookieList with ajax?
I tried next:
1. Created snippet ajaxCookieList:
<?php
if (isset($_POST["action"])) {
$values = $modx->runSnippet('addToCookieLIst',array(
'value' => $_POST['action']
));
$output = $modx->runSnippet('pdoResources',[
'parents' => 6,
'resources' => $values,
'tpl' => 'popup.favorites.item',
'includeTVs' => 'header.bgImage,franchise.logo,franchise.price,title,subtitle',
'prepareTVs' => '1',
'hideContainers' => '1'
]);
return $output;
}
Then i created chunk with this code:
<script>
jQuery(function($){
$('a.franchise-pin, a.franchise-favorite-add').click(function(e){
var value = $(this).data('value');
$.post(document.location.href, {action: value}, function(data) {
$('#favorites').html(data);
$('#favorites').modal('show');
});
e.preventDefault();
});
});
</script>
But response is all page..
What is wrong?
What I usually do in such cases:
I place a snippet call ([[!ajaxCookieList]]) on a service page
accessible via URL like /page-with-snippet/
In JS (ajax) I use that URL to which I send parameters
The snippet has to get the parameters I send. So, I really call it like this: [[!ajaxCookieList? &action=[[!#POST.action]]]]
Access to parameters in snippets is possible like this: $option = $modx->getOption('action', $scriptProperties, 'default_value', true);
I do my stuff in the snippet
But in your case, I think, everything can be simpler. You use one of the pdoTools snippets and if I am not mistaken, you can just place pdoResources snippet call on a page (/page-with-snippet/) like this:
[[pdoResources?
&parents=`6`
&resources=`[[!addToCookieLIst? &value=`[[!#POST.action]]` ]]` // your snippet should return comma-separated list of resources` ids that you pass then to pdoResources
&tpl=`popup.favorites.item`
&includeTVs=`header.bgImage,franchise.logo,franchise.price,title,subtitle`
&prepareTVs=`1`
&hideContainers=`1`
]]
and now you can send parameters to this page (/page-with-snippet/) via AJAX and get the results if there are any. I hope I didn't mess anything - you had better check it again, but you get the idea at least :) BTW, check this article on modx.com that teaches how to write a good snippet.
Also another minor issue: as it has been pointed out here, the use of window.location is preferable to document.location.
Here is another solution I did. I used pdoResources. Hope that you will understand my code and customize it for yourselves.
Create snippet ajaxCookieList
Paste JS-code in your custom.js file
Simple markup for resources. Insert it in the chunk:
Add to wish list
Remove from wish list
Thas all:)
Edit:
A whole series of new insights gained after asking this question have taught me what the issue was, and it definitely did not have anything to do with the described server migration.
The two given answers show how to "fix" this for both CakePHP 2 and 3, though bear in mind this might pose a security risk. The CSRF component is an important security feature, and should not be disabled lightly.
Original question:
I migrated my CakePHP 3 project from XAMPP on my laptop to XAMPP on a server. Ever since when I activate the Security component, cake throws me an error. Here it is, directly from the Error log:
2016-05-21 20:32:01 Error: [Cake\Controller\Exception\AuthSecurityException] '_Token' was not found in request data.
Request URL: /Users/addUser
Referer URL: http://localhost/users/add_user
Stack Trace:
#0 C:\xampp\htdocs\vendor\cakephp\cakephp\src\Controller\Component\SecurityComponent.php(324): Cake\Controller\Component\SecurityComponent->_validToken(Object(App\Controller\UsersController))
#1 C:\xampp\htdocs\vendor\cakephp\cakephp\src\Controller\Component\SecurityComponent.php(130): Cake\Controller\Component\SecurityComponent->_validatePost(Object(App\Controller\UsersController))
#2 C:\xampp\htdocs\vendor\cakephp\cakephp\src\Event\EventManager.php(386): Cake\Controller\Component\SecurityComponent->startup(Object(Cake\Event\Event))
#3 C:\xampp\htdocs\vendor\cakephp\cakephp\src\Event\EventManager.php(356): Cake\Event\EventManager->_callListener(Array, Object(Cake\Event\Event))
#4 C:\xampp\htdocs\vendor\cakephp\cakephp\src\Event\EventDispatcherTrait.php(78): Cake\Event\EventManager->dispatch(Object(Cake\Event\Event))
#5 C:\xampp\htdocs\vendor\cakephp\cakephp\src\Controller\Controller.php(495): Cake\Controller\Controller->dispatchEvent('Controller.star...')
#6 C:\xampp\htdocs\vendor\cakephp\cakephp\src\Routing\Dispatcher.php(109): Cake\Controller\Controller->startupProcess()
#7 C:\xampp\htdocs\vendor\cakephp\cakephp\src\Routing\Dispatcher.php(87): Cake\Routing\Dispatcher->_invoke(Object(App\Controller\UsersController))
#8 C:\xampp\htdocs\webroot\index.php(37): Cake\Routing\Dispatcher->dispatch(Object(Cake\Network\Request), Object(Cake\Network\Response))
#9 {main}
I found CakePHP security component blackholing login (data[_Token][key] field not generated), here on StackOverflow, but no other relevant information as to what's causing my problem. In my Appcontroller:
public function initialize()
{
parent::initialize();
$this->loadComponent('Security');
$this->loadComponent('RequestHandler');
$this->loadComponent('Flash');
The error is related to the _TOKEN. When we create a CakePHP form and then based on the input fields the CakePHP generates hidden field named _TOKEN.
For example:
<?= $this->Form->create(false, [
'id' => "ajaxForm",
'url' => [
'controller' => 'TPCalls',
'action' => 'add'
],
'class'=> "addUpdateDeleteEventForm"
]);
?>
<?= $this->Form->input('id', ['label' => false]); ?>
<?= $this->Form->input('start', ['label' => false]); ?>
<?= $this->Form->input('end', ['label' => false]); ?>
<?= $this->Form->input('title', ['label' => false]); ?>
<?= $this->Form->hidden('ADD', ['value' => 'true']); ?>
<?= $this->Form->end(); ?>
Now you should see _TOKEN value in the form when inspecting the HTML:
<input type="hidden" name="_Token[fields]" autocomplete="off" value="---HASH---">
If you do not have any visible fields then _Token will be empty. If you need to have invisible fields then simply add a hidden class on the form or the field.
Anyways, back to the main question. The error is caused by the _TOKEN field's absence. In above case, I would serialize my form before making the Ajax call.
//serializing the form
var ajaxdata = $("#ajaxForm").serializeArray();
//ajax
$.ajax({
url:$("#ajaxForm").attr("action"),
type:"POST",
beforeSend: function(xhr){
xhr.setRequestHeader("X-CSRF-Token", $('[name="_csrfToken"]').val());
},
data:ajaxdata,
dataType: "json",
success:function(response) {
console.log(response);
},
error: function(response) {
console.error(response.message, response.title);
}
});
Please note, in the ajax, I am using URL from the Cakephp form instead of hard coding it in the ajax. This way, it will be using cakephp url helper.
EDIT after #Invincible comment
Be careful when disabling csrf and security components, they provide protection against csrf and things like form-tampering, forcing ssl, http methods etc https://book.cakephp.org/3.0/en/controllers/components/security.html.
This answer shows only how to disable them, in case if you are sure you do not need them for that request.
Original Answer
In case of ajax requests you can can disable Security Component for that specific action (equivalent to making the action as unlocked in cake 2.x)
put this code in your controller's beforeFilter
$actions = [
'action1',
'action2'
];
if (in_array($this->request->params['action'], $actions)) {
// for csrf
$this->eventManager()->off($this->Csrf);
// for security component
$this->Security->config('unlockedActions', $actions);
}
disabling csrf component http://book.cakephp.org/3.0/en/controllers/components/csrf.html#disabling-the-csrf-component-for-specific-actions
disabling security component http://book.cakephp.org/3.0/en/controllers/components/security.html#disabling-security-component-for-specific-actions
UPDATE: Also, make sure you didn't forget echo $this->Form->end(); as it adds all the necessary tokens. Original answer below.
UPDATE: You may also run into this issue when submitting a form created separately via new \Cake\View\ViewBuilder()
The correct answer is indeed to invest some time into updating your code to where it's following the security component guidelines. Disabling the component, or unlocking a specific action, is a workaround, and not a solution.
Several not-so-obvious things about _Token.
Prerequisites: I have a placeholder <form> used for building repetitive, almost identical AJAX requests (one field is being constantly updated in a javascript loop and re-submitted; don't ask why).
_Token is linked to the form action URL. You can't have your placeholder form point to e.g. javascript:; and have actual requests, signed with it's token, go to some other endpoint
_Token is reusable. It is possible to issue multiple requests (i.e. submit the same form over and over again) signed with the same token, it's not a one-time token like I thought
So what I did was put my varying piece of information into a type="text" input and display:none it, instead of using a type="hidden" input, which would fall under the form tampering prevention. Then I serialize()'d the form and put it into the jQuery.ajax() data property.
// update the CSS-hidden type="text" input
document.forms.exampleForm.quantity.value=newValue;
// submit the form
jQuery.ajax({
url: document.forms.exampleForm.action,
type: document.forms.exampleForm.method,
data: jQuery(document.forms.exampleForm).serialize(),
complete: function(jqXHR) {
//
}
});
Surely one could take the easy unlockedActions route but you'll probably be glad if you don't.
#Invincible answer is good and all, but applying csrf that way seems like a nightmare to apply and maintenance, since we already have like 20 or so Ajax in our application.
So I use Cakephp 3 - element to help abstract some of the code. I am pasting my code here for other to reference if you also want to abstract the csrf token.
Here's the code:
The element: csrf_ajax_element.ctp
<?=
$this->Form->create(false, [
"id" => $name . "Form",
"url" => $url,
]);
?>
<? if(isset($params)): ?>
<? foreach($params as $param) : ?>
<?= $this->Form->input($param, ['label' => false, 'style' => 'display:none;']); ?>
<? endforeach; ?>
<? endif; ?>
<?= $this->Form->end(); ?>
<script type="text/javascript">
var csrfName = '<?=$name?>';
var url = '<?= $this->Url->build($url) ?>';
var csrf = { };
$.each($('#'+csrfName+'Form').serializeArray(), function() {csrf[this.name] = this.value;});
$("#"+csrfName).data('csrf', csrf);
$("#"+csrfName).data('url', url);
</script>
To add an ajax on a page, do the following:
some_page.ctp
<!-- At the top -->
<input id="myAjaxCsrfToken" type="hidden" data-csrf="" data-url="" />
<?= $this->element('csrf_ajax_element',
[
"name" => "myAjaxCsrfToken",
"params" => ['year'],
"url" => ["controller" => "Api", "action" => "myAjax", "_method" => "POST" ]
])
?>
<!-- When you need to use the ajax -->
<script type="text/javascript">
$.ajax({
url: $("#myAjaxCsrfToken").data('url'),
type: 'POST',
data: $.extend(
$("#myAjaxCsrfToken").data('csrf'),
{ year: 2019 }
),
complete: function() {
// things
}
});
</script>
Note: At the above, year is a custom param that need to pass to the ajax along with the token param, if you don't do it cakephp will output security error.
I also suffered from the same things, but it was solved
cakephp2.10.2
$this->Security->unlockedActions = array('action1', 'action2');
Why does CakePHP producing those URLs
<server>/Reporting/OnlineBanking/index/page:1
instead of
<server>/Reporting/OnlineBanking/index?page=1
There are alot of trouble with this using relative paths out of JavaScript for example.
And is there an option to change it?
Pagination with get parameters
By default, all parameters in CakePHP are named parameters (url fragments like /foo:bar/). This also applies, by default, to pagination arguments.
To use get arguments for pagination params - you can do that by setting appropriate config:
public $paginate = array(
'paramType' => 'querystring'
);
This will generate urls of the form:
/the/url?page=1&limit=10
Instead of:
/the/url/page:1/limit:10
Specifying urls as string is extremely fragile
Previously you mentioned using urls like get_backend_requests in javascript.
You will still find problems if you do that even using get arguments of pagination. That's because even for the "same" url, the result can be different:
/the/url => /the/get_backend_requests
/the/url/ => /the/url/get_backend_requests
Instead - specify urls in javascript as absolute urls:
$.ajax({
type: 'POST',
url: "/xyz/get_backend_requests",
...
If you're app is sometimes (or always) installed in a subfolder, you can account for that with a simple function:
e.g. in your html/layout put:
<html>
...
<script>
function url(url) {
return <?php
$base = rtrim(Router::url('/'), '/');
if ($base) {
echo "'$base' + ";
}
?>url;
}
</script>
Which will output:
<html>
...
<script>
function url(url) {
return '/subfolder' + url;
}
</script>
$.ajax({
type: 'POST',
url: url("/xyz/get_backend_requests"), // becomes the string "/subfolder/xyz/get_backend_requests"
...