Working with Ionic and PHP Backends. Remote debugging with PHP7 and Xdebug working with real devices

Sometimes I speak with PHP developers and they don’t use remote debugging in their development environments. Some people don’t like to use remote debugging. They prefer to use TDD and rely on the unit tests. That’s a good point of view, but sometimes they don’t use remote debugging only because they don’t know how to do it, and that’s inadmissible. Remote debugger is a powerful tool especially to handle with legacy applications. I’ve using xdebug for years with my linux workstation for years. This days I’m using Mac and it’s also very simple to set up xdebug here.

First we need to install PHP:

brew install php70

Then Xdebug

brew install php70-xdebug

(in a Ubuntu box we only need to use apt-get instead of brew)

Now we need to setup xdebug to enable remote debugging:
In a standard installation xdebug configuration is located at: /usr/local/etc/php/7.0/conf.d/ext-xdebug.ini

[xdebug]
zend_extension="/usr/local/opt/php70-xdebug/xdebug.so"

xdebug.remote_enable=1
xdebug.remote_port=9000
xdebug.profiler_enable=0
xdebug.profiler_output_dir="/tmp"
xdebug.idekey= "PHPSTORM"
xdebug.remote_connect_back = 1
xdebug.max_nesting_level = 250

And basically that’s all. To set/unset the cookie you can use one bookmarklet in your browser (you can generate your bookmarklets here). Or use a Chrome extension to enable xdebug.

Now se only need to start the built-in server with

php -S 0.0.0.0:8080

And remote debugging will be available
Remote debugger works this way:

  • We open on port within our IDE. In my case PHPStorm (it happens when we click on “Start listening for PHP debug connections”)
  • We set one cookie in our browser (it happens when click on Chrome extension)
  • When our server receives one request with the cookie, it connects to the port that our IDE opens (usually port 9000). If you use a personal firewall in your workstation, ensure that you allow incoming connections to this port.

Nowadays I’m involved with several projects building hybrid applications with Apache Cordova. In the Frontend I’m using ionic and Silex in the Backend. When I’m working with hybrid applications normally I go through two phases.

In the first one I build a working prototype. To to this I run a local server and I use my browser to develop the application. This phase is very similar than a traditional Web development process. If we also set up properly LiveReload, our application will be reloaded each time we change one javaScript file. Ionic framework integrates LiveReload and we only need to run:

ionic serve -l

to start our application. We also need to start our backend server. For example

php -S 0.0.0.0:8080 -t api/www

Now we can debug our Backend with remote debugger and Frontend with Chrome’s developer’s tools. Chrome also allows us to edit Frontend files and save them within the filesystem using workspaces. This phase is the easy one. But sooner or later we’ll need start working with a real device. We need a real device basically if we use plugins such as Camera plugin, Geolocation plugin, or things like that. OK there are emulators, but usually emulators don’t allow to use all plugins in the same way than we use then with a real device. Chrome also allow us to see the console logs of the device from our workstation. OK we can see all logs of our plugged Android device using “adb logcat” but follow the flow of our logs with logcat is similar than understand Matrix code. It’s a mess.

If we plug our android device to our computer and we open with Chrome:

chrome://inspect/#devices

We can see our device’s console, use breakpoints and things like that. Cool, isn’t it? Of course it only works if we compile our application without “–release” option. We can do something similar with Safary and iOS devices.

With ionic if we want to use LiveReload from the real device and not to recompile and re-install again and again our application each time we change our javaScript files, we can run the application using

ionic run android --device -l

When we’re developing our application and we’re in this phase we also need to handle with CORS. CORS isn’t a problem when we run our hybrid application in production. When we run the hybrid application with our device our “origin” is the local filesystem. That’s means CORS don’t apply, but when we run our application in the device, but served from our computer (when we use “-l” option), our origin isn’t local filesystem. So if our Backend is served from another origin we need to enable CORS.

We can enable CORS in the backend. I’ve written about it here, but ionic people allows us a easier way. We can set up a local proxy to serve our backend through the same origin than the application does and forget about CORS. Here we can read a good article about it.

Anyway if we want to start the remote debugger we need to create one cookie called XDEBUG_SESSION. In the browser we can use chrome extension, but when we inspect the plugged device isn’t so simple. It would be cool that ionic people allows us to inject cookies to our proxy server. I’ve try to see how to do it with ionic-cli. Maybe is possible but I didn’t realize how to do it. Because of that I’ve created a simple AngularJS service to inject this cookie. Then, if I start listening debug connections in my IDE I’ll be able to use remote debugger as well as I do when I work with the browser.

First we need to install service via Bower:

bower install ng-xdebugger --save

Now we need to include javaScript files

<script src="lib/angular-cookies/angular-cookies.min.js"></script>
<script src="lib/ng-xdebugger/dist/gonzalo123.xdebugger.min.js"></script>

then we add our service to the project.

angular.module("starter", ["ionic", "gonzalo123.xdebugger"])

Now we only need to configure our application and set de debugger key (it must be the same key than we use within the server-side configuration of xdebug)

.config(function (xdebuggerProvider) {
        xdebuggerProvider.setKey('PHPSTORM');
    })
})

And that’s all. The service is very simple. It only uses one http interceptor to inject the cookie in our http requests:

(function () {
    "use strict";

    angular.module("gonzalo123.xdebugger", ["ngCookies"])
        .provider("xdebugger", ['$httpProvider', function ($httpProvider) {
            var debugKey;

            this.$get = function () {
                return {
                    getDebugKey: function () {
                        return debugKey;
                    }
                };
            };

            this.setKey = function (string) {
                if (string) {
                    debugKey = string;
                    $httpProvider.interceptors.push("xdebuggerCookieInterceptor");
                }
            };
        }])

        .factory("xdebuggerCookieInterceptor", ['$cookieStore', 'xdebugger', function ($cookieStore, xdebugger) {
            return {
                response: function (response) {
                    $cookieStore.put("XDEBUG_SESSION", xdebugger.getDebugKey());

                    return response;
                }
            };
        }])
    ;
})();

And of course you can see the whole project in my github account.

Advertisement

i18n AngularJS provider

There’s more than one way to perform i18n translations within our AngularJS projects. IMHO the best one is https://angular-translate.github.io/, but today I’m going to show you how I’m doing translations in my small AngularJS projects (normally Ionic projects).

I’ve packaged my custom solution and I also create one bower package ready to use via bower command line:

bower install ng-i8n --save

First we add our provider

<script src='lib/ng-i8n/dist/i8n.min.js'></script>

And now we add our new module (‘gonzalo123.i18n’) to our AngularJS project

angular.module('G', ['ionic', 'ngCordova', 'gonzalo123.i18n'])

Now we’re ready to initialise our provider with the default language and translation data

    .config(function (i18nProvider, Conf) {
        i18nProvider.init(Conf.defaultLang, Conf.lang);
    })

I like to use constants to store default lang and translation table, but it isn’t necessary. We can just pass the default language and Lang object to our provider

    .constant('Conf', {
        defaultLang: 'es',
        lang: {
            HI: {
                en: 'Hello',
                es: 'Hola'
            }
        }
    })

And that’s all. We can translate key in templates (the project also provides a filter):

<h1 class="title">{{ 'HI' | i18n }}</h1>

And also inside our controllers

    .controller('HomeController', function ($scope, i18n) {
        $scope.hi = i18n.traslate('HI');
    })

If we need to change user language, we only need to trigger ‘use’ function:

    .controller('HomeController', function ($scope, i18n) {
        $scope.changeLang = function(lang) {
            i18n.use(lang);
        };
    })

Here we can see the code of our provider:

(function () {
    "use strict";

    angular.module('gonzalo123.i8n', [])
        .provider('i18n', function () {
            var myLang = {},
                userLang = 'en',
                translate;

            translate = function (key) {
                if (myLang.hasOwnProperty(key)) {
                    return myLang[key][userLang] || key;
                } else {
                    return key;
                }
            };

            this.$get = function () {
                return {
                    use: this.use,
                    translate: translate
                };
            };

            this.use = function (lang) {
                userLang = lang;
            };

            this.init = function (lang, conf) {
                userLang = lang;
                myLang = conf;
            };
        })

        .filter('i18n', ['i18n', function (i18n) {
            var i18nFilter = function (key) {
                return i18n.translate(key);
            };

            i8nFilter.$stateful = true;

            return i18nFilter;
        }])
    ;
})();

Anyway the project is in my github account

Handling private states within AngularJS applications

One typical task when we work with AngularJs application is login, and private states. We can create different states in our application. Something like this:

    .config(function ($stateProvider, $urlRouterProvider) {
        $stateProvider
            .state('state1', {
                url: '/state1',
                templateUrl: templates/state1.html,
                controller: 'State1Controller'
            })
            .state('state2', {
                url: '/state2',
                templateUrl: templates/state2.html,
                controller: 'State2Controller'
            })
        $urlRouterProvider.otherwise('/state1');
    })

One way to create private states is using $stateChangeStart event. We can mark our private states with state parameters:

    .state('privateState1', {
            url: '/privateState1',
            templateUrl: templates/privateState1.html,
            controller: 'PrivateState1Controller',
            data: {
                isPublic: false
            }
        })

And then we can check out this parameters within $stateChangeStart event, doing one thing or another depending on token is present or not

    .run(function ($rootScope) {
        $rootScope.$on("$stateChangeStart", function (event, toState) {
            if (toState.data && toState.data.isPublic) {
                // do something here with localstorage and auth token
            }
        });
    })

This method works, but last days, reading one project of Aaron K Saunders at github, I just realised that there’s another method. We can listen to $stateChangeError. Let me show you how can we do it.

The idea is to use resolve in our private states. With resolve we can inject objects to our state’s controllers, for example user information. This method is triggered before call to the controller, so that’s a good place to check if token is present. If it isn’t, then we can raise an error. This error will trigger $stateChangeError event, and here we can redirect the user to login state.

It sounds good, but we need to write resolve parameter in every private states, and that’s bored. Especially when all states are private except login state. To by-pass this problem we can use abstract states. The idea is simple, we define one abstract state with “resolve” and then we create our private states under this abstract state.

Here we can see one example: login state isn’t private, but state1 and state2 are private, indeed.

    .config(function ($stateProvider, $urlRouterProvider) {
        .state('login', {
            url: '/login',
            templateUrl: 'templates/login.html',
            controller: 'LoginController'
        })
        .state('private', {
            url: "/private",
            abstract: true,
            template: '<ui-view/>',
            resolve: {
                user: function (UserService) {
                    return UserService.init();
                }
            }
        })
        .state('private.state1', {
            url: '/state1',
            templateUrl: 'templates/state1.html',
            controller: 'State1Controller'
        })
    
        .state('private.state2', {
            url: '/privateState2',
            templateUrl: 'templates/state2.html',
            controller: 'State2Controller'
        });
    
        $urlRouterProvider.otherwise('/private/privateState1');
    })

Our UserService is a AngularJS service. This service provides three methods: init (the method that raises an error if token isn’t present), login (to perform login and validate credentials), and logout (to remove token from localstorage and redirects to login state)

    .service('UserService', function ($q, $state) {
        var user = undefined;

        var UserService = {
            init: function () {
                var deferred = $q.defer();

                // do something here to get user from localstorage

                setTimeout(function () {
                    if (user) {
                        deferred.resolve(user);
                    } else {
                        deferred.reject({error: "noUser"});
                    }
                }, 100);

                return deferred.promise;
            },

            login: function (userName, password) {
                // validate user and password here
            },

            logout: function () {
                // remove token from localstorage
                user = undefined;
                $state.go('login', {});
            }
        };

        return UserService
    })

And finally the magic in $stateChangeError

    .run(function ($rootScope, $state) {
        $rootScope.$on('$stateChangeError',
            function (event, toState, toParams, fromState, fromParams, error) {
                if (error && error.error === "noUser") {
                    $state.go('login', {});
                }
            });
    })

And that’s all. IMHO this solution is cleaner than $stateChangeStart method. What do you think?

WARNING!
Before publishing this post I realize that this technique doesn’t work 100% correctly. Maybe is my implementation but I tried to use it with an ionic application and it doesn’t work with android. Something kinda weird. It works with web applications, it works with IOS, but it doesn’t work with Android. It looks like a bug (not sure about it). Blank screen instead of showing the template (but controller is loaded). We can see this anomalous situation using “ionic serve -l” (IOS ok and Android Not Ok)

To bypass this problem I tried a workaround. instead of using abstract states I create normal states, but to avoid to write again and again the resolve function to mark private states, I create a privateState provider

    .provider('privateState', function () {
        this.$get = function () {
            return {};
        };
    
        this.get = function(obj) {
            return angular.extend({
                resolve: {
                    user: function (UserService) {
                        return UserService.init();
                    }
                }
            }, obj);
        }
    })

Now I can easily create private states without writing ‘resolve’ function.

    .config(function ($stateProvider, $urlRouterProvider, privateStateProvider) {
        $urlRouterProvider.otherwise('/home');

        $stateProvider
            .state('home', privateStateProvider.get({
                url: '/home',
                templateUrl: 'templates/home.html',
                controller: 'HomeController'
            }))
        ;
    })

Building a AngularJS provider for hello.js library

This days I’ve been playing with hello.js. Hello is a A client-side Javascript SDK for authenticating with OAuth2 web services. It’s pretty straightforward to use and well explained at documentation. I want to use it within AngularJS projects. OK, I can include the library and use the global variable “hello”, but it isn’t cool. I want to create a reusable module and available with Bower. Let’s start.

Imagine one simple AngularJS application

(function () {
    angular.module('G', [])
        .config(function ($stateProvider, $urlRouterProvider) {
            $urlRouterProvider.otherwise("/");
            $stateProvider
                .state('login', {
                    url: "/",
                    templateUrl: "partials/home.html",
                    controller: "LoginController"
                })
                .state('home', {
                    url: "/login",
                    template: "partials/home.html"
                });
        })

        .controller('LoginController', function ($scope) {
            $scope.login = function () {
            };
        })
})();

Now we can include our references within our bower.json file

"dependencies": {
    "hello": "~1.4.1",
    "ng-hello": "*"
  }

and append those references to our index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
    <title>G</title>

    <script type="text/javascript" src="assets/hello/dist/hello.all.js"></script>
    <script type="text/javascript" src="assets/ng-hello/dist/ng-hello.js"></script>
    <script src="js/app.js"></script>
</head>
<body ng-app="G">
<div ui-view></div>

</body>
</html>

Our ng-hello is just a service provider that wraps hello.js

(function (hello) {
    angular.module('ngHello', [])
        .provider('hello', function () {
            this.$get = function () {
                return hello;
            };

            this.init = function (services, options) {
                hello.init(services, options);
            };
        });
})(hello);

That’s means that we configure the service in config callback and in our run callback we can set up events

(function () {
    angular.module('G', ['ngHello'])
        .config(function ($stateProvider, $urlRouterProvider, helloProvider) {
            helloProvider.init({
                twitter: 'myTwitterToken'
            });

            $urlRouterProvider.otherwise("/");
            $stateProvider
                .state('login', {
                    url: "/",
                    templateUrl: "partials/home.html",
                    controller: "LoginController"
                })
                .state('home', {
                    url: "/login",
                    template: "partials/home.html"
                });
        })

        .run(function ($ionicPlatform, $log, hello) {
            hello.on("auth.login", function (r) {
                $log.log(r.authResponse);
            });
        });
})();

And finally we can perform a twitter login within our controller

(function () {
    angular.module('G')
        .controller('LoginController', function ($scope, hello) {
            $scope.login = function () {
                hello('twitter').login();
            };
        })
    ;
})();

And that’s all. You can see the whole library in my github account here

Using OpenUI5 table and Angularjs

Last days I’ve been playing with OpenUI5. OpenUI5 is a web toolkit that SAP people has released as an open source project. I’ve read several good reviews about this framework, and because of that I started to hack a little bit with it. OpenUI5 came with a very complete set of controls. In this small example I want to use the “table” control. It’s just a datagrid. This days I playing a lot with Angular.js so I wanted to use together OpenUI5’s table control and Angularjs.

I’m not quite sure if it’s a good decision to use together both frameworks. In fact we don’t need Angular.js to create web applications using OpenUI5. OpenUI5 uses internally jQuery, but I wanted to hack a little bit and create one Angularjs directive to enclose one OpenUI5 datagrid.

First of all, we create one index.html. It’s just a boilerplate with angular + ui-router + ui-bootstrap. We also start our OpenUi5 environment with de default theme and including the table library

<!doctype html>
<html ng-app="G">
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <link rel="stylesheet" href="assets/bootstrap/dist/css/bootstrap.min.css">

    <script src="assets/angular/angular.js"></script>
    <script src="assets/angular-ui-router/release/angular-ui-router.js"></script>
    <script src="assets/angular-bootstrap/ui-bootstrap-tpls.js"></script>

    <script id='sap-ui-bootstrap' type='text/javascript'
            src="https://openui5.hana.ondemand.com/resources/sap-ui-core.js"
            data-sap-ui-theme='sap_bluecrystal'
            data-sap-ui-libs='sap.ui.commons, sap.ui.table'></script>

    <script src="js/ngOpenUI5.js"></script>

    <script src="js/app.js"></script>
    <link rel="stylesheet" href="css/app.css">
</head>
<body class="ng-cloak">

<div class="container">

    <div class="starter-template">
        <div ui-view></div>
    </div>
</div>

<script src="assets/html5shiv/dist/html5shiv.js"></script>
<script src="assets/respond/dest/respond.src.js"></script>

</body>
</html>

Then we create a directive enclosing the OpenUI5 needed code within a Angular module

(function () {
    'use strict';

    angular.module('ng.openui5', [])
        .directive('openui5Table', function () {

            function renderColumns(columns, oTable) {
                for (var i = 0; i <= columns.length; i++) {
                    oTable.addColumn(new sap.ui.table.Column(columns[i]));
                }
            }

            var link = function (scope, element) {

                var oData = scope.model.data,
                    oTable = new sap.ui.table.Table(scope.model.conf),
                    oModel = new sap.ui.model.json.JSONModel();

                oModel.setData({modelData: oData});
                renderColumns(scope.model.columns, oTable);

                oTable.setModel(oModel);
                oTable.bindRows("/modelData");
                oTable.sort(oTable.getColumns()[0]);

                oTable.placeAt(element);

                scope.$watch('model.data', function (data) {
                    if (data) {
                        oModel.setData({modelData: data});
                        oModel.refresh();
                    }
                }, true);

            };

            return {
                restrict: 'E',
                scope: {model: '=ngModel'},
                link: link
            };
        });
}());

And now we can create a simple Angular.js using the ng.openui5 module. In this application we configure the table and fetch the data from an externar API server

(function () {
    'use strict';

    angular.module('G', ['ui.bootstrap', 'ui.router', 'ng.openui5'])

        .value('config', {
            apiUrl: '/api'
        })

        .config(function ($stateProvider, $urlRouterProvider) {
            $urlRouterProvider.otherwise("/");
            $stateProvider
                .state('home', {
                    url: "/",
                    templateUrl: "partials/home.html",
                    controller: 'HomeController'
                });
        })

        .controller('HomeController', function ($scope, $http, $log, config) {
            $scope.refresh = function () {
                $http.get(config.apiUrl + '/gridData').success(function (data) {
                    $scope.datagrid.data = data;
                });
            };

            $scope.datagrid = {
                conf: {
                    title: "Table example",
                    navigationMode: sap.ui.table.NavigationMode.Paginator
                },
                columns: [
                    {
                        label: new sap.ui.commons.Label({text: "Last Name"}),
                        template: new sap.ui.commons.TextView().bindProperty("text", "lastName"),
                        sortProperty: "lastName",
                        filterProperty: "lastName",
                        width: "200px"
                    }, {
                        label: new sap.ui.commons.Label({text: "First Name"}),
                        template: new sap.ui.commons.TextField().bindProperty("value", "name"),
                        sortProperty: "name",
                        filterProperty: "name",
                        width: "100px"
                    }, {
                        label: new sap.ui.commons.Label({text: "Checked"}),
                        template: new sap.ui.commons.CheckBox().bindProperty("checked", "checked"),
                        sortProperty: "checked",
                        filterProperty: "checked",
                        width: "75px",
                        hAlign: "Center"
                    }, {
                        label: new sap.ui.commons.Label({text: "Web Site"}),
                        template: new sap.ui.commons.Link().bindProperty("text", "linkText").bindProperty("href", "href"),
                        sortProperty: "linkText",
                        filterProperty: "linkText"
                    }, {
                        label: new sap.ui.commons.Label({text: "Image"}),
                        template: new sap.ui.commons.Image().bindProperty("src", "src"),
                        width: "75px",
                        hAlign: "Center"
                    }, {
                        label: new sap.ui.commons.Label({text: "Gender"}),
                        template: new sap.ui.commons.ComboBox({
                            items: [
                                new sap.ui.core.ListItem({text: "female"}),
                                new sap.ui.core.ListItem({text: "male"})
                            ]
                        }).bindProperty("value", "gender"),
                        sortProperty: "gender",
                        filterProperty: "gender"
                    }, {
                        label: new sap.ui.commons.Label({text: "Rating"}),
                        template: new sap.ui.commons.RatingIndicator().bindProperty("value", "rating"),
                        sortProperty: "rating",
                        filterProperty: "rating"
                    }

                ]
            };
        })
    ;
}());

The API server is a simple Silex server

<?php
include __DIR__ . '/../../vendor/autoload.php';
use Silex\Application;

$app = new Application();
$app->get("/", function (Application $app) {

$app->get('gridData', function (Application $app) {
    return $app->json([
        ['lastName' => uniqid(), 'name' => "Al", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "male", 'rating' => 4, 'src' => "images/person1.gif"],
        ['lastName' => "Friese", 'name' => "Andy", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "male", 'rating' => 2, 'src' => "images/person1.gif"],
        ['lastName' => "Mann", 'name' => "Anita", 'checked' => false, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "female", 'rating' => 3, 'src' => "images/person1.gif"],
        ['lastName' => "Schutt", 'name' => "Doris", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "female", 'rating' => 4, 'src' => "images/person1.gif"],
        ['lastName' => "Open", 'name' => "Doris", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "female", 'rating' => 2, 'src' => "images/person1.gif"],
        ['lastName' => "Dewit", 'name' => "Kenya", 'checked' => false, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "female", 'rating' => 3, 'src' => "images/person1.gif"],
        ['lastName' => "Zar", 'name' => "Lou", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "male", 'rating' => 1, 'src' => "images/person1.gif"],
        ['lastName' => "Burr", 'name' => "Tim", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "male", 'rating' => 2, 'src' => "images/person1.gif"],
        ['lastName' => "Hughes", 'name' => "Tish", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "male", 'rating' => 5, 'src' => "images/person1.gif"],
        ['lastName' => "Lester", 'name' => "Mo", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "male", 'rating' => 3, 'src' => "images/person1.gif"],
        ['lastName' => "Case", 'name' => "Justin", 'checked' => false, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "male", 'rating' => 3, 'src' => "images/person1.gif"],
        ['lastName' => "Time", 'name' => "Justin", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "male", 'rating' => 4, 'src' => "images/person1.gif"],
        ['lastName' => "Barr", 'name' => "Gaye", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "male", 'rating' => 2, 'src' => "images/person1.gif"],
        ['lastName' => "Poole", 'name' => "Gene", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "male", 'rating' => 1, 'src' => "images/person1.gif"],
        ['lastName' => "Ander", 'name' => "Corey", 'checked' => false, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "male", 'rating' => 5, 'src' => "images/person1.gif"],
        ['lastName' => "Early", 'name' => "Brighton", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "male", 'rating' => 3, 'src' => "images/person1.gif"],
        ['lastName' => "Noring", 'name' => "Constance", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "female", 'rating' => 4, 'src' => "images/person1.gif"],
        ['lastName' => "Haas", 'name' => "Jack", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "male", 'rating' => 2, 'src' => "images/person1.gif"],
        ['lastName' => "Tress", 'name' => "Matt", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "male", 'rating' => 4, 'src' => "images/person1.gif"],
        ['lastName' => "Turner", 'name' => "Paige", 'checked' => true, 'linkText' => "www.sap.com", 'href' => "http://www.sap.com", 'gender' => "female", 'rating' => 3, 'src' => "images/person1.gif"]
    ]);
});
$app->run();

And basically that’s all. You can see the whole project within my github account.

working example
working example

Yet Another example of WebSockets, socket.io and AngularJs working with a Silex backend

Remember my last post about WebSockets and AngularJs? Today we’re going to play with something similar. I want to create a key-value interface to play with websockets. Let me explain it a little bit.

First we’re going to see the backend. One Silex application with two routes: a get one and a post one:

<?php

include __DIR__ . '/../../vendor/autoload.php';
include __DIR__ . '/SqlLiteStorage.php';

use Silex\Application;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Silex\Provider\DoctrineServiceProvider;

$app = new Application([
    'debug'      => true,
    'ioServer'   => 'http://localhost:3000',
    'httpServer' => 'http://localhost:3001',
]);

$app->after(function (Request $request, Response $response) {
    $response->headers->set('Access-Control-Allow-Origin', '*');
});

$app->register(new G\Io\EmitterServiceProvider($app['httpServer']));
$app->register(new DoctrineServiceProvider(), [
    'db.options' => [
        'driver' => 'pdo_sqlite',
        'path'   => __DIR__ . '/../../db/app.db.sqlite',
    ],
]);
$app->register(new G\Io\Storage\Provider(new SqlLiteStorage($app['db'])));

$app->get('conf', function (Application $app, Request $request) {
    $chanel = $request->get('token');
    return $app->json([
        'ioServer' => $app['ioServer'],
        'chanel'   => $chanel
    ]);
});

$app->get('/{key}', function (Application $app, $key) {
    return $app->json($app['gdb.get']($key));
});

$app->post('/{key}', function (Application $app, Request $request, $key) {
    $content = json_decode($request->getContent(), true);

    $chanel = $content['token'];
    $app->json($app['gdb.post']($key, $content['value']));

    $app['io.emit']($chanel, [
        'key'   => $key,
        'value' => $content['value']
    ]);

    return $app->json(true);
});

$app->run();

As we can see we register one service provider:

$app->register(new G\Io\Storage\Provider(new SqlLiteStorage($app['db'])));

This provider needs an instance of StorageIface

namespace G\Io\Storage;

interface StorageIface
{
    public function get($key);

    public function post($key, $value);
}

Our implementation uses SqlLite, but it’s pretty straightforward to change to another Database Storage or even a NoSql Database.

use Doctrine\DBAL\Connection;
use G\Io\Storage\StorageIface;

class SqlLiteStorage implements StorageIface
{
    private $db;

    public function __construct(Connection $db)
    {
        $this->db = $db;
    }

    public function get($key)
    {
        $statement = $this->db->executeQuery('select value from storage where key = :KEY', ['KEY' => $key]);
        $data      = $statement->fetchAll();

        return isset($data[0]['value']) ? $data[0]['value'] : null;
    }

    public function post($key, $value)
    {
        $this->db->beginTransaction();

        $statement = $this->db->executeQuery('select value from storage where key = :KEY', ['KEY' =>; $key]);
        $data      = $statement->fetchAll();

        if (count($data) > 0) {
            $this->db->update('storage', ['value' => $value], ['key' => $key]);
        } else {
            $this->db->insert('storage', ['key' => $key, 'value' => $value]);
        }

        $this->db->commit();

        return $value;
    }
}

We also register another Service provider:

$app->register(new G\Io\EmitterServiceProvider($app['httpServer']));

This provider’s responsibility is to notify to the websocket’s server when anything changes within the storage:

namespace G\Io;

use Pimple\Container;
use Pimple\ServiceProviderInterface;

class EmitterServiceProvider implements ServiceProviderInterface
{
    private $server;

    public function __construct($url)
    {
        $this->server = $url;
    }

    public function register(Container $app)
    {
        $app['io.emit'] = $app->protect(function ($chanel, $params) use ($app) {
            $s = curl_init();
            curl_setopt($s, CURLOPT_URL, '{$this->server}/emit/?' . http_build_query($params) . '&_chanel=' . $chanel);
            curl_setopt($s, CURLOPT_RETURNTRANSFER, true);
            $content = curl_exec($s);
            $status  = curl_getinfo($s, CURLINFO_HTTP_CODE);
            curl_close($s);

            if ($status != 200) throw new \Exception();

            return $content;
        });
    }
}

The Websocket server is a simple socket.io server as well as a Express server to handle the backend’s triggers.

var
    express = require('express'),
    expressApp = express(),
    server = require('http').Server(expressApp),
    io = require('socket.io')(server, {origins: 'localhost:*'})
    ;

expressApp.get('/emit', function (req, res) {
    io.sockets.emit(req.query._chanel, req.query);
    res.json('OK');
});

expressApp.listen(3001);

server.listen(3000);

Our client application is an AngularJs application:

<!doctype html>
<html ng-app="app">
<head>
    <script src="//localhost:3000/socket.io/socket.io.js"></script>
    <script src="assets/angularjs/angular.js"></script>
    <script src="js/app.js"></script>
    <script src="js/gdb.js"></script>
</head>
<body>
 
<div ng-controller="MainController">
    <input type="text" ng-model="key">
    <button ng-click="change()">change</button>
</div>
 
</body>
</html>
angular.module('app', ['Gdb'])

    .run(function (Gdb) {
        Gdb.init({
            server: 'http://localhost:8080/gdb',
            token: '4b96716bcb3d42fc01ff421ea2cfd757'
        });
    })

    .controller('MainController', function ($scope, Gdb) {
        $scope.change = function () {
            Gdb.set('key', $scope.key).then(function() {
                console.log(&quot;Value set&quot;);
            });
        };

        Gdb.get('key').then(function (data) {
            $scope.key = data;
        });

        Gdb.watch('key', function (value) {
            console.log(&quot;Value updated&quot;);
            $scope.key = value;
        });
    })
;

As we can see the AngularJs application uses one small library called Gdb to handle the communications with the backend and WebSockets:

angular.module('Gdb', [])
    .factory('Gdb', function ($http, $q, $rootScope) {

        var socket,
            gdbServer,
            token,
            watches = {};

        var Gdb = {
            init: function (conf) {
                gdbServer = conf.server;
                token = conf.token;

                $http.get(gdbServer + '/conf', {params: {token: token}}).success(function (data) {
                    socket = io.connect(data.ioServer);
                    socket.on(data.chanel, function (data) {
                        watches.hasOwnProperty(data.key) ? watches[data.key](data.value) : null;
                        $rootScope.$apply();
                    });
                });
            },

            set: function (key, value) {
                var deferred = $q.defer();

                $http.post(gdbServer + '/' + key, {value: value, token: token}).success(function (data) {
                    deferred.resolve(data);
                });

                return deferred.promise;
            },

            get: function (key) {
                var deferred = $q.defer();

                $http.get(gdbServer + '/' + key, {params: {token: token}}).success(function (data) {
                    deferred.resolve(JSON.parse(data));
                });

                return deferred.promise;
            },

            watch: function (key, closure) {
                watches[key] = closure;
            }
        };

        return Gdb;
    });

And that’s all. You can see the whole project at github.

Upgrading Cordova-iOS apps outside Apple Store

In one of my last post I explained how to upgrade Cordova-Android apps outside Google Play Store with angularjs. Today is the turn of iOS applications.

If you work with in-house iOS applications you need to define a distribution strategy (you cannot use Apple Store, indeed). Apple provides documentation to do it. Basically we need to place our ipa file in addition to the plist file (generated when we archive our application with xCode). I’m not going to explain how to do it here. As I said before it’s well documented. Here I’m going to explain how to do the same trick than the Android’s post but now with our iOS application.

With iOS, to install the application, we only need to provide the iTunes link to our plist application (something like this: itms-services://?action=download-manifest&url=http://url.to.plist) and open it with the InAppBrowser plugin.

First we install the InAppBrowser plugin:

    $ cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-inappbrowser.git

And now we only need to open the url using the plugin:

var iosPlistUrl = 'http://url.to.plist';
cordova.exec(null, null, "InAppBrowser", "open", [encodeURI("itms-services://?action=download-manifest&url=" + iosPlistUrl), "_system"]);

We can use exactly the same angularJs used the the previous post to check the version and the same server-side verification.

We also can detect the platform with Device plugin and do one thing or another depending on we are using Android or iOS.

Here you can see one example using ionic framework. This example uses one $http interceptor to send version number within each request and we trigger ‘wrong.version’ to the event dispatcher when it detects a wrong versions between client and server

angular.module('G', ['ionic'])

    .value('appConf', {
        version: 1,
        apiHost: 'http://localhost:8080'
    })

    .config(function ($httpProvider, $urlRouterProvider, $stateProvider) {
        $httpProvider.interceptors.push('versionInterceptor');

        $stateProvider
            .state('home', {
                url: '/home',
                templateUrl: 'partials/home.html',
                controller: 'HomeController'
            })
            .state('upgrade', {
                url: '/upgrade',
                templateUrl: 'partials/upgrade.html',
                controller: 'UpgradeController'
            })
        ;

        $urlRouterProvider.otherwise('/home');

    })

    .run(function ($ionicPlatform, $rootScope, $state) {
        $ionicPlatform.ready(function () {
            if (window.cordova && window.cordova.plugins.Keyboard) {
                cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
            }
            if (window.StatusBar) {
                StatusBar.styleDefault();
            }
        });

        $rootScope.$on('wrong.version', function () {
            $state.go("upgrade");
        });
    })

    .controller('HomeController', function ($scope, $http, appConf) {
        $scope.someAction = function () {
            $http.get(appConf.apiHost + "/hello", function (data) {
                alert(data);
            });
        }
    })

    .controller('UpgradeController', function ($scope) {
        $scope.upgrade = function () {
            cordova.exec(null, null, "InAppBrowser", "open", [encodeURI("itms-services://?action=download-manifest&url=https://path/to/plist.plist"), "_system"]);
        }
    })

    .factory('versionInterceptor', function ($rootScope, appConf) {
        var versionInterceptor = {
            request: function (config) {
                config.url = config.url + '?_version=' + appConf.version;

                return config;
            },
            responseError: function(response) {
                if (response.status == 410) {
                    $rootScope.$emit('wrong.version');
                }
            }
        };

        return versionInterceptor;
    })
;

Sharing $scope between controllers with AngularJs

Angular creates one $scope object for each controller. We also have a $rootScope accesible from every controllers. But, can we access to one controller’s $scope from another controller? The sort answer is no. Also if our application needs to access to another controller’s $scope, we probably are doing something wrong and we need to re-think our problem. But anyway it’s possible to access to another controller’s $scope if we store it within a service. Let me show you and example.

Imagine this little example:

<!doctype html>
<html ng-app="app">
<head>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.18/angular.js"></script>
    <script src="app.js"></script>
</head>
<body>

<div ng-controller="OneController">
    <h2>OneController</h2>
    <button ng-click="buttonClick()">
        buttonClick on current scope
    </button>
</div>

<div ng-controller="TwoController">
    <h2>TwoController</h2>
    <button ng-click="buttonClick()">
        buttonClick on current scope
    </button>
</div>
</body>
</html>

As we can see we define two controllers: “OneController” and “TwoController”.

That’s the application:

var app = angular.module('app', []);

app.controller('OneController', function ($scope) {
    $scope.variable1 = "One";

    $scope.buttonClick = function () {
        console.log("OneController");
        console.log("$scope::variable1", $scope.variable1);
    };
});

app.controller('TwoController', function ($scope) {
    $scope.variable1 = "Two";

    $scope.buttonClick = function () {
        console.log("TwoController");
        console.log("$scope::variable1", $scope.variable1);
    };
});

If we need to access to another controller’s $scope we need to store those scopes within a service. For example with this minimal service:

app.factory('Scopes', function ($rootScope) {
    var mem = {};

    return {
        store: function (key, value) {
            mem[key] = value;
        },
        get: function (key) {
            return mem[key];
        }
    };
});

And now we need to store the $scope in the service:

app.controller('OneController', function ($scope, Scopes) {
    Scopes.store('OneController', $scope);
    ...
});
app.controller('TwoController', function ($scope, Scopes) {
    Scopes.store('TwoController', $scope);
    ...
});

And now we can access to another’s $scope

Here the full example:

<!doctype html>
<html ng-app="app">
<head>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.18/angular.js"></script>
    <script src="app.js"></script>
</head>
<body>

<div ng-controller="OneController">
    <h2>OneController</h2>
    <button ng-click="buttonClick()">
        buttonClick on current scope
    </button>
    <button ng-click="buttonClickOnTwoController()">
        buttonClick on TwoController's scope
    </button>
</div>

<div ng-controller="TwoController">
    <h2>TwoController</h2>
    <button ng-click="buttonClick()">
        buttonClick on current scope
    </button>
    <button ng-click="buttonClickOnOneController()">
        buttonClick on OneController's scope
    </button>
</div>
</body>
</html>

and app.js

var app = angular.module('app', []);

app.run(function ($rootScope) {
    $rootScope.$on('scope.stored', function (event, data) {
        console.log("scope.stored", data);
    });
});
app.controller('OneController', function ($scope, Scopes) {

    Scopes.store('OneController', $scope);

    $scope.variable1 = "One";

    $scope.buttonClick = function () {
        console.log("OneController");
        console.log("OneController::variable1", Scopes.get('OneController').variable1);
        console.log("TwoController::variable1", Scopes.get('TwoController').variable1);
        console.log("$scope::variable1", $scope.variable1);
    };

    $scope.buttonClickOnTwoController = function () {
        Scopes.get('TwoController').buttonClick();
    };
});
app.controller('TwoController', function ($scope, Scopes) {

    Scopes.store('TwoController', $scope);

    $scope.variable1 = "Two";

    $scope.buttonClick = function () {
        console.log("TwoController");
        console.log("OneController::variable1", Scopes.get('OneController').variable1);
        console.log("TwoController::variable1", Scopes.get('TwoController').variable1);
        console.log("$scope::variable1", $scope.variable1);
    };

    $scope.buttonClickOnOneController = function () {
        Scopes.get('OneController').buttonClick();
    };
});
app.factory('Scopes', function ($rootScope) {
    var mem = {};

    return {
        store: function (key, value) {
            $rootScope.$emit('scope.stored', key);
            mem[key] = value;
        },
        get: function (key) {
            return mem[key];
        }
    };
});

You can also see it running here

Playing with websockets, angularjs and socket.io

I’m a big fan of websockets. I’ve got various post about them (here, here). Last months I’m working with angularjs projects and because of that I wanna play a little bit with websockets (with socket.io) and angularjs.

I want to build one angular service.

angular.module('io.service', []).
    factory('io', function ($http) {
        var socket,
            apiServer,
            ioEvent,
            watches = {};

        return {
            init: function (conf) {
                apiServer = conf.apiServer;
                ioEvent = conf.ioEvent;

                socket = io.connect(conf.ioServer);
                socket.on(ioEvent, function (data) {
                    return watches.hasOwnProperty(data.item) ? watches[data.item](data) : null;
                });
            },

            emit: function (arguments) {
                return $http.get(apiServer + '/request', {params: arguments});
            },

            watch: function (item, closure) {
                watches[item] = closure;
            },

            unWatch: function (item) {
                delete watches[item];
            }
        };
    });

And now we can build the application

angular.module('myApp', ['io.service']).

    run(function (io) {
        io.init({
            ioServer: 'http://localhost:3000',
            apiServer: 'http://localhost:8080/api',
            ioEvent: 'io.response'
        });
    }).

    controller('MainController', function ($scope, io) {
        $scope.$watch('question', function (newValue, oldValue) {
            if (newValue != oldValue) {
                io.emit({item: 'question', newValue: newValue, oldValue: oldValue});
            }
        });

        io.watch('answer', function (data) {
            $scope.answer = data.value;
            $scope.$apply();
        });
    });

And this’s the html

<!doctype html>
<html>

<head>
    <title>ws experiment</title>
</head>

<body ng-app="myApp">

<div ng-controller="MainController">

    <input type="text" ng-model="question">
    <hr>
    <h1>Hello {{answer}}!</h1>
</div>

<script src="assets/angular/angular.min.js"></script>
<script src="//localhost:3000/socket.io/socket.io.js"></script>

<script src="js/io.js"></script>
<script src="js/app.js"></script>

</body>
</html>

The idea of the application is to watch one model’s variable (‘question’ in this example) and each time it changes we will send the value to the websocket server and we’ll so something (we will convert the string to upper case in our example)

As you can read in one of my previous post I don’t like to send messages from the web browser to the websocket server directly (due do to authentication issues commented here). I prefer to use one server (a Silex server in this example)

include __DIR__ . '/../../vendor/autoload.php';

use Silex\Application;
use Symfony\Component\HttpFoundation\Request;

$app = new Application(['debug' => true]);
$app->register(new G\Io\EmitterServiceProvider());

$app->get('/request', function (Application $app, Request $request) {

    $params = [
        'item'     => $request->get('item'),
        'newValue' => strtoupper($request->get('newValue')),
        'oldValue' => $request->get('oldValue'),
    ];

    try {
        $app['io.emit']($params);
        $params['status'] = true;
    } catch (\Exception $e) {
        $params['status'] = false;
    }

    return $app->json($params);
});

$app->run();

You can see the code within my github account.

Token based authentication with Silex and AngularJS

According to my last post today we’re going to create a AngularJS application that uses the Silex Backend that we create previously. The idea of this application is to use it within a Phonegap/Cordova application running in a mobile device.

The application will be show a login form if device haven’t a correct token.

Gonzalo_Login_Example_and_LoginServiceProvider_php_-_token_-____work_projects_token_

And whit a correct token:

Gonzalo_Login_Example

Nothing new under the sun, isn’t it?

Our front-end application will use AngularJS and Topcoat.

<!DOCTYPE html>
<html xmlns:ng="http://angularjs.org" lang="es" ng-app="G">
<head>
    <meta charset="utf-8"/>
    <meta name="format-detection" content="telephone=no"/>
    <!-- WARNING: for iOS 7, remove the width=device-width and height=device-height attributes. See https://issues.apache.org/jira/browse/CB-4323 -->
    <meta name="viewport"
          content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi"/>
    <link rel="stylesheet" type="text/css" href="/bower_components/topcoat/css/topcoat-mobile-light.min.css">
    <title>Gonzalo Login Example</title>
</head>
<body ng-controller="MainController">

<div ng-view class="main-content"></div>

<script src="/bower_components/angular/angular.min.js"></script>
<script src="/bower_components/angular-route/angular-route.min.js"></script>

<script src="js/app.js"></script>
<script src="js/services.js"></script>

</body>
</html>

And our AngularJS application:

'use strict';
var appControllers, G;
var host = 'http://localhost:8080'; // server API url

appControllers = angular.module('appControllers', []);
G = angular.module('G', ['ngRoute', 'appControllers']);

G.run(function (httpG) {
    httpG.setHost(host);
});

G.config(['$routeProvider', function ($routeProvider) {
    $routeProvider.
        when('/login', {templateUrl: 'partials/login.html', controller: 'LoginController'}).
        when('/home', {templateUrl: 'partials/home.html', controller: 'HomeController'});
}]);

appControllers.controller('HomeController', ['$scope', 'httpG', '$location', function ($scope, httpG, $location) {
    $scope.hello = function () {
        httpG.get('/api/info').success(function (data) {
            if (data.status) {
                alert("Hello " + data.info.name + " " + data.info.surname);
            }
        });
    };

    $scope.logOut = function () {
        alert("Good bye!");
        httpG.removeToken();
        $scope.isAuthenticated = false;
        $location.path('login');
    };
}]);

appControllers.controller('MainController', ['$scope', '$location', 'httpG', function ($scope, $location, httpG) {
    $scope.isAuthenticated = false;

    if (httpG.getToken()) {
        $scope.isAuthenticated = true;
        $location.path('home');
    } else {
        $location.path('login');
    }
}]);


appControllers.controller('LoginController', ['$scope', '$location', 'httpG', function ($scope, $location, httpG) {
    $scope.user = {};

    $scope.doLogIn = function () {
        httpG.get('/auth/validateCredentials', {user: $scope.user.username, pass: $scope.user.password}).success(function (data) {
            if (data.status) {
                httpG.setToken(data.info.token);
                $scope.isAuthenticated = true;
                $location.path('home');
            } else {
                alert("login error");
            }
        }).error(function (error) {
            alert("Login Error!");
        });
    };

    $scope.doLogOut = function () {
        httpG.removeToken();
    };
}]);

In this example I’m using angular-route to handle the application’s routes. Nowadays I’m swaping to angular-ui-router, but this example I’m still using “old-style” routes. We define two partials:

partial/home.html

<div class="topcoat-button-bar full" style="position: fixed; bottom: 0px;">
    <label class="topcoat-button-bar__item">
        <button class="topcoat-button full" ng-click="logOut()">
            <span class="">Logout</span>
        </button>
    </label>
    <label class="topcoat-button-bar__item">
        <button class="topcoat-button--cta full" ng-click="hello()">
            <span class="">Hello</span>
        </button>
    </label>
</div>

partial/login.html

<div class="topcoat-navigation-bar">
    <div class="topcoat-navigation-bar__item center full">
        <h1 class="topcoat-navigation-bar__title">Login</h1>
    </div>
</div>

<ul class="topcoat-list__container">
    <li class="topcoat-list__item center">
        <input ng-model="user.username" class="topcoat-text-input--large" type="text" name="user"
               placeholder="Username"/>
    </li>
    <li class="topcoat-list__item center">
        <input ng-model="user.password" class="topcoat-text-input--large" type="password" name="pass"
               placeholder="Password"/>
    </li>
</ul>

<div class="topcoat-button-bar full" style="position: fixed; bottom: 0px;">
    <label class="topcoat-button-bar__item">
        <button class="topcoat-button--cta full" ng-click="doLogIn()">
            <span class="">Login</span>
        </button>
    </label>
</div>

As we can see in the application we’re using a service to handle Http connections with the token information.

'use strict';

G.factory('httpG', ['$http', '$window', function ($http, $window) {
    var serviceToken, serviceHost, tokenKey;
    tokenKey = 'token';
    if (localStorage.getItem(tokenKey)) {
        serviceToken = $window.localStorage.getItem(tokenKey);
    }

    $http.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded";

    return {
        setHost: function (host) {
            serviceHost = host;
        },

        setToken: function (token) {
            serviceToken = token;
            $window.localStorage.setItem(tokenKey, token);
        },

        getToken: function () {
            return serviceToken;
        },

        removeToken: function() {
            serviceToken = undefined;
            $window.localStorage.removeItem(tokenKey);
        },

        get: function (uri, params) {
            params = params || {};
            params['_token'] = serviceToken;
            return $http.get(serviceHost + uri, {params: params});
        },

        post: function (uri, params) {
            params = params || {};
            params['_token'] = serviceToken;

            return $http.post(serviceHost + uri, params);
        }
    };
}]);

And that’s all. You can see the full example in my github account.