Wednesday, May 11, 2016

Being stupid while calling Twitter's Search API

A few days ago, I just figured out how to authenticate my Ionic app using Twitter's application-only Oauth. Application only authentication allows you call Twitter APIs without that Twitter login screen.

The being stupid part started when I tried using the Search API.

    var twitterStreamURL = ""; 
    var qValue = "queryString";
    var numberOfTweets = "&count=10";

It should be easy to see that to call the Search API you'll concatinate the qValue (or query string) and the numberOfTweets (number of tweets to get) to the twitterStreamURL. You'd do then a ajax call then get the resulting JSON.
    var cURL = "" + qvalue + numberOfTweets; 

These would be no problem with the query string had only one value or no special characters. This stumped me a bit because I used escape() function at first which I knew about. It still works but the escape() function is deprecated. Which lead me to encodeURI(). It worked until it was asked to search for strings with hash tags. I didn't read the fine print for encodeURI which said it doesn't encode certain special characters. This finally lead me to the encodeURIComponent() function.
    var cURL = "" + encodeURIComponent(qvalue) + numberOfTweets; 

The moral of this story is I need read the fine print.

Friday, April 8, 2016

Ionic, Satellizer, Facebook and that "Given URL is not allowed" error

You can have your Ionic mobile application use Facebook authentication. You can do it the hard way - i.e. do it yourself via $http calls - or go the easy route via Satellizer. Being the lazy bastard that I am, I'll be using Satellizer.

Satellizer can be setup quickly, do bower install, add the needed JavaScript bits to your index.html and reference it in you Ionic app.

angular.module('meAwesomeIonicApp', ['ionic', 'ngCordova', 'satellizer', 'ngAnimate']).config(...)

From here you'll need to go to Facebook Developer and register your app. You'll then add the FB application appId to your satellizer settings. It should look something like:

    var commonConfig = {
        popupOptions: {
            location: 'no',
            toolbar: 'yes',
            width: window.screen.width,
            height: window.screen.height

    if (ionic.Platform.isIOS() || ionic.Platform.isAndroid()) {
        commonConfig.redirectUri = 'http://localhost/';
        $authProvider.platform = 'mobile'

    $authProvider.facebook(angular.extend({}, commonConfig, {
        clientId: 'YOUR FB APP ID HERE',
        url: 'http://localhost:3000/auth/facebook',
        responseType: 'token'

This moves us to the controllers. In the controllers we have access to a $auth service which is provided by Satellizer. The $auth service then provides a authenticate(string) function. So we have:
        .then(function() {n
             // Success login
        .catch(function(response) {
             // Error in login

You can easily add this to a ng-click handler. And this is where we encounter the "Given URL is not allowed" error. What's happening is that when we call $auth.authenticate(), it will try to open a FB login page based on the url value we configured in the $authProvider.facebook() call instead we get the error page instead of the login form.

Fortunately, for me the fix was easy. I just didn't configure the settings in the FB developer app page correctly. It isn't enough to just configure the Basic Section in the Settings page. You need to open the Advance Section and also configure the Valid OAuth redirect URIs values also. So if you add the http://localhost value in the textfield, it should fix the "Given URL is not allowed" error.

Wednesday, February 17, 2016

Tapestry5 and the case of the disappearing bootstrap modal dialog

Tapestry5 has a pretty neat way to support JavaScript but once in a while I forget some things. Forgetting things then leads you to errors. In my case, the error was a bootstrap modal not properly displaying or disappearing immediately.

I was working on a simple but reusable modal Tapestry5 component. For some context on how to do this, go here. The important part here is the top part where you declare your JavaScript includes:

@Import(module = {"bootstrap/modal"}) 
public class ModalDialog implements ClientElement{...}

This loads the modal.js from bootstrap. Tapestry5 can do this because the framework is bootstrap aware. We then finish this up with the javascript and tml file for the component.

Thinking all was Ok, I then use this component on a page and this is where I encountered the "disappearing modal" dialog.

After debugging, I followed the bug to my layout component. Again, the important part here is where we declare the JavaScript includes:

@Import(stylesheet = {"context:datatables/css/dataTables.jqueryui.css","context:patternfly/css/patternfly.css", 
        library = {"context:patternfly/js/patternfly.js", "context:mybootstrap/js/bootstrap.min.js"})
public class Layout{...}

Apparently, bootstrap doesn't like when we double load modules. The bootstrap.min.js is a minified bootstrap and it already has the modal.js module. Then, when we load the page where we use the ModalDialog component which double loads the modal.js module.

This bug was easily fixed by removing the import annotation in my ModalDialog component.

I forgot about my JavaScript basics that invoking the script twice equals problems.

Also, this was confirmed by this StackOverflow question.