From UI I make call:
$http.post('services/loadCategory.php', {
'id' :'1',
'type' :'string'
}).then(function(response) {
debugger;
...
}, function(response) {
...
});
On PHP service I can't get variables from body POST request:
include ("bd.php");
header("Content-type: text/html; charset=windows-1251");
// ----- ----- ----- ----- -----
if (isset($_POST['type'])) {
$type = $_POST['type'];
}
if (isset($_POST['id'])) {
$id = $_POST['id'];
}
//
exit(json_encode(
array('type' => iconv('windows-1251', 'UTF-8', $_POST['type']),
'id' => iconv('windows-1251', 'UTF-8', $_POST['id'])
)));
Request from service: { id:'', type:'' } How fix that?
When posting JSON to PHP, the $_POST variable is empty. To get the raw JSON in your PHP, use the following:
if ($_SERVER['REQUEST_METHOD'] == 'POST')
{
$data = json_decode(file_get_contents('php://input'), true);
}
You can then access the data with $data['id'] and $data['type']
Check the incoming $data with print_r($data);
After doing a quick search about this issue, it appears that PHP has a hard time deserializing the POST body sent by AngularJS. AngularJS sends all information JSON encoded (application/json) as compared to most other JavaScript variants which send the content as application/x-www-form-urlencoded.
To fix this, you should either set the content-type of your request to application/x-www-form-urlencoded or you can try one of the solutions below which came from a similar question.
Based on this question, it would seem that the following code (provided by Felipe Miosso) seems to solve the problem:
// Your app's root module...
angular.module('MyModule', [], function($httpProvider) {
// Use x-www-form-urlencoded Content-Type
$httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
/**
* The workhorse; converts an object to x-www-form-urlencoded serialization.
* #param {Object} obj
* #return {String}
*/
var param = function(obj) {
var query = '', name, value, fullSubName, subName, subValue, innerObj, i;
for(name in obj) {
value = obj[name];
if(value instanceof Array) {
for(i=0; i<value.length; ++i) {
subValue = value[i];
fullSubName = name + '[' + i + ']';
innerObj = {};
innerObj[fullSubName] = subValue;
query += param(innerObj) + '&';
}
}
else if(value instanceof Object) {
for(subName in value) {
subValue = value[subName];
fullSubName = name + '[' + subName + ']';
innerObj = {};
innerObj[fullSubName] = subValue;
query += param(innerObj) + '&';
}
}
else if(value !== undefined && value !== null)
query += encodeURIComponent(name) + '=' + encodeURIComponent(value) + '&';
}
return query.length ? query.substr(0, query.length - 1) : query;
};
// Override $http service's default transformRequest
$httpProvider.defaults.transformRequest = [function(data) {
return angular.isObject(data) && String(data) !== '[object File]' ? param(data) : data;
}];
});
Alternatively, you might be able to fix this problem by adding the following line of code to your PHP:
$params = json_decode(file_get_contents('php://input'),true);
Related
I been working on this for days.
Our backend have a signature checking which is done using PHP:
private $HMAC_ALGO = 'md5';
public function decodeAndValidateMessage($data,$signature,$secretkey) {
if (!is_string($data)) {
throw new InvalidRequestException($data);
}
$decodedData = base64_decode($data);
// if not json returned the throw exception...
$jsonDecoded = json_decode($decodedData,true);
if (!$jsonDecoded) {
throw new InvalidRequestException($decodedData);
}
// validate
$signatureRef = base64_encode(hash_hmac($this->HMAC_ALGO,$decodedData,$secretkey,true));
if ($signature === $signatureRef) {
return $jsonDecoded;
} else {
throw new InvalidSignatureException();
}
}
I made it work on iOS:
func hmac(_ algorithm: HMACAlgorithm, key: String) -> String {
let cKey = key.cString(using: String.Encoding.utf8)
let cData = self.cString(using: String.Encoding.utf8)
var result = [CUnsignedChar](repeating: 0, count: Int(algorithm.digestLength()))
CCHmac(algorithm.toCCHmacAlgorithm(), cKey!, Int(strlen(cKey!)), cData!, Int(strlen(cData!)), &result)
let hmacData:Data = Data(bytes: UnsafePointer<UInt8>(result), count: (Int(algorithm.digestLength())))
let hmacBase64 = hmacData.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
print(String(hmacBase64))
return String(hmacBase64)
}
Any idea/help on doing this on Kotlin/Android? I'm being stuck on InvalidSignatureException error.
fun generateSignature(data : HashMap<String, Any>) : String {
val hmac = Mac.getInstance("HmacMD5")
hmac.init(SecretKeySpec(Constant.PRIVATEKEY.toByteArray(Charsets.UTF_8), hmac.algorithm))
return Base64.encodeToString(data.toString().toByteArray(),Base64.URL_SAFE + Base64.NO_PADDING + Base64.NO_CLOSE + Base64.NO_WRAP)
}
Thanks :D I really appreciate for any help :D
Update:
Just to make my question simpler?
Is it possible to make translate the iOS line of code to Kotlin?
enum HMACAlgorithm {
case md5, sha1, sha224, sha256, sha384, sha512
func toCCHmacAlgorithm() -> CCHmacAlgorithm {
var result: Int = 0
switch self {
case .md5:
result = kCCHmacAlgMD5
case .sha1:
result = kCCHmacAlgSHA1
case .sha224:
result = kCCHmacAlgSHA224
case .sha256:
result = kCCHmacAlgSHA256
case .sha384:
result = kCCHmacAlgSHA384
case .sha512:
result = kCCHmacAlgSHA512
}
return CCHmacAlgorithm(result)
}
func digestLength() -> Int {
var result: CInt = 0
switch self {
case .md5:
result = CC_MD5_DIGEST_LENGTH
case .sha1:
result = CC_SHA1_DIGEST_LENGTH
case .sha224:
result = CC_SHA224_DIGEST_LENGTH
case .sha256:
result = CC_SHA256_DIGEST_LENGTH
case .sha384:
result = CC_SHA384_DIGEST_LENGTH
case .sha512:
result = CC_SHA512_DIGEST_LENGTH
}
return Int(result)
}
}
this is how I call the function
var params : Dictionary
params.generateSignature()
fun generateSignature(data : HashMap) : String {
val hmac = Mac.getInstance("HmacMD5")
hmac.init(SecretKeySpec(Constant.PRIVATEKEY.toByteArray(Charsets.UTF_8), hmac.algorithm))
return Base64.encodeToString(data.toString().toByteArray(),Base64.URL_SAFE + Base64.NO_PADDING + Base64.NO_CLOSE + Base64.NO_WRAP)
}
Someone finally found out the answer.
My mistake is hashmap should be run under JSONObject
var obj = JsonObject(data)
and use obj.toString() :D
In both Postman and jQuery, I'm getting responses in the form
{"key1": "value1", "key2": "value2"}null
That trailing null is messing with anything that tries to parse it client-side and I can't figure out where it's coming from. If I error_log the encoded JSON before echoing it, there's no trailing null, so I assume that it's a string terminator, but I didn't think that PHP used null-terminated strings. How do I get rid of these nulls?
The object being encoded and returned:
public function jsonSerialize()
{
return [
'internal_id' => $this->internal_id, //int
'friendly_name' => $this->friendly_name, //string
'external_id' => $this->external_id, //string
'picture' => $this->picture //string
];
}
The actual return statement is just echo(json_encode($retval));
Once a PHP file has executed, you have to exit manually or return instead of echoing, otherwise it'll return NULL implicitly and mess everything up. Lesson learned.
Not the most elegant probably... but this SAVED me:
function removeTrailingNulls(__str){
var sanitized = __str;
var lastCharIndex = sanitized.length - 1;
var lastChar = sanitized[lastCharIndex];
var lastCharCode = lastChar.charCodeAt(0);
var isWeirdNullSpace = lastCharCode === 0;
console.log('checking last char (' + lastChar + ') code: ' + lastCharCode + '...null space end?' + isWeirdNullSpace);
var loopCount = 0;
while(isWeirdNullSpace){
sanitized = sanitized.substring(0, sanitized.length-1);
lastChar = sanitized[sanitized.length-1];
lastCharCode = lastChar.charCodeAt(0);
isWeirdNullSpace = lastCharCode === 0;
loopCount++;
if(loopCount>100) break; // prevent infinite loops just in case.
}
return String(sanitized);
}
I have created a custom module on vTiger 6.5.
I have made an event handler for the module but I am wondering how I could perform some sort of validation on this field. So fat I have done this but I am unable to get it work, I have tested the handler just echoing a sting and it works fine.
Please see my code below. Thanks!
<?php
/*+***********************************************************************************
* The contents of this file are subject to the vtiger CRM Public License Version 1.0
* ("License"); You may not use this file except in compliance with the License
* The Original Code is: vtiger CRM Open Source
* The Initial Developer of the Original Code is vtiger.
* Portions created by vtiger are Copyright (C) vtiger.
* All Rights Reserved.
*************************************************************************************/
# getModuleName : Returns the module name of the entity.
# getId : Returns id of the entity, this will return null if the id has not been saved yet.
# getData : Returns the fields of the entity as an array where the field name is the key and the fields value is the value.
# isNew : Returns true if new record is being created, false otherwise.
# 'vtiger.entity.beforesave.modifiable' : Setting values : $data->set('simple_field', 'value');
class isaHandler extends VTEventHandler {
function handleEvent($eventName, $entityData) {
global $adb;
$moduleName = $entityData->getModuleName();
if($moduleName=='isa'){
if($eventName == 'vtiger.entity.beforesave.modifiable') {}
if($eventName == 'vtiger.entity.beforesave') {
if('currentamount' > 20000){
echo "Please go back and enter less than 20000";
exit;
}
}
if($eventName == 'vtiger.entity.beforesave.final') {}
if($eventName == 'vtiger.entity.aftersave') {
}
}
}
}
?>
After doing some searching around and looking at other peoples event handlers I managed to solve this by changing:
if($eventName == 'vtiger.entity.beforesave') {
if('currentamount' > 20000){
echo "Please go back and enter less than 20000";
exit;
}
to
if($eventName == 'vtiger.entity.beforesave') {
$price = $entityData->get('currentamount');
if($price > 20000){
echo "Please go back and enter less than 20000";
exit;
}
Now I want to see if I can display the message and then give a link to go back to the entity module with all the fields still full.
In my opinion you should use the recordPreSave function.
It's allow you to display an information/error message before to save data on the database
Here is an example:
In your Edit.js:
donCache : {},
checkDon : function(details) {
// alert("checkOverlap");
var aDeferred = jQuery.Deferred();
var params = {
'module' : 'Affectations',
'action' : "checkAffectAmount",
'mode': 'CtrlAffectAmount',
'recordId' : details.don,
'montant' : details.montant
}
AppConnector.request(params).then(
function(data) {
if (data.success == true) {
// console.log(data.result);
var statut = data.result.statut;
var reste = data.result.reste;
if(statut == "error"){
aDeferred.reject(data);
}else {
aDeferred.resolve(data);
}
}
},
function(error,err){
aDeferred.reject();
}
);
return aDeferred.promise();
},
registerRecordPreSaveEvent : function(form) {
var thisInstance = this;
if (typeof form == 'undefined') {
form = this.getForm();
}
form.on(Vtiger_Edit_Js.recordPreSave, function(e, data) {
var check = false;
var recordId = jQuery('input[name="record"]').val();
if (!recordId || recordId) {
var montant_affectation = jQuery("input[name='affectations_montant']").val();
var don_id = jQuery("input[name='affectations_potentialid']").val();
var params = {};
if (!(check in thisInstance.donCache)) {
thisInstance.checkDon({
'montant' : montant_affectation,
'don': don_id
}).then(
function(data){
thisInstance.donCache[check] = data['success'];
form.submit();
},
function(data, err){
thisInstance.donCache[check] = data['success'];
var reste = data.result.reste;
var msg = app.vtranslate("<strong>Attention!</strong> La somme des affectations est supérieure de ")+ reste +app.vtranslate(" Euros au montant du don");
Vtiger_Helper_Js.showPnotify(msg);
delete thisInstance.donCache[check];
}
);
} else {
delete thisInstance.donCache[check];
return true;
}
e.preventDefault();
}
})
},
The PHP part in modules/isa/actions:
<?php
/***********************************
** DEBUT ALTAIR - JPR 15/06/2016 ***
***********************************/
class Affectations_checkAffectAmount_Action extends Vtiger_Action_Controller {
function __construct() {
$this->exposeMethod('CtrlAffectAmount');
}
public function checkPermission(Vtiger_Request $request) {
$moduleName = $request->getModule();
$moduleModel = Vtiger_Module_Model::getInstance($moduleName);
$userPrivilegesModel = Users_Privileges_Model::getCurrentUserPrivilegesModel();
$permission = $userPrivilegesModel->hasModulePermission($moduleModel->getId());
if(!$permission) {
throw new AppException('LBL_PERMISSION_DENIED');
}
}
public function process(Vtiger_Request $request) {
$mode = $request->getMode();
if(!empty($mode) && $this->isMethodExposed($mode)) {
$this->invokeExposedMethod($mode, $request);
return;
}
}
function CtrlAffectAmount(Vtiger_Request $request){
global $adb,$log;
$log->debug("ALTAIR CtrlAffectAmount OK");
$recordId = $request->get('recordId');
$montant = $request->get('montant');
// $query = $adb->pquery("SELECT SUM(unit_price) AS sommeaffectation FROM vtiger_products INNER JOIN vtiger_crmentity ON vtiger_products.productid = vtiger_crmentity.crmid WHERE don_affecte = ? AND vtiger_crmentity.deleted=0",array($recordId));
$query = $adb->pquery("SELECT SUM(affectations_montant) AS sommeaffectation FROM vtiger_affectations INNER JOIN vtiger_crmentity ON vtiger_affectations.affectationsid = vtiger_crmentity.crmid WHERE vtiger_affectations.affectations_potentialid = ? AND vtiger_crmentity.deleted=0",array($recordId));
$sommeAffectation = $adb->query_result($query,0,"sommeaffectation");
$query = $adb->pquery("SELECT amount FROM vtiger_potential INNER JOIN vtiger_crmentity ON vtiger_potential.potentialid = vtiger_crmentity.crmid WHERE potentialid = ? AND vtiger_crmentity.deleted = 0", array($recordId));
$montantDon = $adb->query_result($query,0,"amount");
if ( ($sommeAffectation + $montant) == $montantDon) {
$statut = "ok";
$reste = "";
} else if( ($sommeAffectation + $montant) > $montantDon) {
$statut = "error";
$reste = ($sommeAffectation + $montant) - $montantDon;
}
$value = array('statut'=>$statut, 'reste'=>$reste);
$response = new Vtiger_Response();
$response->setEmitType(Vtiger_Response::$EMIT_JSON);
$response->setResult($value);
$response->emit();
}
}
Oh. you have 1 invalid error here. please change:
if($eventName == 'vtiger.entity.beforesave') {
$currentAmount = $entityData->get('currentamount');
if($currentAmount > 20000){
echo "Please go back and enter less than 20000";
exit;
}
}
I've seend much assistance for everything BUT transforming data when using the findBy query.
What I want is a json string of the resulset from this query ensuring that the objects are serialized so i can use this somewhere else:
$posts = $entityManager->getRepository(\Application\Entity\Post::class)
->findBy(['status'=>\Application\Entity\Post::STATUS_PUBLISHED],
['dateCreated'=>'DESC']);
Json::encode($posts,true) from Zend Framework Json but the data is not showing up when i do this.
The result will be a json encoded string with the entity objects that i can pass somewhere else
I will use for the decoding:
\Zend\Json\Decoder::decode($posts,\Zend\Json\Json::TYPE_OBJECT)
UNLESS I should be using \Zend\Json\Json::TYPE_ARRAY)
Here is the way I do it :
include : use Zend\Json\Json;
here is my example of function / action :
public function getListAction(){
$request = $this->getRequest();
if($request->isPost()){
// recuperer le produit choisi :
$element = $request->getPost("element");
$result = null;
$result = $this->getEntityManager()->getRepository('Application\Entity\Element')->findBy(array('etat' => 'valide' , 'pkElement' => $element));
$champs = array();
$i = 0;
foreach ($result as $value) {
$champs[$i] = array("id"=>$value->getPkElement() , "nom"=>$value->getNom());
$i++;
}
$data = array(
'result' => true,
'data' => $champs
);
return $this->getResponse()->setContent(Json::encode($data));
}
}
Then the call in the view.phtml :
$.post('/application/controller_name/getList', {element: $("select[name=element]").val()}, function(result) {
var options = $("select[name=element]");
var obj = JSON.parse(result);
var data = obj.data;
var selected = "";
options.empty();
for (var i = 0; i < data.length; i++) {
options.append($("<option />").val(data[i]['id']).text(data[i]['nom']));
}
});
Hope it helps.
So I have a custom Drupal module that outputs a formatted version of the song node's data based on a node id in the path (load/song/NID). This function works fine, and when I look at the url in my browser, I see that everything has loaded perfectly.
My module's code:
function load_song($nid){
$node = node_load($nid);
$songname = $node->title;
$albumid = $node->field_album['und'][0]['nid'];
$album = node_load($albumid);
$file = $album->field_cover['und'][0];
//Loads the album filepath from the file array returned above
$filepath = $file['uri'];
//The path returned is something like "public://file.jpg"
$filepath = str_replace("public://", "http://mysite.com/sites/default/files/styles/thumbnail/public/", $filepath);
//I then set a variable (imgurl) to the formatted filepath
$imgurl = $filepath;
$artistid = $album->field_artist['und'][0]['nid'];
$artist = node_load($artistid);
$artistname = $artist->title;
echo 'I output the variables + formatting here';
}
With the output, I then load it in my page.tpl.php file in Drupal using the jQuery .load function. My code:
function loadSongInfo(id) {
$("#current-song").html('Loading').load('http://mysite.com/load/song/' + id);
}
So with this input, what I get is the data from the url (which is perfectly formatted), and the other variables I load (artist name, song name, etc) working fine. However, where I have the filepath, all I get is an empty string.
This of course confuses the hell out of me, because it works fine in my browser, but when I call the function in jQuery, it loads all the other variables fine, except for the filepath.
I've tried (and got unsuccessful results):
Casting the variable $filepath to a string using three different methods
(I thought it might be something weird with the url) I replaced the filepath variable with just the static string of it ("http://mysite.com/sites/default/files/styles/thumbnail/public/file.jpg"), which returned the correct result, but still fails with the actual variable
var_dump
print_r
Does anyone have any idea why this isn't working?
Mymodule
function mymodule_menu(){
$items = array();
$items['js/song'] = array(
'page callback' => 'load_song',
'type' => MENU_CALLBACK,
'access arguments' => array('access content')
);
}
function load_song() {
$nid = $_POST['nid'];
$node = node_load($nid);
/*
* Check for node existing
* Return response.error if not loaded
*/
if(!$node->nid){
drupal_json(array('error' => t('Some text for error')));
die();
}
$songtitle = $node->title;
$albumid = $node->field_album['und'][0]['nid'];
$album = node_load($albumid);
$file = $album->field_cover['und'][0];
// Loads the album filepath from the file array returned above
$filepath = $file['uri'];
// The path returned is something like "public://file.jpg"
$filepath = str_replace("public://", "http://mysite.com/sites/default/files/styles/thumbnail/public/", $filepath);
// I then set a variable (imgurl) to the formatted filepath
$imagepath = $filepath;
$artistid = $album->field_artist['und'][0]['nid'];
$artist = node_load($artistid);
$artistname = $artist->title;
$object = array(
'song_title' => l($songtitle, 'node/'. $node->nid),
'image_path' => $imagepath,
'artist_name' => l($artistname, $artist->nid)
);
drupal_json(array('data' => $object));
die();
}
Javascript:
Drupal.behaviors.SongInit = function(context){
$("#current-song").html('Loading...').load("/load/song", {
nid : id
}, function(response, status, xhr) {
if (status == 'error') {
var msg = Drupal.t("Sorry but there was an error: ");
$("#current-song").html(msg + xhr.status + " " + xhr.statusText);
}
if (response.error) {
var msg = Drupal.t("Sorry but there was an error: ");
$("#current-song").html(msg);
}
else {
var msg = response.data.song_title + '<br />';
var msg = '<img src=/"' + response.data.image_path + ' /><br />';
var msg = response.data.artist_name + '<br />';
$("#current-song").html(msg);
Drupal.attachBehaviors(context);
}
});
}