I am trying to display some values using MQTT on a PHP based page. The PHP code contains the subscriber. I am using Bluemix IoT service for the MQTT broker. Also, the messages are published via Python code on local machine.
When I try to display values on pages using page refresh, the page sometimes fails to display the values. There is no problem at the publisher end as the values are successfully displayed by Bluemix IoT service.
My code is as follows:
<?php
// include class
require('phpMQTT.php');
// set configuration values
if( getenv("VCAP_SERVICES") ) {
// get MySQL service configuration from Bluemix
$services = getenv("VCAP_SERVICES");
$services_json = json_decode($services, true);
$mysql_config = $services_json["iotf-service"][0]["credentials"];
$org_id = $mysql_config["org"];
$port = $mysql_config["mqtt_u_port"];
$username = $mysql_config["apiKey"];
$password = $mysql_config["apiToken"];
}
// set configuration values
$config = array(
'org_id' => $org_id,
'port' => $port,
'app_id' => 'm',
'iotf_api_key' => $username,
'iotf_api_secret' => $password,
'device_id' => 'trial',
'qos' => 1
);
global $Val_A;
global $Val_B;
global $Val_C;
global $Val_D;
//Read already existing file
$ini_b = parse_ini_file("data.ini",true);
$Val_A = $ini_b['Data']['A'];
$Val_B = $ini_b['Data']['B'];
$Val_C = $ini_b['Data']['C'];
$Val_D = $ini_b['Data']['D'];
$config['server'] = $config['org_id'] . '.messaging.internetofthings.ibmcloud.com';
$config['client_id'] = 'a:' . $config['org_id'] . ':' . $config['app_id'];
#echo $config['client_id'];
// initialize client
$mqtt_dev = new phpMQTT($config['server'], $config['port'], $config['client_id']);
$mqtt_dev->debug = false;
// connect to broker
if(!$mqtt_dev->connect(true, null, $config['iotf_api_key'], $config['iotf_api_secret'])){
echo 'ERROR: Could not connect to IoT cloud';
exit();
}
else
{
#echo "Success";
}
$topics['iot-2/type/newdevice/id/' . $config['device_id'] . '/evt/status/fmt/json'] =
array('qos' =>1, 'function' => 'getLocation');
$mqtt_dev->subscribe($topics, 1);
$elapsedSeconds = 0;
while ($mqtt_dev->proc(true)) {
#echo json_encode($json);
if (count($location) == 2) {
break;
}
if ($elapsedSeconds == 5) {
break;
}
$elapsedSeconds++;
sleep(1);
}
// disconnect
//I have tried commenting this too
$mqtt_dev->close();
function getLocation($topic, $msg) {
global $location;
global $json;
$json = json_decode($msg);
$Val_A = $json->A;
$Val_B = $json->B;
$Val_C = $json->C;
$Val_D = $json->D;
//Read already existing file
$ini_backup = parse_ini_file("data.ini",true);
$ValA_b = $ini_backup['Data']['A'];
$ValB_b = $ini_backup['Data']['B'];
$ValC_b = $ini_backup['Data']['C'];
$ValD_b = $ini_backup['Data']['D'];
if($Val_A != 0)
{
$ValA_b = $Val_A;
}
else
{
$Val_A = $ValA_b;
}
if($Val_B != 0)
{
$ValB_b = $Val_B;
}
else
{
$Val_B = $ValB_b;
}
if($Val_C != 0)
{
$ValC_b = $Val_C;
}
else
{
$Val_C = $ValC_b;
}
if($Val_D != 0)
{
$ValD_b = $Val_D;
}
else
{
$Val_D = $ValD_b;
}
$file = fopen("data.ini","w");
fwrite($file,"[Data]". "\n" );
fwrite($file,"A =" . $ValA_b . "\n" );
fwrite($file,"B =" . $ValB_b . "\n" );
fwrite($file,"C =" . $ValC_b . "\n" );
fwrite($file,"D =" . $ValD_b . "\n" );
fclose($file);
return $location;
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="refresh" content="5" >
<div id="footer">
This page will automatically reload every 5 seconds. <br/>
</div>
<label for="A">A</label>
<input type="text" value="<?php echo $Val_A ?>" />
<label for="B">B</label>
<input type="text" value="<?php echo $Val_B ?>" />
<label for="C">C</label>
<input type="text" value="<?php echo $Val_C ?>" />
<label for="D">D</label>
<input type="text" value="<?php echo $Val_D ?>" />
</body>
</html>
Can some one guide where am I going wrong?
Rather than using meta to specify the refresh, try using php header("Refresh:5");
There is an old stack overflow thread discussing using meta versus header for refrseh in php.
I have requirement in which user need to edit/update the s3 file metadata that are uploaded in the previous sessions. I have implemented Initial File List, but I need to make file metadata (filename, caption - new field in my case) editable in the display list. Can it be accomplished?
I see edit files feature, but that is limited to before file gets uploaded. Looks like my requirement not easily supported out of the box FU. I have followed below approach.
In template I have a button with text 'Update Caption', which has onclick="captionUpdate()",that will set JS variable(isCaptionUpdate) to true.
Caption update will trigger DeleteFile endpoint except that it will set param data for caption value from text field that is defines in template
In server side code, the process checks for Caption param, and then call function updateObjectWithCaption()
All of the above works seamlessly with following challenges.Please see the screenshot.
When user click on 'Update Caption', it follows DELETE steps and since I am passing Caption param, it updates S3 file. But problem is in the file list, I will see a status text called 'Deleting.....' appears for brief time. How can I change status to 'Updating Caption....' or something similar
Another issue with #1 is that as soon as S3 update, the File in file list gets removed. UI part still thinks that it is DELETE step for some reason, how can I say to UI that it not really delete?
As you can see in the deleteFile section of JS, caption is taken from document.getElementById('caption').value; that means, even if I click 'Update Caption' of 2nd or 3rd or 4th files, it is taking first occurrence of Caption element. How can I get the caption of the specific file ?
Last but not least, how can I show 'Update Caption' button only for previously uploaded file. I do not want show this button on fresh upload.
Sorry for too many question. I could not separate these question as they are all related to S3 file metadata update topic.
Template:
<div class="qq-uploader-selector qq-uploader" qq-drop-area-text="Drop files here">
<div class="qq-total-progress-bar-container-selector qq-total-progress-bar-container">
<div role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" class="qq-total-progress-bar-selector qq-progress-bar qq-total-progress-bar"></div>
</div>
<div class="qq-upload-drop-area-selector qq-upload-drop-area" qq-hide-dropzone>
<span class="qq-upload-drop-area-text-selector"></span>
</div>
<div class="buttons">
<div class="qq-upload-button-selector qq-upload-button">
<div>Select files</div>
</div>
<button type="button" id="trigger-upload-section1" class="btn btn-primary">
<i class="icon-upload icon-white"></i> Upload
</button>
</div>
<span class="qq-drop-processing-selector qq-drop-processing">
<span>Processing dropped files...</span>
<span class="qq-drop-processing-spinner-selector qq-drop-processing-spinner"></span>
</span>
<ul class="qq-upload-list-selector qq-upload-list" aria-live="polite" aria-relevant="additions removals">
<li>
<div class="qq-progress-bar-container-selector">
<div role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" class="qq-progress-bar-selector qq-progress-bar"></div>
</div>
<span class="qq-upload-spinner-selector qq-upload-spinner"></span>
<img class="qq-thumbnail-selector" qq-max-size="100" qq-server-scale>
<span class="qq-upload-file-selector qq-upload-file"></span>
<span class="qq-edit-filename-icon-selector qq-edit-filename-icon qq-editable" aria-label="Edit filename"></span>
<input class="qq-edit-filename-selector qq-edit-filename" tabindex="0" type="text">
<span class="qq-upload-caption-selector qq-upload-caption"></span>
<span class="qq-edit-caption-icon-selector qq-edit-caption-icon qq-editable" aria-label="Edit caption"></span>
<input class="qq-edit-caption-selector qq-edit-caption qq-editing" placeholder="Caption here ..." tabindex="0" type="text" id="caption">
<span class="qq-upload-size-selector qq-upload-size"></span>
<button type="button" class="qq-btn qq-upload-cancel-selector qq-upload-cancel">Cancel</button>
<button type="button" class="qq-btn qq-upload-retry-selector qq-upload-retry">Retry</button>
<button type="button" class="qq-btn qq-upload-delete-selector qq-upload-delete">Delete</button>
<button type="button" class="qq-btn qq-upload-delete-selector qq-upload-delete" onclick="captionUpdate();">Update Caption</button>
<span role="status" class="qq-upload-status-text-selector qq-upload-status-text"></span>
</li>
</ul>
<dialog class="qq-alert-dialog-selector">
<div class="qq-dialog-message-selector"></div>
<div class="qq-dialog-buttons">
<button type="button" class="qq-cancel-button-selector">Close</button>
</div>
</dialog>
<dialog class="qq-confirm-dialog-selector">
<div class="qq-dialog-message-selector"></div>
<div class="qq-dialog-buttons">
<button type="button" class="qq-cancel-button-selector">No</button>
<button type="button" class="qq-ok-button-selector">Yes</button>
</div>
</dialog>
<dialog class="qq-prompt-dialog-selector">
<div class="qq-dialog-message-selector"></div>
<input type="text">
<div class="qq-dialog-buttons">
<button type="button" class="qq-cancel-button-selector">Cancel</button>
<button type="button" class="qq-ok-button-selector">Ok</button>
</div>
</dialog>
</div>
JS
var isCaptionUpdate = false;
function captionUpdate(){
isCaptionUpdate = true;
};
var manualUploaderSection1 = new qq.s3.FineUploader({
element: document.getElementById('fine-uploader-manual-trigger-section1'),
template: 'qq-template-manual-trigger-section1',
autoUpload: false,
debug: true,
request: {
endpoint: "http://xx_my_bucket_xx.s3.amazonaws.com",
accessKey: "AKIAIAABIA",
},
signature: {
endpoint: "http://localhost/app/ci/php-s3-server/endpoint-cors.php"
},
uploadSuccess: {
endpoint: "http://localhost/app/ci/php-s3-server/endpoint-cors.php?success",
params: {
isBrowserPreviewCapable: qq.supportedFeatures.imagePreviews
}
},
session: {
endpoint: "http://localhost/app/ci/php-s3-server/endpoint-cors.php?filelist"
},
iframeSupport: {
localBlankPagePath: "success.html"
},
cors: {
expected: true
},
chunking: {
enabled: true
},
resume: {
enabled: true
},
deleteFile: {
enabled: true,
method: "POST",
endpoint: "http://localhost/app/ci/php-s3-server/endpoint-cors.php",
params: {
caption: function() {
if (isCaptionUpdate === true) {
isCaptionUpdate = false;
return document.getElementById('caption').value;
}
}
}
},
validation: {
itemLimit: 5,
sizeLimit: 15000000
},
thumbnails: {
placeholders: {
notAvailablePath: "http://localhost/app/ci/s3.fine-uploader/placeholders/not_available-generic.png",
waitingPath: "http://localhost/app/ci/s3.fine-uploader/placeholders/waiting-generic.png"
}
},
callbacks: {
onComplete: function(id, name, response) {
var previewLink = qq(this.getItemByFileId(id)).getByClass('preview-link')[0];
if (response.success) {
previewLink.setAttribute("href", response.tempLink)
}
},
onUpload: function(id, fileName) {
var caption = document.getElementById('caption').value;
this.setParams({'caption':caption});
}
}
});
qq(document.getElementById("trigger-upload-section1")).attach("click", function() {
manualUploaderSection1.uploadStoredFiles();
});
Server side code:
require '/vendor/autoload.php';
use Aws\S3\S3Client;
$clientPrivateKey = 'LB7r54Rgh9sCuTAC8V5F';
$serverPublicKey = 'AKIAU2ZEQ';
$serverPrivateKey = '8Xu6lxcDfKifHfn4pdELnM1E';
$expectedBucketName = 'xx_my_bucket_xx';
$expectedHostName = 'http://s3.amazonaws.com'; // v4-only
$expectedMaxSize = 15000000;
$method = getRequestMethod();
// This first conditional will only ever evaluate to true in a
// CORS environment
if ($method == 'OPTIONS') {
handlePreflight();
}
// This second conditional will only ever evaluate to true if
// the delete file feature is enabled
else if ($method == "DELETE") {
handleCorsRequest();
if (isset($_REQUEST['caption'])) {
updateObjectWithCaption();
} else {
deleteObject();
}
}
// This is all you really need if not using the delete file feature
// and not working in a CORS environment
else if ($method == 'POST') {
handleCorsRequest();
// Assumes the successEndpoint has a parameter of "success" associated with it,
// to allow the server to differentiate between a successEndpoint request
// and other POST requests (all requests are sent to the same endpoint in this example).
// This condition is not needed if you don't require a callback on upload success.
if (isset($_REQUEST["success"])) {
verifyFileInS3(shouldIncludeThumbnail());
}
else {
signRequest();
}
}
//filelist - this is to list already uploaded files
else if ($method == 'GET') {
if (isset($_REQUEST["filelist"])) {
getFileList('test/');
}
}
function getFileList($filePrefix) {
global $expectedBucketName;
$objects = getS3Client()->getIterator('ListObjects', array(
//$objects = getS3Client()->ListObjects(array(
'Bucket' => $expectedBucketName,
'Prefix' => $filePrefix //must have the trailing forward slash "/"
));
$object_list = array();
foreach ($objects as $object) {
//echo $object['Key'] . "<br>";
$object_metadata = getHeadObject($expectedBucketName, $object['Key']);
if (isset($object_metadata['Metadata']['qqfilename'])) {
$keyArr = explode("/", $object['Key']);
$posOfLastString = sizeof($keyArr) - 1;
$uuidArry = explode(".", $keyArr[$posOfLastString]);
$link = getTempLink($expectedBucketName, $object['Key']);
$object_new = array();
$object_new['name'] = $object_metadata['Metadata']['qqfilename'];
$object_new['uuid'] = $uuidArry[0];
$object_new['s3Key'] = $object['Key'];
$object_new['size'] = $object['Size'];
$object_new['s3Bucket'] = $expectedBucketName;
$object_new['thumbnailUrl'] = $link;
array_push($object_list, (object)$object_new);
}
}
echo json_encode($object_list);
}
// This will retrieve the "intended" request method. Normally, this is the
// actual method of the request. Sometimes, though, the intended request method
// must be hidden in the parameters of the request. For example, when attempting to
// send a DELETE request in a cross-origin environment in IE9 or older, it is not
// possible to send a DELETE request. So, we send a POST with the intended method,
// DELETE, in a "_method" parameter.
function getRequestMethod() {
global $HTTP_RAW_POST_DATA;
// This should only evaluate to true if the Content-Type is undefined
// or unrecognized, such as when XDomainRequest has been used to
// send the request.
if(isset($HTTP_RAW_POST_DATA)) {
parse_str($HTTP_RAW_POST_DATA, $_POST);
}
if (isset($_REQUEST['_method'])) {
return $_REQUEST['_method'];
}
return $_SERVER['REQUEST_METHOD'];
}
// Only needed in cross-origin setups
function handleCorsRequest()
// If you are relying on CORS, you will need to adjust the allowed domain here.
header('Access-Control-Allow-Origin: http://localhost');
}
// Only needed in cross-origin setups
function handlePreflight() {
handleCorsRequest();
header('Access-Control-Allow-Methods: POST');
header('Access-Control-Allow-Headers: Content-Type');
}
function getS3Client() {
global $serverPublicKey, $serverPrivateKey;
return S3Client::factory(array(
'key' => $serverPublicKey,
'secret' => $serverPrivateKey
));
}
// Only needed if the delete file feature is enabled
function deleteObject() {
getS3Client()->deleteObject(array(
'Bucket' => $_REQUEST['bucket'],
'Key' => $_REQUEST['key']
));
}
function getHeadObject($bucket, $key) {
$object_metadata = getS3Client()->headObject(array('Bucket' => $bucket,'Key' => $key));
$object_metadata = $object_metadata->toArray();
return $object_metadata;
}
function updateObjectWithCaption() {
$bucket = $_REQUEST['bucket'];
$key = $_REQUEST['key'];
$caption = $_REQUEST['caption'];
$object_metadata = getHeadObject($bucket, $key);
$filename = $object_metadata['Metadata']['qqfilename'];
$fileType = getFileType($key);
getS3Client()->copyObject(array(
'Bucket' => $bucket,
'Key' => $key,
'CopySource' => urlencode($_REQUEST['bucket'] . '/' . $key),
'MetadataDirective' => 'REPLACE',
//'CacheControl' => 'max-age=31536000',
//'Expires' => gmdate('D, d M Y H:i:s T', strtotime('+1 years')), // Set EXPIRES and CACHE-CONTROL headers to +1 year (RFC guidelines max.)
'ContentType' => $fileType,
'Metadata'=>array(
'qqcaption' => $caption,
'qqfilename' => $filename,
),
));
}
function getFileType($key) {
$file_parts = pathinfo($key);
$filetype = "";
switch($file_parts['extension'])
{
case "jpg":
$filetype = "image/jpeg";
break;
case "jpeg":
$filetype = "image/jpeg";
break;
case "png":
$filetype = "image/png";
break;
case "gif":
$filetype = "image/gif";
break;
case "tif":
$filetype = "image/tiff";
break;
case "tiff":
$filetype = "image/tiff";
break;
case "bmp":
$filetype = "image/bmp";
break;
}
return $filetype;
}
function signRequest() {
header('Content-Type: application/json');
$responseBody = file_get_contents('php://input');
$contentAsObject = json_decode($responseBody, true);
$jsonContent = json_encode($contentAsObject);
if (!empty($contentAsObject["headers"])) {
signRestRequest($contentAsObject["headers"]);
}
else {
signPolicy($jsonContent);
}
}
function signRestRequest($headersStr) {
$version = isset($_REQUEST["v4"]) ? 4 : 2;
if (isValidRestRequest($headersStr, $version)) {
if ($version == 4) {
$response = array('signature' => signV4RestRequest($headersStr));
}
else {
$response = array('signature' => sign($headersStr));
}
echo json_encode($response);
}
else {
echo json_encode(array("invalid" => true));
}
}
function isValidRestRequest($headersStr, $version) {
if ($version == 2) {
global $expectedBucketName;
$pattern = "/\/$expectedBucketName\/.+$/";
}
else {
global $expectedHostName;
$pattern = "/host:$expectedHostName/";
}
preg_match($pattern, $headersStr, $matches);
return count($matches) > 0;
}
function signPolicy($policyStr) {
$policyObj = json_decode($policyStr, true);
if (isPolicyValid($policyObj)) {
$encodedPolicy = base64_encode($policyStr);
if (isset($_REQUEST["v4"])) {
$response = array('policy' => $encodedPolicy, 'signature' => signV4Policy($encodedPolicy, $policyObj));
}
else {
$response = array('policy' => $encodedPolicy, 'signature' => sign($encodedPolicy));
}
echo json_encode($response);
}
else {
echo json_encode(array("invalid" => true));
}
}
function isPolicyValid($policy) {
global $expectedMaxSize, $expectedBucketName;
$conditions = $policy["conditions"];
$bucket = null;
$parsedMaxSize = null;
for ($i = 0; $i < count($conditions); ++$i) {
$condition = $conditions[$i];
if (isset($condition["bucket"])) {
$bucket = $condition["bucket"];
}
else if (isset($condition[0]) && $condition[0] == "content-length-range") {
$parsedMaxSize = $condition[2];
}
}
return $bucket == $expectedBucketName && $parsedMaxSize == (string)$expectedMaxSize;
}
function sign($stringToSign) {
global $clientPrivateKey;
return base64_encode(hash_hmac(
'sha1',
$stringToSign,
$clientPrivateKey,
true
));
}
function signV4Policy($stringToSign, $policyObj) {
global $clientPrivateKey;
foreach ($policyObj["conditions"] as $condition) {
if (isset($condition["x-amz-credential"])) {
$credentialCondition = $condition["x-amz-credential"];
}
}
$pattern = "/.+\/(.+)\\/(.+)\/s3\/aws4_request/";
preg_match($pattern, $credentialCondition, $matches);
$dateKey = hash_hmac('sha256', $matches[1], 'AWS4' . $clientPrivateKey, true);
$dateRegionKey = hash_hmac('sha256', $matches[2], $dateKey, true);
$dateRegionServiceKey = hash_hmac('sha256', 's3', $dateRegionKey, true);
$signingKey = hash_hmac('sha256', 'aws4_request', $dateRegionServiceKey, true);
return hash_hmac('sha256', $stringToSign, $signingKey);
}
function signV4RestRequest($rawStringToSign) {
global $clientPrivateKey;
$pattern = "/.+\\n.+\\n(\\d+)\/(.+)\/s3\/aws4_request\\n(.+)/s";
preg_match($pattern, $rawStringToSign, $matches);
$hashedCanonicalRequest = hash('sha256', $matches[3]);
$stringToSign = preg_replace("/^(.+)\/s3\/aws4_request\\n.+$/s", '$1/s3/aws4_request'."\n".$hashedCanonicalRequest, $rawStringToSign);
$dateKey = hash_hmac('sha256', $matches[1], 'AWS4' . $clientPrivateKey, true);
$dateRegionKey = hash_hmac('sha256', $matches[2], $dateKey, true);
$dateRegionServiceKey = hash_hmac('sha256', 's3', $dateRegionKey, true);
$signingKey = hash_hmac('sha256', 'aws4_request', $dateRegionServiceKey, true);
return hash_hmac('sha256', $stringToSign, $signingKey);
}
// This is not needed if you don't require a callback on upload success.
function verifyFileInS3($includeThumbnail) {
global $expectedMaxSize;
$bucket = $_REQUEST["bucket"];
$key = $_REQUEST["key"];
// If utilizing CORS, we return a 200 response with the error message in the body
// to ensure Fine Uploader can parse the error message in IE9 and IE8,
// since XDomainRequest is used on those browsers for CORS requests. XDomainRequest
// does not allow access to the response body for non-success responses.
if (isset($expectedMaxSize) && getObjectSize($bucket, $key) > $expectedMaxSize) {
// You can safely uncomment this next line if you are not depending on CORS
header("HTTP/1.0 500 Internal Server Error");
deleteObject();
echo json_encode(array("error" => "File is too big!", "preventRetry" => true));
}
else {
$link = getTempLink($bucket, $key);
$response = array("tempLink" => $link);
if ($includeThumbnail) {
$response["thumbnailUrl"] = $link;
}
echo json_encode($response);
}
}
// Provide a time-bombed public link to the file.
function getTempLink($bucket, $key) {
$client = getS3Client();
$url = "{$bucket}/{$key}";
$request = $client->get($url);
return $client->createPresignedUrl($request, '+15 minutes');
}
function getObjectSize($bucket, $key) {
$objInfo = getS3Client()->headObject(array(
'Bucket' => $bucket,
'Key' => $key
));
return $objInfo['ContentLength'];
}
// Return true if it's likely that the associate file is natively
// viewable in a browser. For simplicity, just uses the file extension
// to make this determination, along with an array of extensions that one
// would expect all supported browsers are able to render natively.
function isFileViewableImage($filename) {
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
$viewableExtensions = array("jpeg", "jpg", "gif", "png", "tif", "tiff");
return in_array($ext, $viewableExtensions);
}
// Returns true if we should attempt to include a link
// to a thumbnail in the uploadSuccess response. In it's simplest form
// (which is our goal here - keep it simple) we only include a link to
// a viewable image and only if the browser is not capable of generating a client-side preview.
function shouldIncludeThumbnail() {
$filename = $_REQUEST["name"];
$isPreviewCapable = $_REQUEST["isBrowserPreviewCapable"] == "true";
$isFileViewableImage = isFileViewableImage($filename);
return !$isPreviewCapable && $isFileViewableImage;
}
When user click on 'Update Caption', it follows DELETE steps and since I am passing Caption param, it updates S3 file. But problem is in the file list, I will see a status text called 'Deleting.....' appears for brief time. How can I change status to 'Updating Caption....' or something similar
There are various text options you can set for the delete file feature, such as deleteFile.deletingStatusText.
As you can see in the deleteFile section of JS, caption is taken from document.getElementById('caption').value; that means, even if I click 'Update Caption' of 2nd or 3rd or 4th files, it is taking first occurrence of Caption element. How can I get the caption of the specific file ?
Your markup/template is flawed in that you will end up with multiple elements with an ID of "caption". This is not allowed in HTML. You'll need to restructure your markup accordingly. An ID is not appropriate here. Instead, use a class. You can always get the container element for a file using Fine Uploader's getItemByFileId API method. With this, you can query the descendant elements to look for one with a specific attribute.
Last but not least, how can I show 'Update Caption' button only for previously uploaded file. I do not want show this button on fresh upload.
Files submitted by the user (non-canned/initial files) will result in a call to your onSubmitted callback handler after they are represented in the DOM. At this point, you can use the previously mentioned getItemByFileId to retrieve the container element and hide the button.
I'm building a module that will help seller put their products catalogue in the Amazon Product Ads service. For that, I need to make a mapping between the client's category and the Amazon Product Ads categories.
Problem is, I can't find an API that will help me search for Categories, or a file containing all the existing categories for Amazon Product Ads.
I found that link http://docs.aws.amazon.com/AWSECommerceService/latest/DG/BrowseNodeIDs.html, that may be a start, but based on their API, it's not possible to search by text (like "Apparel") but by NodeId, which is not what I'm looking for :/
Can anyone of you help me please?
Thank you for your help :)
You have to use Browse Node Id of parent categories and bases on that you can search for sub categories.
Create a file named amazon_api_class.php
<?php
require_once 'aws_signed_request.php';
class AmazonProductAPI
{
/**
* Your Amazon Access Key Id
* #access private
* #var string
*/
private $public_key = "";
/**
* Your Amazon Secret Access Key
* #access private
* #var string
*/
private $private_key = "";
private $media_type = "";
private $region = "";
private $out_file_fp = "";
public function __construct($public, $private, $region) {
$this->public_key = $public;
$this->private_key = $private;
$this->region = $region;
}
public function getNode($node)
{
$parameters = array("Operation" => "BrowseNodeLookup",
"BrowseNodeId" => $node,
"ResponseGroup" => "BrowseNodeInfo");
$xml_response = aws_signed_request($parameters,
$this->public_key,
$this->private_key,
$this->region);
return ($xml_response);
}
public function setMedia($media, $file = "") {
$media_type = array("display", "csv");
if(!in_array($media,$media_type)) {
throw new Exception("Invalid Media Type");
exit();
}
$this->media_type = $media;
if($media == "csv") {
$this->out_file_fp = fopen($file,'a+');
}
}
private function writeOut($level, $name, $id, $parent) {
if($this->media_type == "display") {
$spaces = str_repeat( ' ', ( $level * 6 ) );
echo $spaces . $parent . ' : ' . $name . ' : ' . $id . "\n";
} elseif ($this->media_type == "csv") {
$csv_line = '"' . $parent . '","' . $name . '","' . $id . '"' . "\n";
fputs($this->out_file_fp, $csv_line);
} else {
throw new Exception("Invalid Media Type");
exit();
}
}
public function getBrowseNodes($nodeValue, $level = 0)
{
try {
$result = $this->getNode($nodeValue);
}
catch(Exception $e) {
echo $e->getMessage();
}
if(!isset($result->BrowseNodes->BrowseNode->Children->BrowseNode)) return;
if(count($result->BrowseNodes->BrowseNode->Children->BrowseNode) > 0) {
foreach($result->BrowseNodes->BrowseNode->Children->BrowseNode as $node) {
$this->writeOut($level, $node->Name,
$node->BrowseNodeId,
$result->BrowseNodes->BrowseNode->Name);
$this->getBrowseNodes($node->BrowseNodeId, $level+1);
}
} else {
return;
}
}
public function getNodeName($nodeValue)
{
try {
$result = $this->getNode($nodeValue);
}
catch(Exception $e) {
echo $e->getMessage();
}
if(!isset($result->BrowseNodes->BrowseNode->Name)) return;
return (string)$result->BrowseNodes->BrowseNode->Name;
}
public function getParentNode($nodeValue)
{
try {
$result = $this->getNode($nodeValue);
}
catch(Exception $e) {
echo $e->getMessage();
}
if(!isset($result->BrowseNodes->BrowseNode->Ancestors->BrowseNode->BrowseNodeId)) return;
$parent_node = array("id" => (string)$result->BrowseNodes->BrowseNode->Ancestors->BrowseNode->BrowseNodeId,
"name" => (string)$result->BrowseNodes->BrowseNode->Ancestors->BrowseNode->Name);
return $parent_node;
}}?>
Create file named as aws_signed_request.php
<?php
function aws_signed_request($params,$public_key,$private_key,$region)
{
$method = "GET";
$host = "ecs.amazonaws.".$region; // must be in small case
$uri = "/onca/xml";
$params["Service"] = "AWSECommerceService";
$params["AWSAccessKeyId"] = $public_key;
$params["AssociateTag"] = 'YOUR-ASSOCIATES-ID-HERE';
$params["Timestamp"] = gmdate("Y-m-d\TH:i:s\Z");
$params["Version"] = "2009-03-31";
/* The params need to be sorted by the key, as Amazon does this at
their end and then generates the hash of the same. If the params
are not in order then the generated hash will be different thus
failing the authetication process.
*/
ksort($params);
$canonicalized_query = array();
foreach ($params as $param=>$value)
{
$param = str_replace("%7E", "~", rawurlencode($param));
$value = str_replace("%7E", "~", rawurlencode($value));
$canonicalized_query[] = $param."=".$value;
}
$canonicalized_query = implode("&", $canonicalized_query);
$string_to_sign = $method."\n".$host."\n".$uri."\n".$canonicalized_query;
/* calculate the signature using HMAC with SHA256 and base64-encoding.
The 'hash_hmac' function is only available from PHP 5 >= 5.1.2.
*/
$signature = base64_encode(hash_hmac("sha256", $string_to_sign, $private_key, True));
/* encode the signature for the request */
$signature = str_replace("%7E", "~", rawurlencode($signature));
/* create request */
$request = "http://".$host.$uri."?".$canonicalized_query."&Signature=".$signature;
/* I prefer using CURL */
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$request);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
$xml_response = curl_exec($ch);
if ($xml_response === False)
{
return False;
}
else
{
/* parse XML */
$parsed_xml = #simplexml_load_string($xml_response);
return ($parsed_xml === False) ? False : $parsed_xml;
}
}
?>
Create file named as index.php
<?php
/* Example usage of the Amazon Product Advertising API */
include("amazon_api_class.php");
$public_key = "YOUR-AMAZON-PUBLIC-KEY";
$private_key = "YOUR-AMAZON-SECRET-KEY";
$region = "com"; // or "CA" or "DE" etc.
$obj = new AmazonProductAPI($public_key, $private_key, $region);
$obj->setMedia("display");
$obj->getBrowseNodes("1036592"); //Apparel US store
?>
Dont forget to update your public and private key in index.php
Im having issues with form validation in an empty field. I've included code if there is an error in the php and really thought that would do the trick, but it still submits the form, regardless and redirects to a thank you page.
The form is on: http://www.softwaretestingconference.com/try/register.html
Here is the php:
<?php
$firstnameErr = $lastnameErr = $positionErr = $companynameErr = $address1Err = $postcodeErr = $countryErr = $emailErr = $numberErr = $elecErr = $howManyErr;
$favFruit = array();
$myemail = 'tina.harris#31media.co.uk';//<-----Put Your email address here.
if ($_SERVER["REQUEST_METHOD"] == "POST") {
if (empty($_POST["firstname"])) {
$firstnameErr = "Please provide your first name";
}
else {
$firstname = $_POST["firstname"];
}
if (empty($_POST["lastname"])) {
$lastnameErr = "Please provide your first name";
}
else {
$lastname = $_POST["lastname"];
}
if (empty($_POST["position"])) {
$positionErr = "Please state your job position";
}
else {
$position = $_POST["position"];
}
if (empty($_POST["companyname"])) {
$companynameErr = "Please state your company's name";
}
else {
$companyname = $_POST["companyname"];
}
if (empty($_POST["address1"])) {
$address1Err = "Please provide an address for your invoice";
}
else {
$address1 = $_POST["address1"];
}
if (empty($_POST['address2'])) {
}
else {
$address2 = $_POST['address2'];
}
if (empty($_POST["postcode"])) {
$postcodeErr = "Please provide a postocde for your invoice";
}
else {
$postcode1 = $_POST["postcode"];
}
if (empty($_POST["country"])) {
$countryErr = "Please provide your country for your invoice";
}
else {
$country = $_POST["country"];
}
if (empty($_POST["email"])) {
$emailErr = "Please provide your email address";
}
else {
$email = $_POST["email"];
}
if (empty($_POST["number"])) {
$numberErr = "Please provide your phone number";
}
else {
$number = $_POST["number"];
}
if (empty($_POST["elec"])) {
$elecErr = "Please provide an electronic signature";
}
else {
$elec = $_POST["elec"];
}
}
if( empty($errors))
{
$to = $myemail;
$email_subject = "NSTC booking: $name";
$email_body = "You have received a new booking from NSTC. ".
" Here are the details: \n Places1: $places1 \n Places2: $places2 \n Places3: $places3 \n Title: $title \n First name: $firstname \n Last name: $lastname \n Position: $position \n Company name: $companyname \n Address1: $address1 \n Address2: $address2 \n Email: $email \n Number: \n $number \n Elec: $elec \n ";
if(isset($_POST['formDoor'])){
if (is_array($_POST['formDoor'])) {
foreach($_POST['formDoor'] as $value){
$email_body .= $value;
}
} else {
$value = $_POST['formDoor'];
$email_body .= $value;
}
}
$headers = "From: $myemail\n";
$headers .= "Reply-To: $email_address";
mail($to,$email_subject,$email_body,$headers);
//redirect to the 'thank you' page
header('Location: register-thanks.html');
}
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Contact form handler</title>
</head>
<body>
<!-- This page is displayed only if there is some error -->
<?php
echo nl2br($errors);
?>
</body>
</html>
Your problem is in the following line (My assumption):
if( empty($errors))
You are not setting anything in the $errors variable.
I would do as follows (I will give you one example, you then have to apply it to each one of your if statements.)
at the beginning of your script, define an empty array:
$errors = array();
and then (this is what you will have to replicate in all your "if" statements:
if (empty($_POST["email"])) {
$errors['mailError'] = "Please provide your email address";
}
else {
$email = $_POST["email"];
}
and at the end of your script:
if (count($errors) === 0) {
//passed, no errors.
} else {
//there are errors. handle them.
}