Upload an image to Drupal 7 / Services 3 from Titanium? - web-services

This must be close but I can't figure out what's causing the error.
In my Titanium app, I have a Webview with a canvas element and this code:
function getImageData() {
return canvas.toDataURL('image/png');
}
I am moving that data to the Titanium app like this:
var imageBase64data = webview.evalJS('getImageData()')
The data looks good starting with "data:image/png;base64,"...
Then in Titanium, I have a logged-in drupal session and call this function:
function uploadImage(imageBase64data, callback) {
var url = REST_PATH + "file.json";
var file = {
filename: utils.createRandomString() + ".png",
file: imageBase64data
// uid: Titanium.App.Properties.getString("userUid"),
// filesize: ""+Titanium.Utils.base64decode(imageBase64data).length,
};
var xhr = Titanium.Network.createHTTPClient({timeout: 30000});
xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");
var authString = Titanium.App.Properties.getString("userSessionName")+'='+Titanium.App.Properties.getString("userSessionId");
xhr.setRequestHeader("Cookie", authString);
xhr.onload = function() {
if(xhr.status == 200) {
var response = xhr.responseText;
callback(response);
}
};
xhr.onerror = function(e) {
alert("There was an error: " + e.error);
Ti.API.info(JSON.stringify(this));
};
xhr.open("POST", url);
xhr.send(file);
}
xhr.onerror is being called with e.error = "undefined"
The trace looks like this:
{
"responseData":{},
"readyState":4,
"connected":true,"UNSENT":0,"DONE":4,"HEADERS_RECEIVED":2,"OPENED":1,"LOADING":3,
"responseText":null,"status":406
}
I think authentication is working because I was previously getting a "need authentication" error until I added the Cookie header.

That was with the installation provided by Drupanium. I just did a fresh Drupal and fresh Services 3 install and my file is uploading nicely.

Related

Set multiple cookies in google app script with UrlFetchApp

I am using the following script to access a web page that requires user to be logged-in. The script works up to the point where I retrieve the login cookies I need help with using the cookies in the last part of the following code.
function login(strUsername, strPassword) {
//request Page
var loginPage = UrlFetchApp.fetch("https://login.url.org/");
//strip out "authState" value from response
var sessionDetails = loginPage.getContentText()
var searchString = sessionDetails.indexOf('"authState":"');
var newStringRight = sessionDetails.substring(searchString+13, searchString+6000);
var authState = newStringRight.substring(0, newStringRight.indexOf('"'));
//Logger.log(authState);
//set payload
var payload =
{"authState":authState,
"username":strUsername,
"password":strPassword
};
var options =
{
"method" : "post",
"payload" : JSON.stringify(payload),
"followRedirects" : false,
muteHttpExceptions: true,
"contentType": "application/json"
};
//Logger.log(options);
var loginResponse = UrlFetchApp.fetch("https://login.url.com/api/authenticate/credentials", options);
var loginHtml = loginResponse.getContentText();
Logger.log(loginHtml);
// Log has the following response
//{"output":{"method":"SUCCESS","cookies":{"ObSSOCookie":"xxx","ChurchSSO":"yyy"}}}
if (loginHtml.indexOf("SUCCESS")>0){
Browser.msgBox('Pass', 'Successfully loged in.', Browser.Buttons.OK);
//strip out cookies
searchStringStart = loginHtml.indexOf('{"ObSSOCookie":"');
searchStringEnd = loginHtml.indexOf('"}}');
var Cookie = loginHtml.substring(searchStringStart, searchStringEnd+2);
Logger.log(Cookie);
/*
Logger.log(cookie) returns
{"ObSSOCookie":"xxxx","ChurchSSO":"yyyy"}
*/
////////////////////////////Works up to here/////////////////////////////////////////
var options =
{
"headers" : {"cookies" : Cookie},
muteHttpExceptions: true,
"contentType": "application/json"
};
var adminpage= UrlFetchApp.fetch("https://url.com/admin",options );
Logger.log(adminpage);
/*
<h1>Access Denied</h1>
<p>You are not authorized to access this page.</p>
<p>Retry
*/
}else{
Browser.msgBox('Fail', 'Sign in failed. Please try again.', Browser.Buttons.OK);
}
}
I think it may have to do with the fact that I am trying to use multiple cookies {"ObSSOCookie":"xxxx","ChurchSSO":"yyyy"}not sure how to approperatly pass them into the new page call. Any help would be great
Cookies should be sent inside a single Cookie header separated by a ;(semicolon and a space):
Cookie: name1=value1; name2=value2;
Snippet:
var options =
{
"headers" : {"Cookie" : "ObSSOCookie=xxxx; ChurchSSO=yyyy"}},
};
Reference:
HTTP Cookies

Read content of SP.File object as text using JSOM

as the title suggests, I am trying to read the contents of a simple text file using JSOM. I am using a Sharepoint-hosted addin for this, the file I am trying to read resides on the host web in a document library.
Here's my JS code:
function printAllListNamesFromHostWeb() {
context = new SP.ClientContext(appweburl);
factory = new SP.ProxyWebRequestExecutorFactory(appweburl);
context.set_webRequestExecutorFactory(factory);
appContextSite = new SP.AppContextSite(context, hostweburl);
this.web = appContextSite.get_web();
documentslist = this.web.get_lists().getByTitle('Documents');
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml('<View><ViewFields><FieldRef Name="Name"/></ViewFields></View>');
listitems = documentslist.getItems(camlQuery);
context.load(listitems, 'Include(File,FileRef)');
context.executeQueryAsync(
Function.createDelegate(this, successHandler),
Function.createDelegate(this, errorHandler)
);
function successHandler() {
var enumerator = listitems.getEnumerator();
while (enumerator.moveNext()) {
var results = enumerator.get_current();
var file = results.get_file();
//Don't know how to get this to work...
var fr = new FileReader();
fr.readAsText(file.get);
}
}
function errorHandler(sender, args) {
console.log('Could not complete cross-domain call: ' + args.get_message());
}
}
However, in my succes callback function, I don't know how I can extract the contents of the SP.File object. I tried using the FileReader object from HTML5 API but I couldn't figure out how to convert the SP.File object to a blob.
Can anybody give me a push here?
Once file url is determined file content could be loaded from the server using a regular HTTP GET request (e.g. using jQuery.get() function)
Example
The example demonstrates how to retrieve the list of files in library and then download files content
loadItems("Documents",
function(items) {
var promises = $.map(items.get_data(),function(item){
return getFileContent(item.get_item('FileRef'));
});
$.when.apply($, promises)
.then(function(content) {
console.log("Done");
//print files content
$.each(arguments, function (idx, args) {
console.log(args[0])
});
},function(e) {
console.log("Failed");
});
},
function(sender,args){
console.log(args.get_message());
}
);
where
function loadItems(listTitle,success,error){
var ctx = SP.ClientContext.get_current();
var web = ctx.get_web();
var list = web.get_lists().getByTitle(listTitle);
var items = list.getItems(createAllFilesQuery());
ctx.load(items, 'Include(File,FileRef)');
ctx.executeQueryAsync(
function() {
success(items);
},
error);
}
function createAllFilesQuery(){
var qry = new SP.CamlQuery();
qry.set_viewXml('<View Scope="RecursiveAll"><Query><Where><Eq><FieldRef Name="FSObjType" /><Value Type="Integer">0</Value></Eq></Where></Query></View>');
return qry;
}
function getFileContent(fileUrl){
return $.ajax({
url: fileUrl,
type: "GET"
});
}

How to set content-length-range for s3 browser upload via boto

The Issue
I'm trying to upload images directly to S3 from the browser and am getting stuck applying the content-length-range permission via boto's S3Connection.generate_url method.
There's plenty of information about signing POST forms, setting policies in general and even a heroku method for doing a similar submission. What I can't figure out for the life of me is how to add the "content-length-range" to the signed url.
With boto's generate_url method (example below), I can specify policy headers and have got it working for normal uploads. What I can't seem to add is a policy restriction on max file size.
Server Signing Code
## django request handler
from boto.s3.connection import S3Connection
from django.conf import settings
from django.http import HttpResponse
import mimetypes
import json
conn = S3Connection(settings.S3_ACCESS_KEY, settings.S3_SECRET_KEY)
object_name = request.GET['objectName']
content_type = mimetypes.guess_type(object_name)[0]
signed_url = conn.generate_url(
expires_in = 300,
method = "PUT",
bucket = settings.BUCKET_NAME,
key = object_name,
headers = {'Content-Type': content_type, 'x-amz-acl':'public-read'})
return HttpResponse(json.dumps({'signedUrl': signed_url}))
On the client, I'm using the ReactS3Uploader which is based on tadruj's s3upload.js script. It shouldn't be affecting anything as it seems to just pass along whatever the signedUrls covers, but copied below for simplicity.
ReactS3Uploader JS Code (simplified)
uploadFile: function() {
new S3Upload({
fileElement: this.getDOMNode(),
signingUrl: /api/get_signing_url/,
onProgress: this.props.onProgress,
onFinishS3Put: this.props.onFinish,
onError: this.props.onError
});
},
render: function() {
return this.transferPropsTo(
React.DOM.input({type: 'file', onChange: this.uploadFile})
);
}
S3upload.js
S3Upload.prototype.signingUrl = '/sign-s3';
S3Upload.prototype.fileElement = null;
S3Upload.prototype.onFinishS3Put = function(signResult) {
return console.log('base.onFinishS3Put()', signResult.publicUrl);
};
S3Upload.prototype.onProgress = function(percent, status) {
return console.log('base.onProgress()', percent, status);
};
S3Upload.prototype.onError = function(status) {
return console.log('base.onError()', status);
};
function S3Upload(options) {
if (options == null) {
options = {};
}
for (option in options) {
if (options.hasOwnProperty(option)) {
this[option] = options[option];
}
}
this.handleFileSelect(this.fileElement);
}
S3Upload.prototype.handleFileSelect = function(fileElement) {
this.onProgress(0, 'Upload started.');
var files = fileElement.files;
var result = [];
for (var i=0; i < files.length; i++) {
var f = files[i];
result.push(this.uploadFile(f));
}
return result;
};
S3Upload.prototype.createCORSRequest = function(method, url) {
var xhr = new XMLHttpRequest();
if (xhr.withCredentials != null) {
xhr.open(method, url, true);
}
else if (typeof XDomainRequest !== "undefined") {
xhr = new XDomainRequest();
xhr.open(method, url);
}
else {
xhr = null;
}
return xhr;
};
S3Upload.prototype.executeOnSignedUrl = function(file, callback) {
var xhr = new XMLHttpRequest();
xhr.open('GET', this.signingUrl + '&objectName=' + file.name, true);
xhr.overrideMimeType && xhr.overrideMimeType('text/plain; charset=x-user-defined');
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
var result;
try {
result = JSON.parse(xhr.responseText);
} catch (error) {
this.onError('Invalid signing server response JSON: ' + xhr.responseText);
return false;
}
return callback(result);
} else if (xhr.readyState === 4 && xhr.status !== 200) {
return this.onError('Could not contact request signing server. Status = ' + xhr.status);
}
}.bind(this);
return xhr.send();
};
S3Upload.prototype.uploadToS3 = function(file, signResult) {
var xhr = this.createCORSRequest('PUT', signResult.signedUrl);
if (!xhr) {
this.onError('CORS not supported');
} else {
xhr.onload = function() {
if (xhr.status === 200) {
this.onProgress(100, 'Upload completed.');
return this.onFinishS3Put(signResult);
} else {
return this.onError('Upload error: ' + xhr.status);
}
}.bind(this);
xhr.onerror = function() {
return this.onError('XHR error.');
}.bind(this);
xhr.upload.onprogress = function(e) {
var percentLoaded;
if (e.lengthComputable) {
percentLoaded = Math.round((e.loaded / e.total) * 100);
return this.onProgress(percentLoaded, percentLoaded === 100 ? 'Finalizing.' : 'Uploading.');
}
}.bind(this);
}
xhr.setRequestHeader('Content-Type', file.type);
xhr.setRequestHeader('x-amz-acl', 'public-read');
return xhr.send(file);
};
S3Upload.prototype.uploadFile = function(file) {
return this.executeOnSignedUrl(file, function(signResult) {
return this.uploadToS3(file, signResult);
}.bind(this));
};
module.exports = S3Upload;
Any help would be greatly appreciated here as I've been banging my head against the wall for quite a few hours now.
You can't add it to a signed PUT URL. This only works with the signed policy that goes along with a POST because the two mechanisms are very different.
Signing a URL is a lossy (for lack of a better term) process. You generate the string to sign, then sign it. You send the signature with the request, but you discard and do not send the string to sign. S3 then reconstructs what the string to sign should have been, for the request it receives, and generates the signature you should have sent with that request. There's only one correct answer, and S3 doesn't know what string you actually signed. The signature matches, or doesn't, either because you built the string to sign incorrectly, or your credentials don't match, and it doesn't know which of these possibilities is the case. It only knows, based on the request you sent, the string you should have signed and what the signature should have been.
With that in mind, for content-length-range to work with a signed URL, the client would need to actually send such a header with the request... which doesn't make a lot of sense.
Conversely, with POST uploads, there is more information communicated to S3. It's not only going on whether your signature is valid, it also has your policy document... so it's possible to include directives -- policies -- with the request. They are protected from alteration by the signature, but they aren't encrypted or hashed -- the entire policy is readable by S3 (so, by contrast, we'll call this the opposite, "lossless.")
This difference is why you can't do what you are trying to do with PUT while you can with POST.

Facebook Login Button: Workaround for bug of Chrome on iOS

Currently, I am trying to solve a bug logged in Facebook Developer:
https://developers.facebook.com/bugs/325086340912814/
I am using ASP.NET MVC 3 with Facebook C# SDK and Facebook Javascript SDK.
Here is javascript code:
window.fbAsyncInit = function () {
// init the FB JS SDK
FB.init({
appId: '298365513556623', // App ID from the App Dashboard
channelUrl: 'http://www.christianinfoportal.com/channel.html', // Channel File for x-domain communication
status: true, // check the login status upon init?
cookie: true, // set sessions cookies to allow your server to access the session?
xfbml: true // parse XFBML tags on this page?
});
// Additional initialization code such as adding Event Listeners goes here
FB.Event.subscribe('auth.authResponseChange', function (response) {
if ((response.status == 'connected')) {
if (fbIgnoreFirstEventFire)
fbIgnoreFirstEventFire = false;
else {
// the user is logged in and has authenticated your
// app, and response.authResponse supplies
// the user's ID, a valid access token, a signed
// request, and the time the access token
// and signed request each expire
var uid = response.authResponse.userID;
var accessToken = response.authResponse.accessToken;
// TODO: Handle the access token
// Do a post to the server to finish the logon
// This is a form post since we don't want to use AJAX
var form = document.createElement("form");
form.setAttribute("method", 'post');
form.setAttribute("action", '/FBLogin');
var field = document.createElement("input");
field.setAttribute("type", "hidden");
field.setAttribute("name", 'accessToken');
field.setAttribute("value", accessToken);
form.appendChild(field);
var field = document.createElement("input");
field.setAttribute("type", "hidden");
field.setAttribute("name", 'returnUrl');
field.setAttribute("value", window.location.href);
form.appendChild(field);
document.body.appendChild(form);
form.submit();
}
} else if (response.status == 'not_authorized') {
// the user is logged in to Facebook,
// but has not authenticated your app
} else {
// the user isn't logged in to Facebook.
}
});
};
// Load the SDK's source Asynchronously
(function (d, debug) {
var js, id = 'facebook-jssdk', ref = d.getElementsByTagName('script')[0];
if (d.getElementById(id)) { return; }
js = d.createElement('script'); js.id = id; js.async = true;
js.src = "//connect.facebook.net/en_US/all" + (debug ? "/debug" : "") + ".js";
ref.parentNode.insertBefore(js, ref);
}(document, /*debug*/ false));
The View code is pretty simple:
<div id="fbLoginButton" class="fb-login-button" data-show-faces="false" data-width="50" data-max-rows="1" data-scope="email"></div>
I basically forward the access token to a server side code page:
public ActionResult FBLogin(String returnUrl)
{
var accessToken = Request["accessToken"];
//FB fires even without the user clicking the Log In button
Session["FBLoginFirstClick"] = true;
//Session["AccessToken"] = accessToken;
//this.lblAccessToken.Text = accessToken;
//Response.Redirect("/MyUrlHere");
var client = new Facebook.FacebookClient(accessToken);
dynamic me = client.Get("me");
String email, fullName;
fullName = me.first_name + " " + me.last_name;
email = me.email;
Models.User.ThirdPartyLogin(email, fullName, Session);
if (!string.IsNullOrEmpty(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
Solved by using Facebook authentication redirect.
https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/

Apple Dashcode and Webservices

I am developing my first Dashboard Widget and trying to call a webservice. But I keep on getting XMLHTTPRequest status 0.
Following is the code
var soapHeader = '<?xml version=\"1.0\" encoding=\"utf-8\"?>\n'
+'<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\n'
+'<soap:Body>\n'
+'<UsersOnline xmlns=\"http://wsSync\" />\n'
+'</soap:Body>\n'
+'</soap:Envelope>';
var destinationURI = 'http://ws.storan.com/webservicesformraiphone/wssync.asmx';
var actionURI = 'http://wsSync/UsersOnline';
function callWebService() {
try{
SOAPObject = new XMLHttpRequest();
SOAPObject.onreadystatechange = function() {fetchEnd(SOAPObject);}
SOAPObject.open('POST', destinationURI, true);
SOAPObject.setRequestHeader('SOAPAction', actionURI);
SOAPObject.setRequestHeader('Content-Length', soapHeader.length);
SOAPObject.setRequestHeader('Content-Type', 'text/xml; charset=utf-8');
var requestBody = soapHeader;
SOAPObject.send(requestBody);
} catch (E)
{
alert('callWebService exception: ' + E);
}
}
function fetchEnd(obj)
{
if(obj.readyState == 4){
if(obj.status==200)
{
alert("Yahooooooo");
}
}
}
Any ideas?
have you added
<key>AllowNetworkAccess</key>
<true/>
to the plist? if not the outside world will not be available.
You may also encounter problems if trying to cross domains.