Merge branch 'master' of github.com:open-cloud/xos
diff --git a/.gitignore b/.gitignore
index bf493e8..617f1f9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,3 +23,4 @@
 .DS_Store
 xos/configurations/setup/
 migrations/
+xos/configurations/frontend/onboarding-docker-compose/
diff --git a/views/ngXosLib/.eslintrc b/views/ngXosLib/.eslintrc
index 1cd7d33..6a8ce79 100644
--- a/views/ngXosLib/.eslintrc
+++ b/views/ngXosLib/.eslintrc
@@ -12,7 +12,7 @@
         "es6": true
     },
     "plugins": [
-        //"angular"
+        "angular"
     ],
     "rules": {
         "quotes": [2, "single"],
@@ -28,12 +28,15 @@
         "no-trailing-spaces": [1, { skipBlankLines: true }],
         "no-unused-vars": [1, {"vars": "all", "args": "after-used"}],
         "new-cap": 0,
+        "no-multiple-empty-lines": 2,
 
-        //"angular/ng_module_name": [2, '/^xos\.*[a-z]*$/'],
-        //"angular/ng_controller_name": [2, '/^[a-z].*Ctrl$/'],
+        "angular/ng_module_name": [2, '/^xos\.*[a-z]*$/'],
+//        "angular/ng_controller_name": [2, '/^[a-z].*Ctrl$/'],
         //"angular/ng_service_name": [2, '/^[A-Z].*Service$/'],
-        //"angular/ng_directive_name": [2, '/^[a-z]+[[A-Z].*]*$/'],
-        //"angular/ng_di": [0, "function or array"]
+        "angular/ng_directive_name": [2, '/^[xos[[A-Z].*]*$/'],
+        "angular/ng_di": [0, "function or array"],
+        "angular/ng_angularelement": 0,
+        "angular/ng_on_watch": 1
     },
     "globals" :{
         "angular": true
diff --git a/views/ngXosLib/xosHelpers/spec/.eslintrc b/views/ngXosLib/xosHelpers/spec/.eslintrc
new file mode 100644
index 0000000..de1e44b
--- /dev/null
+++ b/views/ngXosLib/xosHelpers/spec/.eslintrc
@@ -0,0 +1,7 @@
+{
+  "rules": {
+    "angular/ng_angularelement": 0,
+    "angular/ng_no_digest": 0,
+    "angular/ng_json_functions": 0
+  }
+}
\ No newline at end of file
diff --git a/views/ngXosLib/xosHelpers/spec/log.test.js b/views/ngXosLib/xosHelpers/spec/log.test.js
index f286a8e..21085c6 100644
--- a/views/ngXosLib/xosHelpers/spec/log.test.js
+++ b/views/ngXosLib/xosHelpers/spec/log.test.js
@@ -3,6 +3,7 @@
  *
  * Created by teone on 4/18/16.
  */
+/* eslint-disable angular/ng_window_service*/
 
 // TODO write tests for log
 // NODE Actually the code is working, the tests are not.
@@ -12,9 +13,9 @@
 
   xdescribe('The xos.helper module', function(){
 
-    let log;
+    let log, window;
 
-    var mockLog;
+    let mockLog;
 
     beforeEach(function() {
       mockLog = jasmine.createSpyObj('logMock', ['info']);
@@ -28,8 +29,9 @@
       });
     });
 
-    beforeEach(inject(($log) => {
+    beforeEach(inject(($log, $window) => {
       log = $log;
+      window = $window;
       // log.reset();
     }));
 
diff --git a/views/ngXosLib/xosHelpers/spec/ui/field.test.js b/views/ngXosLib/xosHelpers/spec/ui/field.test.js
index fad0826..f294daa 100644
--- a/views/ngXosLib/xosHelpers/spec/ui/field.test.js
+++ b/views/ngXosLib/xosHelpers/spec/ui/field.test.js
@@ -14,13 +14,13 @@
     if(!scope){
       scope = rootScope.$new();
     }
-    if(!angular.isDefined(element)){
+    if(angular.isUndefined(element)){
       element = angular.element('<xos-field name="name" field="field" ng-model="ngModel"></xos-field>');
     }
     compile(element)(scope);
     scope.$digest();
     isolatedScope = element.isolateScope().vm;
-  }
+  };
 
   describe('The xos.helper module', function(){
 
@@ -104,9 +104,6 @@
         });
       });
 
-
-
-
       describe('when a option is selected in dropdown', () => {
         beforeEach(() => {
           scope = rootScope.$new();
@@ -126,17 +123,18 @@
               }
             ]
           };
-          scope.ngModel = 'label';
+          scope.ngModel = 0;
           compileElement();
         });
 
         it('No of select elements', () => {
-          expect($(element).find('select').children('option').length).toEqual(3);
+          expect($(element).find('select').children('option').length).toEqual(2);
         });
 
-        it('should show a selected value', () => {
-          var elem =  angular.element($(element).find('select').children('option')[1]);
+        it('should show the selected value', () => {
+          var elem =  angular.element($(element).find('select').children('option')[0]);
           expect(elem.text()).toEqual('---Site---');
+          expect(elem).toHaveAttr('selected');
         });
       });
 
diff --git a/views/ngXosLib/xosHelpers/spec/ui/form.test.js b/views/ngXosLib/xosHelpers/spec/ui/form.test.js
index c1387ff..dc9c957 100644
--- a/views/ngXosLib/xosHelpers/spec/ui/form.test.js
+++ b/views/ngXosLib/xosHelpers/spec/ui/form.test.js
@@ -259,15 +259,25 @@
       });
       describe('when correctly configured for feedback', () => {
 
-        let cb = jasmine.createSpy('callback');
+        let fb = jasmine.createSpy('feedback').and.callFake(function(statusFlag) {
+          if(statusFlag){
+            scope.config.feedback.show = true;
+            scope.config.feedback.message = 'Form Submitted';
+            scope.config.feedback.type = 'success';
+          }
+          else {
+            scope.config.feedback.show = true;
+            scope.config.feedback.message = 'Error';
+            scope.config.feedback.type = 'danger';
 
-        beforeEach(inject(($rootScope) => {
+          }
+        });
 
-          scope = $rootScope.$new();
+        beforeEach(()=> {
+          scope = rootScope.$new();
+          scope.config =
+          {
 
-          scope.config = {
-            exclude: ['excludedField'],
-            formName: 'testForm',
             feedback: {
               show: false,
               message: 'Form submitted successfully !!!',
@@ -277,259 +287,33 @@
               {
                 label: 'Save',
                 icon: 'ok', // refers to bootstraps glyphicon
-                cb: cb,
+                cb: () => {},
                 class: 'success'
               }
-            ],
-            fields: {
-              first_name: {
-                label: 'Custom Label'
-              }
-            }
+            ]
           };
-
-          scope.model = {
-            id: 1,
-            first_name: 'Jhon',
-            last_name: 'Snow',
-            age: 25,
-            email: 'test@onlab.us',
-            birthDate: '2016-04-18T23:44:16.883181Z',
-            enabled: true,
-            role: 'user', //select
-            a_permissions: [
-            ],
-            object_field: {
-              string: 'bar',
-              number: 1,
-              email: 'teo@onlab.us'
-            }
-          };
-
+          scope.model={};
           compileElement();
-        }));
-
-        it('should add excluded properties to the list', () => {
-          let expected = ['id', 'validators', 'created', 'updated', 'deleted', 'backend_status', 'excludedField'];
-          expect(isolatedScope.excludedField).toEqual(expected);
         });
 
-        it('should render 10 input field', () => {
-          // boolean are in the form model, but are not input
-          expect(Object.keys(isolatedScope.formField).length).toEqual(9);
-          var field = element[0].getElementsByTagName('input');
-          expect(field.length).toEqual(10);
+        it('should not show feedback when loaded', () => {
+          expect($(element).find('xos-alert > div')).toHaveClass('alert alert-success ng-hide');
         });
 
-        it('should render 1 boolean field', () => {
-          expect($(element).find('.boolean-field > a').length).toEqual(2)
+        it('should show a success feedback', () => {
+          fb(true);
+          scope.$digest();
+          expect(isolatedScope.config.feedback.type).toEqual('success');
+          expect(fb).toHaveBeenCalledWith(true);
+          expect($(element).find('xos-alert > div')).toHaveClass('alert alert-success');
         });
 
-        it('when clicking on action should invoke callback', () => {
-          var link = $(element).find('[role="button"]');
-          //console.log(link);
-          link.click();
-          // TODO : Check correct parameters
-          expect(cb).toHaveBeenCalled();
-
-        });
-
-        it('should set a custom label', () => {
-          let nameField = element[0].getElementsByClassName('form-group')[0];
-          let label = angular.element(nameField.getElementsByTagName('label')[0]).text()
-          expect(label).toEqual('Custom Label:');
-        });
-
-        it('should use the correct input type', () => {
-          expect($(element).find('[name="age"]')).toHaveAttr('type', 'number');
-          expect($(element).find('[name="birthDate"]')).toHaveAttr('type', 'date');
-          expect($(element).find('[name="email"]')).toHaveAttr('type', 'email');
-        });
-
-
-
-        describe('A spy,configured with an alternate config implementation for Feedback', function() {
-          var feedbackParams, statusFlag, fetchedBar;
-
-          let fb = jasmine.createSpy('feedback').and.callFake(function(statusFlag) {
-            //console.log(statusFlag , '------------------------------------------------------');
-            if(statusFlag){
-              scope.config.feedback.show = true;
-              scope.config.feedback.message = 'Form Submitted';
-              scope.config.feedback.type = 'success';
-            }
-            else {
-              scope.config.feedback.show = true;
-              scope.config.feedback.message = 'Error';
-              scope.config.feedback.type = 'danger';
-
-            }
-            console.log(scope.config.feedback, '################################');
-
-
-
-          });
-
-          beforeEach(()=> {
-            scope = rootScope.$new();
-            scope.config =
-            {
-
-              feedback: {
-                show: false,
-                message: 'Form submitted successfully !!!',
-                type: 'success'
-              },
-              actions: [
-                {
-                  label: 'Save',
-                  icon: 'ok', // refers to bootstraps glyphicon
-                  cb: cb,
-                  class: 'success'
-                }
-              ]
-            };
-            scope.model={};
-            compileElement();
-          })
-
-          it('tracks that the spy was called', function() {
-            expect($(element).find('xos-alert > div')).toHaveClass('alert alert-success ng-hide');
-            scope.$digest();
-            fb(true);
-            scope.$digest();
-            expect(isolatedScope.config.feedback.type).toEqual('success');
-            expect(fb).toHaveBeenCalledWith(true);
-            expect($(element).find('xos-alert > div')).toHaveClass('alert alert-success');
-            fb(false);
-            scope.$digest();
-            expect(isolatedScope.config.feedback.type).toEqual('danger');
-            expect(fb).toHaveBeenCalledWith(false);
-            expect($(element).find('xos-alert > div')).toHaveClass('alert alert-danger');
-          });
-        });
-        xdescribe('the boolean field test', () => {
-
-          let setFalse, setTrue;
-
-          beforeEach(() => {
-            setFalse= $(element).find('.boolean-field > button:first-child');
-            setTrue = $(element).find('.boolean-field > button:last-child');
-          });
-
-          it('should change value to false', () => {
-            expect(isolatedScope.ngModel.enabled).toEqual(true);
-            setFalse.click();
-            expect(isolatedScope.ngModel.enabled).toEqual(false);
-          });
-
-          it('should change value to true', () => {
-            isolatedScope.ngModel.enabled = false;
-            scope.$apply();
-            expect(isolatedScope.ngModel.enabled).toEqual(false);
-            setTrue.click()
-            expect(isolatedScope.ngModel.enabled).toEqual(true);
-          });
-        });
-
-        // NOTE not sure why this tests are failing
-        xdescribe('the custom validation options', () => {
-          beforeEach(() => {
-            scope.config.fields.first_name.validators = {
-              minlength: 10,
-              maxlength: 15,
-              required: true
-            };
-
-            scope.config.fields.age = {
-              validators: {
-                min: 10,
-                max: 20
-              }
-            };
-
-            scope.$digest();
-          });
-
-          it('should validate required', () => {
-            scope.model.first_name = null;
-            scope.$digest();
-
-            expect(isolatedScope.testForm.first_name.$valid).toBeFalsy();
-            expect(isolatedScope.testForm.first_name.$error.required).toBeTruthy();
-          });
-
-          it('should validate minlength', () => {
-            scope.model.first_name = 'short';
-            scope.$digest();
-
-            expect(isolatedScope.testForm.first_name.$valid).toBeFalsy();
-            expect(isolatedScope.testForm.first_name.$error.minlength).toBeTruthy();
-          });
-
-          it('should validate maxlength', () => {
-            scope.model.first_name = 'this is way too long!';
-            scope.$digest();
-
-            expect(isolatedScope.testForm.first_name.$valid).toBeFalsy();
-            expect(isolatedScope.testForm.first_name.$error.maxlength).toBeTruthy();
-          });
-
-          it('should validate min', () => {
-            // not validating min and max for now
-            scope.model.age = 8;
-            scope.$digest();
-
-            expect(isolatedScope.testForm.age.$valid).toBeFalsy();
-            expect(isolatedScope.testForm.age.$error.min).toBeTruthy();
-          });
-        });
-
-        describe('when a deep model is passed', () => {
-
-          beforeEach(inject(($rootScope) => {
-
-            scope = $rootScope.$new();
-
-            scope.config = {
-              exclude: ['excludedField'],
-              formName: 'testForm',
-              actions: [
-                {
-                  label: 'Save',
-                  icon: 'ok', // refers to bootstraps glyphicon
-                  cb: cb,
-                  class: 'success'
-                }
-              ],
-              fields: {
-                object_field: {
-                  field_one: {
-                    label: 'Custom Label'
-                  }
-                }
-              }
-            };
-
-            scope.model = {
-              object_field: {
-                field_one: 'bar',
-                number: 1,
-                email: 'teo@onlab.us'
-              }
-            };
-
-            compileElement();
-          }));
-
-          it('should print nested field', () => {
-            expect($(element).find('input').length).toBe(3);
-          });
-
-          xit('should configure nested fields', () => {
-            let custom_label = $(element).find('input[name=field_one]').parent().find('label');
-            expect(custom_label.text()).toBe('Custom Label');
-          });
+        it('should show an error feedback', function() {
+          fb(false);
+          scope.$digest();
+          expect(isolatedScope.config.feedback.type).toEqual('danger');
+          expect(fb).toHaveBeenCalledWith(false);
+          expect($(element).find('xos-alert > div')).toHaveClass('alert alert-danger');
         });
       });
 
diff --git a/views/ngXosLib/xosHelpers/spec/ui/validation.test.js b/views/ngXosLib/xosHelpers/spec/ui/validation.test.js
index f8350ed..1029f2d 100644
--- a/views/ngXosLib/xosHelpers/spec/ui/validation.test.js
+++ b/views/ngXosLib/xosHelpers/spec/ui/validation.test.js
@@ -15,7 +15,7 @@
     if(!scope){
       scope = rootScope.$new();
     }
-    if(!angular.isDefined(element)){
+    if(angular.isUndefined(element)){
       element = angular.element('<xos-validation field="field" form="form"></xos-validation>');
     }
     compile(element)(scope);
diff --git a/views/ngXosLib/xosHelpers/src/services/helpers/ui/form.helpers.js b/views/ngXosLib/xosHelpers/src/services/helpers/ui/form.helpers.js
index bc6a503..0a9d3ec 100644
--- a/views/ngXosLib/xosHelpers/src/services/helpers/ui/form.helpers.js
+++ b/views/ngXosLib/xosHelpers/src/services/helpers/ui/form.helpers.js
@@ -60,7 +60,7 @@
       }
 
       // if null return string
-      if(typeof value === 'string' || value === null){
+      if(angular.isString(value) || value === null){
         return 'text';
       }
 
diff --git a/views/ngXosLib/xosHelpers/src/services/helpers/user-prefs.service.js b/views/ngXosLib/xosHelpers/src/services/helpers/user-prefs.service.js
index 27edf7f..7bf8ae1 100644
--- a/views/ngXosLib/xosHelpers/src/services/helpers/user-prefs.service.js
+++ b/views/ngXosLib/xosHelpers/src/services/helpers/user-prefs.service.js
@@ -23,7 +23,7 @@
 
   .service('XosUserPrefs', function($cookies){
 
-    let userPrefs = $cookies.get('xosUserPrefs') ? JSON.parse($cookies.get('xosUserPrefs')) : {};
+    let userPrefs = $cookies.get('xosUserPrefs') ? angular.fromJson($cookies.get('xosUserPrefs')) : {};
 
     /**
     * @ngdoc method
@@ -34,7 +34,7 @@
     * @returns {object} The user preferences
     **/
     this.getAll = () => {
-      userPrefs = $cookies.get('xosUserPrefs') ? JSON.parse($cookies.get('xosUserPrefs')) : {};
+      userPrefs = $cookies.get('xosUserPrefs') ? angular.fromJson($cookies.get('xosUserPrefs')) : {};
       return userPrefs;
     };
 
@@ -47,7 +47,7 @@
     * @param {object} prefs The user preferences
     **/
     this.setAll = (prefs) => {
-      $cookies.put('xosUserPrefs', JSON.stringify(prefs));
+      $cookies.put('xosUserPrefs', angular.toJson(prefs));
     };
 
     /**
diff --git a/views/ngXosLib/xosHelpers/src/services/log.decorator.js b/views/ngXosLib/xosHelpers/src/services/log.decorator.js
index b8c5297..e6bd621 100644
--- a/views/ngXosLib/xosHelpers/src/services/log.decorator.js
+++ b/views/ngXosLib/xosHelpers/src/services/log.decorator.js
@@ -1,5 +1,7 @@
 // TODO write tests for log
 
+/* eslint-disable  angular/ng_window_service*/
+
 angular.module('xos.helpers')
 .config([ '$provide', function( $provide )
 {
@@ -11,7 +13,7 @@
 
     const isLogEnabled = () => {
       return window.location.href.indexOf('debug=true') >= 0;
-    }
+    };
     // Save the original $log.debug()
     let logFn = $delegate.log;
     let infoFn = $delegate.info;
@@ -34,7 +36,7 @@
         args[0] = `[${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}] ${args[0]}`;
 
         // HACK awfull fix for angular mock implementation whithin jasmine test failing issue
-        if (typeof $delegate.reset === 'function' && !($delegate.debug.logs instanceof Array)) {
+        if (angular.isFunction($delegate.reset) && !($delegate.debug.logs instanceof Array)) {
           // if we are within the mock and did not reset yet, we call it to avoid issue
           // console.log('mock log impl fix to avoid logs array not existing...');
           $delegate.reset();
diff --git a/views/ngXosLib/xosHelpers/src/services/notification.service.js b/views/ngXosLib/xosHelpers/src/services/notification.service.js
index e190451..9a1598e 100644
--- a/views/ngXosLib/xosHelpers/src/services/notification.service.js
+++ b/views/ngXosLib/xosHelpers/src/services/notification.service.js
@@ -1,3 +1,4 @@
+/* eslint-disable  angular/ng_window_service*/
 (function() {
   'use strict';
 
diff --git a/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/form/form.component.js b/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/form/form.component.js
index ca6d7d8..1706483 100644
--- a/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/form/form.component.js
+++ b/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/form/form.component.js
@@ -253,22 +253,16 @@
           this.excludedField = this.excludedField.concat(this.config.exclude);
         }
 
-
         this.formField = [];
+
         $scope.$watch(() => this.config, ()=> {
-
-
-
           if(!this.ngModel){
             return;
           }
           let diff = _.difference(Object.keys(this.ngModel), this.excludedField);
           let modelField = XosFormHelpers.parseModelField(diff);
           this.formField = XosFormHelpers.buildFormStructure(modelField, this.config.fields, this.ngModel);
-
-
-        } ,true);
-
+        }, true);
 
         $scope.$watch(() => this.ngModel, (model) => {
           // empty from old stuff
diff --git a/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/table/table.component.js b/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/table/table.component.js
index 6092f42..94e2ab3 100644
--- a/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/table/table.component.js
+++ b/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/table/table.component.js
@@ -388,7 +388,7 @@
               </tbody>
               <tbody>
                 <tr ng-repeat="item in vm.data | filter:vm.query:vm.comparator | orderBy:vm.orderBy:vm.reverse | pagination:vm.currentPage * vm.config.pagination.pageSize | limitTo: (vm.config.pagination.pageSize || vm.data.length) track by $index">
-                  <td ng-repeat="col in vm.columns" link-wrapper>
+                  <td ng-repeat="col in vm.columns" xos-link-wrapper>
                     <span ng-if="!col.type">{{item[col.prop]}}</span>
                     <span ng-if="col.type === 'boolean'">
                       <i class="glyphicon"
@@ -534,7 +534,7 @@
       }
     })
     // TODO test
-    .directive('linkWrapper', function() {
+    .directive('xosLinkWrapper', function() {
       return {
         restrict: 'A',
         transclude: true,
diff --git a/views/ngXosLib/xosHelpers/src/ui_components/smartComponents/smartTable/smartTable.component.js b/views/ngXosLib/xosHelpers/src/ui_components/smartComponents/smartTable/smartTable.component.js
index 08844f6..622952f 100644
--- a/views/ngXosLib/xosHelpers/src/ui_components/smartComponents/smartTable/smartTable.component.js
+++ b/views/ngXosLib/xosHelpers/src/ui_components/smartComponents/smartTable/smartTable.component.js
@@ -242,7 +242,7 @@
                 prop: p
               };
 
-              if(typeof item[p] !== 'string' && typeof item[p] !== 'undefined'){
+              if(angular.isString(item[p]) && typeof item[p] !== 'undefined'){
                 fieldConfig.type = typeof item[p];
               }
 
diff --git a/views/ngXosLib/xosHelpers/src/xosHelpers.module.js b/views/ngXosLib/xosHelpers/src/xosHelpers.module.js
index fe246a6..3d534b9 100644
--- a/views/ngXosLib/xosHelpers/src/xosHelpers.module.js
+++ b/views/ngXosLib/xosHelpers/src/xosHelpers.module.js
@@ -1,6 +1,8 @@
 (function() {
   'use strict';
-  
+
+  /* eslint-disable angular/ng_module_name */
+
   angular.module('bugSnag', []).factory('$exceptionHandler', function () {
     return function (exception, cause) {
       if( window.Bugsnag ){
@@ -12,6 +14,8 @@
     };
   });
 
+  /* eslint-enable angular/ng_module_name */
+
   /**
   * @ngdoc overview
   * @name xos.helpers