Concrete5 Custom Ajax in Block View - php

On Concrete5-8.1.0 I have created a custom block with Ajax functionality based largely on the concrete5 docs - Implementing Ajax in Block View Templates. However, unlike the example I do not want to reload the block view, I want to pass specific messages based on the input. I tried a simple echo '{"msg":"ok"}'; and return '{"msg":"ok"}); as a test, but requests to the function yielded an empty response.
I found How To Send JSON Responses with Concrete5.7 and used Option 2 (for greater control of error codes) resulting in the following test code:
public function action_submit($token = false, $bID = false) {
if ($this->bID != $bID) {
return false;
}
if (Core::make('token')->validate('get_paper', $token)) {
//save to database
//removed for brevity
//send email
if ($this->emailto != '') {
//removed for brevity
}
if ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
return new Response(
json_encode(array('msg' => 'ok')),
200,
['Content-Type' => 'application/json']
);
} else {
Redirect::page($page)->send();
}
}
else {
return false;
}
exit;
}
The database save and email function as expected, but the response is still empty. In Chrome Dev Tools, I see the correct Content-Type (as a test, I tried text/html and saw that change in dev tools), but no content. Interestingly, if I change the status from 200 to 500 I not only see the status change reflected in dev tools, I also see the {"msg":"ok"} content that I'm expecting, but changing the status back to 200 and the content is again empty.
It seems I'm missing something simple... I have verified that all caching is turned off within C5 (site is still in development), I have also verified the jQuery request includes cache:false, but the solution escapes me.

Related

PHP header not working in foreach

Somewhat strange situation here
$location = 'Location: http://localhost/pages';
//header($location); exit; works
$response->header($location)->send(); exit; //doesn't work
and the $response object's class
public headers = [];
public function header($string, $replace = true, $code = 200)
{
$this->headers[] = [
'string' => $string,
'replace' => $replace,
'code' => $code
];
return $this;
}
public function send()
{
foreach ($this->headers as $header) {
header($header['string'], $header['replace'], $header['code']);
}
}
The code works fine when using vanilla header but it doesn't when using the methods. Am I missing something here?
You are returning the Location header to the browser with a 200 status code.
For a redirection to actually occur, a 3xx response code should be sent instead (usually, a 302). A 200 response code simply means "OK, content follows". For a redirection to actually take place, a 3xx response code must be given.
Your code is ultimately calling
header('Location: http://localhost/pages', true, 200);
which isn't going to result in the browser redirecting you to the desired location.
PHP itself special-cases calls to header('Location: ...') and unless otherwise specified, uses a 302 instead of leaving the response code unchanged. You may want to adjust your code to do the same to keep the same behavior as PHP.
Also, important to note that, while every HTTP response only has one response code, header() allows you to set the response code each time you call it.
Thus, if you use your code like this:
$response
->header("Location: http://localhost/pages", true, 302)
->header("SomeOtherheader: value")
->send()
;
the 302 you intended to send will get replaced with the 200 that gets set in the next call to header().
Instead, what you should do is either separate the concept of setting the status code from actually setting the header content, e.g.:
$response
->header("Location: http://localhost/pages"))
->header("SomeOtherheader: value")
->responseCode(302)
->send()
;
or instead do what header() does and treat an unspecified response code as meaning, don't change the what's already been set:
public function header($string, $replace = true, $code = false) { ... }
false (or 0) passed on to PHP's header() will indicate that.

PHP Process page 404'ing when sending back a good JSON response

Have a sendmail.php page that i'm calling via ajax on a WordPress site.
This is the basics of how it looks:
if ($_POST) {
foreach($_POST as $field => $val) {
if ($val == '') {
$jsonReturn = ['success' => false, 'message' => 'Validation errors whilst processing your form, please go back and check.'];
echo json_encode($jsonReturn);
die();
}
}
if ($noErrors) { // set elsewhere, but works okay
/*
Send an email
*/
if ($mail->send()){
$jsonReturn = ['success' => true, 'message' => "Thank you, message sent."];
echo json_encode($jsonReturn);
}
}
} else {
header("Location: /");
die();
}
If the 'validation' fails at the top of the page, I get a 200 page back containing the JSON return of success false.
However, if I pass validation and send the email thens end the json return it 404's the page.
I tested also by putting:
$jsonReturn = ['success' => true, 'message' => "Thank you, message sent."];
echo json_encode($jsonReturn);
Directly under the first foreach and it also 404's. So im guessing there is something wrong with that?
Any help.
Sorted, the input field has a name of "name".
Changing that worked, speaking to a colleague it appears that WordPress has a certain set of field names reserved for its queries. In this case it was seeing the field name and sending the request off to another page which doesn't exist.
Im sure a person with more knowledge at WP can explain better, but for now if anyone comes across this issue just make sure to check the input names.

_redirect still continues php code execution

I'm working on custom module and in my IndexController.php I'd written this function to add user to database
public function addAction() {
if($this->getRequest()->getParam('name', '') == ''){
$this->_redirect('etech/user');
//die; or exit;
}
$form = $this->getRequest()->getParams();
$user = Mage::getModel('test/test');
foreach ($form as $key => $val){
$user->setData($key, $val);
}
try{
$user->save();
}catch(Exception $e){
print_r($e);
}
$this->_redirect('etech/user', array('msg'=>'success'));
}
I want to prevent users from accessing this url directly as www.example.com/index.php/etech/user/add/. For this I'd made a check if($this->getRequest()->getParam('name', '') == ''){}. The redirect is working well except the code in there keeps executing and user sees a success message which should not be seen. For this, I'd used old fashioned exit or die to stop executing the code then it doesn't even redirect.
What is the magento way to handle it? Also, as I'm using getRequest()->getParams(), it return both parameters either in get or post. Isn't any way out to get only post parametrs?
It is correct to use $this->_redirect(), but you must follow it up with a return, ideally return $this;. You could also use exit or die, as you have been doing, but as I'm sure you know it would be better to let Magento do whatever it wants to do before redirecting you.
As long as you return immediately after $this->_redirect(), you won't have any issues.
Edit: And as for the request params question, I think you can call something like $this->getRequest()->getPostData() (that was false). The general convention is to use getParams() regardless of whether the data was sent via GET or POST, because technically your code shouldn't be concerned about that.
Edit #2:
If the general convention doesn't apply and you desperately need to restrict access to your page based on POST vs. GET, here's a handy snippet from Mohammad:
public function addAction()
{
if ($this->getRequest()->isPost()) {
// echo 'post'; do your stuff
} else {
// echo 'get'; redirect
}
}

Unable to Simulate HTTP 302 response

I'm trying to incorporate PagSeguro (a payment gateway - Brazil's version of PayPal) into my site. After the customer finishes with PagSeguro, they send data (via POST) to a function which I specify. However, I'm not receiving the POST. After doing all the troubleshooting I could think of, I contacted PagSeguro. They said that their log indicates that the POST is being sent as normal but they are receiving an HTTP 302 response.
In order to figure out why this is happening, I created a form with hidden values to simulate sending a POST to my function. I put this form under a different domain just in case it had something to do with that. Every time I send the POST from my simulation form, I receive an HTTP 200 response, and my log indicates that the POST was received.
How is it possible that PagSeguro is receiving a different response than my simulation? Could it have something to do with the server or is it something to do with my script?
Here is the function (using CodeIgniter) that should be receiving the POST:
function pagseguro_retorno(){
if (count($_POST) == 0) {
return FALSE;
}
$msg = 'POST RECEIVED';
$simulate = $this->input->post('Simulate');
if ( ! empty($simulate)){
$result = 'VERIFICADO';
$msg .= ' FROM SIMULATOR';
} else {
$this->load->library(PagSeguroNpi);
$result = $this->PagSeguroNpi->notificationPost();
}
$this->log($msg);
if ($result == "VERIFICADO") {
$id = $this->input->post('Referencia');//cart id
$this->load->model('transacao_model');
$trans_row = $this->transacao_model->get_transaction($id);
if ( ! is_object($trans_row)){
//LOAD NEW TRANSACTION
if ( ! $this->new_transaction($id)){
$notice = "Unable to load new transaction</p><p>";
$this->log($notice);
$notice .= '<pre>'.print_r($_POST, TRUE).'</pre>';
$this->email_notice($notice);
}
}
$this->load->model('carrinho_model');
if($_POST['StatusTransacao'] == 'Aprovado'){
$status = 'apr';
}elseif($_POST['StatusTransacao'] == 'Em AnĂ¡lise'){
$status = 'anl';
}elseif($_POST['StatusTransacao'] == 'Aguardando Pagto'){
$status = 'wtg';
}elseif($_POST['StatusTransacao'] == 'Completo'){
$status = 'cmp';
//nothing more happens here - client must click on 'mark as shipped' before cart is removed and history data is loaded
}elseif($_POST['StatusTransacao'] == 'Cancelado'){
//reshelf - don't set $status, because the cart's about to be destroyed
$this->carrinho_model->reshelf(array($id));
}
if (isset($status)){
$this->carrinho_model->update_carrinho($id, array('status' => $status));
}
} else if ($result == "FALSO") {
$notice = "PagSeguro return was invalid.";
$this->log($notice);
$notice .= '<pre>'.print_r($_POST, TRUE).'</pre>';
$this->email_notice($notice);
} else {
$notice = "Error in PagSeguro request";
$this->log($notice);
$notice .= '<pre>'.print_r($_POST, TRUE).'</pre>';
$this->email_notice($notice);
}
}
SECURITY UPDATE:
After posting, I soon realized that I was opening myself up to hack attempts. The function necessarily has to be public, so anyone who knows the name of the function could access it and post 'simulate' to get immediate verification. Then they could pass whatever data they wanted.
I changed the name of the function to something that would be impossible to guess, and when not in production mode, I've disabled the simulate option.
The problem was that my CSRF protection causes a redirect when the proper CSRF code isn't sent with the POST. My simulator was working, because I copied the HTML generated by a dynamic simulator that I made on the site in question. I then pasted the HTML to create a static simulator and put it under a different URL. Along with the HTML, I inadvertently pasted the CSRF code as well. The static simulator worked fine until the code expired. I quit using it after a couple times, so I didn't discover that it was no longer working until today.
I know that this exact scenario probably won't happen again in the next 500 years, but things like CSRF security can cause problems with things like payment gateways if not disabled for the function receiving the POST.

calling ajax from within a php function having a switch ($this->method)

when creating an XMLrequest in a php file having a code which goes something like this... I am using a MVC ( model-view-controller structure ) and this is a controller php file..
Controller_Institute extends Controller_Default{
function register(){
try {
$this->requireLogin();
switch($this->method){
case 'GET':
$content = $this->render('institute_registration_confirm');
break;
case 'POST':
$result = mysql_query("SELECT * FROM password WHERE pass='".mysql_real_escape_string($_POST['pass'])."'");
$num=mysql_num_rows($result);
if($num==2)
{
$content = $this->render('institute_registration');
}
else
{
$content = $this- >render("message",array('msg'=>'Your password is incorrect'));
}
break;
}
$institute = R::dispense('institute');
$institute- >import($_POST,'name,latitude,state,longitude,address,phone,year,url');
$id = R::store($institute);
}
catch(exception $e){
//If there was an error anywhere, go to the error page.
$content = $this->render('error',array('exception'=>$e));
}
$page = $this->render('default',array('content'=>$content));
return $page;
}
i am sending the ajax request from within the function ... so when the ajax sends back the request , it gets caught in the switch case... and then the response text becomes the function return value replacing the actual text... any idea how to prevent the xml response from getting into the switch case...? the institute_registration is the view file and i am including that file in my framework and then triggering the ajax function from within that file to check whether the password ( to enable registration form ) is correct or not...
Given the limited information and pseudo-code, I recommend setting up a stand-alone page called say... "ajax.php" that is stand alone and doesn't base it's return value on the request method. The pages that use AJAX will need to either POST or GET from this page depending.
If you determine whether or not regular output vs AJAX output is returned via request method, then you are limiting yourself in 2 ways. The first is you will not be able to do 1 or the other on your web pages (GET vs POST) instead of both. Also, the second, when it comes to the AJAX, you will not be able to run GET & POST AJAX requests, and yes, you can do both with AJAX: http://net.tutsplus.com/tutorials/javascript-ajax/5-ways-to-make-ajax-calls-with-jquery/

Categories