keycloak-memoizeit

flow editing

8/3/2015 7:58:14 PM

Details

diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
index 1ad2411..fbaefd1 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
@@ -1102,6 +1102,48 @@ module.config([ '$routeProvider', function($routeProvider) {
             },
             controller : 'AuthenticationFlowsCtrl'
         })
+        .when('/realms/:realm/authentication/flows/:flow/create/execution', {
+            templateUrl : resourceUrl + '/partials/create-execution.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                parentFlow : function(AuthenticationFlowLoader) {
+                    return AuthenticationFlowLoader();
+                },
+                formActionProviders : function(AuthenticationFormActionProvidersLoader) {
+                    return AuthenticationFormActionProvidersLoader();
+                },
+                authenticatorProviders : function(AuthenticatorProvidersLoader) {
+                    return AuthenticatorProvidersLoader();
+                }
+            },
+            controller : 'CreateExecutionCtrl'
+        })
+        .when('/realms/:realm/authentication/flows/:flow/create/flow/execution', {
+            templateUrl : resourceUrl + '/partials/create-flow-execution.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                parentFlow : function(AuthenticationFlowLoader) {
+                    return AuthenticationFlowLoader();
+                },
+                formProviders : function(AuthenticationFormProvidersLoader) {
+                    return AuthenticationFormProvidersLoader();
+                }
+            },
+            controller : 'CreateExecutionFlowCtrl'
+        })
+        .when('/realms/:realm/authentication/create/flow', {
+            templateUrl : resourceUrl + '/partials/create-flow.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                }
+            },
+            controller : 'CreateFlowCtrl'
+        })
         .when('/realms/:realm/authentication/required-actions', {
             templateUrl : resourceUrl + '/partials/required-actions.html',
             resolve : {
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
index d4db267..d17505c 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
@@ -1616,8 +1616,91 @@ module.controller('IdentityProviderMapperCreateCtrl', function($scope, realm, id
 
 });
 
-module.controller('AuthenticationFlowsCtrl', function($scope, realm, flows, selectedFlow,
-                                                      AuthenticationFlowsCopy, AuthenticationFlowExecutions,
+module.controller('CreateFlowCtrl', function($scope, realm,
+                                             AuthenticationFlows,
+                                             Notifications, $location) {
+    $scope.realm = realm;
+    $scope.flow = {
+        alias: "",
+        providerId: "basic-flow",
+        description: "",
+        topLevel: true,
+        builtIn: false
+    }
+
+    $scope.save = function() {
+        AuthenticationFlows.save({realm: realm.realm, flow: ""}, $scope.flow, function() {
+            $location.url("/realms/" + realm.realm + "/authentication/flows/" + $scope.flow.alias);
+            Notifications.success("Flow Created.");
+        })
+    }
+    $scope.cancel = function() {
+        $location.url("/realms/" + realm.realm + "/authentication/flows");
+    };
+});
+
+module.controller('CreateExecutionFlowCtrl', function($scope, realm, parentFlow, formProviders,
+                                                      CreateExecutionFlow,
+                                                      Notifications, $location) {
+    $scope.realm = realm;
+    $scope.formProviders = formProviders;
+    $scope.flow = {
+        alias: "",
+        type: "basic-flow",
+        description: ""
+    }
+    $scope.provider = {};
+    if (formProviders.length > 0) {
+        $scope.provider = formProviders[0];
+    }
+
+    $scope.save = function() {
+        $scope.flow.provider = $scope.provider.id;
+        CreateExecutionFlow.save({realm: realm.realm, alias: parentFlow.alias}, $scope.flow, function() {
+            $location.url("/realms/" + realm.realm + "/authentication/flows/" + parentFlow.alias);
+            Notifications.success("Flow Created.");
+        })
+    }
+    $scope.cancel = function() {
+        $location.url("/realms/" + realm.realm + "/authentication/flows/" + parentFlow.alias);
+    };
+});
+
+module.controller('CreateExecutionCtrl', function($scope, realm, parentFlow, formActionProviders, authenticatorProviders,
+                                                      CreateExecution,
+                                                      Notifications, $location) {
+    $scope.realm = realm;
+    $scope.parentFlow = parentFlow;
+    console.log('parentFlow.providerId: ' + parentFlow.providerId);
+    if (parentFlow.providerId == 'form-flow') {
+        $scope.providers = formActionProviders;
+    } else {
+        $scope.providers = authenticatorProviders;
+    }
+
+    $scope.provider = {};
+    if ($scope.providers.length > 0) {
+        $scope.provider = $scope.providers[0];
+    }
+
+    $scope.save = function() {
+        var execution = {
+            provider: $scope.provider.id
+        }
+        CreateExecution.save({realm: realm.realm, alias: parentFlow.alias}, execution, function() {
+            $location.url("/realms/" + realm.realm + "/authentication/flows/" + parentFlow.alias);
+            Notifications.success("Execution Created.");
+        })
+    }
+    $scope.cancel = function() {
+        $location.url("/realms/" + realm.realm + "/authentication/flows/" + parentFlow.alias);
+    };
+});
+
+
+
+module.controller('AuthenticationFlowsCtrl', function($scope, $route, realm, flows, selectedFlow,
+                                                      AuthenticationFlows, AuthenticationFlowsCopy, AuthenticationFlowExecutions,
                                                       AuthenticationExecution, AuthenticationExecutionRaisePriority, AuthenticationExecutionLowerPriority,
                                                       $modal, Notifications, CopyDialog, $location) {
     $scope.realm = realm;
@@ -1637,12 +1720,12 @@ module.controller('AuthenticationFlowsCtrl', function($scope, realm, flows, sele
     var setupForm = function() {
         AuthenticationFlowExecutions.query({realm: realm.realm, alias: $scope.flow.alias}, function(data) {
             $scope.executions = data;
-            $scope.flowmax = 0;
+            $scope.choicesmax = 0;
             $scope.levelmax = 0;
             for (var i = 0; i < $scope.executions.length; i++ ) {
                 var execution = $scope.executions[i];
-                if (execution.requirementChoices.length > $scope.flowmax) {
-                    $scope.flowmax = execution.requirementChoices.length;
+                if (execution.requirementChoices.length > $scope.choicesmax) {
+                    $scope.choicesmax = execution.requirementChoices.length;
                 }
                 if (execution.level > $scope.levelmax) {
                     $scope.levelmax = execution.level;
@@ -1657,12 +1740,16 @@ module.controller('AuthenticationFlowsCtrl', function($scope, realm, flows, sele
             for (var i = 0; i < $scope.executions.length; i++ ) {
                 var execution = $scope.executions[i];
                 execution.empties = [];
-                for (j = 0; j < $scope.flowmax - execution.requirementChoices.length; j++) {
+                for (j = 0; j < $scope.choicesmax - execution.requirementChoices.length; j++) {
                     execution.empties.push(j);
                 }
-                execution.levels = [];
+                execution.preLevels = [];
                 for (j = 0; j < execution.level; j++) {
-                    execution.levels.push(j);
+                    execution.preLevels.push(j);
+                }
+                execution.postLevels = [];
+                for (j = execution.level; j < $scope.levelmax; j++) {
+                    execution.postLevels.push(j);
                 }
             }
         })
@@ -1679,6 +1766,39 @@ module.controller('AuthenticationFlowsCtrl', function($scope, realm, flows, sele
         })
     };
 
+    $scope.removeFlow = function() {
+        AuthenticationFlows.remove({realm: realm, flow: flow.id}, function() {
+            $route.reload();
+            Notifications.success("Flow removed");
+
+        })
+
+    };
+
+    $scope.addFlow = function() {
+        $location.url("/realms/" + realm.realm + '/authentication/flows/' + $scope.flow.id + '/create/flow/execution');
+
+    }
+
+    $scope.addSubFlow = function(execution) {
+        $location.url("/realms/" + realm.realm + '/authentication/flows/' + execution.flowId + '/create/flow/execution');
+
+    }
+
+    $scope.addSubFlowExecution = function(execution) {
+        $location.url("/realms/" + realm.realm + '/authentication/flows/' + execution.flowId + '/create/execution');
+
+    }
+
+    $scope.addExecution = function() {
+        $location.url("/realms/" + realm.realm + '/authentication/flows/' + $scope.flow.id + '/create/execution');
+
+    }
+
+    $scope.createFlow = function() {
+        $location.url("/realms/" + realm.realm + '/authentication/create/flow');
+    }
+
     $scope.updateExecution = function(execution) {
         var copy = angular.copy(execution);
         delete copy.empties;
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js
index c347759..2c30dd3 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js
@@ -354,11 +354,45 @@ module.factory('IdentityProviderMapperLoader', function(Loader, IdentityProvider
 module.factory('AuthenticationFlowsLoader', function(Loader, AuthenticationFlows, $route, $q) {
     return Loader.query(AuthenticationFlows, function() {
         return {
+            realm : $route.current.params.realm,
+            flow: ''
+        }
+    });
+});
+
+module.factory('AuthenticationFormProvidersLoader', function(Loader, AuthenticationFormProviders, $route, $q) {
+    return Loader.query(AuthenticationFormProviders, function() {
+        return {
+            realm : $route.current.params.realm
+        }
+    });
+});
+
+module.factory('AuthenticationFormActionProvidersLoader', function(Loader, AuthenticationFormActionProviders, $route, $q) {
+    return Loader.query(AuthenticationFormActionProviders, function() {
+        return {
             realm : $route.current.params.realm
         }
     });
 });
 
+module.factory('AuthenticatorProvidersLoader', function(Loader, AuthenticatorProviders, $route, $q) {
+    return Loader.query(AuthenticatorProviders, function() {
+        return {
+            realm : $route.current.params.realm
+        }
+    });
+});
+
+module.factory('AuthenticationFlowLoader', function(Loader, AuthenticationFlows, $route, $q) {
+    return Loader.get(AuthenticationFlows, function() {
+        return {
+            realm : $route.current.params.realm,
+            flow: $route.current.params.flow
+        }
+    });
+});
+
 module.factory('AuthenticationConfigDescriptionLoader', function(Loader, AuthenticationConfigDescription, $route, $q) {
     return Loader.get(AuthenticationConfigDescription, function () {
         return {
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
index 479702e..64a5ff5 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
@@ -1172,11 +1172,46 @@ module.factory('AuthenticationFlowExecutions', function($resource) {
     });
 });
 
+module.factory('CreateExecutionFlow', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/authentication/flows/:alias/executions/flow', {
+        realm : '@realm',
+        alias : '@alias'
+    });
+});
+
+module.factory('CreateExecution', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/authentication/flows/:alias/executions/execution', {
+        realm : '@realm',
+        alias : '@alias'
+    });
+});
+
 module.factory('AuthenticationFlows', function($resource) {
-    return $resource(authUrl + '/admin/realms/:realm/authentication/flows', {
+    return $resource(authUrl + '/admin/realms/:realm/authentication/flows/:flow', {
+        realm : '@realm',
+        flow: '@flow'
+    });
+});
+
+module.factory('AuthenticationFormProviders', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/authentication/form-providers', {
+        realm : '@realm'
+    });
+});
+
+module.factory('AuthenticationFormActionProviders', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/authentication/form-action-providers', {
         realm : '@realm'
     });
 });
+
+module.factory('AuthenticatorProviders', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/authentication/authenticator-providers', {
+        realm : '@realm'
+    });
+});
+
+
 module.factory('AuthenticationFlowsCopy', function($resource) {
     return $resource(authUrl + '/admin/realms/:realm/authentication/flows/:alias/copy', {
         realm : '@realm',
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/authentication-flows.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/authentication-flows.html
index 7f98234..4f2794b 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/authentication-flows.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/authentication-flows.html
@@ -6,15 +6,16 @@
     <table class="table table-striped table-bordered">
         <thead>
         <tr>
-            <th colspan="{{levelmax + 1 + flowmax + 4}}" class="kc-table-actions">
+            <th colspan="{{levelmax + 1 + choicesmax + 4}}" class="kc-table-actions">
                 <div class="dropdown pull-left">
                     <select class="form-control" ng-model="flow"
                             ng-options="(flow.alias|capitalize) for flow in flows"
                             data-ng-change="setupForm()">
                     </select>
                 </div>
+                &nbsp;&nbsp;<i class="fa fa-question-circle text-muted" tooltip="{{flow.description}}" tooltip-placement="right"> </i>
                 <div class="pull-right" data-ng-show="access.manageRealm">
-                    <button class="btn btn-default" data-ng-click="newFlow()">New</button>
+                    <button class="btn btn-default" data-ng-click="createFlow()">New</button>
                     <button class="btn btn-default" data-ng-click="copyFlow()">Copy</button>
                     <button class="btn btn-default" data-ng-hide="flow.builtIn" data-ng-click="removeFlow()">Delete</button>
                     <button class="btn btn-default" data-ng-hide="flow.builtIn" data-ng-click="addExecution()">Add Execution</button>
@@ -24,15 +25,15 @@
          </tr>
         <tr data-ng-hide="executions.length == 0">
             <th colspan="{{levelmax + 1}}">Auth Type</th>
-            <th colspan="{{flowmax}}">Requirement</th>
-            <th colspan="3">Actions</th>
+            <th colspan="{{choicesmax}}">Requirement</th>
+            <th></th>
         </tr>
         </thead>
         <tbody>
         <tr ng-repeat="execution in executions" data-ng-show="executions.length > 0">
-            <td ng-repeat="lev in execution.levels"></td>
+            <td ng-repeat="lev in execution.preLevels"></td>
             <td><button data-ng-hide="flow.builtIn || $first" class="btn btn-default" data-ng-click="raisePriority(execution)"><i class="fa fa-angle-up"></i></button><button data-ng-hide="flow.builtIn || $last" class="btn btn-default" data-ng-click="lowerPriority(execution)"><i class="fa fa-angle-down"></i></button> {{execution.displayName|capitalize}}</td>
-            <td data-ng-show="execution.level == 0" ng-repeat="levmax in levelmaxempties"></td>
+            <td ng-repeat="lev in execution.postLevels"></td>
             <td ng-repeat="choice in execution.requirementChoices">
             <label >
                 <input type="radio" ng-model="execution.requirement" ng-value="choice" ng-change="updateExecution(execution)">
@@ -41,20 +42,21 @@
 
             </td>
             <td ng-repeat="emptee in execution.empties"></td>
-            <td  data-ng-hide="flow.builtIn" class="kc-action-cell">
-                <button class="btn btn-default btn-block btn-sm" data-ng-click="removeExecution(execution)">Delete</button>
-            </td>
-            <td data-ng-hide="flow.builtIn || !execution.authenticationFlow" class="kc-action-cell">
-                <button class="btn btn-default btn-block btn-sm" data-ng-click="addSubFlowExecution(execution)">Add Execution</button>
-            </td>
-            <td data-ng-hide="flow.builtIn || !execution.authenticationFlow" class="kc-action-cell">
-                <button class="btn btn-default btn-block btn-sm" data-ng-click="addSubFlowExecution(execution)">Add Flow</button>
-            </td>
-            <td data-ng-show="execution.configurable && execution.authenticationConfig == null" class="kc-action-cell">
-                <button data-ng-show="execution.configurable && execution.authenticationConfig == null" class="btn btn-default btn-block btn-sm" kc-open="/create/authentication/{{realm.realm}}/execution/{{execution.id}}/provider/{{execution.providerId}}">Config</button>
-            </td>
-            <td data-ng-show="execution.configurable && execution.authenticationConfig != null" class="kc-action-cell">
-                <button data-ng-show="execution.configurable && execution.authenticationConfig != null" class="btn btn-default btn-block btn-sm" kc-open="/realms/{{realm.realm}}/authentication/config/{{execution.providerId}}/{{execution.authenticationConfig}}">Config</button>
+            <td>
+                <ul class="nav navbar-nav navbar-utility" data-ng-hide="flow.builtIn && !execution.configurable">
+                    <li class="dropdown">
+                        <a href="#" class="dropdown-toggle" data-toggle="dropdown">
+                            Actions <b class="caret"></b>
+                        </a>
+                        <ul class="dropdown-menu"  >
+                            <li data-ng-hide="flow.builtIn"><a href="" ng-click="removeExecution(execution)">Delete</a></li>
+                            <li data-ng-hide="flow.builtIn || !execution.authenticationFlow"><a href="" ng-click="addSubFlowExecution(execution)">Add Execution</a></li>
+                            <li data-ng-hide="flow.builtIn || !execution.authenticationFlow"><a href="" ng-click="addSubFlow(execution)">Add Flow</a></li>
+                            <li data-ng-show="execution.configurable && execution.authenticationConfig == null"><a href="#/create/authentication/{{realm.realm}}/execution/{{execution.id}}/provider/{{execution.providerId}}">Config</a></li>
+                            <li data-ng-show="execution.configurable && execution.authenticationConfig != null"><a href="#/realms/{{realm.realm}}/authentication/config/{{execution.providerId}}/{{execution.authenticationConfig}}">Config</a></li>
+                        </ul>
+                    </li>
+                </ul>
             </td>
          </tr>
         <tr data-ng-show="executions.length == 0">
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/create-execution.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/create-execution.html
new file mode 100755
index 0000000..53c82d1
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/create-execution.html
@@ -0,0 +1,29 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+    <div>
+        <h1 data-ng-show="parentFlow.providerId == 'basic-flow'">Create Authenticator Execution</h1>
+        <h1 data-ng-show="parentFlow.providerId == 'for-flow'">Create Form Action Execution</h1>
+    </div>
+    <kc-tabs-authentication></kc-tabs-authentication>
+    <form class="form-horizontal" name="clientForm" novalidate kc-read-only="!access.manageRealm">
+        <div class="form-group">
+            <label class="col-md-2 control-label" for="provider">Provider</label>
+            <div class="col-sm-6">
+                <div>
+                    <select class="form-control" id="provider"
+                            ng-model="provider"
+                            ng-options="provider.id for provider in providers">
+                    </select>
+                </div>
+            </div>
+            <kc-tooltip>{{provider.description}}</kc-tooltip>
+        </div>
+        <div class="form-group">
+            <div class="col-md-10 col-md-offset-2">
+                <button kc-save>Save</button>
+                <button type="button" class="btn btn-default" ng-click="cancel()">Cancel</button>
+            </div>
+        </div>
+    </form>
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/create-flow.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/create-flow.html
new file mode 100755
index 0000000..a298b85
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/create-flow.html
@@ -0,0 +1,30 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+    <h1>Create Top Level Flow</h1>
+
+    <kc-tabs-authentication></kc-tabs-authentication>
+
+    <form class="form-horizontal" name="clientForm" novalidate kc-read-only="!access.manageRealm">
+        <div class="form-group">
+            <label class="col-md-2 control-label" for="alias">Alias </label>
+            <div class="col-sm-6">
+                <input class="form-control" type="text" id="alias" name="alias" data-ng-model="flow.alias" autofocus>
+            </div>
+            <kc-tooltip>Specifies display name for the flow.</kc-tooltip>
+        </div>
+        <div class="form-group">
+            <label class="col-md-2 control-label" for="description">Description </label>
+
+            <div class="col-md-6">
+                <textarea class="form-control" rows="5" cols="50" id="description" name="description" data-ng-model="flow.description"></textarea>
+            </div>
+        </div>
+        <div class="form-group">
+            <div class="col-md-10 col-md-offset-2">
+                <button kc-save>Save</button>
+                <button type="button" class="btn btn-default" ng-click="cancel()">Cancel</button>
+            </div>
+        </div>
+    </form>
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/create-flow-execution.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/create-flow-execution.html
new file mode 100755
index 0000000..c88d76b
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/create-flow-execution.html
@@ -0,0 +1,55 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+    <h1>Create Execution Flow</h1>
+
+    <kc-tabs-authentication></kc-tabs-authentication>
+
+    <form class="form-horizontal" name="clientForm" novalidate kc-read-only="!access.manageRealm">
+        <div class="form-group">
+            <label class="col-md-2 control-label" for="alias">Alias </label>
+            <div class="col-sm-6">
+                <input class="form-control" type="text" id="alias" name="alias" data-ng-model="flow.alias" autofocus>
+            </div>
+            <kc-tooltip>Specifies display name for the flow.</kc-tooltip>
+        </div>
+        <div class="form-group">
+            <label class="col-md-2 control-label" for="description">Description </label>
+
+            <div class="col-md-6">
+                <textarea class="form-control" rows="5" cols="50" id="description" name="description" data-ng-model="flow.description"></textarea>
+            </div>
+        </div>
+        <div class="form-group">
+            <label class="col-md-2 control-label" for="flowType">Flow Type</label>
+            <div class="col-sm-6">
+                <div>
+                    <select class="form-control" id="flowType"
+                            ng-model="flow.type">
+                        <option value="basic-flow">generic</option>
+                        <option value="form-flow">form</option>
+                    </select>
+                </div>
+            </div>
+            <kc-tooltip>What kind of form is it</kc-tooltip>
+        </div>
+        <div class="form-group" data-ng-show="flow.type == 'form-flow'">
+            <label class="col-md-2 control-label" for="provider">Form Provider</label>
+            <div class="col-sm-6">
+                <div>
+                    <select class="form-control" id="provider"
+                            ng-model="provider"
+                            ng-options="provider.id for provider in formProviders">
+                    </select>
+                </div>
+            </div>
+            <kc-tooltip>{{provider.description}}</kc-tooltip>
+        </div>
+        <div class="form-group">
+            <div class="col-md-10 col-md-offset-2">
+                <button kc-save>Save</button>
+                <button type="button" class="btn btn-default" ng-click="cancel()">Cancel</button>
+            </div>
+        </div>
+    </form>
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/authentication/forms/RegistrationPage.java b/services/src/main/java/org/keycloak/authentication/forms/RegistrationPage.java
index 8748b54..9a0e995 100755
--- a/services/src/main/java/org/keycloak/authentication/forms/RegistrationPage.java
+++ b/services/src/main/java/org/keycloak/authentication/forms/RegistrationPage.java
@@ -45,7 +45,7 @@ public class RegistrationPage implements FormAuthenticator, FormAuthenticatorFac
 
     @Override
     public String getHelpText() {
-        return null;
+        return "This is the controller for the registration page";
     }
 
     @Override
diff --git a/services/src/main/java/org/keycloak/authentication/forms/RegistrationPassword.java b/services/src/main/java/org/keycloak/authentication/forms/RegistrationPassword.java
index 9cf771d..ade4e00 100755
--- a/services/src/main/java/org/keycloak/authentication/forms/RegistrationPassword.java
+++ b/services/src/main/java/org/keycloak/authentication/forms/RegistrationPassword.java
@@ -34,7 +34,7 @@ public class RegistrationPassword implements FormAction, FormActionFactory {
 
     @Override
     public String getHelpText() {
-        return null;
+        return "Validates that password matches password confirmation field.  It also will store password in user's credential store.";
     }
 
     @Override
diff --git a/services/src/main/java/org/keycloak/authentication/forms/RegistrationProfile.java b/services/src/main/java/org/keycloak/authentication/forms/RegistrationProfile.java
index 2019db8..2fd3c85 100755
--- a/services/src/main/java/org/keycloak/authentication/forms/RegistrationProfile.java
+++ b/services/src/main/java/org/keycloak/authentication/forms/RegistrationProfile.java
@@ -31,7 +31,7 @@ public class RegistrationProfile implements FormAction, FormActionFactory {
 
     @Override
     public String getHelpText() {
-        return null;
+        return "Validates email, first name, and last name attributes and stores them in user data.";
     }
 
     @Override
diff --git a/services/src/main/java/org/keycloak/authentication/forms/RegistrationRecaptcha.java b/services/src/main/java/org/keycloak/authentication/forms/RegistrationRecaptcha.java
index b4bb3b0..df75e4f 100755
--- a/services/src/main/java/org/keycloak/authentication/forms/RegistrationRecaptcha.java
+++ b/services/src/main/java/org/keycloak/authentication/forms/RegistrationRecaptcha.java
@@ -191,7 +191,7 @@ public class RegistrationRecaptcha implements FormAction, FormActionFactory, Con
 
     @Override
     public String getHelpText() {
-        return null;
+        return "Adds Google Recaptcha button.  Recaptchas verify that the entity that is registering is a human.  This can only be used on the internet and must be configured after you add it.";
     }
 
     private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
diff --git a/services/src/main/java/org/keycloak/authentication/forms/RegistrationUserCreation.java b/services/src/main/java/org/keycloak/authentication/forms/RegistrationUserCreation.java
index 0a446da..40d2fb0 100755
--- a/services/src/main/java/org/keycloak/authentication/forms/RegistrationUserCreation.java
+++ b/services/src/main/java/org/keycloak/authentication/forms/RegistrationUserCreation.java
@@ -35,7 +35,7 @@ public class RegistrationUserCreation implements FormAction, FormActionFactory {
 
     @Override
     public String getHelpText() {
-        return null;
+        return "This action must always be first! Validates the username of the user in validation phase.  In success phase, this will create the user in the database.";
     }
 
     @Override
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java b/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java
index 41380ab..b792213 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java
@@ -5,15 +5,22 @@ import org.jboss.resteasy.annotations.cache.NoCache;
 import org.jboss.resteasy.spi.BadRequestException;
 import org.jboss.resteasy.spi.NotFoundException;
 import org.keycloak.authentication.AuthenticationFlow;
+import org.keycloak.authentication.Authenticator;
 import org.keycloak.authentication.AuthenticatorUtil;
 import org.keycloak.authentication.ConfigurableAuthenticatorFactory;
+import org.keycloak.authentication.DefaultAuthenticationFlow;
+import org.keycloak.authentication.FormAction;
+import org.keycloak.authentication.FormAuthenticationFlow;
+import org.keycloak.authentication.FormAuthenticator;
 import org.keycloak.models.AuthenticationExecutionModel;
 import org.keycloak.models.AuthenticationFlowModel;
 import org.keycloak.models.AuthenticatorConfigModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RequiredActionProviderModel;
+import org.keycloak.provider.ConfiguredProvider;
 import org.keycloak.provider.ProviderConfigProperty;
+import org.keycloak.provider.ProviderFactory;
 import org.keycloak.representations.idm.ConfigPropertyRepresentation;
 import org.keycloak.utils.CredentialHelper;
 
@@ -68,6 +75,7 @@ public class AuthenticationManagementResource {
         protected Boolean authenticationFlow;
         protected String providerId;
         protected String authenticationConfig;
+        protected String flowId;
         protected int level;
         protected int index;
 
@@ -150,8 +158,59 @@ public class AuthenticationManagementResource {
         public void setIndex(int index) {
             this.index = index;
         }
+
+        public String getFlowId() {
+            return flowId;
+        }
+
+        public void setFlowId(String flowId) {
+            this.flowId = flowId;
+        }
     }
 
+    @Path("/form-providers")
+    @GET
+    @NoCache
+    @Produces(MediaType.APPLICATION_JSON)
+    public List<Map<String, String>> getFormProviders() {
+        this.auth.requireView();
+        List<ProviderFactory> factories = session.getKeycloakSessionFactory().getProviderFactories(FormAuthenticator.class);
+        return buildProviderMetadata(factories);
+    }
+
+    @Path("/authenticator-providers")
+    @GET
+    @NoCache
+    @Produces(MediaType.APPLICATION_JSON)
+    public List<Map<String, String>> getAuthenticatorProviders() {
+        this.auth.requireView();
+        List<ProviderFactory> factories = session.getKeycloakSessionFactory().getProviderFactories(Authenticator.class);
+        return buildProviderMetadata(factories);
+    }
+
+    public List<Map<String, String>> buildProviderMetadata(List<ProviderFactory> factories) {
+        List<Map<String, String>> providers = new LinkedList<>();
+        for (ProviderFactory factory : factories) {
+            Map<String, String> data = new HashMap<>();
+            data.put("id", factory.getId());
+            ConfiguredProvider configured = (ConfiguredProvider)factory;
+            data.put("description", configured.getHelpText());
+            providers.add(data);
+        }
+        return providers;
+    }
+
+    @Path("/form-action-providers")
+    @GET
+    @NoCache
+    @Produces(MediaType.APPLICATION_JSON)
+    public List<Map<String, String>> getFormActionProviders() {
+        this.auth.requireView();
+        List<ProviderFactory> factories = session.getKeycloakSessionFactory().getProviderFactories(FormAction.class);
+        return buildProviderMetadata(factories);
+    }
+
+
     @Path("/flows")
     @GET
     @NoCache
@@ -183,6 +242,36 @@ public class AuthenticationManagementResource {
 
     }
 
+    @Path("/flows/{id}")
+    @GET
+    @NoCache
+    @Produces(MediaType.APPLICATION_JSON)
+    public AuthenticationFlowModel getFlow(@PathParam("id") String id) {
+        this.auth.requireView();
+
+        AuthenticationFlowModel flow = realm.getAuthenticationFlowById(id);
+        if (flow == null) {
+            throw new NotFoundException("Could not find flow with id");
+        }
+        return flow;
+    }
+
+    @Path("/flows/{id}")
+    @DELETE
+    @NoCache
+    public void deleteFlow(@PathParam("id") String id) {
+        this.auth.requireView();
+
+        AuthenticationFlowModel flow = realm.getAuthenticationFlowById(id);
+        if (flow == null) {
+            throw new NotFoundException("Could not find flow with id");
+        }
+        if (flow.isBuiltIn()) {
+            throw new BadRequestException("Can't delete built in flow");
+        }
+        realm.removeAuthenticationFlow(flow);
+    }
+
     @Path("/flows/{flowAlias}/copy")
     @POST
     @NoCache
@@ -233,6 +322,65 @@ public class AuthenticationManagementResource {
         }
     }
 
+    @Path("/flows/{flowAlias}/executions/flow")
+    @POST
+    @NoCache
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void addExecutionFlow(@PathParam("flowAlias") String flowAlias, Map<String, String> data) {
+        this.auth.requireManage();
+
+        AuthenticationFlowModel parentFlow = realm.getFlowByAlias(flowAlias);
+        if (parentFlow == null) {
+            throw new BadRequestException("Parent flow doesn't exists");
+        }
+        String alias = data.get("alias");
+        String type = data.get("type");
+        String provider = data.get("provider");
+        String description = data.get("description");
+
+
+        AuthenticationFlowModel newFlow = realm.getFlowByAlias(alias);
+        if (newFlow != null) {
+            throw new BadRequestException("New flow alias name already exists");
+        }
+        newFlow = new AuthenticationFlowModel();
+        newFlow.setAlias(alias);
+        newFlow.setDescription(description);
+        newFlow.setProviderId(type);
+        newFlow = realm.addAuthenticationFlow(newFlow);
+        AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
+        execution.setParentFlow(parentFlow.getId());
+        execution.setFlowId(newFlow.getId());
+        execution.setRequirement(AuthenticationExecutionModel.Requirement.DISABLED);
+        execution.setAuthenticatorFlow(true);
+        execution.setAuthenticator(provider);
+        realm.addAuthenticatorExecution(execution);
+    }
+
+    @Path("/flows/{flowAlias}/executions/execution")
+    @POST
+    @NoCache
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void addExecution(@PathParam("flowAlias") String flowAlias, Map<String, String> data) {
+        this.auth.requireManage();
+
+        AuthenticationFlowModel parentFlow = realm.getFlowByAlias(flowAlias);
+        if (parentFlow == null) {
+            throw new BadRequestException("Parent flow doesn't exists");
+        }
+        String provider = data.get("provider");
+
+
+        AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
+        execution.setParentFlow(parentFlow.getId());
+        execution.setRequirement(AuthenticationExecutionModel.Requirement.DISABLED);
+        execution.setAuthenticatorFlow(false);
+        execution.setAuthenticator(provider);
+        realm.addAuthenticatorExecution(execution);
+    }
+
+
+
     @Path("/flows/{flowAlias}/executions")
     @GET
     @NoCache
@@ -278,6 +426,7 @@ public class AuthenticationManagementResource {
                 rep.setId(execution.getId());
                 rep.setAuthenticationFlow(execution.isAuthenticatorFlow());
                 rep.setRequirement(execution.getRequirement().name());
+                rep.setFlowId(execution.getFlowId());
                 result.add(rep);
                 AuthenticationFlowModel subFlow = realm.getAuthenticationFlowById(execution.getFlowId());
                 recurseExecutions(subFlow, result, level + 1);