Open OneNote from my Ipad Application - Need OneNote URL SCHEME? - objective-c

When I use onenote:// for opening OneNote from my ipad Application, it opens OneNote but gives an error message that says
Cannot Open Link" There’s a problem with this link in One Note.
This is my Code
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"onenote://"]];
PS: OneNote for Ipad already has to be installed in the Ipad Device before trying this.
Is this the right URL SCHEME for OneNote - onenote://
Or am I doing it wrong.
The Error Message does note create a problem when closed, but I just want to get rid of the Error Message.
Also can parameters be passed to OneNote from my Application.
Help Appreciated!!

Using "onenote-cmd://" should open up OneNote without any errors.

The reason you're getting an error is that the iOS OneNote client doesn't support being launched by a protocol handler without having parameters completed for notebook, section, and page (it doesn't know where to navigate to).If you're just using onenote:// without specifying a destination, it's "by design" that you will receive an error.
We recommend you link to a specific page when opening OneNote from your application to avoid the error. The OneNote REST API returns a link to the page you've created when Creating a Page, and you can also retrieve a link using the "Copy Link to Page" function from within the iOS app.

As Nick pointed out, the OneNote app needs additional parameters to open successfully, without showing the "Cannot Open Link" alert. Here is what I did to solve the problem:
Note: In my app, the user is already logged in using the LiveSDK (Microsoft Account Login) so I have their Access Token saved.
I created an "Authorize OneNote" method that sends a request to the OneNote API to create a "Page" in the User's default notebook:
(void)AuthorizeOneNote
{
NSString *date = [NSString stringWithFormat:#"%#", [NSDate date]];
NSString *simpleHtml = [NSString stringWithFormat:
#"<html>"
"<head>"
"<title>\"%#\"</title>"
"<meta name=\"created\" content=\"%#\" />"
"</head>"
"<body>"
"<p></p>"
"</body>"
"</html>", #"Page Title", date];
NSData *presentation = [simpleHtml dataUsingEncoding:NSUTF8StringEncoding];
NSString *endpointToRequest = #"https://www.onenote.com/api/v1.0/pages";
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:endpointToRequest]];
request.HTTPMethod = #"POST";
request.HTTPBody = presentation;
[request addValue:#"text/html" forHTTPHeaderField:#"Content-Type"];
[request setValue:[#"Bearer " stringByAppendingString:#"LIVE-ID-ACCESS-TOKEN"] forHTTPHeaderField:#"Authorization"];
objConnectionOneNote = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
}
This is how I handle the response:
(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if (connection == objConnectionOneNote)
{
NSString *strData = [[NSString alloc]initWithData:objData encoding:NSUTF8StringEncoding];
NSMutableDictionary *dictData1 = [[NSMutableDictionary alloc] init];
dictData1 = [NSJSONSerialization JSONObjectWithData:objData options:kNilOptions error:nil];
oneNoteClientUrl = [dictData1 valueForKeyPath:#"links.oneNoteClientUrl.href"];
NSLog(#"Response: %#",oneNoteClientUrl);
NSLog(#"Response: %#",strData);
// Launch OneNote Client
if([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:#"onenote://"]])
{
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:oneNoteClientUrl]];
}
else
{
[[UIApplication sharedApplication] openURL: [NSURL URLWithString:#"https://itunes.apple.com/us/app/microsoft-onenote-for-iphone/id410395246?mt=8&uo=4"]];
}
}
There are couple things to note here:
This will create a new page every single time OneNote is launched from the app. In order to open the same page, you can persist the returned URL and use that to Open the client.
The Access Token from the MS Live Authentication expires in about an hour. When this happens, the OneNote API request returns a 401 Unauthorized response. To mitigate this, you can refresh the Access Token using the refresh token, which is also returned during Live Authentication.
Hope it helps!

With the addition of new automation app Workflow for iOS I'm extremely interested in figuring out how to incorporate OneNote into my actions.
I'm not looking to make an app and thus far I cannot find any examples for advanced url schemes for OneNote so the ability to grab a link that would open the OneNote app on either iPhone or iPad and have it go to the correct notebook and page would be great.
Ideally we could use a url scheme which OneNote:// works but need additional parameters for which notebook and page and how to create either a new notebook or page with that.
Workflow goes above and beyond what IFTTT can do (situation depending) and really looking forward to starting to use OneNote more due to automation possibilities.

Related

Programmatically logging in to website with saved username and password

One of the functions of an app I am making involves logging the user into our Campus Portal website. For ease of use, the user may enter a username and password into the app once, and it will be saved for all future log-ins. When the user clicks a button, the information will automatically be sent to log in, and the website will be displayed in a UIWebView.
Let us say that the username and password are each stored in an NSString. How can I use this data to log in to this website programmatically and display the page it in a UIWebView?
I know little about posting and forms, so any help is appreciated.
Would something like this Stackoverflow answer help?
Here's the shell of my code for this
- (IBAction)btnGo:(id)sender {
username = usernameField.text;
password = passwordField.text;
if (saveSwitch.isOn) {
//Save data if the user wants
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
usernameSaved = username;
passwordSaved = password;
[appDelegate.listOfPortalCredentials replaceObjectAtIndex:0 withObject:usernameSaved];
[appDelegate.listOfPortalCredentials replaceObjectAtIndex:1 withObject:passwordSaved];
}
//Insert username and password to web form, log in to portal
//This is where I need help
}
Edit:
Thanks to Karthik I was able to find the HTTP Post using Firebug. It gave me:
appName=ridgefield&portalUrl=portal%2Fridgefield.jsp%3F%26rID%3D0.18423783694092&username=<username>&password=<password>&B1=Log+In&url=portal%2Fmain.xsl%3FrID%3D0.6845596700302482&lang=en
where and represent the real username and password. I believe this is what I need. Using this, how can I display the logged-in page in a UIWebView? Do I just need to load the above URL in the UIWebView?
Use UIWebView, and do a http POST to https://ic.ridgefield.org/campus/verify.jsp with username and password.
To understand how it works, install Firebug on Firefox browser and go to 'Net' tab on firebug, and then open your website and enter some username/password.
You should mimic that action using code. (I always get invalid username or password response from server, coz i dont have an account and i try some random ones, and since there is no signup on the site, there is no way for me to verify this)
UIWebView* webView = [[UIWebView alloc] initWithFrame:self.view.frame];
[self.view addSubview:webView];
NSString* username = #"";
NSString* password = #"";
NSURL* url = [NSURL URLWithString:#"https://ic.ridgefield.org/campus/verify.jsp"];
NSString* body = [NSString stringWithFormat:#"appName=ridgefield&username=%#&password=%#", username, password];
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:30];
request.HTTPMethod = #"POST";
request.HTTPBody = [body dataUsingEncoding:NSStringEncodingConversionAllowLossy];
[webView loadRequest:request];

How to load a facebook page link on iOS Facebook App

I'm trying to open a Facebook link and using the following code would conform with the URL protocol on iOS.
NSString *url = [self.dataSource getFacebookURL];
url = [url stringByReplacingOccurrencesOfString:#"http://www.facebook.com/" withString:#"fb://profile/"];
NSLog(#"LINK: %#", url);
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]];
But when trying to load a profile or a page (i.e. of an organization) it always brings me to the screen that was left open last. Is there a way of linking directly to the page and profile? I followed the instructions as described here, but no success.
The page I want to link to is "https://www.facebook.com/junction11" if that helps...
Thanks, Max
FIX
So I found a solution which is to load the group/person/page's graph values first, parse them and then make a new url with the Facebook-ID. The code works (don't know how well) and looks like this:
// Look at graph page instead of www page
NSString *dataURL = [url stringByReplacingOccurrencesOfString:#"www" withString:#"graph"];
// Load data and parse using JSON parser
NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:dataURL]];
NSError *error;
NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
if (!error) { NSLog(#"ERROR: %#", error); return; }
// If no error occurred, make new url and replace the username by the facebook-ID
url = [url stringByReplacingOccurrencesOfString:[dictionary objectForKey:#"username"] withString:[dictionary objectForKey:#"id"]];
// And voilà, it works :]
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]];
Thank you google and time...
NSURL *url = [NSURL URLWithString:#"fb://profile/<id>"];
[[UIApplication sharedApplication] openURL:url];
Schemes
fb://profile – Open Facebook app to the user’s profile
fb://friends – Open Facebook app to the friends list
fb://notifications – Open Facebook app to the notifications list (NOTE: there appears to be a bug with this URL. The Notifications page opens. However, it’s not possible to navigate to anywhere else in the Facebook app)
fb://feed – Open Facebook app to the News Feed
fb://events – Open Facebook app to the Events page
fb://requests – Open Facebook app to the Requests list
fb://notes – Open Facebook app to the Notes page
fb://albums – Open Facebook app to Photo Albums list

Post photo to Instagram using their iOS hooks

I use the following code in my iOS app to use Instagram iPhone hooks to post a photo to Instagram. I only want the "Open In..." menu to have Instagram app, no other apps. But in my case Camera+ also shows up. How can I restrict to Instagram?
Also, can I directly open Instagram instead of showing Open In menu?
NSURL *instagramURL = [NSURL URLWithString:#"instagram://app"];
if ([[UIApplication sharedApplication] canOpenURL:instagramURL]) {
//imageToUpload is a file path with .ig file extension
self.documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:imageToUpload]];
self.documentInteractionController.UTI = #"com.instagram.photo";
self.documentInteractionController.annotation = [NSDictionary dictionaryWithObject:#"my caption" forKey:#"InstagramCaption"];
[self.documentInteractionController presentOpenInMenuFromBarButtonItem:self.exportBarButtonItem animated:YES];
}
BTW Instagram added an exclusive file extention (ig) and UTI (com.instagram.exclusivegram) for this. It still opens the Open with... menu but the only option is Instagram.
More info here: https://instagram.com/developer/mobile-sharing/iphone-hooks/
You can get the solution from this link.
Save image with the .igo extension instead of .ig. This is the "exclusive" version of the filetype.
Create a UIDocumentInteractionController, then assign the value com.instagram.exclusivegram to the property UTI.
Present your UIDocumentInteractionController with presentOpenInMenuFromRect:inView:animated.
This worked for me, do it like this and you will have only Instagram as the exclusive app to open your image.
NSString *documentDirectory = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents"];
// *.igo is exclusive to instagram
NSString *saveImagePath = [documentDirectory stringByAppendingPathComponent:#"Image.igo"];
NSData *imageData = UIImagePNGRepresentation(filteredImage);
[imageData writeToFile:saveImagePath atomically:YES];
NSURL *imageURL=[NSURL fileURLWithPath:saveImagePath];
_docController=[[UIDocumentInteractionController alloc]init];
_docController.delegate=self;
_docController.UTI=#"com.instagram.photo";
[_docController setURL:imageURL];
_docController.annotation=[NSDictionary dictionaryWithObjectsAndKeys:#"#yourHashTagGoesHere",#"InstagramCaption", nil];
[_docController presentOpenInMenuFromRect:CGRectZero inView:self.view animated:YES];
To answer only your first question: you may probably be able to restrict the "Open in ..." menu to just showing Instagram for your device (by deleting the Camera+ App, for example), but you won't be able to restrict users that install your app to their devices. And that's because the iPhone recognizes which applications are able to open a specific kind of files and it automatically show every one that does.
self.documentInteractionController = [self setupControllerWithURL:imgurl usingDelegate:self];
self.documentInteractionController=[UIDocumentInteractionController interactionControllerWithURL:imgurl];
self.documentInteractionController.UTI = #"com.instagram.exclusivegram";
use this code in same sequence .
here documentInteractionController is object of UIDocumentInteractionController.just for your knowledge.
you will get instagram only in "open in" window.

UIWebView, enter a password

I want to enter a password in a webpage I load in an UIWebView. The password is a string, and I want to enter the password and hit a button in the page, automatically when the View loads.
Any help?
If you're using a UIWebView, I assume you're loading some web service into that WebView. Why not build your login system into the web service? You could even just use HTTP Basic Auth.
If you're managing the passwords within your cocoa app, then I would probably implement a UIAlertView with a UITextField asking for the password. When the user presses Login you can validate the password and then load your web service in your UIWebView.
If you're managing the passwords in a database on the website then you'd build an html/js/php/mysql login form and load it in the UIWebView first.
Update
I'd go with the first option and display your own login form using cocoa controls like the UIAlertView with 2 UITextFields. You can then save the username and password using the keychain services and submit them every time the users launches the app or the web session expires.
If you want to try this method I'd suggest having a look at the ASIHTTPRequest framework. In particular ASIFormDataRequest which let's you easily send a form with the correct fields.
- (void)login
{
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
// Change these values to the ones in NSUserDefaults and the KeyChain
[request setPostValue:#"Joe" forKey:#"username"];
[request setPostValue:#"Secret" forKey:#"password"];
[request setDelegate:self];
[request startAsynchronous];
}
- (void)requestFinished:(ASIHTTPRequest *)request
{
// Use when fetching text data
NSString *responseString = [request responseString];
// Use when fetching binary data.
NSData *responseData = [request responseData];
}
- (void)requestFailed:(ASIHTTPRequest *)request
{
NSError *error = [request error];
}
Just an idea : you could try to add some javascript to the page before displaying it in the webview and use the ONLOAD event to fill the password field and post the form.
To do so you have 2 solutions :
Use the stringByEvaluatingJavaScriptFromString instance method of UIWebView , you could evaluate your javascript in the webViewDidFinishLoad delegate method for example. You will find an example here : http://iphoneincubator.com/blog/windows-views/how-to-inject-javascript-functions-into-a-uiwebview
Or if for some reason you can't use this solution in your context you can always download the HTML using NSURLConnection, and then edit the string to insert your javascript. Then load the WebView using loadHTMLString:baseURL:

Google App Engine with ClientLogin Interface for Objective-C

I'm experiencing the same problem in this previous stackoverflow.com post.
Specifically, I seem to be able to get the "Auth" token correctly, but attempts to use it in the header when I access later pages still just return me the login page's HTML.
Following links related to this post, I've determined that you need to make a subsequent call to this URL.
A call to the URL will then give you an ACSID cookie which then needs to be passed in subsequent calls in order to maintain an authenticated state.
When requesting this cookie, I've read various posts saying you need to specify your original auth token by appending it to the query string such that:
?auth=this_is_my_token
I've also read that you should set it in the http header as described in google's documentation such that a http header name/value is:
Authorization: GoogleLogin auth=yourAuthToken
I've tried both approaches and am not seeing any cookies returned. I've used Wireshark, LiveHttpHeaders for Firefox, and simple NSLog statements trying to see if anything like this is returned.
Below is the code snippet I've been using.
NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:#"http://yourapp.appspot.com/_ah/login?auth=%#", [token objectForKey:#"Auth"]]];
NSHTTPURLResponse* response;
NSError* error;
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setValue:[NSString stringWithFormat:#"GoogleLogin auth=%#", [token objectForKey:#"Auth"]] forHTTPHeaderField:#"Authorization"];
NSData * data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
//show me all header fields
NSLog([[response allHeaderFields] description]);
//show me the response
NSLog(#"%#", [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease]);
NSArray * all = [NSHTTPCookie cookiesWithResponseHeaderFields:[response allHeaderFields] forURL:[NSURL URLWithString:#"http://yourapp.appspot.com/_ah/login"]];
//show me all cookies
for (NSHTTPCookie *cookie in all)
{
NSLog(#"Name: %# : Value: %#", cookie.name, cookie.value);
}
I hope you can use ClientLogin for Google App Engine code.
Adding sample code to this question because someone contacted me directly about my solution. Note that you must set the "service" parameter equal to "ah" on the initial token request.
Initial Request of Token [done synchronously] NOTE: the "service" parameter is set to "ah" and the "source" is just set to "myapp", you should use your app name.
//create request
NSString* content = [NSString stringWithFormat:#"accountType=HOSTED_OR_GOOGLE&Email=%#&Passwd=%#&service=ah&source=myapp", [loginView username].text, [loginView password].text];
NSURL* authUrl = [NSURL URLWithString:#"https://www.google.com/accounts/ClientLogin"];
NSMutableURLRequest* authRequest = [[NSMutableURLRequest alloc] initWithURL:authUrl];
[authRequest setHTTPMethod:#"POST"];
[authRequest setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-type"];
[authRequest setHTTPBody:[content dataUsingEncoding:NSASCIIStringEncoding]];
NSHTTPURLResponse* authResponse;
NSError* authError;
NSData * authData = [NSURLConnection sendSynchronousRequest:authRequest returningResponse:&authResponse error:&authError];
NSString *authResponseBody = [[NSString alloc] initWithData:authData encoding:NSASCIIStringEncoding];
//loop through response body which is key=value pairs, seperated by \n. The code below is not optimal and certainly error prone.
NSArray *lines = [authResponseBody componentsSeparatedByString:#"\n"];
NSMutableDictionary* token = [NSMutableDictionary dictionary];
for (NSString* s in lines) {
NSArray* kvpair = [s componentsSeparatedByString:#"="];
if ([kvpair count]>1)
[token setObject:[kvpair objectAtIndex:1] forKey:[kvpair objectAtIndex:0]];
}
//if google returned an error in the body [google returns Error=Bad Authentication in the body. which is weird, not sure if they use status codes]
if ([token objectForKey:#"Error"]) {
//handle error
};
The next step is to get your app running on google app engine to give you the ASCID cookie. I'm not sure why there is this extra step, it seems to be an issue on google's end and probably why GAE is not currently in their listed obj-c google data api library. My tests show I have to request the cookie in order sync with GAE. Also, notice I don't do anything with the cookie. It seems just by requesting it and getting cookied, future requests will automatically contain the cookie. I'm not sure if this is an iphone thing bc my app is an iphone app but I don't fully understand what is happening with this cookie. NOTE: the use of "myapp.appspot.com".
NSURL* cookieUrl = [NSURL URLWithString:[NSString stringWithFormat:#"http://myapp.appspot.com/_ah/login?continue=http://myapp.appspot.com/&auth=%#", [token objectForKey:#"Auth"]]];
NSLog([cookieUrl description]);
NSHTTPURLResponse* cookieResponse;
NSError* cookieError;
NSMutableURLRequest *cookieRequest = [[NSMutableURLRequest alloc] initWithURL:cookieUrl];
[cookieRequest setHTTPMethod:#"GET"];
NSData* cookieData = [NSURLConnection sendSynchronousRequest:cookieRequest returningResponse:&cookieResponse error:&cookieError];
Finally, I can post json to my gae app. NOTE: the snippet below is an async request. We can handle responses by implementing didReceiveResponse, didReceiveData, didFailWIthError.
NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:#"http://myapp.appspot.com/addRun?auth=%#", mytoken]];
NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:#"my http body";
NSURLConnection *connectionResponse = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (!connectionResponse) {
NSLog(#"Failed to submit request");
} else {
NSLog(#"Request submitted");
}
Check out the code that does this in the official SDK. The latest SDK release even has it split into its own file.
1st - thanks for the great post it really got me started.
2nd - I have been slugging it out with my app, trying to POST to the GAE while authenticated.
This is the request is built when POSTing, once you have acquired the authtoken:
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
[request setURL:[NSURL URLWithString:url]];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"image/png" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
[request setValue:authtoken forHTTPHeaderField:#"auth"]; // <-- the magic
mattb
I created a few obj-c classes for implementing ClientLogin, including support for Google App Engine:
http://github.com/cameronr/GoogleAppEngineAuth
Note that Google has recently changed the way authorization failure is indicated. They used to place an Error token in the response. Now they just return a 403 (Forbidden) status. This broke my code!
Thank for this post and especially the answer from Keith but it does not works for me.
Even if it seems ok for me ... very strange.
I check this post (How do you access an authenticated Google App Engine service from a (non-web) python client?) which talk about doing the same thing in python. I test it and it works.
And the objective C code proposed by Keith is really similar to the python code.
But when I try to get the "Auth" token authData contains Error=BadAuthentication.
Some one got an idea about possibles problems ?
Using HOSTED_OR_GOOGLE is wrong, and I will explain why.
There are two kinds of accounts in the Google world. The ones you create for GMail, etc are "Google" accounts. The ones you create for Apps for Domains are "Hosted" accounts. You can use a Hosted Account email to make a Google Account, thus creating an email address that is associated with both kinds of accounts.
Your Google App Engine app can be configured to work with (1) Google Accounts or (2) Hosted Accounts for a particular domain.
Assume that we are developing an app for Google Accounts. A user enters in an email address that is associated with a Google Account and a Hosted Account. Google will use their Google Account for the login. This all works fine.
Now, if we use ClientLogin with this same email address and use HOSTED_OR_GOOGLE for the account type, login will be successful, but it will use the Hosted Account, since the Hosted Account takes precedence. As I mentioned above, you cannot use a Hosted Account for an app that expects a Google Account. So the authentication will not work.
So, when using ClientLogin to authenticate with a Google App Engine app, you need to use GOOGLE for the account type if the app is for Google Accounts, or HOSTED for the account type if the app is for a domain.