You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by da...@apache.org on 2017/06/30 22:41:48 UTC

[18/52] [partial] incubator-trafficcontrol git commit: promotes TO experimental UI to the new Traffic Portal

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/job/form.job.tpl.html
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/job/form.job.tpl.html b/traffic_portal/app/src/common/modules/form/job/form.job.tpl.html
new file mode 100644
index 0000000..4839aeb
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/job/form.job.tpl.html
@@ -0,0 +1,65 @@
+<!--
+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.
+-->
+
+<div class="x_panel">
+    <div class="x_title">
+        <ol class="breadcrumb">
+            <li><a ng-click="navigateToPath('/admin/jobs')">Invalidate Content Jobs</a></li>
+            <li class="active">{{jobName}}</li>
+        </ol>
+        <div class="clearfix"></div>
+    </div>
+    <div class="x_content">
+        <br>
+        <form name="jobForm" class="form-horizontal form-label-left" novalidate>
+            <div class="form-group" ng-class="{'has-error': hasError(jobForm.deliveryservice), 'has-feedback': hasError(jobForm.deliveryservice)}">
+                <label class="control-label col-md-2 col-sm-2 col-xs-12">Delivery Service *</label>
+                <div class="col-md-10 col-sm-10 col-xs-12">
+                    <select id="deliveryservice" name="deliveryservice" class="form-control" ng-model="job.dsId" ng-options="deliveryservice.id as deliveryservice.xmlId for deliveryservice in deliveryservices" required>
+                        <option value="">Select...</option>
+                    </select>
+                    <small class="input-error" ng-show="hasPropertyError(jobForm.deliveryservice, 'required')">Required</small>
+                </div>
+            </div>
+            <div class="form-group" ng-class="{'has-error': hasError(jobForm.regex), 'has-feedback': hasError(jobForm.regex)}">
+                <label class="control-label col-md-2 col-sm-2 col-xs-12">Path Regex *</label>
+                <div class="col-md-10 col-sm-10 col-xs-12">
+                    <input id="regex" name="regex" type="text" class="form-control" placeholder="eg. /path/to/content/.*\.jpg" ng-model="job.regex" ng-required="true" ng-maxlength="255" ng-pattern="/^\//" autofocus>
+                    <small class="input-error" ng-show="hasPropertyError(jobForm.regex, 'required')">Required</small>
+                    <small class="input-error" ng-show="hasPropertyError(jobForm.regex, 'maxlength')">Too Long</small>
+                    <small class="input-error" ng-show="hasPropertyError(jobForm.regex, 'pattern')">Must Start with /</small>
+                    <span ng-show="hasError(jobForm.regex)" class="form-control-feedback"><i class="fa fa-times"></i></span>
+                </div>
+            </div>
+            <div class="form-group" ng-class="{'has-error': hasError(jobForm.ttl), 'has-feedback': hasError(jobForm.ttl)}">
+                <label class="control-label col-md-2 col-sm-2 col-xs-12">TTL (hours) *</label>
+                <div class="col-md-10 col-sm-10 col-xs-12">
+                    <input id="ttl" name="ttl" type="text" class="form-control" ng-model="job.ttl" ng-required="true" ng-maxlength="1000" ng-pattern="/^\d+$/" autofocus>
+                    <small class="input-error" ng-show="hasPropertyError(jobForm.ttl, 'required')">Required</small>
+                    <small class="input-error" ng-show="hasPropertyError(jobForm.ttl, 'maxlength')">Too Long</small>
+                    <small class="input-error" ng-show="hasPropertyError(jobForm.ttl, 'pattern')">Number</small>
+                    <span ng-show="hasError(jobForm.ttl)" class="form-control-feedback"><i class="fa fa-times"></i></span>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-success" ng-disabled="jobForm.$pristine || jobForm.$invalid" ng-click="save(job)">{{settings.saveLabel}}</button>
+            </div>
+        </form>
+    </div>
+</div>

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/job/index.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/job/index.js b/traffic_portal/app/src/common/modules/form/job/index.js
new file mode 100644
index 0000000..99b48f9
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/job/index.js
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+module.exports = angular.module('trafficPortal.form.job', [])
+	.controller('FormJobController', require('./FormJobController'));

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/job/new/FormNewJobController.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/job/new/FormNewJobController.js b/traffic_portal/app/src/common/modules/form/job/new/FormNewJobController.js
new file mode 100644
index 0000000..4ecde31
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/job/new/FormNewJobController.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.
+ */
+
+var FormNewJobController = function(job, $scope, $controller, jobService, messageModel, locationUtils) {
+
+	// extends the FormJobController to inherit common methods
+	angular.extend(this, $controller('FormJobController', { job: job, $scope: $scope }));
+
+	$scope.jobName = 'New';
+
+	$scope.settings = {
+		isNew: true,
+		saveLabel: 'Create'
+	};
+
+	$scope.save = function(job) {
+		jobService.createJob(job)
+			.then(
+				function() {
+					messageModel.setMessages([ { level: 'success', text: 'Invalidate Content Job Created' } ], true);
+					locationUtils.navigateToPath('/admin/jobs');
+				},
+				function(fault) {
+					messageModel.setMessages(fault.data.alerts, false);
+				}
+			);
+	};
+
+};
+
+FormNewJobController.$inject = ['job', '$scope', '$controller', 'jobService', 'messageModel', 'locationUtils'];
+module.exports = FormNewJobController;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/job/new/index.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/job/new/index.js b/traffic_portal/app/src/common/modules/form/job/new/index.js
new file mode 100644
index 0000000..6c118d9
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/job/new/index.js
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+module.exports = angular.module('trafficPortal.form.job.new', [])
+	.controller('FormNewJobController', require('./FormNewJobController'));

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/parameter/FormParameterController.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/parameter/FormParameterController.js b/traffic_portal/app/src/common/modules/form/parameter/FormParameterController.js
new file mode 100644
index 0000000..4de9b5a
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/parameter/FormParameterController.js
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+var FormParameterController = function(parameter, $scope, $location, formUtils, stringUtils, locationUtils) {
+
+    $scope.parameter = parameter;
+
+    $scope.props = [
+        { name: 'name', type: 'text', required: true, maxLength: 1024 },
+        { name: 'configFile', type: 'text', required: true, maxLength: 45 },
+        { name: 'value', type: 'text', required: true, maxLength: 1024 }
+    ];
+
+    $scope.labelize = stringUtils.labelize;
+
+    $scope.viewProfiles = function() {
+        $location.path($location.path() + '/profiles');
+    };
+
+    $scope.viewCacheGroups = function() {
+        $location.path($location.path() + '/cache-groups');
+    };
+
+    $scope.navigateToPath = locationUtils.navigateToPath;
+
+    $scope.hasError = formUtils.hasError;
+
+    $scope.hasPropertyError = formUtils.hasPropertyError;
+
+};
+
+FormParameterController.$inject = ['parameter', '$scope', '$location', 'formUtils', 'stringUtils', 'locationUtils'];
+module.exports = FormParameterController;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/parameter/edit/FormEditParameterController.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/parameter/edit/FormEditParameterController.js b/traffic_portal/app/src/common/modules/form/parameter/edit/FormEditParameterController.js
new file mode 100644
index 0000000..4074104
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/parameter/edit/FormEditParameterController.js
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+var FormEditParameterController = function(parameter, $scope, $controller, $uibModal, $anchorScroll, locationUtils, parameterService) {
+
+    // extends the FormParameterController to inherit common methods
+    angular.extend(this, $controller('FormParameterController', { parameter: parameter, $scope: $scope }));
+
+    var deleteParameter = function(parameter) {
+        parameterService.deleteParameter(parameter.id)
+            .then(function() {
+                locationUtils.navigateToPath('/admin/parameters');
+            });
+    };
+
+    $scope.parameterName = angular.copy(parameter.name);
+
+    $scope.settings = {
+        isNew: false,
+        saveLabel: 'Update'
+    };
+
+    $scope.save = function(parameter) {
+        parameterService.updateParameter(parameter).
+            then(function() {
+                $scope.parameterName = angular.copy(parameter.name);
+                $anchorScroll(); // scrolls window to top
+            });
+    };
+
+    $scope.confirmDelete = function(parameter) {
+        var params = {
+            title: 'Delete Parameter: ' + parameter.name,
+            key: parameter.name
+        };
+        var modalInstance = $uibModal.open({
+            templateUrl: 'common/modules/dialog/delete/dialog.delete.tpl.html',
+            controller: 'DialogDeleteController',
+            size: 'md',
+            resolve: {
+                params: function () {
+                    return params;
+                }
+            }
+        });
+        modalInstance.result.then(function() {
+            deleteParameter(parameter);
+        }, function () {
+            // do nothing
+        });
+    };
+
+};
+
+FormEditParameterController.$inject = ['parameter', '$scope', '$controller', '$uibModal', '$anchorScroll', 'locationUtils', 'parameterService'];
+module.exports = FormEditParameterController;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/parameter/edit/index.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/parameter/edit/index.js b/traffic_portal/app/src/common/modules/form/parameter/edit/index.js
new file mode 100644
index 0000000..76da6e4
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/parameter/edit/index.js
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+module.exports = angular.module('trafficPortal.form.parameter.edit', [])
+    .controller('FormEditParameterController', require('./FormEditParameterController'));

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/parameter/form.parameter.tpl.html
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/parameter/form.parameter.tpl.html b/traffic_portal/app/src/common/modules/form/parameter/form.parameter.tpl.html
new file mode 100644
index 0000000..6f31745
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/parameter/form.parameter.tpl.html
@@ -0,0 +1,51 @@
+<!--
+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.
+-->
+
+<div class="x_panel">
+    <div class="x_title">
+        <ol class="breadcrumb pull-left">
+            <li><a ng-click="navigateToPath('/admin/parameters')">Parameters</a></li>
+            <li class="active">{{parameterName}}</li>
+        </ol>
+        <div class="pull-right" role="group" ng-show="!settings.isNew">
+            <button class="btn btn-primary" title="View Profiles" ng-click="viewProfiles()">View Profiles</button>
+            <!-- todo: show cachegroups for a parameter -->
+            <!--<button class="btn btn-primary" title="View Cache Groups" ng-click="viewCacheGroups()">View Cache Groups</button>-->
+        </div>
+        <div class="clearfix"></div>
+    </div>
+    <div class="x_content">
+        <br>
+        <form name="parameterForm" class="form-horizontal form-label-left" novalidate>
+            <div class="form-group" ng-class="{'has-error': hasError(parameterForm[prop.name]), 'has-feedback': hasError(parameterForm[prop.name])}" ng-repeat="prop in props">
+                <label class="control-label col-md-2 col-sm-2 col-xs-12">{{labelize(prop.name)}} <span ng-show="prop.required">*</span></label>
+                <div class="col-md-10 col-sm-10 col-xs-12">
+                    <input id="{{prop.name}}" name="{{prop.name}}" type="{{prop.type}}" class="form-control" ng-model="parameter[prop.name]" ng-readonly="prop.readonly" ng-required="prop.required" ng-maxlength="prop.maxLength" autofocus>
+                    <small class="input-error" ng-show="hasPropertyError(parameterForm[prop.name], 'required')">Required</small>
+                    <small class="input-error" ng-show="hasPropertyError(parameterForm[prop.name], 'maxlength')">Too Long</small>
+                    <span ng-show="hasError(parameterForm[prop.name])" class="form-control-feedback"><i class="fa fa-times"></i></span>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-danger" ng-show="!settings.isNew" ng-click="confirmDelete(parameter)">Delete</button>
+                <button type="button" class="btn btn-success" ng-disabled="parameterForm.$pristine || parameterForm.$invalid" ng-click="save(parameter)">{{settings.saveLabel}}</button>
+            </div>
+        </form>
+    </div>
+</div>

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/parameter/index.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/parameter/index.js b/traffic_portal/app/src/common/modules/form/parameter/index.js
new file mode 100644
index 0000000..04b7fbb
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/parameter/index.js
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+module.exports = angular.module('trafficPortal.form.parameter', [])
+    .controller('FormParameterController', require('./FormParameterController'));

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/parameter/new/FormNewParameterController.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/parameter/new/FormNewParameterController.js b/traffic_portal/app/src/common/modules/form/parameter/new/FormNewParameterController.js
new file mode 100644
index 0000000..56b9202
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/parameter/new/FormNewParameterController.js
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+var FormNewParameterController = function(parameter, $scope, $controller, parameterService) {
+
+    // extends the FormParameterController to inherit common methods
+    angular.extend(this, $controller('FormParameterController', { parameter: parameter, $scope: $scope }));
+
+    $scope.parameterName = 'New';
+
+    $scope.settings = {
+        isNew: true,
+        saveLabel: 'Create'
+    };
+
+    $scope.save = function(parameter) {
+        parameterService.createParameter(parameter);
+    };
+
+};
+
+FormNewParameterController.$inject = ['parameter', '$scope', '$controller', 'parameterService'];
+module.exports = FormNewParameterController;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/parameter/new/index.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/parameter/new/index.js b/traffic_portal/app/src/common/modules/form/parameter/new/index.js
new file mode 100644
index 0000000..56d835d
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/parameter/new/index.js
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+module.exports = angular.module('trafficPortal.form.parameter.new', [])
+    .controller('FormNewParameterController', require('./FormNewParameterController'));

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/physLocation/FormPhysLocationController.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/physLocation/FormPhysLocationController.js b/traffic_portal/app/src/common/modules/form/physLocation/FormPhysLocationController.js
new file mode 100644
index 0000000..d7f78fc
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/physLocation/FormPhysLocationController.js
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+var FormPhysLocationController = function(physLocation, $scope, $location, formUtils, stringUtils, locationUtils, regionService) {
+
+    var getRegions = function() {
+        regionService.getRegions()
+            .then(function(result) {
+                $scope.regions = result;
+            });
+    };
+
+    $scope.physLocation = physLocation;
+
+    $scope.props = [
+        { name: 'name', type: 'text', required: true, maxLength: 45 },
+        { name: 'shortName', type: 'text', required: true, maxLength: 12 },
+        { name: 'address', type: 'text', required: true, maxLength: 128 },
+        { name: 'city', type: 'text', required: true, maxLength: 128 },
+        { name: 'state', type: 'text', required: true, maxLength: 2 },
+        { name: 'zip', type: 'text', required: true, maxLength: 5 },
+        { name: 'poc', type: 'text', required: false, maxLength: 128 },
+        { name: 'phone', type: 'text', required: false, maxLength: 45 },
+        { name: 'email', type: 'email', required: false, maxLength: 128 }
+    ];
+
+    $scope.labelize = stringUtils.labelize;
+
+    $scope.viewServers = function() {
+        $location.path($location.path() + '/servers');
+    };
+
+    $scope.navigateToPath = locationUtils.navigateToPath;
+
+    $scope.hasError = formUtils.hasError;
+
+    $scope.hasPropertyError = formUtils.hasPropertyError;
+
+    var init = function () {
+        getRegions();
+    };
+    init();
+
+};
+
+FormPhysLocationController.$inject = ['physLocation', '$scope', '$location', 'formUtils', 'stringUtils', 'locationUtils', 'regionService'];
+module.exports = FormPhysLocationController;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/physLocation/edit/FormEditPhysLocationController.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/physLocation/edit/FormEditPhysLocationController.js b/traffic_portal/app/src/common/modules/form/physLocation/edit/FormEditPhysLocationController.js
new file mode 100644
index 0000000..688829a
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/physLocation/edit/FormEditPhysLocationController.js
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+var FormEditPhysLocationController = function(physLocation, $scope, $controller, $uibModal, $anchorScroll, locationUtils, physLocationService) {
+
+    // extends the FormPhysLocationController to inherit common methods
+    angular.extend(this, $controller('FormPhysLocationController', { physLocation: physLocation, $scope: $scope }));
+
+    var deletePhysLocation = function(physLocation) {
+        physLocationService.deletePhysLocation(physLocation.id)
+            .then(function() {
+                locationUtils.navigateToPath('/admin/phys-locations');
+            });
+    };
+
+    $scope.physLocationName = angular.copy(physLocation.name);
+
+    $scope.settings = {
+        isNew: false,
+        saveLabel: 'Update'
+    };
+
+    $scope.save = function(physLocation) {
+        physLocationService.updatePhysLocation(physLocation).
+            then(function() {
+                $scope.physLocationName = angular.copy(physLocation.name);
+                $anchorScroll(); // scrolls window to top
+            });
+    };
+
+    $scope.confirmDelete = function(physLocation) {
+        var params = {
+            title: 'Delete Physical Location: ' + physLocation.name,
+            key: physLocation.name
+        };
+        var modalInstance = $uibModal.open({
+            templateUrl: 'common/modules/dialog/delete/dialog.delete.tpl.html',
+            controller: 'DialogDeleteController',
+            size: 'md',
+            resolve: {
+                params: function () {
+                    return params;
+                }
+            }
+        });
+        modalInstance.result.then(function() {
+            deletePhysLocation(physLocation);
+        }, function () {
+            // do nothing
+        });
+    };
+
+};
+
+FormEditPhysLocationController.$inject = ['physLocation', '$scope', '$controller', '$uibModal', '$anchorScroll', 'locationUtils', 'physLocationService'];
+module.exports = FormEditPhysLocationController;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/physLocation/edit/index.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/physLocation/edit/index.js b/traffic_portal/app/src/common/modules/form/physLocation/edit/index.js
new file mode 100644
index 0000000..d042d95
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/physLocation/edit/index.js
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+module.exports = angular.module('trafficPortal.form.physLocation.edit', [])
+    .controller('FormEditPhysLocationController', require('./FormEditPhysLocationController'));

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/physLocation/form.physLocation.tpl.html
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/physLocation/form.physLocation.tpl.html b/traffic_portal/app/src/common/modules/form/physLocation/form.physLocation.tpl.html
new file mode 100644
index 0000000..c2d66d4
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/physLocation/form.physLocation.tpl.html
@@ -0,0 +1,64 @@
+<!--
+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.
+-->
+
+<div class="x_panel">
+    <div class="x_title">
+        <ol class="breadcrumb pull-left">
+            <li><a ng-click="navigateToPath('/admin/phys-locations')">Physical Locations</a></li>
+            <li class="active">{{physLocationName}}</li>
+        </ol>
+        <div class="pull-right" role="group" ng-show="!settings.isNew">
+            <button class="btn btn-primary" title="View Servers" ng-click="viewServers()">View Servers</button>
+        </div>
+        <div class="clearfix"></div>
+    </div>
+    <div class="x_content">
+        <br>
+        <form name="physLocationForm" class="form-horizontal form-label-left" novalidate>
+            <div class="form-group" ng-class="{'has-error': hasError(physLocationForm[prop.name]), 'has-feedback': hasError(physLocationForm[prop.name])}" ng-repeat="prop in props">
+                <label class="control-label col-md-2 col-sm-2 col-xs-12">{{labelize(prop.name)}} <span ng-show="prop.required">*</span></label>
+                <div class="col-md-10 col-sm-10 col-xs-12">
+                    <input id="{{prop.name}}" name="{{prop.name}}" type="{{prop.type}}" class="form-control" ng-model="physLocation[prop.name]" ng-readonly="prop.readonly" ng-required="prop.required" ng-maxlength="prop.maxLength" autofocus>
+                    <small class="input-error" ng-show="hasPropertyError(physLocationForm[prop.name], 'required')">Required</small>
+                    <small class="input-error" ng-show="hasPropertyError(physLocationForm[prop.name], 'maxlength')">Too Long</small>
+                    <span ng-show="hasError(physLocationForm[prop.name])" class="form-control-feedback"><i class="fa fa-times"></i></span>
+                </div>
+            </div>
+            <div class="form-group" ng-class="{'has-error': hasError(physLocationForm.region), 'has-feedback': hasError(physLocationForm.region)}">
+                <label class="control-label col-md-2 col-sm-2 col-xs-12">Region *</label>
+                <div class="col-md-10 col-sm-10 col-xs-12">
+                    <select id="region" name="region" class="form-control" ng-model="physLocation.regionId" ng-options="region.id as region.name for region in regions" required>
+                        <option value="">Select...</option>
+                    </select>
+                    <small class="input-error" ng-show="hasPropertyError(physLocationForm.region, 'required')">Required</small>
+                </div>
+            </div>
+            <div class="form-group">
+                <label class="control-label col-md-2 col-sm-2 col-xs-12">Comments</label>
+                <div class="col-md-10 col-sm-10 col-xs-12">
+                    <textarea id="comments" name="comments" rows="5" cols="17" class="form-control" ng-model="physLocation.comments" maxlength="256"></textarea>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-danger" ng-show="!settings.isNew" ng-click="confirmDelete(physLocation)">Delete</button>
+                <button type="button" class="btn btn-success" ng-disabled="physLocationForm.$pristine || physLocationForm.$invalid" ng-click="save(physLocation)">{{settings.saveLabel}}</button>
+            </div>
+        </form>
+    </div>
+</div>

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/physLocation/index.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/physLocation/index.js b/traffic_portal/app/src/common/modules/form/physLocation/index.js
new file mode 100644
index 0000000..402f625
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/physLocation/index.js
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+module.exports = angular.module('trafficPortal.form.physLocation', [])
+    .controller('FormPhysLocationController', require('./FormPhysLocationController'));

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/physLocation/new/FormNewPhysLocationController.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/physLocation/new/FormNewPhysLocationController.js b/traffic_portal/app/src/common/modules/form/physLocation/new/FormNewPhysLocationController.js
new file mode 100644
index 0000000..ba4aaa3
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/physLocation/new/FormNewPhysLocationController.js
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+var FormNewPhysLocationController = function(physLocation, $scope, $controller, physLocationService) {
+
+    // extends the FormPhysLocationController to inherit common methods
+    angular.extend(this, $controller('FormPhysLocationController', { physLocation: physLocation, $scope: $scope }));
+
+    $scope.physLocationName = 'New';
+
+    $scope.settings = {
+        isNew: true,
+        saveLabel: 'Create'
+    };
+
+    $scope.save = function(physLocation) {
+        physLocationService.createPhysLocation(physLocation);
+    };
+
+};
+
+FormNewPhysLocationController.$inject = ['physLocation', '$scope', '$controller', 'physLocationService'];
+module.exports = FormNewPhysLocationController;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/physLocation/new/index.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/physLocation/new/index.js b/traffic_portal/app/src/common/modules/form/physLocation/new/index.js
new file mode 100644
index 0000000..5297288
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/physLocation/new/index.js
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+module.exports = angular.module('trafficPortal.form.physLocation.new', [])
+    .controller('FormNewPhysLocationController', require('./FormNewPhysLocationController'));

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/profile/FormProfileController.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/profile/FormProfileController.js b/traffic_portal/app/src/common/modules/form/profile/FormProfileController.js
new file mode 100644
index 0000000..1523ee0
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/profile/FormProfileController.js
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+var FormProfileController = function(profile, $scope, $location, formUtils, locationUtils, cdnService) {
+
+    var getCDNs = function() {
+        cdnService.getCDNs(true)
+            .then(function(result) {
+                $scope.cdns = result;
+            });
+    };
+
+    $scope.profile = profile;
+
+    $scope.types = [
+        { value: 'ATS_PROFILE' },
+        { value: 'TR_PROFILE' },
+        { value: 'TM_PROFILE' },
+        { value: 'TS_PROFILE' },
+        { value: 'TP_PROFILE' },
+        { value: 'INFLUXDB_PROFILE' },
+        { value: 'RIAK_PROFILE' },
+        { value: 'SPLUNK_PROFILE' },
+        { value: 'DS_PROFILE' },
+        { value: 'ORG_PROFILE' },
+        { value: 'KAFKA_PROFILE' },
+        { value: 'LOGSTASH_PROFILE' },
+        { value: 'ES_PROFILE' },
+        { value: 'UNK_PROFILE' }
+    ];
+
+    $scope.viewParams = function() {
+        $location.path($location.path() + '/parameters');
+    };
+
+    $scope.viewServers = function() {
+        $location.path($location.path() + '/servers');
+    };
+
+    $scope.viewDeliveryServices = function() {
+        $location.path($location.path() + '/delivery-services');
+    };
+
+    $scope.cloneProfile = function() {
+        alert('not hooked up yet: cloneProfile');
+    };
+
+    $scope.exportProfile = function() {
+        alert('not hooked up yet: exportProfile');
+    };
+
+    $scope.navigateToPath = locationUtils.navigateToPath;
+
+    $scope.hasError = formUtils.hasError;
+
+    $scope.hasPropertyError = formUtils.hasPropertyError;
+
+    var init = function () {
+        getCDNs();
+    };
+    init();
+
+};
+
+FormProfileController.$inject = ['profile', '$scope', '$location', 'formUtils', 'locationUtils', 'cdnService'];
+module.exports = FormProfileController;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/profile/edit/FormEditProfileController.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/profile/edit/FormEditProfileController.js b/traffic_portal/app/src/common/modules/form/profile/edit/FormEditProfileController.js
new file mode 100644
index 0000000..cf80884
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/profile/edit/FormEditProfileController.js
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+var FormEditProfileController = function(profile, $scope, $controller, $uibModal, $anchorScroll, locationUtils, profileService) {
+
+    // extends the FormProfileController to inherit common methods
+    angular.extend(this, $controller('FormProfileController', { profile: profile, $scope: $scope }));
+
+    var deleteProfile = function(profile) {
+        profileService.deleteProfile(profile.id)
+            .then(function() {
+                locationUtils.navigateToPath('/admin/profiles');
+            });
+    };
+
+    $scope.profileName = angular.copy(profile.name);
+
+    $scope.settings = {
+        isNew: false,
+        saveLabel: 'Update'
+    };
+
+    $scope.save = function(profile) {
+        profileService.updateProfile(profile).
+            then(function() {
+                $scope.profileName = angular.copy(profile.name);
+                $anchorScroll(); // scrolls window to top
+            });
+    };
+
+    $scope.confirmDelete = function(profile) {
+        var params = {
+            title: 'Delete Profile: ' + profile.name,
+            key: profile.name
+        };
+        var modalInstance = $uibModal.open({
+            templateUrl: 'common/modules/dialog/delete/dialog.delete.tpl.html',
+            controller: 'DialogDeleteController',
+            size: 'md',
+            resolve: {
+                params: function () {
+                    return params;
+                }
+            }
+        });
+        modalInstance.result.then(function() {
+            deleteProfile(profile);
+        }, function () {
+            // do nothing
+        });
+    };
+
+};
+
+FormEditProfileController.$inject = ['profile', '$scope', '$controller', '$uibModal', '$anchorScroll', 'locationUtils', 'profileService'];
+module.exports = FormEditProfileController;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/profile/edit/index.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/profile/edit/index.js b/traffic_portal/app/src/common/modules/form/profile/edit/index.js
new file mode 100644
index 0000000..1050ddb
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/profile/edit/index.js
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+module.exports = angular.module('trafficPortal.form.profile.edit', [])
+    .controller('FormEditProfileController', require('./FormEditProfileController'));

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/profile/form.profile.tpl.html
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/profile/form.profile.tpl.html b/traffic_portal/app/src/common/modules/form/profile/form.profile.tpl.html
new file mode 100644
index 0000000..e88ff0d
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/profile/form.profile.tpl.html
@@ -0,0 +1,87 @@
+<!--
+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.
+-->
+
+<div class="x_panel">
+    <div class="x_title">
+        <ol class="breadcrumb pull-left">
+            <li><a ng-click="navigateToPath('/admin/profiles')">Profiles</a></li>
+            <li class="active">{{profileName}}</li>
+        </ol>
+        <div class="pull-right" role="group" ng-show="!settings.isNew">
+            <button class="btn btn-primary" title="View Parameters" ng-click="viewParams()">View Parameters</button>
+            <div class="btn-group" role="group" uib-dropdown is-open="more.isopen">
+                <button type="button" class="btn btn-default dropdown-toggle" uib-dropdown-toggle aria-haspopup="true" aria-expanded="false">
+                    More&nbsp;
+                    <span class="caret"></span>
+                </button>
+                <ul class="dropdown-menu-right dropdown-menu" uib-dropdown-menu>
+                    <li role="menuitem"><a ng-click="viewDeliveryServices()" ng-if="profile.type == 'DS_PROFILE'">View Delivery Services</a></li>
+                    <li role="menuitem"><a ng-click="viewParams()">View Parameters</a></li>
+                    <li role="menuitem"><a ng-click="viewServers()" ng-if="profile.type != 'DS_PROFILE'">View Servers</a></li>
+                    <li class="divider"></li>
+                    <li role="menuitem"><a ng-click="cloneProfile()">Clone Profile</a></li>
+                    <li role="menuitem"><a ng-click="exportProfile()">Export Profile</a></li>
+                </ul>
+            </div>
+        </div>
+        <div class="clearfix"></div>
+    </div>
+    <div class="x_content">
+        <br>
+        <form name="profileForm" class="form-horizontal form-label-left" novalidate>
+            <div class="form-group" ng-class="{'has-error': hasError(profileForm.name), 'has-feedback': hasError(profileForm.name)}">
+                <label class="control-label col-md-2 col-sm-2 col-xs-12">Name *</label>
+                <div class="col-md-10 col-sm-10 col-xs-12">
+                    <input id="name" name="name" type="text" class="form-control" ng-model="profile.name" ng-required="true" ng-maxlength="45" autofocus>
+                    <small class="input-error" ng-show="hasPropertyError(profileForm.name, 'required')">Required</small>
+                    <small class="input-error" ng-show="hasPropertyError(profileForm.name, 'maxlength')">Too Long</small>
+                    <span ng-show="hasError(profileForm.name)" class="form-control-feedback"><i class="fa fa-times"></i></span>
+                </div>
+            </div>
+            <div class="form-group" ng-class="{'has-error': hasError(profileForm.cdn), 'has-feedback': hasError(profileForm.cdn)}">
+                <label class="control-label col-md-2 col-sm-2 col-xs-12">CDN *</label>
+                <div class="col-md-10 col-sm-10 col-xs-12">
+                    <select id="cdn" name="cdn" class="form-control" ng-model="profile.cdn" ng-options="cdn.id as cdn.name for cdn in cdns" required>
+                        <option value="">Select...</option>
+                    </select>
+                    <small class="input-error" ng-show="hasPropertyError(profileForm.cdn, 'required')">Required</small>
+                </div>
+            </div>
+            <div class="form-group" ng-class="{'has-error': hasError(profileForm.type), 'has-feedback': hasError(profileForm.type)}">
+                <label class="control-label col-md-2 col-sm-2 col-xs-12">Type *</label>
+                <div class="col-md-10 col-sm-10 col-xs-12">
+                    <select id="type" name="type" class="form-control" ng-model="profile.type" ng-options="type.value as type.value for type in types" required>
+                        <option value="">Select...</option>
+                    </select>
+                    <small class="input-error" ng-show="hasPropertyError(profileForm.cdn, 'required')">Required</small>
+                </div>
+            </div>
+            <div class="form-group">
+                <label class="control-label col-md-2 col-sm-2 col-xs-12">Description</label>
+                <div class="col-md-10 col-sm-10 col-xs-12">
+                    <textarea id="description" name="description" rows="3" cols="17" class="form-control" ng-model="profile.description" maxlength="256"></textarea>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-danger" ng-show="!settings.isNew" ng-click="confirmDelete(profile)">Delete</button>
+                <button type="button" class="btn btn-success" ng-disabled="profileForm.$pristine || profileForm.$invalid" ng-click="save(profile)">{{settings.saveLabel}}</button>
+            </div>
+        </form>
+    </div>
+</div>

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/profile/index.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/profile/index.js b/traffic_portal/app/src/common/modules/form/profile/index.js
new file mode 100644
index 0000000..293da23
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/profile/index.js
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+module.exports = angular.module('trafficPortal.form.profile', [])
+    .controller('FormProfileController', require('./FormProfileController'));

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/profile/new/FormNewProfileController.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/profile/new/FormNewProfileController.js b/traffic_portal/app/src/common/modules/form/profile/new/FormNewProfileController.js
new file mode 100644
index 0000000..19b42c9
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/profile/new/FormNewProfileController.js
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+var FormNewProfileController = function(profile, $scope, $controller, profileService) {
+
+    // extends the FormProfileController to inherit common methods
+    angular.extend(this, $controller('FormProfileController', { profile: profile, $scope: $scope }));
+
+    $scope.profileName = 'New';
+
+    $scope.settings = {
+        isNew: true,
+        saveLabel: 'Create'
+    };
+
+    $scope.save = function(profile) {
+        profileService.createProfile(profile);
+    };
+
+};
+
+FormNewProfileController.$inject = ['profile', '$scope', '$controller', 'profileService'];
+module.exports = FormNewProfileController;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/profile/new/index.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/profile/new/index.js b/traffic_portal/app/src/common/modules/form/profile/new/index.js
new file mode 100644
index 0000000..d36a4c3
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/profile/new/index.js
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+module.exports = angular.module('trafficPortal.form.profile.new', [])
+    .controller('FormNewProfileController', require('./FormNewProfileController'));

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/region/FormRegionController.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/region/FormRegionController.js b/traffic_portal/app/src/common/modules/form/region/FormRegionController.js
new file mode 100644
index 0000000..83c0bef
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/region/FormRegionController.js
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+var FormRegionController = function(region, $scope, $location, formUtils, stringUtils, locationUtils, divisionService) {
+
+    var getDivisions = function() {
+        divisionService.getDivisions()
+            .then(function(result) {
+                $scope.divisions = result;
+            });
+    };
+
+    $scope.region = region;
+
+    $scope.props = [
+        { name: 'name', type: 'text', required: true, maxLength: 45 }
+    ];
+
+    $scope.labelize = stringUtils.labelize;
+
+    $scope.viewPhysLocations = function() {
+        $location.path($location.path() + '/phys-locations');
+    };
+
+    $scope.navigateToPath = locationUtils.navigateToPath;
+
+    $scope.hasError = formUtils.hasError;
+
+    $scope.hasPropertyError = formUtils.hasPropertyError;
+
+    var init = function () {
+        getDivisions();
+    };
+    init();
+
+};
+
+FormRegionController.$inject = ['region', '$scope', '$location', 'formUtils', 'stringUtils', 'locationUtils', 'divisionService'];
+module.exports = FormRegionController;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/region/edit/FormEditRegionController.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/region/edit/FormEditRegionController.js b/traffic_portal/app/src/common/modules/form/region/edit/FormEditRegionController.js
new file mode 100644
index 0000000..0e11293
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/region/edit/FormEditRegionController.js
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+var FormEditRegionController = function(region, $scope, $controller, $uibModal, $anchorScroll, locationUtils, regionService) {
+
+    // extends the FormRegionController to inherit common methods
+    angular.extend(this, $controller('FormRegionController', { region: region, $scope: $scope }));
+
+    var deleteRegion = function(region) {
+        regionService.deleteRegion(region.id)
+            .then(function() {
+                locationUtils.navigateToPath('/admin/regions');
+            });
+    };
+
+    $scope.regionName = angular.copy(region.name);
+
+    $scope.settings = {
+        isNew: false,
+        saveLabel: 'Update'
+    };
+
+    $scope.save = function(region) {
+        regionService.updateRegion(region).
+            then(function() {
+                $scope.regionName = angular.copy(region.name);
+                $anchorScroll(); // scrolls window to top
+            });
+    };
+
+    $scope.confirmDelete = function(region) {
+        var params = {
+            title: 'Delete Region: ' + region.name,
+            key: region.name
+        };
+        var modalInstance = $uibModal.open({
+            templateUrl: 'common/modules/dialog/delete/dialog.delete.tpl.html',
+            controller: 'DialogDeleteController',
+            size: 'md',
+            resolve: {
+                params: function () {
+                    return params;
+                }
+            }
+        });
+        modalInstance.result.then(function() {
+            deleteRegion(region);
+        }, function () {
+            // do nothing
+        });
+    };
+
+};
+
+FormEditRegionController.$inject = ['region', '$scope', '$controller', '$uibModal', '$anchorScroll', 'locationUtils', 'regionService'];
+module.exports = FormEditRegionController;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/region/edit/index.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/region/edit/index.js b/traffic_portal/app/src/common/modules/form/region/edit/index.js
new file mode 100644
index 0000000..bda4bea
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/region/edit/index.js
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+module.exports = angular.module('trafficPortal.form.region.edit', [])
+    .controller('FormEditRegionController', require('./FormEditRegionController'));

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/region/form.region.tpl.html
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/region/form.region.tpl.html b/traffic_portal/app/src/common/modules/form/region/form.region.tpl.html
new file mode 100644
index 0000000..a37325e
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/region/form.region.tpl.html
@@ -0,0 +1,58 @@
+<!--
+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.
+-->
+
+<div class="x_panel">
+    <div class="x_title">
+        <ol class="breadcrumb pull-left">
+            <li><a ng-click="navigateToPath('/admin/regions')">Regions</a></li>
+            <li class="active">{{regionName}}</li>
+        </ol>
+        <div class="pull-right" role="group" ng-show="!settings.isNew">
+            <button class="btn btn-primary" title="View Phys Locations" ng-click="viewPhysLocations()">View Phys Locations</button>
+        </div>
+        <div class="clearfix"></div>
+    </div>
+    <div class="x_content">
+        <br>
+        <form name="regionForm" class="form-horizontal form-label-left" novalidate>
+            <div class="form-group" ng-class="{'has-error': hasError(regionForm[prop.name]), 'has-feedback': hasError(regionForm[prop.name])}" ng-repeat="prop in props">
+                <label class="control-label col-md-2 col-sm-2 col-xs-12">{{labelize(prop.name)}} <span ng-show="prop.required">*</span></label>
+                <div class="col-md-10 col-sm-10 col-xs-12">
+                    <input id="{{prop.name}}" name="{{prop.name}}" type="{{prop.type}}" class="form-control" ng-model="region[prop.name]" ng-readonly="prop.readonly" ng-required="prop.required" ng-maxlength="prop.maxLength" autofocus>
+                    <small class="input-error" ng-show="hasPropertyError(regionForm[prop.name], 'required')">Required</small>
+                    <small class="input-error" ng-show="hasPropertyError(regionForm[prop.name], 'maxlength')">Too Long</small>
+                    <span ng-show="hasError(regionForm[prop.name])" class="form-control-feedback"><i class="fa fa-times"></i></span>
+                </div>
+            </div>
+            <div class="form-group" ng-class="{'has-error': hasError(regionForm.division), 'has-feedback': hasError(regionForm.division)}">
+                <label class="control-label col-md-2 col-sm-2 col-xs-12">Division *</label>
+                <div class="col-md-10 col-sm-10 col-xs-12">
+                    <select id="division" name="division" class="form-control" ng-model="region.division" ng-options="division.id as division.name for division in divisions" required>
+                        <option value="">Select...</option>
+                    </select>
+                    <small class="input-error" ng-show="hasPropertyError(regionForm.division, 'required')">Required</small>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-danger" ng-show="!settings.isNew" ng-click="confirmDelete(region)">Delete</button>
+                <button type="button" class="btn btn-success" ng-disabled="regionForm.$pristine || regionForm.$invalid" ng-click="save(region)">{{settings.saveLabel}}</button>
+            </div>
+        </form>
+    </div>
+</div>

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/region/index.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/region/index.js b/traffic_portal/app/src/common/modules/form/region/index.js
new file mode 100644
index 0000000..0032290
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/region/index.js
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+module.exports = angular.module('trafficPortal.form.region', [])
+    .controller('FormRegionController', require('./FormRegionController'));

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/region/new/FormNewRegionController.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/region/new/FormNewRegionController.js b/traffic_portal/app/src/common/modules/form/region/new/FormNewRegionController.js
new file mode 100644
index 0000000..8aa7b36
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/region/new/FormNewRegionController.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.
+ */
+
+var FormNewRegionController = function(region, $scope, $controller, locationUtils, regionService) {
+
+    // extends the FormRegionController to inherit common methods
+    angular.extend(this, $controller('FormRegionController', { region: region, $scope: $scope }));
+
+    $scope.regionName = 'New';
+
+    $scope.settings = {
+        isNew: true,
+        saveLabel: 'Create'
+    };
+
+    $scope.save = function(region) {
+        regionService.createRegion(region).
+            then(function() {
+                locationUtils.navigateToPath('/admin/regions');
+            });
+    };
+
+};
+
+FormNewRegionController.$inject = ['region', '$scope', '$controller', 'locationUtils', 'regionService'];
+module.exports = FormNewRegionController;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/region/new/index.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/region/new/index.js b/traffic_portal/app/src/common/modules/form/region/new/index.js
new file mode 100644
index 0000000..4155ac7
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/region/new/index.js
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+module.exports = angular.module('trafficPortal.form.region.new', [])
+    .controller('FormNewRegionController', require('./FormNewRegionController'));

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/server/FormServerController.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/server/FormServerController.js b/traffic_portal/app/src/common/modules/form/server/FormServerController.js
new file mode 100644
index 0000000..c23b03d
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/server/FormServerController.js
@@ -0,0 +1,164 @@
+/*
+ * 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.
+ */
+
+var FormServerController = function(server, $scope, $location, $state, $uibModal, formUtils, locationUtils, serverService, cacheGroupService, cdnService, physLocationService, profileService, statusService, typeService, messageModel) {
+
+    var getPhysLocations = function() {
+        physLocationService.getPhysLocations()
+            .then(function(result) {
+                $scope.physLocations = result;
+            });
+    };
+
+    var getCacheGroups = function() {
+        cacheGroupService.getCacheGroups()
+            .then(function(result) {
+                $scope.cacheGroups = result;
+            });
+    };
+
+    var getTypes = function() {
+        typeService.getTypes({ useInTable: 'server' })
+            .then(function(result) {
+                $scope.types = result;
+            });
+    };
+
+    var getCDNs = function() {
+        cdnService.getCDNs(true)
+            .then(function(result) {
+                $scope.cdns = result;
+            });
+    };
+
+    var getStatuses = function() {
+        statusService.getStatuses()
+            .then(function(result) {
+                $scope.statuses = result;
+            });
+    };
+
+    var getProfiles = function() {
+        profileService.getProfiles({ orderby: 'name' })
+            .then(function(result) {
+                $scope.profiles = _.filter(result, function(profile) {
+                    return profile.type != 'DS_PROFILE';
+                });
+            });
+    };
+
+    var updateStatus = function(status) {
+        serverService.updateStatus(server.id, { status: status.id, offlineReason: status.offlineReason })
+            .then(
+                function(result) {
+                    messageModel.setMessages(result.data.alerts, false);
+                    refresh();
+                },
+	            function(fault) {
+		            messageModel.setMessages(fault.data.alerts, false);
+	            }
+            );
+    };
+    
+    var refresh = function() {
+        $state.reload(); // reloads all the resolves for the view
+    };
+
+
+    // supposedly matches IPv4 and IPv6 formats. but actually need one that matches each. todo.
+    $scope.validations = {
+        ipRegex: new RegExp(/^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$|^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3
 }))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/)
+    };
+
+
+    $scope.server = server;
+
+    $scope.falseTrue = [
+        { value: false, label: 'false' },
+        { value: true, label: 'true' }
+    ];
+
+    $scope.queueServerUpdates = function(server) {
+        serverService.queueServerUpdates(server.id)
+            .then(
+                function() {
+                    refresh();
+                }
+            );
+    };
+
+    $scope.clearServerUpdates = function(server) {
+        serverService.clearServerUpdates(server.id)
+            .then(
+                function() {
+                    refresh();
+                }
+            );
+    };
+
+    $scope.confirmStatusUpdate = function() {
+        var modalInstance = $uibModal.open({
+            templateUrl: 'common/modules/dialog/select/status/dialog.select.status.tpl.html',
+            controller: 'DialogSelectStatusController',
+            size: 'md',
+            resolve: {
+                server: function() {
+                    return server;
+                },
+                statuses: function() {
+                    return $scope.statuses;
+                }
+            }
+        });
+        modalInstance.result.then(function(status) {
+            updateStatus(status);
+        }, function () {
+            // do nothing
+        });
+    };
+
+
+    $scope.viewConfig = function() {
+        alert('not hooked up yet: view config files for server');
+    };
+
+    $scope.viewDeliveryServices = function() {
+        $location.path($location.path() + '/delivery-services');
+    };
+
+    $scope.navigateToPath = locationUtils.navigateToPath;
+
+    $scope.hasError = formUtils.hasError;
+
+    $scope.hasPropertyError = formUtils.hasPropertyError;
+
+    var init = function () {
+        getPhysLocations();
+        getCacheGroups();
+        getTypes();
+        getCDNs();
+        getStatuses();
+        getProfiles();
+    };
+    init();
+
+};
+
+FormServerController.$inject = ['server', '$scope', '$location', '$state', '$uibModal', 'formUtils', 'locationUtils', 'serverService', 'cacheGroupService', 'cdnService', 'physLocationService', 'profileService', 'statusService', 'typeService', 'messageModel'];
+module.exports = FormServerController;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/3195e0cc/traffic_portal/app/src/common/modules/form/server/edit/FormEditServerController.js
----------------------------------------------------------------------
diff --git a/traffic_portal/app/src/common/modules/form/server/edit/FormEditServerController.js b/traffic_portal/app/src/common/modules/form/server/edit/FormEditServerController.js
new file mode 100644
index 0000000..a7fbc08
--- /dev/null
+++ b/traffic_portal/app/src/common/modules/form/server/edit/FormEditServerController.js
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+var FormEditServerController = function(server, $scope, $controller, $uibModal, $anchorScroll, locationUtils, serverService) {
+
+    // extends the FormServerController to inherit common methods
+    angular.extend(this, $controller('FormServerController', { server: server, $scope: $scope }));
+
+    var deleteServer = function(server) {
+        serverService.deleteServer(server.id)
+            .then(function() {
+                locationUtils.navigateToPath('/configure/servers');
+            });
+    };
+
+    $scope.serverName = angular.copy(server.hostName);
+
+    $scope.settings = {
+        isNew: false,
+        saveLabel: 'Update'
+    };
+
+    $scope.save = function(server) {
+        serverService.updateServer(server).
+            then(function() {
+                $scope.serverName = angular.copy(server.hostName);
+                $anchorScroll(); // scrolls window to top
+            });
+    };
+
+    $scope.confirmDelete = function(server) {
+        var params = {
+            title: 'Delete Server: ' + server.hostName,
+            key: server.hostName
+        };
+        var modalInstance = $uibModal.open({
+            templateUrl: 'common/modules/dialog/delete/dialog.delete.tpl.html',
+            controller: 'DialogDeleteController',
+            size: 'md',
+            resolve: {
+                params: function () {
+                    return params;
+                }
+            }
+        });
+        modalInstance.result.then(function() {
+            deleteServer(server);
+        }, function () {
+            // do nothing
+        });
+    };
+
+};
+
+FormEditServerController.$inject = ['server', '$scope', '$controller', '$uibModal', '$anchorScroll', 'locationUtils', 'serverService'];
+module.exports = FormEditServerController;