You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@falcon.apache.org by sr...@apache.org on 2015/04/01 13:10:40 UTC

[13/21] falcon git commit: FALCON-790 Falcon UI to enable entity/process/feed edits and management. Contributed by Armando Reyna/Kenneth Ho

http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/app/js/app.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/app.js b/falcon-ui/app/js/app.js
new file mode 100644
index 0000000..1705cb4
--- /dev/null
+++ b/falcon-ui/app/js/app.js
@@ -0,0 +1,141 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function () {
+  'use strict';
+
+  var app = angular.module('app', [
+    'ui.bootstrap', 'ui.router', 'ngCookies', 'ngAnimate', 'ngMessages', 'checklist-model', 'app.controllers', 'app.directives', 'app.services'
+  ]);
+
+  app.config(["$stateProvider", "$urlRouterProvider", "$httpProvider", function ($stateProvider, $urlRouterProvider, $httpProvider) {
+  	
+  	$httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content');
+  	
+  	$httpProvider.defaults.headers.common["X-Requested-By"] = 'X-Requested-By';
+  	
+    $urlRouterProvider.otherwise("/");
+
+    $stateProvider
+      .state('main', {
+        url: '/',
+        templateUrl: 'html/mainTpl.html',
+        controller: 'DashboardCtrl'
+      })
+      .state('entityDetails', {
+        controller: 'EntityDetailsCtrl',
+        templateUrl: 'html/entityDetailsTpl.html'
+      })
+      .state('forms', {
+        templateUrl: 'html/formsTpl.html'
+      })
+      .state('forms.cluster', {
+        controller: 'ClusterFormCtrl',
+        templateUrl: 'html/cluster/clusterFormTpl.html'
+      })
+      .state('forms.cluster.general', {
+        templateUrl: 'html/cluster/clusterFormGeneralStepTpl.html'
+      })
+      .state('forms.cluster.summary', {
+        templateUrl: 'html/cluster/clusterFormSummaryStepTpl.html'
+      })
+      .state('forms.feed', {
+        templateUrl: 'html/feed/feedFormTpl.html',
+        controller: 'FeedController'
+      })
+      .state('forms.feed.general', {
+        templateUrl: 'html/feed/feedFormGeneralStepTpl.html',
+        controller: 'FeedGeneralInformationController'
+      })
+      .state('forms.feed.properties', {
+        templateUrl: 'html/feed/feedFormPropertiesStepTpl.html',
+        controller: 'FeedPropertiesController'
+      })
+      .state('forms.feed.location', {
+        templateUrl: 'html/feed/feedFormLocationStepTpl.html',
+        controller: 'FeedLocationController'
+      })
+      .state('forms.feed.clusters', {
+        templateUrl: 'html/feed/feedFormClustersStepTpl.html',
+        controller: 'FeedClustersController',
+        resolve: {
+          clustersList: ['Falcon', function(Falcon) {
+            return Falcon.getEntities('cluster').then(
+              function(response) {
+                return response.data;
+              });
+          }]
+        }
+      })
+      .state('forms.feed.summary', {
+        templateUrl: 'html/feed/feedFormSummaryStepTpl.html',
+        controller: 'FeedSummaryController'
+      })
+      .state('forms.process', {
+        templateUrl: 'html/process/processFormTpl.html',
+        controller: 'ProcessRootCtrl'
+      })
+      .state('forms.process.general', {
+        templateUrl: 'html/process/processFormGeneralStepTpl.html',
+        controller: 'ProcessGeneralInformationCtrl'
+      })
+      .state('forms.process.properties', {
+        templateUrl: 'html/process/processFormPropertiesStepTpl.html',
+        controller: 'ProcessPropertiesCtrl'
+      })
+      .state('forms.process.clusters', {
+        templateUrl: 'html/process/processFormClustersStepTpl.html',
+        controller: 'ProcessClustersCtrl',
+        resolve: {
+          clustersList: ['Falcon', function(Falcon) {
+            return Falcon.getEntities('cluster').then(
+              function(response) {
+                return response.data;
+              });
+          }]
+        }
+      })
+      .state('forms.process.io', {
+        templateUrl: 'html/process/processFormInputsAndOutputsStepTpl.html',
+        controller: 'ProcessInputsAndOutputsCtrl',
+        resolve: {
+          feedsList: ['Falcon', function(Falcon) {
+            return Falcon.getEntities('feed').then(
+              function(response) {
+                return response.data;
+              });
+          }]
+        }
+      })
+      .state('forms.process.summary', {
+        templateUrl: 'html/process/processFormSummaryStepTpl.html',
+        controller: 'ProcessSummaryCtrl'
+      });
+    
+  }]);
+
+  app.run(['$rootScope', 
+           function ($rootScope) {	
+    
+    $rootScope.$on('$stateChangeError',
+      function(event, toState, toParams, fromState, fromParams, error){
+        console.log('Manual log of stateChangeError: ' + error);
+      });
+		
+  }]);
+
+})();
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/app/js/controllers/cluster/cluster-module.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/controllers/cluster/cluster-module.js b/falcon-ui/app/js/controllers/cluster/cluster-module.js
new file mode 100644
index 0000000..d00a937
--- /dev/null
+++ b/falcon-ui/app/js/controllers/cluster/cluster-module.js
@@ -0,0 +1,306 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function () {
+  'use strict';
+
+  /***
+   * @ngdoc controller
+   * @name app.controllers.feed.FeedController
+   * @requires EntityModel the entity model to copy the feed entity from
+   * @requires Falcon the falcon service to talk with the Falcon REST API
+   */
+  var clusterModule = angular.module('app.controllers.cluster', [ 'app.services' ]);
+
+  clusterModule.controller('ClusterFormCtrl', [
+    "$scope", "$interval", "Falcon", "EntityModel", "$state", "X2jsService", "ValidationService",
+    function ($scope, $interval, Falcon, EntityModel, $state, X2jsService, validationService) {
+
+      $scope.clusterEntity = EntityModel;
+      $scope.xmlPreview = { edit: false };
+      $scope.secondStep = false;
+
+      function normalizeModel() {
+        //------------INTERFACE-----------//
+        var requiredInterfaceFields = ["readonly", "write", "execute", "workflow", "messaging", "registry"],
+          requiredLocationFields = ["staging", "temp", "working", ""],
+          modelInterfaceArray = $scope.clusterEntity.clusterModel.cluster.interfaces.interface,
+          modelLocationsArray = $scope.clusterEntity.clusterModel.cluster.locations.location;
+
+        modelInterfaceArray.forEach(function (element) {
+          requiredInterfaceFields.forEach(function (requiredField) {
+            if (element._type === requiredField) { requiredInterfaceFields.splice(requiredField, 1); }
+          });
+        });
+        $scope.registry = { check: true };
+        requiredInterfaceFields.forEach(function (fieldToPush) {
+          var fieldObject = { _type: fieldToPush, _endpoint: "", _version: "" };
+          if (fieldToPush === "registry") { $scope.registry = { check: false }; }
+          modelInterfaceArray.push(fieldObject);
+        });
+        //--------------TAGS--------------//
+        if ($scope.clusterEntity.clusterModel.cluster.tags === "" ||
+            $scope.clusterEntity.clusterModel.cluster.tags === undefined) {
+          $scope.clusterEntity.clusterModel.cluster.tags = "";
+          $scope.tagsArray = [{key: null, value: null}];
+        } else {
+          $scope.splitTags();
+        }
+        //-------------ACL----------------//
+        if (!$scope.clusterEntity.clusterModel.cluster.ACL) {
+          $scope.clusterEntity.clusterModel.cluster.ACL = {
+            _owner: "", _group: "", _permission: ""
+          };
+        }
+        //------------Location------------//
+        modelLocationsArray.forEach(function(element) {
+          requiredLocationFields.forEach(function(requiredField) {
+            if(element._name === requiredField) { requiredLocationFields.splice(requiredField, 1); }
+          });
+        });
+        requiredLocationFields.forEach(function(fieldToPush) {
+          var fieldObject = {_name: fieldToPush, _path: ""};
+          modelLocationsArray.push(fieldObject);
+        });
+        //----------Properties -------------//
+        if(!$scope.clusterEntity.clusterModel.cluster.properties) {
+          $scope.clusterEntity.clusterModel.cluster.properties = { property : [{ _name: "", _value: ""}] };
+        }
+
+      }
+
+      function cleanModel() {
+        //if registry check is false backups the object and removes it from array
+        if (!$scope.registry.check) {
+          $scope.clusterEntity.clusterModel.cluster.interfaces.interface.forEach(function(registry, index) {
+            if (registry._type === "registry") {
+              $scope.backupRegistryObject = $scope.clusterEntity.clusterModel.cluster.interfaces.interface[index];
+              $scope.clusterEntity.clusterModel.cluster.interfaces.interface.splice(index, 1);
+            }
+          });
+        }
+        //deletes property empty last object and array if empty
+        var lastOne = $scope.clusterEntity.clusterModel.cluster.properties.property.length - 1;
+        if (
+          $scope.clusterEntity.clusterModel.cluster.properties.property[lastOne]._name === "" ||
+          $scope.clusterEntity.clusterModel.cluster.properties.property[lastOne]._name === undefined ||
+          $scope.clusterEntity.clusterModel.cluster.properties.property[lastOne]._value === "" ||
+          $scope.clusterEntity.clusterModel.cluster.properties.property[lastOne]._value === undefined
+        ) {
+
+          $scope.removeProperty(lastOne);
+        }
+        if ($scope.clusterEntity.clusterModel.cluster.properties.property.length === 0) {
+          delete $scope.clusterEntity.clusterModel.cluster.properties;
+        }
+        var lastLocationIndex = $scope.clusterEntity.clusterModel.cluster.locations.location.length - 1;
+        if (
+          $scope.clusterEntity.clusterModel.cluster.locations.location[lastLocationIndex]._name === "" ||
+          $scope.clusterEntity.clusterModel.cluster.locations.location[lastLocationIndex]._name === undefined ||
+          $scope.clusterEntity.clusterModel.cluster.locations.location[lastLocationIndex]._path === "" ||
+          $scope.clusterEntity.clusterModel.cluster.locations.location[lastLocationIndex]._path === undefined
+        ) {
+          $scope.removeLocation(lastLocationIndex);
+        }
+        //deletes ACL if empty
+        if ($scope.clusterEntity.clusterModel.cluster.ACL &&
+            $scope.clusterEntity.clusterModel.cluster.ACL._owner === "") {
+          delete $scope.clusterEntity.clusterModel.cluster.ACL;
+        }
+        //deletes tags if empty
+        if ($scope.clusterEntity.clusterModel.cluster.tags.length === 0) {
+          delete $scope.clusterEntity.clusterModel.cluster.tags;
+        }
+        //moves properties to be the last element if acl exists
+        $scope.arrangeFieldsOrder();
+      }
+      $scope.arrangeFieldsOrder = function () {
+        var BK = $scope.clusterEntity.clusterModel.cluster,
+          orderedObj = {};
+
+        orderedObj._xmlns = 'uri:falcon:cluster:0.1';
+        orderedObj._name = BK._name;
+        orderedObj._description = BK._description;
+        orderedObj._colo = BK._colo;
+
+        if (BK.tags) { orderedObj.tags = BK.tags; }
+        if (BK.interfaces) { orderedObj.interfaces = BK.interfaces; }
+        if (BK.locations) { orderedObj.locations = BK.locations; }
+        if (BK.ACL) { orderedObj.ACL = BK.ACL; }
+        if (BK.properties) { orderedObj.properties = BK.properties; }
+
+        delete $scope.clusterEntity.clusterModel.cluster;
+        $scope.clusterEntity.clusterModel.cluster = orderedObj;
+
+      };
+      //--------------TAGS------------------------//
+
+      $scope.convertTags = function () {
+        var result = [];
+        $scope.tagsArray.forEach(function(element) {
+          if(element.key && element.value) {
+            result.push(element.key + "=" + element.value);
+          }
+        });
+        result = result.join(",");
+        $scope.clusterEntity.clusterModel.cluster.tags = result;
+      };
+      $scope.splitTags = function () {
+        $scope.tagsArray = [];
+        $scope.clusterEntity.clusterModel.cluster.tags.split(",").forEach(function (fieldToSplit) {
+          var splittedString = fieldToSplit.split("=");
+          $scope.tagsArray.push({key: splittedString[0], value: splittedString[1]});
+        });
+      };
+      $scope.addTag = function () {
+        $scope.tagsArray.push({key: null, value: null});
+      };
+      $scope.removeTag = function (index) {
+        if (!isNaN(index) && index !== undefined && index !== null) {
+          $scope.tagsArray.splice(index, 1);
+          $scope.convertTags();
+        }
+      };
+      //-------------------------------------//
+      //----------LOCATION-------------------//
+
+      $scope.addLocation = function () {
+        var lastOneIndex = $scope.clusterEntity.clusterModel.cluster.locations.location.length - 1;
+
+        if (!$scope.clusterEntity.clusterModel.cluster.locations.location[lastOneIndex]._name ||
+            !$scope.clusterEntity.clusterModel.cluster.locations.location[lastOneIndex]._path) {
+          //console.log('location empty');
+        } else {
+          $scope.clusterEntity.clusterModel.cluster.locations.location.push({_name: "", _path: ""});
+        }
+      };
+      $scope.removeLocation = function (index) {
+        if(!isNaN(index) && index !== undefined && index !== null) {
+          $scope.clusterEntity.clusterModel.cluster.locations.location.splice(index, 1);
+        }
+      };
+      //-----------PROPERTIES----------------//
+      $scope.addProperty = function () {
+        var lastOne = $scope.clusterEntity.clusterModel.cluster.properties.property.length - 1;
+        if($scope.clusterEntity.clusterModel.cluster.properties.property[lastOne]._name && $scope.clusterEntity.clusterModel.cluster.properties.property[lastOne]._value){
+          $scope.clusterEntity.clusterModel.cluster.properties.property.push({ _name: "", _value: ""});
+        // $scope.tempPropModel = { _name: "", _value: ""};
+        }
+      };
+      $scope.removeProperty = function(index) {
+        if(index !== null && $scope.clusterEntity.clusterModel.cluster.properties.property[index]) {
+          $scope.clusterEntity.clusterModel.cluster.properties.property.splice(index, 1);
+        }
+      };
+      //--------------------------------------//
+      $scope.goSummaryStep = function (formInvalid) {
+        if (!$scope.validations.nameAvailable || formInvalid) {
+          validationService.displayValidations.show = true;
+          validationService.displayValidations.nameShow = true;
+          return;
+        }
+        cleanModel();
+        $scope.secondStep = true;
+        $state.go("forms.cluster.summary");
+
+      };
+      $scope.goGeneralStep = function () {
+        $scope.secondStep = false;
+        validationService.displayValidations.show = false;
+        validationService.displayValidations.nameShow = false;
+        $scope.validations.nameAvailable = true;
+        if(!$scope.registry.check) {
+          //recovers previously deleted registry object
+          $scope.clusterEntity.clusterModel.cluster.interfaces.interface.push($scope.backupRegistryObject);
+        }
+        if(!$scope.clusterEntity.clusterModel.cluster.tags) {
+          $scope.clusterEntity.clusterModel.cluster.tags = "";
+        }
+        if(!$scope.clusterEntity.clusterModel.cluster.properties) {
+          $scope.clusterEntity.clusterModel.cluster.properties = {property : [{ _name: "", _value: ""}]};
+        }
+        var lastLocationIndex = $scope.clusterEntity.clusterModel.cluster.locations.location.length - 1;
+        if($scope.clusterEntity.clusterModel.cluster.locations.location[lastLocationIndex]._name !== "") {
+          $scope.addLocation();
+        }
+      };
+      $scope.saveCluster = function () {
+        $scope.saveModelBuffer();
+        Falcon.logRequest();
+        Falcon.postSubmitEntity($scope.jsonString, "cluster").success(function (response) {
+           Falcon.logResponse('success', response, false);
+           $state.go('main');
+         }).error(function (err) {
+           Falcon.logResponse('error', err, false);
+         });
+      };
+    
+      //--------------------------------------//
+      //----------XML preview-----------------//
+    
+      $scope.xmlPreview.editXML = function () {
+        $scope.xmlPreview.edit = !$scope.xmlPreview.edit;
+      };
+      $scope.showInPreview = function() {
+        var xmlStr = X2jsService.json2xml_str(angular.copy($scope.clusterEntity.clusterModel));
+        $scope.prettyXml = X2jsService.prettifyXml(xmlStr);
+        $scope.xml = xmlStr;
+      };
+      $scope.transformBack = function() {
+        try {
+          var xmlObj = X2jsService.xml_str2json($scope.prettyXml);
+          $scope.clusterEntity.clusterModel = xmlObj;
+
+          if($scope.clusterEntity.clusterModel.cluster.properties && $scope.clusterEntity.clusterModel.cluster.properties.property[0] === '') {
+            $scope.clusterEntity.clusterModel.cluster.properties.property=[];
+          }
+        }
+        catch(err) {
+          console.log('xml malformed');
+        }
+      };
+      $scope.saveModelBuffer = function () {
+        $scope.jsonString = angular.toJson($scope.clusterEntity.clusterModel);
+        //goes back to js to have x2js parse it correctly
+        $scope.jsonString = JSON.parse($scope.jsonString);
+        $scope.jsonString = X2jsService.json2xml_str($scope.jsonString);
+      };
+      function xmlPreviewCallback() {
+        if ($state.current.name !== 'forms.cluster.general' && $state.current.name !== 'forms.cluster.summary') {
+          $interval.cancel(refresher);
+        }
+        if(!$scope.xmlPreview.edit) {
+          if($scope.clusterEntity.clusterModel.cluster.tags !== undefined) { $scope.convertTags(); }
+          $scope.showInPreview();
+        }
+        else {
+          $scope.splitTags();
+          $scope.transformBack();
+        }
+      }
+      var refresher = $interval(xmlPreviewCallback, 1000);
+
+
+      //------------init------------//
+      normalizeModel();
+    }
+  ]);
+})();
+
+
+
+

http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/app/js/controllers/controllers.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/controllers/controllers.js b/falcon-ui/app/js/controllers/controllers.js
new file mode 100644
index 0000000..9fa878e
--- /dev/null
+++ b/falcon-ui/app/js/controllers/controllers.js
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function () {
+  'use strict';
+  
+  angular.module('app.controllers', [
+                                      'app.controllers.navHeader',
+                                      'app.controllers.rootCtrl',
+                                      'app.controllers.dashboardCtrl',
+                                      'app.controllers.view',
+                                      'app.controllers.cluster',
+                                      'app.controllers.feed',
+                                      'app.controllers.process',
+                                      'app.controllers.entity'   
+                                    ]);
+
+})();

http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/app/js/controllers/dashboard-controller.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/controllers/dashboard-controller.js b/falcon-ui/app/js/controllers/dashboard-controller.js
new file mode 100644
index 0000000..bd60736
--- /dev/null
+++ b/falcon-ui/app/js/controllers/dashboard-controller.js
@@ -0,0 +1,143 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function () {
+  'use strict';
+
+  var dashboardCtrlModule = angular.module('app.controllers.dashboardCtrl', ['app.services']);
+
+  dashboardCtrlModule.controller('DashboardCtrl', [ "$scope", "Falcon", "EntityModel", "FileApi", "$state", "X2jsService",
+    function ($scope, Falcon, EntityModel, FileApi, $state, X2jsService) {
+      
+      $scope.$parent.refreshLists();
+
+      $scope.deleteEntity = function (type, name) {
+        type = type.toLowerCase(); //new sandbox returns uppercase type
+        Falcon.logRequest();
+        Falcon.deleteEntity(type, name)
+          .success(function (data) {          
+            Falcon.logResponse('success', data, type);           
+            $scope.$parent.refreshList(type);              
+          })
+          .error(function (err) {
+            
+            Falcon.logResponse('error', err, type);
+          });
+      };
+      $scope.cloneEntity = function (type, name) {
+        type = type.toLowerCase(); //new sandbox returns uppercase type
+        
+        Falcon.logRequest();
+        Falcon.getEntityDefinition(type, name)
+          .success(function (data) {
+            Falcon.logResponse('success', data, false, true);
+            var modelName = type + "Model",
+                entityModel = X2jsService.xml_str2json(data);
+                
+            EntityModel[modelName] = entityModel;
+            EntityModel[modelName][type]._name = "";
+            $scope.models[modelName] = angular.copy(entityModel);
+            $scope.cloningMode = true; // dont know utility of this
+            $scope.$parent.cloningMode = true;
+            $state.go('forms.' + type + ".general");
+          })
+          .error(function (err) {
+            Falcon.logResponse('error', err, false, true);
+          });
+      };
+      $scope.editEntity = function (type, name) {        
+        type = type.toLowerCase(); //new sandbox returns uppercase type
+        
+        Falcon.logRequest();
+        Falcon.getEntityDefinition(type, name)
+          .success(function (data) {
+            Falcon.logResponse('success', data, false, true);
+            var entityModel = X2jsService.xml_str2json(data);
+            var modelName = type + "Model";
+            EntityModel[modelName] = entityModel;
+            $scope.models[modelName] = angular.copy(entityModel);
+            $scope.editingMode = true;// dont know utility of this
+            $scope.$parent.cloningMode = false;
+            $state.go('forms.' + type + ".general");
+          })
+          .error(function (err) {
+            Falcon.logResponse('error', err, false, true);
+          });
+      };
+      //-----------------------------------------//
+      $scope.entityDetails = function (name, type) {
+    	  type = type.toLowerCase(); //new sandbox returns uppercase type
+    	  
+    	  Falcon.logRequest();
+          Falcon.getEntityDefinition(type, name)
+            .success(function (data) {
+              Falcon.logResponse('success', data, false, true);
+              var entityModel = X2jsService.xml_str2json(data);
+              var modelName = type + "Model";
+              EntityModel[modelName] = entityModel;
+              $scope.models[modelName] = angular.copy(entityModel);
+              $scope.editingMode = true;// dont know utility of this
+              $scope.$parent.cloningMode = false;
+              //$state.go('forms.' + type + ".general");
+              $state.go('entityDetails');
+            })
+            .error(function (err) {
+              Falcon.logResponse('error', err, false, true);
+            });
+      };
+      //----------------------------------------//
+      $scope.resumeEntity = function (type, name) {
+        Falcon.logRequest();
+        Falcon.postResumeEntity(type, name).success(function (data) {
+          Falcon.logResponse('success', data, type);
+          $scope.$parent.refreshList(type);      
+        })
+        .error(function (err) {
+          Falcon.logResponse('error', err, type);
+        });
+      };
+      $scope.scheduleEntity = function (type, name) {
+        Falcon.logRequest();
+        Falcon.postScheduleEntity(type, name).success(function (data) {
+          Falcon.logResponse('success', data, type);
+          $scope.$parent.refreshList(type);      
+        })
+        .error(function (err) {
+          Falcon.logResponse('error', err, type);
+        });
+      };
+
+      $scope.suspendEntity = function (type, name) {
+        Falcon.logRequest();
+        Falcon.postSuspendEntity(type, name)
+          .success(function (message) {
+            Falcon.logResponse('success', message, type);           
+            $scope.$parent.refreshList(type);      
+          })
+          .error(function (err) {
+            Falcon.logResponse('error', err, type);
+            
+          });
+      };
+      $scope.relationsEntity = function (type, name) {
+        console.log("relations " + type + " - " + name);
+      };
+      
+      
+    }]);
+
+})();
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/app/js/controllers/entity/entity-module.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/controllers/entity/entity-module.js b/falcon-ui/app/js/controllers/entity/entity-module.js
new file mode 100644
index 0000000..cb36b45
--- /dev/null
+++ b/falcon-ui/app/js/controllers/entity/entity-module.js
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function () {
+  'use strict';
+
+  var entityModule = angular.module('app.controllers.entity',[ 'app.services' ]);
+
+  entityModule.controller('EntityRootCtrl',
+    [ '$scope',
+      function($scope) {
+
+        $scope.baseInit = function() {
+          $scope.editXmlDisabled = true;
+        };
+
+        $scope.toggleEditXml = function() {
+          $scope.editXmlDisabled = !$scope.editXmlDisabled;
+        };
+
+        $scope.capitalize = function(input) {
+            return input.charAt(0).toUpperCase() + input.slice(1);
+        };
+
+        $scope.cancel = function() {
+          var type = $scope.entityType;
+          $scope[type] = null;
+        };
+
+      }
+    ]
+  );
+
+})();

http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/app/js/controllers/entity/entity-view.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/controllers/entity/entity-view.js b/falcon-ui/app/js/controllers/entity/entity-view.js
new file mode 100644
index 0000000..d58f7f6
--- /dev/null
+++ b/falcon-ui/app/js/controllers/entity/entity-view.js
@@ -0,0 +1,221 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function () {
+  'use strict';
+
+  /***
+   * @ngdoc controller
+   * @name app.controllers.feed.FeedController
+   * @requires EntityModel the entity model to copy the feed entity from
+   * @requires Falcon the falcon service to talk with the Falcon REST API
+   */
+  var clusterModule = angular.module('app.controllers.view', [ 'app.services' ]);
+
+  clusterModule.controller('EntityDetailsCtrl', [
+    "$scope", "$interval", "Falcon", "EntityModel", "$state", "X2jsService",
+    function ($scope, $interval, Falcon, EntityModel, $state, X2jsService) {
+
+      $scope.clusterEntity = EntityModel;
+      $scope.xmlPreview = { edit: false };
+      $scope.secondStep = false;
+
+      function normalizeModel() {
+        //------------INTERFACE-----------//
+        var requiredInterfaceFields = ["readonly", "write", "execute", "workflow", "messaging", "registry"],
+          requiredLocationFields = ["staging", "temp", "working", ""],
+          modelInterfaceArray = $scope.clusterEntity.clusterModel.cluster.interfaces.interface,
+          modelLocationsArray = $scope.clusterEntity.clusterModel.cluster.locations.location;
+
+        modelInterfaceArray.forEach(function (element) {
+          requiredInterfaceFields.forEach(function (requiredField) {
+            if (element._type === requiredField) { requiredInterfaceFields.splice(requiredField, 1); }
+          });
+        });
+        $scope.registry = { check: true };
+        requiredInterfaceFields.forEach(function (fieldToPush) {
+          var fieldObject = { _type: fieldToPush, _endpoint: "", _version: "" };
+          if (fieldToPush === "registry") { $scope.registry = { check: false }; }
+          modelInterfaceArray.push(fieldObject);
+        });
+        //--------------TAGS--------------//
+        if ($scope.clusterEntity.clusterModel.cluster.tags === "" ||
+            $scope.clusterEntity.clusterModel.cluster.tags === undefined) {
+          $scope.clusterEntity.clusterModel.cluster.tags = "";
+          $scope.tagsArray = [{key: null, value: null}];
+        } else {
+          $scope.splitTags();
+        }
+        //-------------ACL----------------//
+        if (!$scope.clusterEntity.clusterModel.cluster.ACL) {
+          $scope.clusterEntity.clusterModel.cluster.ACL = {
+            _owner: "", _group: "", _permission: ""
+          };
+        }
+        //------------Location------------//
+        modelLocationsArray.forEach(function(element) {
+          requiredLocationFields.forEach(function(requiredField) {
+            if(element._name === requiredField) { requiredLocationFields.splice(requiredField, 1); }
+          });
+        });
+        requiredLocationFields.forEach(function(fieldToPush) {
+          var fieldObject = {_name: fieldToPush, _path: ""};
+          modelLocationsArray.push(fieldObject);
+        });
+        //----------Properties -------------//
+        if(!$scope.clusterEntity.clusterModel.cluster.properties) {
+          $scope.clusterEntity.clusterModel.cluster.properties = { property : [{ _name: "", _value: ""}] };
+        }
+
+      }
+
+      $scope.arrangeFieldsOrder = function () {
+        var BK = $scope.clusterEntity.clusterModel.cluster,
+          orderedObj = {};
+
+        orderedObj._xmlns = 'uri:falcon:cluster:0.1';
+        orderedObj._name = BK._name;
+        orderedObj._description = BK._description;
+        orderedObj._colo = BK._colo;
+
+        if (BK.tags) { orderedObj.tags = BK.tags; }
+        if (BK.interfaces) { orderedObj.interfaces = BK.interfaces; }
+        if (BK.locations) { orderedObj.locations = BK.locations; }
+        if (BK.ACL) { orderedObj.ACL = BK.ACL; }
+        if (BK.properties) { orderedObj.properties = BK.properties; }
+
+        delete $scope.clusterEntity.clusterModel.cluster;
+        $scope.clusterEntity.clusterModel.cluster = orderedObj;
+
+      };
+      //--------------TAGS------------------------//
+
+      $scope.convertTags = function () {
+        var result = [];
+        $scope.tagsArray.forEach(function(element) {
+          if(element.key && element.value) {
+            result.push(element.key + "=" + element.value);
+          }
+        });
+        result = result.join(",");
+        $scope.clusterEntity.clusterModel.cluster.tags = result;
+      };
+      $scope.splitTags = function () {
+        $scope.tagsArray = [];
+        $scope.clusterEntity.clusterModel.cluster.tags.split(",").forEach(function (fieldToSplit) {
+          var splittedString = fieldToSplit.split("=");
+          $scope.tagsArray.push({key: splittedString[0], value: splittedString[1]});
+        });
+      };
+      $scope.addTag = function () {
+        $scope.tagsArray.push({key: null, value: null});
+      };
+      $scope.removeTag = function (index) {
+        if (!isNaN(index) && index !== undefined && index !== null) {
+          $scope.tagsArray.splice(index, 1);
+          $scope.convertTags();
+        }
+      };
+      //-------------------------------------//
+      //----------LOCATION-------------------//
+
+      $scope.addLocation = function () {
+        var lastOneIndex = $scope.clusterEntity.clusterModel.cluster.locations.location.length - 1;
+
+        if (!$scope.clusterEntity.clusterModel.cluster.locations.location[lastOneIndex]._name ||
+            !$scope.clusterEntity.clusterModel.cluster.locations.location[lastOneIndex]._path) {
+          //console.log('location empty');
+        } else {
+          $scope.clusterEntity.clusterModel.cluster.locations.location.push({_name: "", _path: ""});
+        }
+      };
+      $scope.removeLocation = function (index) {
+        if(!isNaN(index) && index !== undefined && index !== null) {
+          $scope.clusterEntity.clusterModel.cluster.locations.location.splice(index, 1);
+        }
+      };
+      //-----------PROPERTIES----------------//
+      $scope.addProperty = function () {
+        var lastOne = $scope.clusterEntity.clusterModel.cluster.properties.property.length - 1;
+        if($scope.clusterEntity.clusterModel.cluster.properties.property[lastOne]._name && $scope.clusterEntity.clusterModel.cluster.properties.property[lastOne]._value){
+          $scope.clusterEntity.clusterModel.cluster.properties.property.push({ _name: "", _value: ""});
+        // $scope.tempPropModel = { _name: "", _value: ""};
+        }
+      };
+      $scope.removeProperty = function(index) {
+        if(index !== null && $scope.clusterEntity.clusterModel.cluster.properties.property[index]) {
+          $scope.clusterEntity.clusterModel.cluster.properties.property.splice(index, 1);
+        }
+      };
+    
+      //--------------------------------------//
+      //----------XML preview-----------------//
+    
+      $scope.xmlPreview.editXML = function () {
+        $scope.xmlPreview.edit = !$scope.xmlPreview.edit;
+      };
+      $scope.showInPreview = function() {
+        var xmlStr = X2jsService.json2xml_str(angular.copy($scope.clusterEntity.clusterModel));
+        $scope.prettyXml = X2jsService.prettifyXml(xmlStr);
+        $scope.xml = xmlStr;
+      };
+      $scope.transformBack = function() {
+        try {
+          var xmlObj = X2jsService.xml_str2json($scope.prettyXml);
+          $scope.clusterEntity.clusterModel = xmlObj;
+
+          if($scope.clusterEntity.clusterModel.cluster.properties && $scope.clusterEntity.clusterModel.cluster.properties.property[0] === '') {
+            $scope.clusterEntity.clusterModel.cluster.properties.property=[];
+          }
+        }
+        catch(err) {
+          console.log('xml malformed');
+        }
+      };
+      $scope.saveModelBuffer = function () {
+        $scope.jsonString = angular.toJson($scope.clusterEntity.clusterModel);
+        //goes back to js to have x2js parse it correctly
+        $scope.jsonString = JSON.parse($scope.jsonString);
+        $scope.jsonString = X2jsService.json2xml_str($scope.jsonString);
+      };
+      function xmlPreviewCallback() {
+        if ($state.current.name !== 'forms.cluster.general' && $state.current.name !== 'forms.cluster.summary') {
+          $interval.cancel(refresher);
+        }
+        if(!$scope.xmlPreview.edit) {
+          if($scope.clusterEntity.clusterModel.cluster.tags !== undefined) { $scope.convertTags(); }
+          $scope.showInPreview();
+        }
+        else {
+          $scope.splitTags();
+          $scope.transformBack();
+        }
+      }
+      var refresher = $interval(xmlPreviewCallback, 1000);
+      
+      //xmlPreviewCallback();
+
+
+      //------------init------------//
+      normalizeModel();
+    }
+  ]);
+})();
+
+
+
+

http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/app/js/controllers/feed/feed-clusters-controller.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/controllers/feed/feed-clusters-controller.js b/falcon-ui/app/js/controllers/feed/feed-clusters-controller.js
new file mode 100644
index 0000000..55a5651
--- /dev/null
+++ b/falcon-ui/app/js/controllers/feed/feed-clusters-controller.js
@@ -0,0 +1,117 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function () {
+  'use strict';
+
+  /***
+   * @ngdoc controller
+   * @name app.controllers.feed.FeedController
+   * @requires clusters the list of clusters to display for selection of source
+   * @requires EntityModel the entity model to copy the feed entity from
+   * @requires Falcon the falcon entity service
+   */
+  var feedModule = angular.module('app.controllers.feed');
+
+  feedModule.controller('FeedClustersController', ["$scope","clustersList", "EntityFactory",
+
+    function($scope, clustersList, entityFactory) {
+
+      unwrapClusters(clustersList);
+
+      $scope.updateRetention = function() {
+        if($scope.selectedCluster.retention.action === 'archive' && $scope.selectedCluster.type === 'source') {
+          $scope.allClusters.length = 0;
+          $scope.allClusters.concat($scope.feed.clusters);
+
+          $scope.feed.clusters.length = 0;
+          $scope.feed.clusters.push($scope.sourceCluster);
+          $scope.feed.clusters.push($scope.archiveCluster);
+
+          $scope.sourceCluster.selected = false;
+          $scope.archiveCluster.selected = true;
+          $scope.archiveCluster.active = true;
+          $scope.selectedCluster = $scope.archiveCluster;
+        }
+
+        if($scope.selectedCluster.retention.action !== 'archive'&& $scope.selectedCluster.type === 'source' && $scope.archiveCluster.active) {
+          $scope.archiveCluster.selected = false;
+          $scope.feed.clusters.length = 0;
+          $scope.allClusters.length = 0;
+          $scope.feed.clusters.push($scope.sourceCluster);
+          $scope.sourceCluster.selected = true;
+          $scope.archiveCluster.active = false;
+        }
+      };
+
+      $scope.addCluster = function() {
+        $scope.selectedCluster.selected = false;
+        var cluster = $scope.newCluster(true);
+        $scope.feed.clusters.push(cluster);
+        $scope.selectedCluster = cluster;
+      };
+
+      $scope.newCluster = function(selected) {
+        return entityFactory.newCluster('target', selected);
+      };
+
+      $scope.handleCluster = function(cluster, index) {
+        if(cluster.selected) {
+          $scope.removeCluster(index);
+        } else {
+          $scope.selectCluster(cluster);
+        }
+      };
+
+      $scope.selectCluster = function (cluster) {
+        $scope.selectedCluster.selected = false;
+        cluster.selected = true;
+        $scope.selectedCluster = cluster;
+      };
+
+      $scope.removeCluster = function(index) {
+        if(index >= 0 && $scope.feed.clusters.length > 1 &&
+          $scope.feed.clusters[index].type !== 'source' &&
+          !$scope.archiveCluster.active) {
+          $scope.feed.clusters.splice(index, 1);
+          $scope.selectCluster($scope.sourceCluster);
+        }
+      };
+
+      function unwrapClusters(clusters) {
+        $scope.clusterList = [];
+        var typeOfData = Object.prototype.toString.call(clusters.entity);
+        if(typeOfData === "[object Array]") {
+          $scope.clusterList = clusters.entity;
+        } else if(typeOfData === "[object Object]") {
+          $scope.clusterList = [clusters.entity];
+        } else {
+          //console.log("type of data not recognized");
+        }
+      }
+
+
+      $scope.selectedCluster = $scope.selectedCluster || $scope.feed.clusters[0];
+      $scope.sourceCluster = $scope.sourceCluster || $scope.feed.clusters[0];
+      $scope.archiveCluster = $scope.newCluster(false);
+      $scope.archiveCluster.active = false;
+      $scope.allClusters = [];
+
+
+    }]);
+
+})();

http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/app/js/controllers/feed/feed-general-information-controller.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/controllers/feed/feed-general-information-controller.js b/falcon-ui/app/js/controllers/feed/feed-general-information-controller.js
new file mode 100644
index 0000000..40dbdbc
--- /dev/null
+++ b/falcon-ui/app/js/controllers/feed/feed-general-information-controller.js
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function () {
+  'use strict';
+
+  /***
+   * @ngdoc controller
+   * @name app.controllers.feed.FeedController
+   * @requires clusters the list of clusters to display for selection of source
+   * @requires EntityModel the entity model to copy the feed entity from
+   * @requires Falcon the falcon entity service
+   */
+  var feedModule = angular.module('app.controllers.feed');
+
+
+  feedModule.controller('FeedGeneralInformationController', [ "$scope", function ($scope) {
+
+    $scope.nameValid = false;
+
+    $scope.addTag = function () {
+      $scope.feed.tags.push({key: null, value: null});
+    };
+
+    $scope.removeTag = function (index) {
+      if (index >= 0 && $scope.feed.tags.length > 1) {
+        $scope.feed.tags.splice(index, 1);
+      }
+    };
+
+  }]);
+
+
+}());

http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/app/js/controllers/feed/feed-location-controller.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/controllers/feed/feed-location-controller.js b/falcon-ui/app/js/controllers/feed/feed-location-controller.js
new file mode 100644
index 0000000..bbb488b
--- /dev/null
+++ b/falcon-ui/app/js/controllers/feed/feed-location-controller.js
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function () {
+  'use strict';
+
+  /***
+   * @ngdoc controller
+   * @name app.controllers.feed.FeedController
+   * @requires clusters the list of clusters to display for selection of source
+   * @requires EntityModel the entity model to copy the feed entity from
+   * @requires Falcon the falcon entity service
+   */
+  var feedModule = angular.module('app.controllers.feed');
+
+  feedModule.controller('FeedLocationController', [ "$scope",function($scope) {
+
+    $scope.toggleStorage = function() {
+      toggle($scope.feed.storage.fileSystem);
+      toggle($scope.feed.storage.catalog);
+    };
+
+    function toggle(storage) {
+      storage.active = !storage.active;
+    }
+
+  }]);
+
+
+})();

http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/app/js/controllers/feed/feed-module.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/controllers/feed/feed-module.js b/falcon-ui/app/js/controllers/feed/feed-module.js
new file mode 100644
index 0000000..4ad3308
--- /dev/null
+++ b/falcon-ui/app/js/controllers/feed/feed-module.js
@@ -0,0 +1,34 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function () {
+  'use strict';
+
+  /***
+   * @ngdoc controller
+   * @name app.controllers.feed.FeedController
+   * @requires clusters the list of clusters to display for selection of source
+   * @requires EntityModel the entity model to copy the feed entity from
+   * @requires Falcon the falcon entity service
+   */
+  angular.module('app.controllers.feed',
+    [
+     'app.controllers.entity',
+     'app.services'    
+    ]);
+
+})();

http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/app/js/controllers/feed/feed-properties-controller.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/controllers/feed/feed-properties-controller.js b/falcon-ui/app/js/controllers/feed/feed-properties-controller.js
new file mode 100644
index 0000000..0f10ac9
--- /dev/null
+++ b/falcon-ui/app/js/controllers/feed/feed-properties-controller.js
@@ -0,0 +1,42 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function () {
+  'use strict';
+
+  /***
+   * @ngdoc controller
+   * @name app.controllers.feed.FeedController
+   * @requires clusters the list of clusters to display for selection of source
+   * @requires EntityModel the entity model to copy the feed entity from
+   * @requires Falcon the falcon entity service
+   */
+  var feedModule = angular.module('app.controllers.feed');
+
+  feedModule.controller('FeedPropertiesController', [ "$scope",function($scope) {
+    $scope.addCustomProperty = function () {
+      $scope.feed.customProperties.push({key: null, value: null});
+    };
+
+    $scope.removeCustomProperty = function (index) {
+      if(index >= 0 && $scope.feed.customProperties.length > 1) {
+        $scope.feed.customProperties.splice(index, 1);
+      }
+    };
+  }]);
+
+})();

http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/app/js/controllers/feed/feed-root-ctrl.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/controllers/feed/feed-root-ctrl.js b/falcon-ui/app/js/controllers/feed/feed-root-ctrl.js
new file mode 100644
index 0000000..27dad77
--- /dev/null
+++ b/falcon-ui/app/js/controllers/feed/feed-root-ctrl.js
@@ -0,0 +1,178 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * 'License'); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function () {
+  'use strict';
+
+  /***
+   * @ngdoc controller
+   * @name app.controllers.feed.FeedController
+   * @requires clusters the list of clusters to display for selection of source
+   * @requires EntityModel the entity model to copy the feed entity from
+   * @requires Falcon the falcon entity service
+   */
+  var feedModule = angular.module('app.controllers.feed');
+
+  feedModule.controller('FeedController',
+    [ '$scope', '$state', '$timeout',
+      'Falcon', 'X2jsService',
+      'JsonTransformerFactory', 'EntityFactory',
+      'EntitySerializer', '$interval',
+      '$controller', "ValidationService",
+      function($scope, $state, $timeout, Falcon,
+               X2jsService, transformerFactory, entityFactory,
+               serializer, $interval, $controller, validationService) {
+
+        $scope.entityType = 'feed';
+
+        //extending root controller
+        $controller('EntityRootCtrl', {
+          $scope: $scope
+        });
+
+        $scope.loadOrCreateEntity = function() {
+          var type = $scope.entityType;
+          var model = $scope.models[type + 'Model'];
+          $scope.models[type + 'Model'] = null;
+          return model ? serializer.preDeserialize(model, type) : entityFactory.newEntity(type);
+        };
+
+        $scope.init = function() {
+          $scope.baseInit();
+          var type = $scope.entityType;
+          $scope[type] = $scope.loadOrCreateEntity();
+          $scope.dateFormat ='dd-MMMM-yyyy';
+        };
+
+        $scope.openDatePicker = function($event, container) {
+          $event.preventDefault();
+          $event.stopPropagation();
+          container.opened = true;
+        };
+
+        $scope.init();
+
+        $scope.transform = function() {
+          var type = $scope.entityType;
+          var xml = serializer.serialize($scope[type], $scope.entityType);
+          $scope.prettyXml = X2jsService.prettifyXml(xml);
+          $scope.xml = xml;
+          return xml;
+        };
+
+        $scope.saveEntity = function() {
+          var type = $scope.entityType;
+          if(!$scope.$parent.cloningMode) {
+            Falcon.logRequest();
+            Falcon.postUpdateEntity($scope.xml, $scope.entityType, $scope[type].name)
+              .success(function (response) {
+                Falcon.logResponse('success', response, false); 
+                $state.go('main');
+              })
+              .error(function(err) {
+                Falcon.logResponse('error', err, false);
+              });
+          } else {
+            Falcon.logRequest();
+            Falcon.postSubmitEntity($scope.xml, $scope.entityType)
+              .success(function (response) {
+                Falcon.logResponse('success', response, false); 
+                $state.go('main');
+              })
+              .error(function(err) {
+                Falcon.logResponse('error', err, false);
+              });
+          }
+
+          $scope.editingMode = false;
+          $scope.cloningMode = false;
+        };
+
+        $scope.isActive = function (route) {
+          return route === $state.$current.name;
+        };
+
+        $scope.parseDate = function(input) {
+          return input ? input.split('T')[0] : input;
+        };
+
+        $scope.parseTime = function(input) {
+          if (input) {
+            var partialTime = input.split('T')[1].split(':');
+            partialTime = partialTime[0] + ':' + partialTime[1];
+            return partialTime;
+          }
+          return 'Not defined';
+        };
+
+        $scope.appendVariable = function(timeVariable, holder, fieldName) {
+          holder[fieldName] = holder[fieldName] ? (holder[fieldName] + '-' + timeVariable) : timeVariable;
+          holder.focused = false;
+        };
+
+        var xmlPreviewCallback = function() {
+          var type = $scope.entityType;
+          if($scope.editXmlDisabled) {
+            try {
+              $scope.transform();
+            } catch (exception) {
+              console.log('error when transforming xml');
+              console.log(exception);
+            }
+          } else {
+            try {
+              $scope[type] = serializer.deserialize($scope.prettyXml, type);
+            } catch (exception) {
+              console.log('user entered xml incorrect format');
+              console.log(exception);
+            }
+          }
+
+        };
+
+        var xmlPreviewWorker = $interval(xmlPreviewCallback, 1000);
+
+        $scope.$on('$destroy', function () {
+          $interval.cancel(xmlPreviewWorker);
+        });
+
+        //$scope.nameValid = $scope.$parent.nameValid;
+        /*
+        * needed for validation
+        * */
+        $scope.goNext = function (formInvalid, stateName) {
+          if (!validationService.nameAvailable || formInvalid) {
+            validationService.displayValidations.show = true;
+            validationService.displayValidations.nameShow = true;
+            return;
+          }
+          validationService.displayValidations.show = false;
+          validationService.displayValidations.nameShow = false;
+          $state.go(stateName);
+        };
+        $scope.goBack = function (stateName) {
+          validationService.displayValidations.show = false;
+          validationService.displayValidations.nameShow = false;
+          $state.go(stateName);
+        };
+
+      }]);
+
+
+  
+
+})();

http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/app/js/controllers/feed/feed-summary-controller.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/controllers/feed/feed-summary-controller.js b/falcon-ui/app/js/controllers/feed/feed-summary-controller.js
new file mode 100644
index 0000000..77e1dcc
--- /dev/null
+++ b/falcon-ui/app/js/controllers/feed/feed-summary-controller.js
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function () {
+  'use strict';
+
+  /***
+   * @ngdoc controller
+   * @name app.controllers.feed.FeedController
+   * @requires clusters the list of clusters to display for selection of source
+   * @requires EntityModel the entity model to copy the feed entity from
+   * @requires Falcon the falcon entity service
+   */
+  var feedModule = angular.module('app.controllers.feed');
+
+  feedModule.controller('FeedSummaryController', [ "$scope", "$filter", function($scope, $filter) {
+
+    if($scope.transform) {
+      $scope.transform();
+    }
+
+    $scope.hasTags = function() {
+      var filteredTags = $filter('filter')($scope.feed.tags, {key: '!!'});
+      return filteredTags.length > 0;
+    };
+
+    $scope.optional = function(input, output) {
+      return input ? (output || input) : 'Not specified';
+    };
+
+  }]);
+
+
+})();

http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/app/js/controllers/header-controller.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/controllers/header-controller.js b/falcon-ui/app/js/controllers/header-controller.js
new file mode 100644
index 0000000..5039b99
--- /dev/null
+++ b/falcon-ui/app/js/controllers/header-controller.js
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function () {
+  'use strict';
+
+  var navHeaderModule = angular.module('app.controllers.navHeader', [
+    'app.services.entity.model',
+    'app.services.validation',
+    'ngCookies'
+  ]);
+
+  navHeaderModule.controller('HeaderController', [
+    '$rootScope', '$scope', '$state', '$cookieStore', 'EntityModel', 'ValidationService',
+    function ($rootScope, $scope, $state, $cookieStore, EntityModel, validationService) {
+
+      $scope.resetCluster = function () {
+        validationService.displayValidations = {show: false, nameShow: false};
+        EntityModel.clusterModel = { cluster: { tags: "", interfaces: { interface: [
+            { _type: "readonly", _endpoint: "hftp://sandbox.hortonworks.com:50070", _version: "2.2.0"},
+            { _type: "write", _endpoint: "hdfs://sandbox.hortonworks.com:8020", _version: "2.2.0"},
+            { _type: "execute", _endpoint: "sandbox.hortonworks.com:8050", _version: "2.2.0"},
+            { _type: "workflow", _endpoint: "http://sandbox.hortonworks.com:11000/oozie/", _version: "4.0.0"},
+            { _type: "messaging", _endpoint: "tcp://sandbox.hortonworks.com:61616?daemon=true", _version: "5.1.6"}
+          ]}, locations: { location: [{ _name: "staging", _path: ""}, { _name: "temp", _path: ""}, { _name: "working", _path: ""}]},
+          ACL: { _owner: "", _group: "", _permission: ""}, properties: { property: [{ _name: "", _value: ""}]},
+          _xmlns: "uri:falcon:cluster:0.1", _name: "", _description: "", _colo: ""}};
+        $state.go("forms.cluster.general");
+      };
+
+      $scope.resetProcess = function () {
+        validationService.displayValidations = {show: false, nameShow: false};
+        $scope.cloningMode = true;
+        $state.go("forms.process.general");
+      };
+
+      $scope.resetFeed = function () {
+        validationService.displayValidations = {show: false, nameShow: false};
+        $scope.cloningMode = true;
+        $state.go("forms.feed.general");
+      };
+        
+    }]);
+
+})();
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/app/js/controllers/process/process-clusters-ctrl.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/controllers/process/process-clusters-ctrl.js b/falcon-ui/app/js/controllers/process/process-clusters-ctrl.js
new file mode 100644
index 0000000..b588c84
--- /dev/null
+++ b/falcon-ui/app/js/controllers/process/process-clusters-ctrl.js
@@ -0,0 +1,71 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function () {
+  'use strict';
+
+  /***
+   * @ngdoc controller
+   * @name app.controllers.feed.FeedController
+   * @requires clusters the list of clusters to display for selection of source
+   * @requires EntityModel the entity model to copy the feed entity from
+   * @requires Falcon the falcon entity service
+   */
+  var feedModule = angular.module('app.controllers.process');
+
+  feedModule.controller('ProcessClustersCtrl',
+    ['$scope', 'clustersList', 'EntityFactory', function($scope, clustersList, entityFactory) {
+
+    $scope.init = function() {
+      $scope.dateFormat = 'dd-MMMM-yyyy';
+    };
+
+    $scope.openDatePicker = function($event, container) {
+      $event.preventDefault();
+      $event.stopPropagation();
+      container.opened = true;
+    };
+
+    $scope.addCluster = function() {
+      $scope.process.clusters.push(entityFactory.newCluster());
+    };
+
+    $scope.removeCluster = function(index) {
+      if(index >= 0 && $scope.process.clusters.length > 1) {
+        $scope.process.clusters.splice(index, 1);
+      }
+    };
+
+    unwrapClusters(clustersList);
+
+    function unwrapClusters(clusters) {
+      $scope.clusterList = [];
+      var typeOfData = Object.prototype.toString.call(clusters.entity);
+      if(typeOfData === "[object Array]") {
+        $scope.clusterList = clusters.entity;
+      } else if(typeOfData === "[object Object]") {
+        $scope.clusterList = [clusters.entity];
+      } else {
+        //console.log("type of data not recognized"); 
+      }
+    }
+
+    $scope.init();
+
+  }]);
+
+})();

http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/app/js/controllers/process/process-general-information-ctrl.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/controllers/process/process-general-information-ctrl.js b/falcon-ui/app/js/controllers/process/process-general-information-ctrl.js
new file mode 100644
index 0000000..ec689f3
--- /dev/null
+++ b/falcon-ui/app/js/controllers/process/process-general-information-ctrl.js
@@ -0,0 +1,66 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function () {
+  'use strict';
+
+  /***
+   * @ngdoc controller
+   * @name app.controllers.feed.FeedController
+   * @requires clusters the list of clusters to display for selection of source
+   * @requires EntityModel the entity model to copy the feed entity from
+   * @requires Falcon the falcon entity service
+   */
+  var feedModule = angular.module('app.controllers.process');
+
+  feedModule.controller('ProcessGeneralInformationCtrl', [ '$scope', function($scope) {
+    var availableVerions = {
+      oozie: ['3.1.3-incubating', '3.2.0-incubating', '3.3.0', '3.3.1', '3.3.2', '4.0.0', '4.0.1'],
+      pig: ['pig-0.10.0', 'pig-0.10.1', 'pig-0.11.0', 'pig-0.11.1', 'pig-0.12.0', 'pig-0.12.1', 'pig-0.13.0', 'pig-0.8.0', 'pig-0.8.1', ' pig-0.9.0', ' pig-0.9.1', 'pig-0.9.2'],
+      hive: ['hive-0.10.0', 'hive-0.11.0', 'hive-0.12.0', 'hive-0.13.0', 'hive-0.13.1', 'hive-0.6.0', 'hive-0.7.0', 'hive-0.8.0', 'hive-0.8.1', 'hive-0.9.0']
+    };
+    $scope.nameValid = false;
+    
+    $scope.init = function() {
+      $scope.versions = [];
+    };
+
+    $scope.addTag = function() {
+      $scope.process.tags.push({key: null, value: null});
+    };
+
+    $scope.removeTag = function(index) {
+      if(index >= 0 && $scope.process.tags.length > 1) {
+        $scope.process.tags.splice(index, 1);
+      }
+    };
+
+    $scope.selectWorkflow = function() {
+      
+      if($scope.process.workflow) {        
+        var engine = $scope.process.workflow.engine;
+        $scope.versions = availableVerions[engine];
+      }
+    };
+
+    $scope.init();
+    $scope.selectWorkflow();
+
+  }]);
+
+
+})();

http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/app/js/controllers/process/process-inputs-and-outputs-ctrl.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/controllers/process/process-inputs-and-outputs-ctrl.js b/falcon-ui/app/js/controllers/process/process-inputs-and-outputs-ctrl.js
new file mode 100644
index 0000000..c9b22d0
--- /dev/null
+++ b/falcon-ui/app/js/controllers/process/process-inputs-and-outputs-ctrl.js
@@ -0,0 +1,76 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function () {
+  'use strict';
+
+  /***
+   * @ngdoc controller
+   * @name app.controllers.feed.FeedController
+   * @requires clusters the list of clusters to display for selection of source
+   * @requires EntityModel the entity model to copy the feed entity from
+   * @requires Falcon the falcon entity service
+   */
+  var feedModule = angular.module('app.controllers.process');
+
+  feedModule.controller('ProcessInputsAndOutputsCtrl',
+
+    ['$scope', 'EntityFactory', 'feedsList', function($scope, entityFactory, feedsList) {
+
+      $scope.init = function() {
+
+      };
+
+      $scope.addInput = function() {
+        $scope.process.inputs.push(entityFactory.newInput());
+      };
+
+      $scope.removeInput = function(index) {
+        if(index >= 0) {
+          $scope.process.inputs.splice(index, 1);
+        }
+      };
+
+      $scope.addOutput = function() {
+        $scope.process.outputs.push(entityFactory.newOutput());
+      };
+
+      $scope.removeOutput = function(index) {
+        if(index >= 0) {
+          $scope.process.outputs.splice(index, 1);
+        }
+      };
+
+
+      unwrapClusters(feedsList);
+
+      function unwrapClusters(feeds) {
+        $scope.feedsList = [];
+        var typeOfData = Object.prototype.toString.call(feeds.entity);
+        if(typeOfData === "[object Array]") {
+          $scope.feedsList = feeds.entity;
+        } else if(typeOfData === "[object Object]") {
+          $scope.feedsList = [feeds.entity];
+        } else {
+          //console.log("type of data not recognized");
+        }
+      }
+
+      $scope.init();
+
+    }]);
+})();

http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/app/js/controllers/process/process-module.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/controllers/process/process-module.js b/falcon-ui/app/js/controllers/process/process-module.js
new file mode 100644
index 0000000..dbbcc6f
--- /dev/null
+++ b/falcon-ui/app/js/controllers/process/process-module.js
@@ -0,0 +1,33 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function () {
+  'use strict';
+
+  /***
+   * @ngdoc controller
+   * @name app.controllers.feed.FeedController
+   * @requires clusters the list of clusters to display for selection of source
+   * @requires EntityModel the entity model to copy the feed entity from
+   * @requires Falcon the falcon entity service
+   */
+  angular.module('app.controllers.process', [
+    'app.services',
+    'app.controllers.entity'
+  ]);
+
+})();

http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/app/js/controllers/process/process-properties-controller.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/controllers/process/process-properties-controller.js b/falcon-ui/app/js/controllers/process/process-properties-controller.js
new file mode 100644
index 0000000..d4e747c
--- /dev/null
+++ b/falcon-ui/app/js/controllers/process/process-properties-controller.js
@@ -0,0 +1,34 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function () {
+  'use strict';
+
+  /***
+   * @ngdoc controller
+   * @name app.controllers.feed.FeedController
+   * @requires clusters the list of clusters to display for selection of source
+   * @requires EntityModel the entity model to copy the feed entity from
+   * @requires Falcon the falcon entity service
+   */
+  var feedModule = angular.module('app.controllers.process');
+
+  feedModule.controller('ProcessPropertiesCtrl', [ '$scope', function() {
+
+  }]);
+
+})();

http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/app/js/controllers/process/process-root-ctrl.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/controllers/process/process-root-ctrl.js b/falcon-ui/app/js/controllers/process/process-root-ctrl.js
new file mode 100644
index 0000000..ba69705
--- /dev/null
+++ b/falcon-ui/app/js/controllers/process/process-root-ctrl.js
@@ -0,0 +1,117 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function () {
+  'use strict';
+
+  /***
+   * @ngdoc controller
+   * @name app.controllers.feed.FeedController
+   * @requires clusters the list of clusters to display for selection of source
+   * @requires EntityModel the entity model to copy the feed entity from
+   * @requires Falcon the falcon entity service
+   */
+  var feedModule = angular.module('app.controllers.process');
+
+  feedModule.controller('ProcessRootCtrl', [
+    '$scope', '$state', '$interval', '$controller', 'EntityFactory',
+    'EntitySerializer', 'X2jsService', 'ValidationService',
+    function ($scope, $state, $interval, $controller, entityFactory, serializer, X2jsService, validationService) {
+
+      $scope.entityType = 'process';
+
+        //extending root controller
+      $controller('EntityRootCtrl', {
+        $scope: $scope
+      });
+
+      $scope.init = function() {
+        $scope.baseInit();
+        var type = $scope.entityType;
+        $scope[type] = $scope.loadOrCreateEntity();
+      };
+
+      $scope.isActive = function (route) {
+        return route === $state.$current.name;
+      };
+
+      $scope.loadOrCreateEntity = function() {
+        var type = $scope.entityType;
+        var model = $scope.models[type + 'Model'];
+        $scope.models[type + 'Model'] = null;
+        if(model) {
+          return serializer.preDeserialize(model, type);
+        }
+        return entityFactory.newEntity(type);
+      };
+
+      $scope.init();
+
+      $scope.transform = function() {
+        var type = $scope.entityType;
+        var xml = serializer.serialize($scope[type], $scope.entityType);
+        $scope.prettyXml = X2jsService.prettifyXml(xml);
+        $scope.xml = xml;
+        return xml;
+      };
+
+      var xmlPreviewCallback = function() {
+        var type = $scope.entityType;
+        if($scope.editXmlDisabled) {
+          try {
+            $scope.transform();
+          } catch (exception) {
+            console.log('error when transforming xml');
+            console.log(exception);
+          }
+        } else {
+          try {
+            $scope[type] = serializer.deserialize($scope.prettyXml, type);
+          } catch (exception) {
+            console.log('user entered xml incorrect format');
+            console.log(exception);
+          }
+        }
+
+      };
+
+      var xmlPreviewWorker = $interval(xmlPreviewCallback, 1000);
+
+      $scope.$on('$destroy', function() {
+        $interval.cancel(xmlPreviewWorker);
+      });
+
+      //---------------------------------//
+      $scope.goNext = function (formInvalid, stateName) {
+        if (!validationService.nameAvailable || formInvalid) {
+          validationService.displayValidations.show = true;
+          validationService.displayValidations.nameShow = true;
+          return;
+        }
+        validationService.displayValidations.show = false;
+        validationService.displayValidations.nameShow = false;
+        $state.go(stateName);
+      };
+      $scope.goBack = function (stateName) {
+        validationService.displayValidations.show = false;
+        validationService.displayValidations.nameShow = false;
+        $state.go(stateName);
+      };
+    }
+  ]);
+
+}());

http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/app/js/controllers/process/process-summary-ctrl.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/controllers/process/process-summary-ctrl.js b/falcon-ui/app/js/controllers/process/process-summary-ctrl.js
new file mode 100644
index 0000000..d81bb62
--- /dev/null
+++ b/falcon-ui/app/js/controllers/process/process-summary-ctrl.js
@@ -0,0 +1,83 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function () {
+  'use strict';
+
+  /***
+   * @ngdoc controller
+   * @name app.controllers.feed.FeedController
+   * @requires clusters the list of clusters to display for selection of source
+   * @requires EntityModel the entity model to copy the feed entity from
+   * @requires Falcon the falcon entity service
+   */
+  var feedModule = angular.module('app.controllers.process');
+
+  feedModule.controller('ProcessSummaryCtrl', [ '$scope', '$state', '$timeout', '$filter', 'Falcon',
+                                                  function($scope, $state, $timeout, $filter, Falcon) {
+
+    $scope.init = function() {
+      if($scope.transform) {
+        $scope.transform();
+      }
+    };
+
+    $scope.hasTags = function() {
+      var filteredTags = $filter('filter')($scope.process.tags, {key: '!!'});
+      return filteredTags.length > 0;
+    };
+
+    $scope.optional = function(input, output) {
+      return input ? (output || input) : 'Not specified';
+    };
+
+    $scope.saveEntity = function() {
+      var type = $scope.entityType;
+      if(!$scope.$parent.cloningMode) { 
+        Falcon.logRequest();
+        Falcon.postUpdateEntity($scope.xml, $scope.entityType, $scope[type].name)
+          .success(function (response) {
+             Falcon.logResponse('success', response, false); 
+             $state.go('main'); 
+
+          })
+          .error(function (err) {   
+            Falcon.logResponse('error', err, false);          
+          });
+      } 
+      else {
+        Falcon.logRequest();
+        Falcon.postSubmitEntity($scope.xml, $scope.entityType)
+          .success(function (response) {
+             Falcon.logResponse('success', response, false); 
+             $state.go('main'); 
+ 
+          })
+          .error(function (err) {   
+            Falcon.logResponse('error', err, false);          
+          });
+      }
+
+      $scope.editingMode = false;
+      $scope.cloningMode = false;
+    };
+
+    $scope.init();
+
+  }]);
+
+})();

http://git-wip-us.apache.org/repos/asf/falcon/blob/c4df0a5e/falcon-ui/app/js/controllers/root-controller.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/controllers/root-controller.js b/falcon-ui/app/js/controllers/root-controller.js
new file mode 100644
index 0000000..9a71d30
--- /dev/null
+++ b/falcon-ui/app/js/controllers/root-controller.js
@@ -0,0 +1,91 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function () {
+  'use strict';
+
+  var app = angular.module('app.controllers.rootCtrl', ['app.services']);
+
+  app.controller('RootCtrl', [
+    "$scope", "$timeout", "Falcon", "FileApi", "EntityModel", "$state", "X2jsService", "ValidationService",
+    function ($scope, $timeout, Falcon, FileApi, EntityModel, $state, X2jsService, validationService) {
+
+      $scope.server = Falcon;
+      $scope.validations = validationService;
+      $scope.models = {};
+
+      $scope.handleFile = function (evt) {
+        Falcon.logRequest();
+        FileApi.loadFile(evt).then(function () {
+          Falcon.postSubmitEntity(FileApi.fileRaw, EntityModel.type).success(function (response) {
+            Falcon.logResponse('success', response, false);
+            $scope.refreshList(EntityModel.type);
+          }).error(function (err) {
+            Falcon.logResponse('error', err, false);
+          });
+        });
+      };
+
+      $scope.lists = {};
+      $scope.lists.feedList = [];
+      $scope.lists.clusterList = [];
+      $scope.lists.processList = [];
+
+      $scope.refreshList = function (type) {
+        type = type.toLowerCase();
+        Falcon.responses.listLoaded[type] = false;
+        if (Falcon.responses.multiRequest[type] > 0) { return; }
+
+        Falcon.logRequest();
+
+        Falcon.getEntities(type)
+          .success(function (data) {
+            
+            Falcon.logResponse('success', data, false, true);
+            Falcon.responses.listLoaded[type] = true;
+            $scope.lists[type + 'List'] = [];
+
+            if (data === null) {
+              $scope.lists[type + 'List'] = [];
+            }else{
+              var typeOfData = Object.prototype.toString.call(data.entity);	
+        	  if (typeOfData === "[object Array]") {
+                $scope.lists[type + 'List'] = data.entity;
+              } else if (typeOfData === "[object Object]") {
+                $scope.lists[type + 'List'][0] = data.entity;
+              } else {
+                console.log("type of data not recognized");
+              }
+            }
+          })
+          .error(function (err) {
+            Falcon.logResponse('error', err);
+          });
+      };
+
+      $scope.refreshLists = function () {
+        $scope.refreshList('cluster');
+        $scope.refreshList('feed');
+        $scope.refreshList('process');
+      };
+      $scope.closeAlert = function (index) {
+        Falcon.removeMessage(index);
+      };
+
+    }]);
+
+}());
\ No newline at end of file