AngularJS update database using X-Editable - javascript

So, i'm using AngularJS with X-Editable to make an easier way to edit my data.
I have a table with all the information of a client such as name, phone, address, etc.. And I could apply X-Editablejust fine until the moment I need to actually save the edit on the database.
Also, this page just show one single client, is an individual client page, with only his details.
This is the code I'm using:
page.html
<table fixed-header class="detcli_table" ng-init="get_detcliente()">
<thead>
<tr>
<th>Campo</th>
<th>Informação</th>
</tr>
</thead>
<tbody>
<tr>
<td>Código</td>
<td>{{cliente.id}}</td>
</tr>
<tr>
<td>Nome</td>
<td><span editable-text="cliente.nm_cliente" onaftersave="updatePerson(cliente.nm_cliente)">{{cliente.nm_cliente || "Empty"}}</span></td>
</tr>
<tr>
<td>Tel.</td>
<td><span editable-text="cliente.num_tel" onaftersave="updatePerson(cliente.num_tel)">{{cliente.num_tel || "Empty"}}</span></td>
</tr>
[... more code ...]
</tbody>
</table>
app.js
myApp.controller('DetClientesCtrl', ['$scope', '$http', '$routeParams', function ($scope, $http, $routeParams) {
var clienteId = $routeParams.id;
$scope.get_detcliente = function() {
var url = 'scripts/php/db.php?action=get_cliente';
return $http.get(url).success(httpSuccess).error(function() {
alert("Oops, erro!");
});
}
httpSuccess = function(response) {
$scope.detRes = response;
}
function getById(arr, id) {
for (var d = 0, len = arr.length; d < len; d += 1) {
if (arr[d].id === id) {
return arr[d];
}
}
}
$scope.get_detcliente().then(function(){
$scope.cliente = getById($scope.detRes,$routeParams.id);
});
//Update Client
$scope.updatePerson = function() {
$http.post('scripts/php/db.php?action=upd_cliente',
{
'id': $routeParams.id,
'nm_cliente' : $scope.nm_cliente,
'num_tel' : $scope.num_tel
}
).success(function (data, status, headers, config) {
$scope.get_detcliente();
console.log("efeutou o post!");
}).error(function (data, status, headers, config) {
console.log("Algo deu errado!");
});
};
}]);
control.php
This is the method i'm using to add new data, delete and, in this case, to update an existing data
function upd_cliente() {
$data = json_decode(file_get_contents("php://input"));
$id = $data->id;
$nm_cliente = $data->nm_cliente;
$num_tel = $data->num_tel;
$qry = "update cad_cliente set nm_cliente = '$nm_cliente', num_tel = '$num_tel' where cd = '$id'";
}
When I run the code, I get no errors at all. The console.log I'm using is showing properly in the console, the editing I do, is working fine on the screen but when I refresh the page, there is no data saved, it goes back to the previous data.
What may be wrong?
And also I don't know if this is the best way to do it, since I have a table with about 10 to 15 lines of information, so if I edit just 1 or 5 lines, the code will have to run for each edit I make.
Is there a better way to process it?

Well, after some research and a lot of try/fail i cam up with the solution.
in the page page.html i needed to remove the remove the code inside the (), so it is going to be like this:
page.html
<td><span editable-text="cliente.nm_cliente" onaftersave="updatePerson()">{{cliente.nm_cliente || "Empty"}}</span></td>
And on the app.js i needed to use $scope.cliente.nm_cliente instead of $scope.nm_cliente. So the code will be like this:
app.js
$scope.updatePerson = function() {
$http.post('scripts/php/db.php?action=upd_cliente',
{
'id': $routeParams.id,
'nm_cliente' : $scope.cliente.nm_cliente,
'num_tel' : $scope.cliente.num_tel
}
).success(function (data, status, headers, config) {
//Success code here
}).error(function (data, status, headers, config) {
//Error code here
});
};
Then, on the php file i just need to write the other fields i need to update on the database, in my case, it will be more than 15 fields to be possible to update.
Note: As far as i know, this code only works with the option onaftersave="" because if we use the option onbeforesave="", like the name itself, the data will not be passed since it's being executed before the new data is passed to the $scope.
I'm sorry if any of my information is wrong, i'm starting learning AngularJS right now. But this is working for me.
Also, i don't know if there is a better way to achieve this result, so, if anyone knows, please share it with us!

Related

How to send value from jsp page to database without refreshing the page

I am developing a spring+hibernate webapp for practicing translation skill from Russian to English.
In one of my jsp pages I am retrieving all the questions from database and placing them into a table with the following columns: text in Russian, field for user's translation, button for checking the result. The goal is to save user's input into database without refreshing the page. How can I do it?
I tried several options, but none of them worked for me.
I used the solution from Send javascript variables to spring controller in my project, but nothing happened at all.
Part of "firstPage.jsp" ("/first" path in the controller):
<head>
<title>Title</title>
<script>
function searchViaAjax(id) {
var tempId = id;
alert("Start");
$.ajax({
type : "POST",
url : "./search/api/getSearchResult",
data : {id:tempId},
timeout : 100000,
success : function(id) {
alert("success");
console.log("SUCCESS: ", id);
display(id);
alert(response);
},
error : function(e) {
alert("error");
console.log("ERROR: ", e);
display(e);
},
done : function(e) {
alert("done");
console.log("DONE");
}
});
}
</script>
</head>
<body>
<button onclick="searchViaAjax(1)">Simple button</button>
</body>
Controller class:
#Controller
public class DemoController {
#RequestMapping("/first")
public String getFirst(){
return "firstPage";
}
#ResponseBody
#RequestMapping(value = "/search/api/getSearchResult", method=RequestMethod.POST)
public String getSearchResultViaAjax(#RequestParam("id") Integer id) {
System.out.println("come to ajax"+ id);
return "hello";
}
}
The "Start" message gets printed, but other messages from searchViaAjax() don't. And controller method doesn't start.
You can pass id in controller as it is no issue in your 'id', and also you can skip value attribute in #RequestParam.
#ResponseBody
#RequestMapping(value = "/search/api/getSearchResult")
public String getSearchResultViaAjax(#RequestParam("id") integer id) {
System.out.println("come to ajax"+ id);
return "hello";
}
Specify the methodType
#RequestMapping(value = "/search/api/getSearchResult", methodType=RequestMethod.POST)
It is also a good practice to use wrapper instead of primitive
#RequestParam("tempId") Integer id
the problem is in your ajax url attribute.
It should be url : "./search/api/getSearchResult",
Root Cause:
When you are about to hit your controller, it construct the url like this
http://localhost:8080/search/api/getSearchResult
and hence such resource is not available and it causes 404 not found error.
In actual the url should be
http://localhost:8080/contextroot/search/api/getSearchResult
here contextroot refers your project name.
Now if you hit url ./search/api/getSearchResult then ./ refers the base url i,e localhost:8080/contextroot and the entire url will be constructed properly.
I would like to recommend you to create global variable in JavaScript say baseUri and assign./ into it.
<script>
var baseUri="./";
</script>
In your AJAX it becomes
url : baseUri+"search/api/getSearchResult",
Hope this will help
The code from user9634982 was fine, thanks to him. The problem was because I was using slim jQuery version so my browser was giving me "$.ajax is not a function" error. And I didn't see it for hours because I didn't know where to look :facepalm: Thanks again to user9634982 for discovering browser inspector to me :D After replacing slim version to usual it still didn't work because of spring security. I added _csrf token and all worked fine.
.jsp:
<meta name="_csrf" content="${_csrf.token}"/>
<meta name="_csrf_header" content="${_csrf.headerName}"/>
<script>
function searchViaAjax(id) {
var csrfHeaderName = "X-CSRF-TOKEN";
var csrfTokenValue;
var metaTags = document.getElementsByTagName('meta');
for(var i = 0; i < metaTags.length; i++) {
var metaTagName = metaTags[i].getAttribute("name");
if(metaTagName === "_csrf_header")
csrfHeaderName = metaTags[i].getAttribute("content");
if(metaTagName === "_csrf")
csrfTokenValue = metaTags[i].getAttribute("content");
}
$.ajax({
type : "POST",
url : "./addAnsweredQuestion",
data : {id:id},
timeout : 100000,
beforeSend:function(xhr){xhr.setRequestHeader(csrfHeaderName, csrfTokenValue);},
success : function(id) {
alert("success");
console.log("SUCCESS: ", id);
display(id);
alert(response);
},
error : function(e) {
alert("error");
console.log("ERROR: ", e);
display(e);
},
done : function(e) {
alert("done");
console.log("DONE");
}
});
}
</script>
Controller:
#PostMapping(value = "/addAnsweredQuestion")
public void getSearchResultViaAjax(#RequestParam("id") Long id) {
System.out.println("come to ajax"+ id);
}

AngularJS code hangs browser

I am calling a Web Service which returns around 3000 records as data entries as HTML response & i am trying to read this response using angularJS.
Below is my AngularJS code i am using to call the service
angular.module('tabApp', ['ngSanitize'])
.controller('TabController', ['$scope', 'HttpService', function($scope, HttpService) {
$scope.tab = 0;
$scope.setTab = function(newTab){
$scope.tab = newTab;
$scope.loading = true;
HttpService.CallService('Service.php?id='+newTab,newTab, function (data) {
$scope.myText = data;
$('.count').show();
$("[id^=searchg]").show();
$('.results').show();
});
};
$scope.isSet = function(tabNum){
return $scope.tab === tabNum;
};
$scope.setTab1 = function(newTab1){
$scope.tab = newTab1;
$('.loaderImage').hide();
};
$scope.isSet1 = function(tabNum){
return $scope.tab === tabNum;
};
}])
.service('HttpService', ['$rootScope', '$http', function ($rootScope, $http) {
$rootScope.loading = true;
return {
CallService: function (url,tabnum, callback) {
$http({
method: "POST",
url: url,
data: {id: tabnum}})
.success(function (data, status) {
$('.loaderImage').hide();
callback(data, status);
}).error(function (data, status) {
$('.loaderImage').hide();
callback(status);
});
}
}
}]);
My problem is the browser hangs if the returned records are more than 1500. Please advise on how i can improve this.
Update:
My html code looks like this
<div ng-show="isSet(1)">
<div id=matches style="display:none"></div>
<input type=text id=searchg placeholder="Type to search..." style="display:none" />
<p class="preload" ng-bind-html="myText"></p>
</div>
As we can see it is the bulky data which you are trying to bind. In Future, it could be more bulky.
You should use the server side pagination and get only the number of records, what your pagination is.
Here is the JSFiddle link for the reference.
http://jsfiddle.net/dwahlin/3Kewg/
Hope this helps! CHEERS TO CODE! :)
As #Mohit Dixit suggested, you should prefer to do server side paging and request only active page records.
I would advise you to use smart table library for this. Here is the official website for same. They support paging(both server side and client side), filter and sorting in one go.
Please note that there are many library available for this purpose but I am suggesting this as I am using it from past few years.

how to pass parameters to redirected page?

I have script that work on button click:
$scope.ShowDetailsAboutTicket = function (pinTicket) {
if ($scope.showdetail == false && pinTicket != null) {
$scope.pinTicket = pinTicket;
$http.get(TicketUrl + 'GetTicket/' + $scope.terminalId + '/' + $scope.pinTicket)
.success(function(data, status, headers, config)
{
$("#background").addClass("darker");
$scope.showdetail = true;
$scope.ticketNotFound = false;
$location.search("ticketid", pinTicket);
})
.error(function(data, status, headers, config) {
$scope.ticketNotFound = true;
$scope.showdetail = false;
})
.then(TicketDetails, ErrorResponse);
}
}
Everything works fine.I get url :http://localhost:60664/Home#?ticketid=8837278738 but i want when user click on button that redirect him to Sport page with those parameters...how can i do that? I tried with window.location but i dont get parameters
You should define routes in your configuration and then you should invoke that route when a user clicks your button.
You can use either ng-route or the more extended version ui-router.
You can use $routeParams for this
Something like this:
Module.controller("yourControler", ["$routeParams", function($routeParams){
//do whatever you need with the controller
$scope.ticketId = $routeParams.ticketId;
$scope.dosomethingOnSubmit = function() {
//read $routeParams.ticketId and use $location to redirect user where you need to (but that will result in GET action)
//Or you can perform your own POST request via $http service or $resource
//Read the docs :)
}
}]);
Then in your template you reference the ticketId
<a class="myButton" ng-href="some/path/?ticketId={{ticketId}}">Go to Sport page</a>
Ok, if that is submit button, then you can do this
<form ng-submit="dosomethingOnSubmit()">
<button type="submit">Submit</button>
</form>
ngSubmit - https://docs.angularjs.org/api/ng/directive/ngSubmit
$http - https://docs.angularjs.org/api/ng/service/$http

How to get AngularJS and KendoUI working in harmony?

In stages, I setup my .Net MVC solution and ensured both Angular JS and KendoUI are working independently.
app.js:
var app = angular.module("app", ['kendo.directives']);
and in my controller, I have the following defined:
app.controller('contentTypesController', ['$scope', '$log', 'contentTypesRepository',
function ($scope, $log, contentTypesRepository) {
var a = {};
$scope.status;
$scope.contentTypes;
$scope.contentTypeOptions;
// for testing purposes, but not used - used for navigation properties
$scope.users;
getContentTypes();
function getContentTypes() {
// call the data repository
contentTypesRepository.getList()
.success(function (contentTypes) {
//console.log(contentTypes.value[0].Description);
//$log.log(contentTypes.value[0].Description)
$scope.contentTypes = contentTypes;
$scope.contentTypeOptions = {
dataSource: {
data: contentTypes
},
dataTextField: "Description",
dataValueField: "ContentTypeId",
optionLabel: "Select a Content Type"
};
})
.error(function (error) {
$scope.status = 'Unable to load data: ' + error.message;
});
}
$scope.updateContentTypes = function (id) {
var contentType;
for (var i = 0; i < $scope.contentTypes.length; i++) {
var currentType = $scope.contentTypes[i];
if (currentType.ID === id) {
contentType = currentType;
break;
}
}
};
$scope.insertContentType = function () {
// get contentType description from the client
var contentType = { 'Description': $scope.newContentType };
contentTypesRepository.insert(contentType)
.success(function () {
$scope.status = 'Added new content type. Refreshing list.';
// add the new content type to the client-side collection
$scope.contentTypes.value.push(
{ 'Description': $scope.newContentType }
);
$scope.newContentType = "";
})
.error(function (error) {
$scope.status = 'Unable to insert new content type: ' + error.message;
});
};
$scope.deleteContentType = function(id) {
contentTypesRepository.remove(id)
.success(function () {
$scope.status = 'Deleted content type. Refreshing list.';
for (var i = 0; i < $scope.contentTypes.length; i++) {
var contentType = $scope.contentTypes[i];
if (contentType.ID === id) {
// remove the content type from the client-side collection
$scope.contentTypes.splice(i, 1);
break;
}
}
// navigation properties = null
// $scope.xxxx = null;
})
.error(function (error) {
$scope.status = 'Unable to delete content type: ' + error.message;
});
};
// get some navigation property
//$scope.getCustomerOrders = function (id) {
// dataFactory.getOrders(id)
// .success(function (orders) {
// $scope.status = 'Retrieved orders!';
// $scope.orders = orders;
// })
// .error(function (error) {
// $scope.status = 'Error retrieving customers! ' + error.message;
// });
//};
$scope.addContentType = function () {
//return $scope.newContentType.$save();
$scope.contentTypes.value.push(
{ 'Description': $scope.newContentType }
);
$scope.newContentType = "";
}
In following the Angluar/Kendo examples here, I added code related to $scope.contentTypeOptions.
In my view:
<select kendo-drop-down-list k-options="contentTypeOptions"></select>
Which displays a dropdown, but no data.
I am able to view the data in a ng-repeater:
<ul>
<li ng-repeat="contentType in contentTypes.value">
{{ contentType.Description }}
</li>
</ul>
And the raw data by {{ contentTypeOptions }}.
Since the repeater uses contentTypes.value, I tried this as well in
$scope.contentTypeOptions = {
dataSource: {
data: contentTypes.value // tried both contentTypes and contentTypes.value
},
dataTextField: "Description",
dataValueField: "ContentTypeId",
optionLabel: "Select a Content Type"
};
... which is based on the JSON data:
Ultimately, I would like to get all the CRUD hooked up for a grid (which I have done in the past with OData, but now adding AngularJS to the mix) and thought simply displaying the data in an Angular/Kendo mix would be a good start. I'm hoping that after getting this pinned down the rest will be simple, and appreciate any suggestions.
Your code is a bit confusing since methods like $scope.updateContentTypes treat $scope.contentTypes as an array, but at the same time contentTypes appears to be an object with a property value which is an array.
One thing to be aware of is that Kendo UI widgets will convert your array to a Kendo DataSource internally. This means that changes you make to $scope.contentTypes won't affect the items in your data source in $scope.contentTypeOptions.
Another issue is that there is no full two-way binding between widgets and the data source in angular-kendo, and until recently, the data source wouldn't update at all unless you specifically declared it as a DataSource. There have been some improvements lately, although it's still not fully integrated, as far as I can see.
(you can try creating a deep watch on the data yourself, but that may create performance problems; see related post here).
Your dropdown doesn't show the data because you replace $scope.contentTypeOptions after creating the widget, and there is no $watch on that property that would update the widget with these options.
You can either create a DataSource explicitly and update that with:
$scope.contentTypeOptions.dataSource.data(contentType.value);
or you can use the attribute:
k-data-source="contentTypes"
which will create a $watch on $scope.contentTypes, so when you replace it, the widget will update as well.
Maybe this basic (although admittedly a bit messy) example will help you somewhat (I set up the 2nd dropdown in the same way you did; the "change" button updates the data source).
You will need to use the Angular Kendo bindings from Kendo labs.
Here is a an article with live demo and full source code:
http://blog.longle.io/2014/05/01/angularjs-kendo-ui-using-angular-kendo-with-asp-net-mvc-5-web-api-odata

How to post image on twitter in Phonegap using javascript

I'm currently implementing a web smartphone application with Phonegap. On this application, users can post images they take with the phone camera on Facebook. This feature has been succesfully implemented only using javascript, by sending a base 64 encoded image. Now, I want to implement the same feature using Twitter.
I found some very interesting blog posts about this and I'm already be able to update the user status only using javascript... but I can't post images too using the update_with_media Twitter web service.
According too this post, someone says it's impossible to implement this operation without using a server side code (like a php script for example).
So my question is : is it possible to use the update_with_media Twitter web service only with javascript ?
I send you my code to have an overview of the current solution. I've taken this article as working base : http://oodlestechnologies.com/blogs/Twitter-integration-on-PhoneGap-using-ChildBrowser-and-OAuth-for-iOS-and-Android-Platforms
Here is my HTML code.
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<script type="text/javascript" src="../js/jquery/jquery.min.js"></script>
<script type="text/javascript" src="../cordova-2.5.0.js"></script>
<script type="text/javascript" src="../js/childBrowser/childbrowser.js"></script>
<script type="text/javascript" src="../js/helpers/jsOAuth-1.3.6.js"></script>
<script type="text/javascript" src="../js/helpers/twitter.js"></script>
</head>
<body>
<h4>Oodles Twitter App</h4>
<table border="1">
<tr>
<th>Login using Twitter</th>
<th>
<button id="loginBtn" onclick="Twitter.init();">Login</button>
<button id="logoutBtn" onclick="logOut();">Logout</button>
</th>
</tr>
<tr id="tweetText">
<td colspan="2"><textarea id="tweet"></textarea></td>
</tr>
<tr id="tweetBtn">
<td colspan="2" align="right">
<button id="tweeter" onclick="Twitter.tweet();">Tweet</button>
</td>
</tr>
<tr><td colspan="2"><div id="welcome">Please Login to use this app</div></td></tr>
</table>
<br/>
<br/>
<button onclick="javascript:location.reload();">Recharger la page</button>
</body>
</html>
Here is my twitter.js code : (The point is in the post method)
$(document).ready(function() {
document.addEventListener("deviceready", onDeviceReady, false);
});
function onDeviceReady() {
var root = this;
cb = window.plugins.childBrowser;
if (!localStorage.getItem(twitterKey)) {
$("#loginBtn").show();
$("#logoutBtn").hide();
$("tweetBtn").hide();
$("tweetText").hide();
}
else {
$("#loginBtn").hide();
$("#logoutBtn").show();
$("tweetBtn").show();
$("tweetText").show();
}
if (cb != null) {
cb.onLocationChange = function(loc) {
root.locChanged(loc);
};
cb.onClose = function() {
root.onCloseBrowser()
};
cb.onOpenExternal = function() {
root.onOpenExternal();
};
}
}
function onCloseBrowser() {
console.log("onCloseBrowser!");
}
function locChanged(loc) {
console.log("locChanged!");
}
function onOpenExternal() {
console.log("onOpenExternal!");
}
// Consumer key : ...
// Consumer secret : ...
// GLOBAL VARS
var oauth; // It Holds the oAuth data request
var requestParams; // Specific param related to request
var options = {consumerKey: '...', consumerSecret: '...', callbackUrl: "http://www.google.fr"};
var twitterKey = "twtrKey"; // This key is used for storing Information related
var Twitter = {
init: function() {
// Apps storedAccessData , Apps Data in Raw format
var storedAccessData, rawData = localStorage.getItem(twitterKey);
// here we are going to check whether the data about user is already with us.
if (localStorage.getItem(twitterKey) !== null) {
// when App already knows data
storedAccessData = JSON.parse(rawData); //JSON parsing
//options.accessTokenKey = storedAccessData.accessTokenKey; // data will be saved when user first time signin
options.accessTokenSecret = storedAccessData.accessTokenSecret; // data will be saved when user first first signin
// javascript OAuth take care of everything for app we need to provide just the options
oauth = OAuth(options);
oauth.get('https://api.twitter.com/1/account/verify_credentials.json?skip_status=true',
function(data) {
var entry = JSON.parse(data.text);
console.log("USERNAME: " + entry.screen_name);
}
);
}
else {
// we have no data for save user
oauth = OAuth(options);
oauth.get('https://api.twitter.com/oauth/request_token',
function(data) {
requestParams = data.text;
cb.showWebPage('https://api.twitter.com/oauth/authorize?' + data.text); // This opens the Twitter authorization / sign in page
cb.onLocationChange = function(loc) {
Twitter.success(loc);
}; // Here will will track the change in URL of ChildBrowser
},
function(data) {
console.log("ERROR: " + JSON.stringify(data));
}
);
}
},
/*
When ChildBrowser's URL changes we will track it here.
We will also be acknowledged was the request is a successful or unsuccessful
*/
success: function(loc) {
// Here the URL of supplied callback will Load
/*
Here Plugin will check whether the callback Url matches with the given Url
*/
if (loc.indexOf("http://www.google.fr") >= 0) {
// Parse the returned URL
var index, verifier = '';
var params = loc.substr(loc.indexOf('?') + 1);
params = params.split('&');
for (var i = 0; i < params.length; i++) {
var y = params[i].split('=');
if (y[0] === 'oauth_verifier') {
verifier = y[1];
}
}
// Here we are going to change token for request with token for access
/*
Once user has authorised us then we have to change the token for request with token of access
here we will give data to localStorage.
*/
oauth.get('https://api.twitter.com/oauth/access_token?oauth_verifier=' + verifier + '&' + requestParams,
function(data) {
var accessParams = {};
var qvars_tmp = data.text.split('&');
for (var i = 0; i < qvars_tmp.length; i++) {
var y = qvars_tmp[i].split('=');
accessParams[y[0]] = decodeURIComponent(y[1]);
}
$('#oauthStatus').html('<span style="color:green;">Success!</span>');
$('#stage-auth').hide();
$('#stage-data').show();
oauth.setAccessToken([accessParams.oauth_token, accessParams.oauth_token_secret]);
// Saving token of access in Local_Storage
var accessData = {};
accessData.accessTokenKey = accessParams.oauth_token;
accessData.accessTokenSecret = accessParams.oauth_token_secret;
// Configuring Apps LOCAL_STORAGE
console.log("TWITTER: Storing token key/secret in localStorage");
localStorage.setItem(twitterKey, JSON.stringify(accessData));
oauth.get('https://api.twitter.com/1/account/verify_credentials.json?skip_status=true',
function(data) {
var entry = JSON.parse(data.text);
console.log("TWITTER USER: " + entry.screen_name);
$("#welcome").show();
document.getElementById("welcome").innerHTML = "welcome " + entry.screen_name;
successfulLogin();
// Just for eg.
app.init();
},
function(data) {
console.log("ERROR: " + data);
}
);
// Now we have to close the child browser because everthing goes on track.
window.plugins.childBrowser.close();
},
function(data) {
console.log(data);
}
);
}
else {
// Just Empty
}
},
tweet: function() {
var storedAccessData, rawData = localStorage.getItem(twitterKey);
storedAccessData = JSON.parse(rawData); // Paring Json
options.accessTokenKey = storedAccessData.accessTokenKey; // it will be saved on first signin
options.accessTokenSecret = storedAccessData.accessTokenSecret; // it will be save on first login
// javascript OAuth will care of else for app we need to send only the options
oauth = OAuth(options);
oauth.get('https://api.twitter.com/1/account/verify_credentials.json?skip_status=true',
function(data) {
var entry = JSON.parse(data.text);
Twitter.post();
}
);
},
/*
We now have the data to tweet
*/
post: function() {
alert('Post !');
var theTweet = $("#tweet").val(); // You can change it with what else you likes.
oauth.post('https://upload.twitter.com/1/statuses/update_with_media.json',
{
'status': theTweet,
'media': //HERE IS THE PROBLEM, WHAT TO DO HERE ?
}, "multipart/form-data",
function(data)
{
alert('Data 1 !');
console.log('------Data1 : ' + data);
var entry = JSON.parse(data.text);
console.log(entry);
done();
},
function(data) {
//var json_result = JSON.parse(data);
//alert(json_result.text.error);
var entry = JSON.stringify(data);
console.log('------Data2 : ' + entry);
}
);
}
}
function done() {
alert("OKKK !");
$("#tweet").val('');
}
function successfulLogin() {
$("#loginBtn").hide();
$("#logoutBtn,#tweet,#tweeter,#tweetBtn,#tweetText").show();
}
function logOut() {
//localStorage.clear();
window.localStorage.removeItem(twitterKey);
document.getElementById("welcome").innerHTML = "Please Login to use this app";
$("#loginBtn").show();
$("#logoutBtn,#tweet,#tweeter,#tweetText,#tweetBtn").hide();
}
After many tests (sending a base64 image, sending a blob, sending a binary file, ...) here is the return message from Twitter I have :
{\"errors\":[{\"message\":\"Internal
error\",\"code\":131}]}","xml":"","requestHeaders":{"Content-Type":"multipart/form-data"},"responseHeaders":{"date":"Fri,
19 Apr 2013 15:45:28
GMT","content-encoding":"deflate","strict-transport-security":"max-age=631138519","status":"500
Internal Server
Error","server":"tfe","content-type":"application/json;
charset=utf-8","version":"HTTP/1.1"}}
A "solution" (by send a blob) have been posted on the Twitter dev forum but not working for me : dev.twitter.com/discussions/6969
Does anyone want to implement the same feature or have a solution ? Thank you !
------ EDITED :
I just want to use Javascript and I don't want to implement any server-side solution (no PHP, C#, Java...).
According to the docs, Twitter requires the multipart/form-data enctype, which means a base64 string isn't going to work.
Unlike POST statuses/update, this method expects raw multipart data. Your POST request's Content-Type should be set to multipart/form-data with the media[] parameter ~ https://dev.twitter.com/docs/api/1/post/statuses/update_with_media
However, you could host an endpoint that takes base64, converts it to a real file, and forwards the request to Twitter. For example (untested):
<?php
$base64 = $_POST['image'];
$data = base64_decode( $base64 );
// Make name unique to avoid conflicts.
$temp_file = uniqid() . $_POST['name'];
// Save the file to a temp location.
file_put_contents( $temp_file, $data );
$temp_info = pathinfo( $temp_file );
$temp_type = $temp_info['extension'];
$temp_name = basename( $temp_file, '.' . $temp_type );
// OAuth library recommended by Twitter: https://github.com/themattharris/tmhOAuth
// See original: https://github.com/themattharris/tmhOAuth-examples/blob/master/images.php
require 'tmhOAuth.php';
require 'tmhUtilities.php';
$tmhOAuth = new tmhOAuth( array(
'consumer_key' => $_POST['consumer_key'],
'consumer_secret' => $_POST['consumer_secret'],
'user_token' => $_POST['user_token'],
'user_secret' => $_POST['user_secret'],
));
// note the type and filename are set here as well
// Edit: Not sure if the `type` and `filename` params are necessary.
$params = array( 'media[]' => "#{$temp_file};type={$temp_type};filename={$temp_name}" );
$code = $tmhOAuth->request( 'POST', $tmhOAuth->url( '1/status/update_with_media' ),
$params,
true, // use auth
true // multipart
);
// Remove temp file.
unlink( $temp_file );
if ( $code == 200 ) {
tmhUtilities::pr( json_decode( $tmhOAuth->response['response'] ) );
}
tmhUtilities::pr( htmlentities( $tmhOAuth->response['response'] ) );
?>
And you might call it like:
$.ajax({
// You'll want to use https to protect the oauth info.
url: "https://mysite.com/proxy.php",
type: "POST",
data: {
image: "base64 data...",
name: "foo.png",
consumer_key: options.consumerKey,
consumer_secret: options.consumerSecret,
user_token: options.accessTokenKey,
user_secret: options.accessTokenSecret
},
success: function( data ) {
console.log( data );
}
});
For anyone trying to post images to Twitter using client JS, I was able to post to twitter using the solution by gary-buynary-co-za (https://github.com/bytespider/jsOAuth/pull/11) at the end of this forum. Pretty much ended up using Phonegap FileTransfer and FileTransferOptions objects for transferring image to twitter api, but used jsOAuth for preparing FileTransferOptions headers and signatures. The solution could definitely be cleaned up though.

Categories