Building app and caching templates
diff --git a/xos/core/xoslib/xos-builder/dist/xosContentProvider.js b/xos/core/xoslib/xos-builder/dist/xosContentProvider.js
new file mode 100644
index 0000000..9c2b58d
--- /dev/null
+++ b/xos/core/xoslib/xos-builder/dist/xosContentProvider.js
@@ -0,0 +1 @@
+angular.module("xos.contentProviderApp",["ngResource","ngRoute","ngCookies","ngLodash"]).config(["$interpolateProvider","$routeProvider","$resourceProvider",function(n,e,t){n.startSymbol("{$"),n.endSymbol("$}"),t.defaults.stripTrailingSlashes=!1,e.when("/",{template:"<content-provider-list></content-provider-list>"}).when("/contentProvider/:id?",{template:"<content-provider-detail></content-provider-detail>"}).when("/contentProvider/:id/cdn_prefix",{template:"<content-provider-cdn></content-provider-cdn>"}).when("/contentProvider/:id/origin_server",{template:"<content-provider-server></content-provider-server>"}).when("/contentProvider/:id/users",{template:"<content-provider-users></content-provider-users>"}).otherwise("/")}]).config(["$httpProvider",function(n){n.interceptors.push("SetCSRFToken")}]).factory("SetCSRFToken",["$cookies",function(n){return{request:function(e){return-1===e.url.indexOf(".html")&&(e.url+="?no_hyperlinks=1"),"GET"!==e.method&&(e.headers["X-CSRFToken"]=n.get("xoscsrftoken")),e}}}]).service("ContentProvider",["$resource",function(n){return n("/hpcapi/contentproviders/:id/",{id:"@id"},{update:{method:"PUT"}})}]).service("ServiceProvider",["$resource",function(n){return n("/hpcapi/serviceproviders/:id/",{id:"@id"})}]).service("CdnPrefix",["$resource",function(n){return n("/hpcapi/cdnprefixs/:id/",{id:"@id"})}]).service("OriginServer",["$resource",function(n){return n("/hpcapi/originservers/:id/",{id:"@id"})}]).service("User",["$resource",function(n){return n("/xos/users/:id/",{id:"@id"})}]).directive("cpActions",["ContentProvider","$location",function(n,e){return{restrict:"E",scope:{id:"=id"},bindToController:!0,controllerAs:"vm",templateUrl:"../../static/templates/contentProvider/cp_actions.html",controller:function(){this.deleteCp=function(t){n["delete"]({id:t}).$promise.then(function(){e.url("/")})}}}}]).directive("contentProviderList",["ContentProvider","lodash",function(n,e){return{restrict:"E",controllerAs:"vm",scope:{},templateUrl:"../../static/templates/contentProvider/cp_list.html",controller:function(){var t=this;n.query().$promise.then(function(n){t.contentProviderList=n})["catch"](function(n){throw new Error(n)}),this.deleteCp=function(i){n["delete"]({id:i}).$promise.then(function(){e.remove(t.contentProviderList,{id:i})})}}}}]).directive("contentProviderDetail",["ContentProvider","ServiceProvider","$routeParams","$location",function(n,e,t,i){return{restrict:"E",controllerAs:"vm",scope:{},templateUrl:"../../static/templates/contentProvider/cp_detail.html",controller:function(){this.pageName="detail";var s=this;t.id?n.get({id:t.id}).$promise.then(function(n){s.cp=n})["catch"](function(n){s.result={status:0,msg:n.data.detail}}):s.cp=new n,e.query().$promise.then(function(n){s.sp=n}),this.saveContentProvider=function(n){var e,t=!1;n.id?e=n.$update():(t=!0,n.name=n.humanReadableName,e=n.$save()),e.then(function(n){s.result={status:1,msg:"Content Provider Saved"},t&&i.url("contentProvider/"+n.id+"/")})["catch"](function(n){s.result={status:0,msg:n.data.detail}})}}}}]).directive("contentProviderCdn",["$routeParams","CdnPrefix","ContentProvider","lodash",function(n,e,t,i){return{restrict:"E",controllerAs:"vm",scope:{},templateUrl:"../../static/templates/contentProvider/cp_cdn_prefix.html",controller:function(){var s=this;this.pageName="cdn",n.id&&t.get({id:n.id}).$promise.then(function(n){s.cp=n})["catch"](function(n){s.result={status:0,msg:n.data.detail}}),e.query().$promise.then(function(e){s.prf=e,s.cp_prf=i.where(e,{contentProvider:parseInt(n.id)})})["catch"](function(n){s.result={status:0,msg:n.data.detail}}),this.addPrefix=function(t){t.contentProvider=n.id;var i=new e(t);i.$save().then(function(n){s.cp_prf.push(n)})["catch"](function(n){s.result={status:0,msg:n.data.detail}})},this.removePrefix=function(n){n.$delete().then(function(){i.remove(s.cp_prf,n)})["catch"](function(n){s.result={status:0,msg:n.data.detail}})}}}}]).directive("contentProviderServer",["$routeParams","OriginServer","ContentProvider","lodash",function(n,e,t,i){return{restrict:"E",controllerAs:"vm",scope:{},templateUrl:"../../static/templates/contentProvider/cp_origin_server.html",controller:function(){this.pageName="server",this.protocols={http:"HTTP",rtmp:"RTMP",rtp:"RTP",shout:"SHOUTcast"};var s=this;n.id&&t.get({id:n.id}).$promise.then(function(n){s.cp=n})["catch"](function(n){s.result={status:0,msg:n.data.detail}}),e.query({contentProvider:n.id}).$promise.then(function(n){s.cp_os=n})["catch"](function(n){s.result={status:0,msg:n.data.detail}}),this.addOrigin=function(t){t.contentProvider=n.id;var i=new e(t);i.$save().then(function(n){s.cp_os.push(n)})["catch"](function(n){s.result={status:0,msg:n.data.detail}})},this.removeOrigin=function(n){n.$delete().then(function(){i.remove(s.cp_os,n)})["catch"](function(n){s.result={status:0,msg:n.data.detail}})}}}}]).directive("contentProviderUsers",["$routeParams","ContentProvider","User","lodash",function(n,e,t,i){return{restrict:"E",controllerAs:"vm",scope:{},templateUrl:"../../static/templates/contentProvider/cp_user.html",controller:function(){var s=this;this.pageName="user",this.cp_users=[],n.id&&t.query().$promise.then(function(t){return s.users=t,e.get({id:n.id}).$promise}).then(function(n){return n.users=s.populateUser(n.users,s.users),n}).then(function(n){s.cp=n})["catch"](function(n){s.result={status:0,msg:n.data.detail}}),this.populateUser=function(n,e){for(var t=0;t<n.length;t++)n[t]=i.find(e,{id:n[t]});return n},this.addUserToCp=function(n){s.cp.users.push(n)},this.removeUserFromCp=function(n){i.remove(s.cp.users,n)},this.saveContentProvider=function(n){n.users=i.pluck(n.users,"id"),n.$update().then(function(n){s.cp.users=s.populateUser(n.users,s.users),s.result={status:1,msg:"Content Provider Saved"}})["catch"](function(n){s.result={status:0,msg:n.data.detail}})}}}}]),angular.module("xos.contentProviderApp").run(["$templateCache",function(n){n.put("../../static/templates/contentProvider/cp_actions.html",'<a href="#/" class="btn btn-default">\n  <i class="icon icon-arrow-left"></i>Back\n</a>\n<a href="#/contentProvider/" class="btn btn-success">\n  <i class="icon icon-plus"></i>Create\n</a>\n<a ng-click="vm.deleteCp(vm.id)" class="btn btn-danger">\n  <i class="icon icon-remove"></i>Remove\n</a>'),n.put("../../static/templates/contentProvider/cp_cdn_prefix.html",'<div class="row-fluid">\n  <div class="span6">\n    <h1>{$ vm.cp.humanReadableName $}</h1>\n  </div>\n  <div class="span6 text-right">\n    <cp-actions id="vm.cp.id"></cp-actions>\n  </div>\n</div>\n<hr>\n<div class="row-fluid">\n  <div class="span2">\n    <div ng-include="\'../../static/templates/contentProvider/cp_side_nav.html\'"></div>\n  </div>\n  <div class="span10">\n    <div ng-repeat="item in vm.cp_prf" class="well">\n      <div class="row-fluid">\n        <div class="span4">\n          {{item.humanReadableName}}\n        </div>\n        <div class="span6">\n          <!-- TODO show the name instead that id -->\n          {{item.defaultOriginServer}}\n        </div>\n        <div class="span2">\n          <a ng-click="vm.removePrefix(item)" class="btn btn-danger pull-right">\n            <i class="icon icon-remove"></i>\n          </a>\n        </div>\n      </div>\n    </div>\n    <hr>\n    <form ng-submit="vm.addPrefix(vm.new_prf)">\n      <div class="row-fluid">\n        <div class="span4">\n          <label>Prefix</label>\n          <input type="text" ng-model="vm.new_prf.prefix" required style="max-width: 90%">\n        </div>\n        <div class="span6">\n          <label>Default Origin Server</label>\n          <select ng-model="vm.new_prf.defaultOriginServer" style="max-width: 100%">\n            <option ng-repeat="prf in vm.prf" ng-value="prf.id">{$ prf.humanReadableName $}</option>\n          </select>\n        </div>\n        <div class="span2 text-right">\n          <button class="btn btn-success margin-wells">\n            <i class="icon icon-plus"></i>\n          </button>\n        </div>\n      </div>\n    </form>\n    <div class="alert" ng-show="vm.result" ng-class="{\'alert-success\': vm.result.status === 1,\'alert-error\': vm.result.status === 0}">\n      {$ vm.result.msg $}\n    </div>\n  </div>\n</div>'),n.put("../../static/templates/contentProvider/cp_detail.html",'<div class="row-fluid">\n  <div class="span6">\n    <h1>{$ vm.cp.humanReadableName $}</h1>\n  </div>\n  <div class="span6 text-right">\n    <cp-actions id="vm.cp.id"></cp-actions>\n  </div>\n</div>\n<hr>\n<div class="row-fluid">\n  <div ng-show="vm.cp.id" class="span2">\n    <div ng-include="\'../../static/templates/contentProvider/cp_side_nav.html\'"></div>\n  </div>\n  <div ng-class="{span10: vm.cp.id, span12: !vm.cp.id}">\n  <!-- TODO hide form on not found -->\n    <form ng-submit="vm.saveContentProvider(vm.cp)">\n      <fieldset>\n        <div class="row-fluid">\n          <div class="span6">\n            <label>Name:</label>\n            <input type="text" ng-model="vm.cp.humanReadableName" required/>\n          </div>\n          <div class="span6">\n            <label class="checkbox">\n              <input type="checkbox" ng-model="vm.cp.enabled" /> Enabled\n            </label>\n          </div>\n        </div>\n        <div class="row-fluid">\n          <div class="span12">\n            <label>Description</label>\n            <textarea style="width: 100%" ng-model="vm.cp.description"></textarea>\n          </div>\n        </div>\n        <div class="row-fluid">\n          <div class="span12">\n            <label>Service provider</label>\n            <select required ng-model="vm.cp.serviceProvider" ng-options="sp.id as sp.humanReadableName for sp in vm.sp"></select>\n          </div>\n        </div>\n        <div class="row-fluid">\n          <div class="span12">\n            <button class="btn btn-success">\n              <span ng-show="vm.cp.id">Save</span>\n              <span ng-show="!vm.cp.id">Create</span>\n            </button>\n          </div>\n        </div>\n      </fieldset>\n    </form>\n    <div class="alert" ng-show="vm.result" ng-class="{\'alert-success\': vm.result.status === 1,\'alert-error\': vm.result.status === 0}">\n      {$ vm.result.msg $}\n    </div>\n  </div>\n</div>'),n.put("../../static/templates/contentProvider/cp_list.html",'<table class="table table-striped" ng-show="vm.contentProviderList.length > 0">\n  <thead>\n    <tr>\n      <th>\n        Name\n      </th>\n      <th>Description</th>\n      <th>Status</th>\n      <th></th>\n    </tr>\n  </thead>\n  <tr ng-repeat="item in vm.contentProviderList">\n    <td>\n      <a href="#/contentProvider/{$ item.id $}">{$ item.humanReadableName $}</a>\n    </td>\n    <td>\n      {$ item.description $}\n    </td>\n    <td>\n      {$ item.enabled $}\n    </td>\n    <td class="text-right">\n      <a ng-click="vm.deleteCp(item.id)" class="btn btn-danger"><i class="icon icon-remove"></i></a></td>\n  </tr>\n</table>\n<div class="alert alert-error" ng-show="vm.contentProviderList.length == 0">\n  No Content Provider defined\n</div>\n\n<div class="row">\n  <div class="span12 text-right">\n    <a class="btn btn-success"href="#/contentProvider/">Create</a>\n  </div>\n</div>'),n.put("../../static/templates/contentProvider/cp_origin_server.html",'<div class="row-fluid">\n  <div class="span6">\n    <h1>{$ vm.cp.humanReadableName $}</h1>\n  </div>\n  <div class="span6 text-right">\n    <cp-actions id="vm.cp.id"></cp-actions>\n  </div>\n</div>\n<hr>\n<div class="row-fluid">\n  <div class="span2">\n    <div ng-include="\'../../static/templates/contentProvider/cp_side_nav.html\'"></div>\n  </div>\n  <div class="span10">\n    <div ng-repeat="item in vm.cp_os" class="well">\n      <div class="row-fluid">\n        <div class="span4">\n          {{item.humanReadableName}}\n        </div>\n        <div class="span6">\n          <!-- TODO shoe the name instead that url -->\n          {{item.defaultOriginServer}}\n        </div>\n        <div class="span2">\n          <a ng-click="vm.removeOrigin(item)" class="btn btn-danger pull-right">\n            <i class="icon icon-remove"></i>\n          </a>\n        </div>\n      </div>\n    </div>\n    <hr>\n    <form ng-submit="vm.addOrigin(vm.new_os)">\n      <div class="row-fluid">\n        <div class="span4">\n          <label>Protocol</label>\n          <select ng-model="vm.new_os.protocol" ng-options="k as v for (k,v) in vm.protocols" style="max-width: 100%;"></select>\n        </div>\n        <div class="span6">\n          <label>Url</label>\n          <input type="text" ng-model="vm.new_os.url" required>\n        </div>\n        <div class="span2 text-right">\n          <button class="btn btn-success margin-wells">\n            <i class="icon icon-plus"></i>\n          </button>\n        </div>\n      </div>\n    </form>\n    <div class="alert" ng-show="vm.result" ng-class="{\'alert-success\': vm.result.status === 1,\'alert-error\': vm.result.status === 0}">\n      {$ vm.result.msg $}\n    </div>\n  </div>\n</div>'),n.put("../../static/templates/contentProvider/cp_side_nav.html",'<ul class="nav nav-list">\n  <li>\n    <a class="btn" ng-class="{\'btn-primary\': vm.pageName == \'detail\'}" href="#/contentProvider/{$ vm.cp.id $}">Details</a>\n  </li>\n  <li>\n    <a class="btn" ng-class="{\'btn-primary\': vm.pageName == \'cdn\'}" href="#/contentProvider/{$ vm.cp.id $}/cdn_prefix">Cdn Prexix</a>\n  </li>\n  <li>\n    <a class="btn" ng-class="{\'btn-primary\': vm.pageName == \'server\'}" href="#/contentProvider/{$ vm.cp.id $}/origin_server">Origin Server</a>\n  </li>\n  <li>\n    <a class="btn" ng-class="{\'btn-primary\': vm.pageName == \'user\'}" href="#/contentProvider/{$ vm.cp.id $}/users">Users</a>\n  </li>\n</ul>'),n.put("../../static/templates/contentProvider/cp_user.html",'<div class="row-fluid">\n  <div class="span6">\n    <h1>{$ vm.cp.humanReadableName $}</h1>\n  </div>\n  <div class="span6 text-right">\n    <cp-actions id="vm.cp.id"></cp-actions>\n  </div>\n</div>\n<hr>\n<div class="row-fluid">\n  <div class="span2">\n    <div ng-include="\'../../static/templates/contentProvider/cp_side_nav.html\'"></div>\n  </div>\n  <div class="span10">\n    <div ng-repeat="item in vm.cp.users" class="well">\n      <div class="row-fluid">\n        <div class="span3">\n          {{item.firstname}}\n        </div>\n        <div class="span3">\n          {{item.lastname}}\n        </div>\n        <div class="span4">\n          {{item.email}}\n        </div>\n        <div class="span2">\n          <a ng-click="vm.removeUserFromCp(item)" class="btn btn-danger pull-right">\n            <i class="icon icon-remove"></i>\n          </a>\n        </div>\n      </div>\n    </div>\n    <hr>\n    <form ng-submit="vm.saveContentProvider(vm.cp)">\n      <div class="row-fluid">\n        <div class="span8">\n          <label>Select user:</label>\n          <select ng-model="vm.user" ng-options="u as u.username for u in vm.users" ng-change="vm.addUserToCp(vm.user)"></select>\n        </div>  \n        <div class="span4 text-right">\n          <button class="btn btn-success margin-wells">\n            Save\n          </button>\n        </div>\n      </div>\n    </form>\n    <div class="alert" ng-show="vm.result" ng-class="{\'alert-success\': vm.result.status === 1,\'alert-error\': vm.result.status === 0}">\n      {$ vm.result.msg $}\n    </div>\n  </div>\n</div>')}]);
\ No newline at end of file
diff --git a/xos/core/xoslib/xos-builder/gulpfile.js b/xos/core/xoslib/xos-builder/gulpfile.js
new file mode 100644
index 0000000..87dda39
--- /dev/null
+++ b/xos/core/xoslib/xos-builder/gulpfile.js
@@ -0,0 +1,56 @@
+'use strict';
+
+// BUILD
+// 
+// The only purpose of this gulpfile is to build a XOS view and copy the correct files into
+// .html => dashboards
+// .js (minified and concat) => static/js
+// 
+// The template are parsed and added to js with angular $templateCache
+
+var gulp = require('gulp');
+var ngmin = require('gulp-ngmin');
+var uglify = require('gulp-uglify');
+var templateCache = require('gulp-angular-templatecache');
+var runSequence = require('run-sequence');
+var minifyHtml = require("gulp-minify-html");
+var concat = require("gulp-concat");
+var del = require('del');
+
+gulp.task('clean', function(){
+  return del(['dist/**/*']);
+});
+
+gulp.task('scripts', function() {
+  return gulp.src([
+      'src/xosContentProvider.js',
+      'src/templates.js'
+    ])
+    .pipe(ngmin())
+    .pipe(concat('xosContentProvider.js'))
+    .pipe(uglify())
+    .pipe(gulp.dest('dist'));
+});
+
+gulp.task('templates', function(){
+  return gulp.src("./src/templates/*.html")
+    .pipe(templateCache({
+      module: 'xos.contentProviderApp',
+      root: '../../static/templates/contentProvider/'
+    }))
+    .pipe(gulp.dest("src"));
+});
+
+gulp.task('copyJs', function(){
+  return gulp.src('dist/xosContentProvider.js')
+    .pipe(gulp.dest('../static/js/'))
+});
+
+gulp.task('default', function() {
+  runSequence(
+    'clean',
+    'templates',
+    'scripts',
+    'copyJs'
+  );
+});
diff --git a/xos/core/xoslib/xos-builder/package.json b/xos/core/xoslib/xos-builder/package.json
new file mode 100644
index 0000000..e5800b0
--- /dev/null
+++ b/xos/core/xoslib/xos-builder/package.json
@@ -0,0 +1,27 @@
+{
+  "name": "xos-builder",
+  "version": "1.0.0",
+  "description": "Angular Application Builder tailored to XOS needings",
+  "main": "xos-builder.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "keywords": [
+    "XOS",
+    "Angular",
+    "XOSlib"
+  ],
+  "author": "Matteo Scandolo",
+  "license": "ISC",
+  "dependencies": {},
+  "devDependencies": {
+    "del": "^2.0.2",
+    "gulp": "^3.9.0",
+    "gulp-angular-templatecache": "^1.8.0",
+    "gulp-concat": "^2.6.0",
+    "gulp-minify-html": "^1.0.4",
+    "gulp-ngmin": "^0.3.0",
+    "gulp-uglify": "^1.4.2",
+    "run-sequence": "^1.1.4"
+  }
+}
diff --git a/xos/core/xoslib/xos-builder/src/contentProvider.html b/xos/core/xoslib/xos-builder/src/contentProvider.html
new file mode 100644
index 0000000..24cd7f1
--- /dev/null
+++ b/xos/core/xoslib/xos-builder/src/contentProvider.html
@@ -0,0 +1,21 @@
+<!-- 
+To setup hpc:
+- cd /opt/xos/tosca
+- python ./run.py padmin@vicci.org samples/cdn.yaml
+
+To generate hpcapi:
+- cd /opt/xos
+- python apigen/modelgen apigen/hpc-api.template.py -n User -n Service -a hpc > xos/hpcapi.py
+ -->
+
+<div ng-app="xos.contentProviderApp">
+  <ng-view></ng-view>
+</div>
+
+<link rel="stylesheet" href="{{ STATIC_URL }}/css/xosLib.css">
+<script src="{{ STATIC_URL }}/js/vendor/angular/angular.min.js"></script>
+<script src="{{ STATIC_URL }}/js/vendor/angular-resource/angular-resource.min.js"></script>
+<script src="{{ STATIC_URL }}/js/vendor/angular-route/angular-route.min.js"></script>
+<script src="{{ STATIC_URL }}/js/vendor/angular-cookies/angular-cookies.min.js"></script>
+<script src="{{ STATIC_URL }}/js/vendor/ng-lodash/build/ng-lodash.min.js"></script>
+<script src="{{ STATIC_URL }}/js/xosContentProvider.js"></script>
diff --git a/xos/core/xoslib/xos-builder/src/templates.js b/xos/core/xoslib/xos-builder/src/templates.js
new file mode 100644
index 0000000..8abe693
--- /dev/null
+++ b/xos/core/xoslib/xos-builder/src/templates.js
@@ -0,0 +1,7 @@
+angular.module("xos.contentProviderApp").run(["$templateCache", function($templateCache) {$templateCache.put("../../static/templates/contentProvider/cp_actions.html","<a href=\"#/\" class=\"btn btn-default\">\n  <i class=\"icon icon-arrow-left\"></i>Back\n</a>\n<a href=\"#/contentProvider/\" class=\"btn btn-success\">\n  <i class=\"icon icon-plus\"></i>Create\n</a>\n<a ng-click=\"vm.deleteCp(vm.id)\" class=\"btn btn-danger\">\n  <i class=\"icon icon-remove\"></i>Remove\n</a>");
+$templateCache.put("../../static/templates/contentProvider/cp_cdn_prefix.html","<div class=\"row-fluid\">\n  <div class=\"span6\">\n    <h1>{$ vm.cp.humanReadableName $}</h1>\n  </div>\n  <div class=\"span6 text-right\">\n    <cp-actions id=\"vm.cp.id\"></cp-actions>\n  </div>\n</div>\n<hr>\n<div class=\"row-fluid\">\n  <div class=\"span2\">\n    <div ng-include=\"\'../../static/templates/contentProvider/cp_side_nav.html\'\"></div>\n  </div>\n  <div class=\"span10\">\n    <div ng-repeat=\"item in vm.cp_prf\" class=\"well\">\n      <div class=\"row-fluid\">\n        <div class=\"span4\">\n          {{item.humanReadableName}}\n        </div>\n        <div class=\"span6\">\n          <!-- TODO show the name instead that id -->\n          {{item.defaultOriginServer}}\n        </div>\n        <div class=\"span2\">\n          <a ng-click=\"vm.removePrefix(item)\" class=\"btn btn-danger pull-right\">\n            <i class=\"icon icon-remove\"></i>\n          </a>\n        </div>\n      </div>\n    </div>\n    <hr>\n    <form ng-submit=\"vm.addPrefix(vm.new_prf)\">\n      <div class=\"row-fluid\">\n        <div class=\"span4\">\n          <label>Prefix</label>\n          <input type=\"text\" ng-model=\"vm.new_prf.prefix\" required style=\"max-width: 90%\">\n        </div>\n        <div class=\"span6\">\n          <label>Default Origin Server</label>\n          <select ng-model=\"vm.new_prf.defaultOriginServer\" style=\"max-width: 100%\">\n            <option ng-repeat=\"prf in vm.prf\" ng-value=\"prf.id\">{$ prf.humanReadableName $}</option>\n          </select>\n        </div>\n        <div class=\"span2 text-right\">\n          <button class=\"btn btn-success margin-wells\">\n            <i class=\"icon icon-plus\"></i>\n          </button>\n        </div>\n      </div>\n    </form>\n    <div class=\"alert\" ng-show=\"vm.result\" ng-class=\"{\'alert-success\': vm.result.status === 1,\'alert-error\': vm.result.status === 0}\">\n      {$ vm.result.msg $}\n    </div>\n  </div>\n</div>");
+$templateCache.put("../../static/templates/contentProvider/cp_detail.html","<div class=\"row-fluid\">\n  <div class=\"span6\">\n    <h1>{$ vm.cp.humanReadableName $}</h1>\n  </div>\n  <div class=\"span6 text-right\">\n    <cp-actions id=\"vm.cp.id\"></cp-actions>\n  </div>\n</div>\n<hr>\n<div class=\"row-fluid\">\n  <div ng-show=\"vm.cp.id\" class=\"span2\">\n    <div ng-include=\"\'../../static/templates/contentProvider/cp_side_nav.html\'\"></div>\n  </div>\n  <div ng-class=\"{span10: vm.cp.id, span12: !vm.cp.id}\">\n  <!-- TODO hide form on not found -->\n    <form ng-submit=\"vm.saveContentProvider(vm.cp)\">\n      <fieldset>\n        <div class=\"row-fluid\">\n          <div class=\"span6\">\n            <label>Name:</label>\n            <input type=\"text\" ng-model=\"vm.cp.humanReadableName\" required/>\n          </div>\n          <div class=\"span6\">\n            <label class=\"checkbox\">\n              <input type=\"checkbox\" ng-model=\"vm.cp.enabled\" /> Enabled\n            </label>\n          </div>\n        </div>\n        <div class=\"row-fluid\">\n          <div class=\"span12\">\n            <label>Description</label>\n            <textarea style=\"width: 100%\" ng-model=\"vm.cp.description\"></textarea>\n          </div>\n        </div>\n        <div class=\"row-fluid\">\n          <div class=\"span12\">\n            <label>Service provider</label>\n            <select required ng-model=\"vm.cp.serviceProvider\" ng-options=\"sp.id as sp.humanReadableName for sp in vm.sp\"></select>\n          </div>\n        </div>\n        <div class=\"row-fluid\">\n          <div class=\"span12\">\n            <button class=\"btn btn-success\">\n              <span ng-show=\"vm.cp.id\">Save</span>\n              <span ng-show=\"!vm.cp.id\">Create</span>\n            </button>\n          </div>\n        </div>\n      </fieldset>\n    </form>\n    <div class=\"alert\" ng-show=\"vm.result\" ng-class=\"{\'alert-success\': vm.result.status === 1,\'alert-error\': vm.result.status === 0}\">\n      {$ vm.result.msg $}\n    </div>\n  </div>\n</div>");
+$templateCache.put("../../static/templates/contentProvider/cp_list.html","<table class=\"table table-striped\" ng-show=\"vm.contentProviderList.length > 0\">\n  <thead>\n    <tr>\n      <th>\n        Name\n      </th>\n      <th>Description</th>\n      <th>Status</th>\n      <th></th>\n    </tr>\n  </thead>\n  <tr ng-repeat=\"item in vm.contentProviderList\">\n    <td>\n      <a href=\"#/contentProvider/{$ item.id $}\">{$ item.humanReadableName $}</a>\n    </td>\n    <td>\n      {$ item.description $}\n    </td>\n    <td>\n      {$ item.enabled $}\n    </td>\n    <td class=\"text-right\">\n      <a ng-click=\"vm.deleteCp(item.id)\" class=\"btn btn-danger\"><i class=\"icon icon-remove\"></i></a></td>\n  </tr>\n</table>\n<div class=\"alert alert-error\" ng-show=\"vm.contentProviderList.length == 0\">\n  No Content Provider defined\n</div>\n\n<div class=\"row\">\n  <div class=\"span12 text-right\">\n    <a class=\"btn btn-success\"href=\"#/contentProvider/\">Create</a>\n  </div>\n</div>");
+$templateCache.put("../../static/templates/contentProvider/cp_origin_server.html","<div class=\"row-fluid\">\n  <div class=\"span6\">\n    <h1>{$ vm.cp.humanReadableName $}</h1>\n  </div>\n  <div class=\"span6 text-right\">\n    <cp-actions id=\"vm.cp.id\"></cp-actions>\n  </div>\n</div>\n<hr>\n<div class=\"row-fluid\">\n  <div class=\"span2\">\n    <div ng-include=\"\'../../static/templates/contentProvider/cp_side_nav.html\'\"></div>\n  </div>\n  <div class=\"span10\">\n    <div ng-repeat=\"item in vm.cp_os\" class=\"well\">\n      <div class=\"row-fluid\">\n        <div class=\"span4\">\n          {{item.humanReadableName}}\n        </div>\n        <div class=\"span6\">\n          <!-- TODO shoe the name instead that url -->\n          {{item.defaultOriginServer}}\n        </div>\n        <div class=\"span2\">\n          <a ng-click=\"vm.removeOrigin(item)\" class=\"btn btn-danger pull-right\">\n            <i class=\"icon icon-remove\"></i>\n          </a>\n        </div>\n      </div>\n    </div>\n    <hr>\n    <form ng-submit=\"vm.addOrigin(vm.new_os)\">\n      <div class=\"row-fluid\">\n        <div class=\"span4\">\n          <label>Protocol</label>\n          <select ng-model=\"vm.new_os.protocol\" ng-options=\"k as v for (k,v) in vm.protocols\" style=\"max-width: 100%;\"></select>\n        </div>\n        <div class=\"span6\">\n          <label>Url</label>\n          <input type=\"text\" ng-model=\"vm.new_os.url\" required>\n        </div>\n        <div class=\"span2 text-right\">\n          <button class=\"btn btn-success margin-wells\">\n            <i class=\"icon icon-plus\"></i>\n          </button>\n        </div>\n      </div>\n    </form>\n    <div class=\"alert\" ng-show=\"vm.result\" ng-class=\"{\'alert-success\': vm.result.status === 1,\'alert-error\': vm.result.status === 0}\">\n      {$ vm.result.msg $}\n    </div>\n  </div>\n</div>");
+$templateCache.put("../../static/templates/contentProvider/cp_side_nav.html","<ul class=\"nav nav-list\">\n  <li>\n    <a class=\"btn\" ng-class=\"{\'btn-primary\': vm.pageName == \'detail\'}\" href=\"#/contentProvider/{$ vm.cp.id $}\">Details</a>\n  </li>\n  <li>\n    <a class=\"btn\" ng-class=\"{\'btn-primary\': vm.pageName == \'cdn\'}\" href=\"#/contentProvider/{$ vm.cp.id $}/cdn_prefix\">Cdn Prexix</a>\n  </li>\n  <li>\n    <a class=\"btn\" ng-class=\"{\'btn-primary\': vm.pageName == \'server\'}\" href=\"#/contentProvider/{$ vm.cp.id $}/origin_server\">Origin Server</a>\n  </li>\n  <li>\n    <a class=\"btn\" ng-class=\"{\'btn-primary\': vm.pageName == \'user\'}\" href=\"#/contentProvider/{$ vm.cp.id $}/users\">Users</a>\n  </li>\n</ul>");
+$templateCache.put("../../static/templates/contentProvider/cp_user.html","<div class=\"row-fluid\">\n  <div class=\"span6\">\n    <h1>{$ vm.cp.humanReadableName $}</h1>\n  </div>\n  <div class=\"span6 text-right\">\n    <cp-actions id=\"vm.cp.id\"></cp-actions>\n  </div>\n</div>\n<hr>\n<div class=\"row-fluid\">\n  <div class=\"span2\">\n    <div ng-include=\"\'../../static/templates/contentProvider/cp_side_nav.html\'\"></div>\n  </div>\n  <div class=\"span10\">\n    <div ng-repeat=\"item in vm.cp.users\" class=\"well\">\n      <div class=\"row-fluid\">\n        <div class=\"span3\">\n          {{item.firstname}}\n        </div>\n        <div class=\"span3\">\n          {{item.lastname}}\n        </div>\n        <div class=\"span4\">\n          {{item.email}}\n        </div>\n        <div class=\"span2\">\n          <a ng-click=\"vm.removeUserFromCp(item)\" class=\"btn btn-danger pull-right\">\n            <i class=\"icon icon-remove\"></i>\n          </a>\n        </div>\n      </div>\n    </div>\n    <hr>\n    <form ng-submit=\"vm.saveContentProvider(vm.cp)\">\n      <div class=\"row-fluid\">\n        <div class=\"span8\">\n          <label>Select user:</label>\n          <select ng-model=\"vm.user\" ng-options=\"u as u.username for u in vm.users\" ng-change=\"vm.addUserToCp(vm.user)\"></select>\n        </div>  \n        <div class=\"span4 text-right\">\n          <button class=\"btn btn-success margin-wells\">\n            Save\n          </button>\n        </div>\n      </div>\n    </form>\n    <div class=\"alert\" ng-show=\"vm.result\" ng-class=\"{\'alert-success\': vm.result.status === 1,\'alert-error\': vm.result.status === 0}\">\n      {$ vm.result.msg $}\n    </div>\n  </div>\n</div>");}]);
\ No newline at end of file
diff --git a/xos/core/xoslib/xos-builder/src/templates/cp_actions.html b/xos/core/xoslib/xos-builder/src/templates/cp_actions.html
new file mode 100644
index 0000000..8c6ae97
--- /dev/null
+++ b/xos/core/xoslib/xos-builder/src/templates/cp_actions.html
@@ -0,0 +1,9 @@
+<a href="#/" class="btn btn-default">
+  <i class="icon icon-arrow-left"></i>Back
+</a>
+<a href="#/contentProvider/" class="btn btn-success">
+  <i class="icon icon-plus"></i>Create
+</a>
+<a ng-click="vm.deleteCp(vm.id)" class="btn btn-danger">
+  <i class="icon icon-remove"></i>Remove
+</a>
\ No newline at end of file
diff --git a/xos/core/xoslib/xos-builder/src/templates/cp_cdn_prefix.html b/xos/core/xoslib/xos-builder/src/templates/cp_cdn_prefix.html
new file mode 100644
index 0000000..cc39aab
--- /dev/null
+++ b/xos/core/xoslib/xos-builder/src/templates/cp_cdn_prefix.html
@@ -0,0 +1,55 @@
+<div class="row-fluid">
+  <div class="span6">
+    <h1>{$ vm.cp.humanReadableName $}</h1>
+  </div>
+  <div class="span6 text-right">
+    <cp-actions id="vm.cp.id"></cp-actions>
+  </div>
+</div>
+<hr>
+<div class="row-fluid">
+  <div class="span2">
+    <div ng-include="'../../static/templates/contentProvider/cp_side_nav.html'"></div>
+  </div>
+  <div class="span10">
+    <div ng-repeat="item in vm.cp_prf" class="well">
+      <div class="row-fluid">
+        <div class="span4">
+          {{item.humanReadableName}}
+        </div>
+        <div class="span6">
+          <!-- TODO show the name instead that id -->
+          {{item.defaultOriginServer}}
+        </div>
+        <div class="span2">
+          <a ng-click="vm.removePrefix(item)" class="btn btn-danger pull-right">
+            <i class="icon icon-remove"></i>
+          </a>
+        </div>
+      </div>
+    </div>
+    <hr>
+    <form ng-submit="vm.addPrefix(vm.new_prf)">
+      <div class="row-fluid">
+        <div class="span4">
+          <label>Prefix</label>
+          <input type="text" ng-model="vm.new_prf.prefix" required style="max-width: 90%">
+        </div>
+        <div class="span6">
+          <label>Default Origin Server</label>
+          <select ng-model="vm.new_prf.defaultOriginServer" style="max-width: 100%">
+            <option ng-repeat="prf in vm.prf" ng-value="prf.id">{$ prf.humanReadableName $}</option>
+          </select>
+        </div>
+        <div class="span2 text-right">
+          <button class="btn btn-success margin-wells">
+            <i class="icon icon-plus"></i>
+          </button>
+        </div>
+      </div>
+    </form>
+    <div class="alert" ng-show="vm.result" ng-class="{'alert-success': vm.result.status === 1,'alert-error': vm.result.status === 0}">
+      {$ vm.result.msg $}
+    </div>
+  </div>
+</div>
\ No newline at end of file
diff --git a/xos/core/xoslib/xos-builder/src/templates/cp_detail.html b/xos/core/xoslib/xos-builder/src/templates/cp_detail.html
new file mode 100644
index 0000000..0c11329
--- /dev/null
+++ b/xos/core/xoslib/xos-builder/src/templates/cp_detail.html
@@ -0,0 +1,55 @@
+<div class="row-fluid">
+  <div class="span6">
+    <h1>{$ vm.cp.humanReadableName $}</h1>
+  </div>
+  <div class="span6 text-right">
+    <cp-actions id="vm.cp.id"></cp-actions>
+  </div>
+</div>
+<hr>
+<div class="row-fluid">
+  <div ng-show="vm.cp.id" class="span2">
+    <div ng-include="'../../static/templates/contentProvider/cp_side_nav.html'"></div>
+  </div>
+  <div ng-class="{span10: vm.cp.id, span12: !vm.cp.id}">
+  <!-- TODO hide form on not found -->
+    <form ng-submit="vm.saveContentProvider(vm.cp)">
+      <fieldset>
+        <div class="row-fluid">
+          <div class="span6">
+            <label>Name:</label>
+            <input type="text" ng-model="vm.cp.humanReadableName" required/>
+          </div>
+          <div class="span6">
+            <label class="checkbox">
+              <input type="checkbox" ng-model="vm.cp.enabled" /> Enabled
+            </label>
+          </div>
+        </div>
+        <div class="row-fluid">
+          <div class="span12">
+            <label>Description</label>
+            <textarea style="width: 100%" ng-model="vm.cp.description"></textarea>
+          </div>
+        </div>
+        <div class="row-fluid">
+          <div class="span12">
+            <label>Service provider</label>
+            <select required ng-model="vm.cp.serviceProvider" ng-options="sp.id as sp.humanReadableName for sp in vm.sp"></select>
+          </div>
+        </div>
+        <div class="row-fluid">
+          <div class="span12">
+            <button class="btn btn-success">
+              <span ng-show="vm.cp.id">Save</span>
+              <span ng-show="!vm.cp.id">Create</span>
+            </button>
+          </div>
+        </div>
+      </fieldset>
+    </form>
+    <div class="alert" ng-show="vm.result" ng-class="{'alert-success': vm.result.status === 1,'alert-error': vm.result.status === 0}">
+      {$ vm.result.msg $}
+    </div>
+  </div>
+</div>
\ No newline at end of file
diff --git a/xos/core/xoslib/xos-builder/src/templates/cp_list.html b/xos/core/xoslib/xos-builder/src/templates/cp_list.html
new file mode 100644
index 0000000..3303564
--- /dev/null
+++ b/xos/core/xoslib/xos-builder/src/templates/cp_list.html
@@ -0,0 +1,34 @@
+<table class="table table-striped" ng-show="vm.contentProviderList.length > 0">
+  <thead>
+    <tr>
+      <th>
+        Name
+      </th>
+      <th>Description</th>
+      <th>Status</th>
+      <th></th>
+    </tr>
+  </thead>
+  <tr ng-repeat="item in vm.contentProviderList">
+    <td>
+      <a href="#/contentProvider/{$ item.id $}">{$ item.humanReadableName $}</a>
+    </td>
+    <td>
+      {$ item.description $}
+    </td>
+    <td>
+      {$ item.enabled $}
+    </td>
+    <td class="text-right">
+      <a ng-click="vm.deleteCp(item.id)" class="btn btn-danger"><i class="icon icon-remove"></i></a></td>
+  </tr>
+</table>
+<div class="alert alert-error" ng-show="vm.contentProviderList.length == 0">
+  No Content Provider defined
+</div>
+
+<div class="row">
+  <div class="span12 text-right">
+    <a class="btn btn-success"href="#/contentProvider/">Create</a>
+  </div>
+</div>
\ No newline at end of file
diff --git a/xos/core/xoslib/xos-builder/src/templates/cp_origin_server.html b/xos/core/xoslib/xos-builder/src/templates/cp_origin_server.html
new file mode 100644
index 0000000..ff77864
--- /dev/null
+++ b/xos/core/xoslib/xos-builder/src/templates/cp_origin_server.html
@@ -0,0 +1,53 @@
+<div class="row-fluid">
+  <div class="span6">
+    <h1>{$ vm.cp.humanReadableName $}</h1>
+  </div>
+  <div class="span6 text-right">
+    <cp-actions id="vm.cp.id"></cp-actions>
+  </div>
+</div>
+<hr>
+<div class="row-fluid">
+  <div class="span2">
+    <div ng-include="'../../static/templates/contentProvider/cp_side_nav.html'"></div>
+  </div>
+  <div class="span10">
+    <div ng-repeat="item in vm.cp_os" class="well">
+      <div class="row-fluid">
+        <div class="span4">
+          {{item.humanReadableName}}
+        </div>
+        <div class="span6">
+          <!-- TODO shoe the name instead that url -->
+          {{item.defaultOriginServer}}
+        </div>
+        <div class="span2">
+          <a ng-click="vm.removeOrigin(item)" class="btn btn-danger pull-right">
+            <i class="icon icon-remove"></i>
+          </a>
+        </div>
+      </div>
+    </div>
+    <hr>
+    <form ng-submit="vm.addOrigin(vm.new_os)">
+      <div class="row-fluid">
+        <div class="span4">
+          <label>Protocol</label>
+          <select ng-model="vm.new_os.protocol" ng-options="k as v for (k,v) in vm.protocols" style="max-width: 100%;"></select>
+        </div>
+        <div class="span6">
+          <label>Url</label>
+          <input type="text" ng-model="vm.new_os.url" required>
+        </div>
+        <div class="span2 text-right">
+          <button class="btn btn-success margin-wells">
+            <i class="icon icon-plus"></i>
+          </button>
+        </div>
+      </div>
+    </form>
+    <div class="alert" ng-show="vm.result" ng-class="{'alert-success': vm.result.status === 1,'alert-error': vm.result.status === 0}">
+      {$ vm.result.msg $}
+    </div>
+  </div>
+</div>
\ No newline at end of file
diff --git a/xos/core/xoslib/xos-builder/src/templates/cp_side_nav.html b/xos/core/xoslib/xos-builder/src/templates/cp_side_nav.html
new file mode 100644
index 0000000..a2c8633
--- /dev/null
+++ b/xos/core/xoslib/xos-builder/src/templates/cp_side_nav.html
@@ -0,0 +1,14 @@
+<ul class="nav nav-list">
+  <li>
+    <a class="btn" ng-class="{'btn-primary': vm.pageName == 'detail'}" href="#/contentProvider/{$ vm.cp.id $}">Details</a>
+  </li>
+  <li>
+    <a class="btn" ng-class="{'btn-primary': vm.pageName == 'cdn'}" href="#/contentProvider/{$ vm.cp.id $}/cdn_prefix">Cdn Prexix</a>
+  </li>
+  <li>
+    <a class="btn" ng-class="{'btn-primary': vm.pageName == 'server'}" href="#/contentProvider/{$ vm.cp.id $}/origin_server">Origin Server</a>
+  </li>
+  <li>
+    <a class="btn" ng-class="{'btn-primary': vm.pageName == 'user'}" href="#/contentProvider/{$ vm.cp.id $}/users">Users</a>
+  </li>
+</ul>
\ No newline at end of file
diff --git a/xos/core/xoslib/xos-builder/src/templates/cp_user.html b/xos/core/xoslib/xos-builder/src/templates/cp_user.html
new file mode 100644
index 0000000..b35c2e9
--- /dev/null
+++ b/xos/core/xoslib/xos-builder/src/templates/cp_user.html
@@ -0,0 +1,51 @@
+<div class="row-fluid">
+  <div class="span6">
+    <h1>{$ vm.cp.humanReadableName $}</h1>
+  </div>
+  <div class="span6 text-right">
+    <cp-actions id="vm.cp.id"></cp-actions>
+  </div>
+</div>
+<hr>
+<div class="row-fluid">
+  <div class="span2">
+    <div ng-include="'../../static/templates/contentProvider/cp_side_nav.html'"></div>
+  </div>
+  <div class="span10">
+    <div ng-repeat="item in vm.cp.users" class="well">
+      <div class="row-fluid">
+        <div class="span3">
+          {{item.firstname}}
+        </div>
+        <div class="span3">
+          {{item.lastname}}
+        </div>
+        <div class="span4">
+          {{item.email}}
+        </div>
+        <div class="span2">
+          <a ng-click="vm.removeUserFromCp(item)" class="btn btn-danger pull-right">
+            <i class="icon icon-remove"></i>
+          </a>
+        </div>
+      </div>
+    </div>
+    <hr>
+    <form ng-submit="vm.saveContentProvider(vm.cp)">
+      <div class="row-fluid">
+        <div class="span8">
+          <label>Select user:</label>
+          <select ng-model="vm.user" ng-options="u as u.username for u in vm.users" ng-change="vm.addUserToCp(vm.user)"></select>
+        </div>  
+        <div class="span4 text-right">
+          <button class="btn btn-success margin-wells">
+            Save
+          </button>
+        </div>
+      </div>
+    </form>
+    <div class="alert" ng-show="vm.result" ng-class="{'alert-success': vm.result.status === 1,'alert-error': vm.result.status === 0}">
+      {$ vm.result.msg $}
+    </div>
+  </div>
+</div>
\ No newline at end of file
diff --git a/xos/core/xoslib/xos-builder/src/xosContentProvider.js b/xos/core/xoslib/xos-builder/src/xosContentProvider.js
new file mode 100644
index 0000000..632ba5b
--- /dev/null
+++ b/xos/core/xoslib/xos-builder/src/xosContentProvider.js
@@ -0,0 +1,393 @@
+/* global angular */
+/* eslint-disable dot-location*/
+
+// TODO
+// - Add Cache
+// - Refactor routing with ui.router and child views (share the navigation and header)
+// - Add Eslint
+// - Add Es6 (Babel) and a build script
+// - Autogenerate ngResource from swagger definition json
+
+angular.module('xos.contentProviderApp', [
+  'ngResource',
+  'ngRoute',
+  'ngCookies',
+  'ngLodash'
+])
+.config(function($interpolateProvider, $routeProvider, $resourceProvider){
+  $interpolateProvider.startSymbol('{$');
+  $interpolateProvider.endSymbol('$}');
+
+  // NOTE http://www.masnun.com/2013/09/18/django-rest-framework-angularjs-resource-trailing-slash-problem.html
+  $resourceProvider.defaults.stripTrailingSlashes = false;
+
+  $routeProvider
+  .when('/', {
+    template: '<content-provider-list></content-provider-list>',
+  })
+  .when('/contentProvider/:id?', {
+    template: '<content-provider-detail></content-provider-detail>'
+  })
+  .when('/contentProvider/:id/cdn_prefix', {
+    template: '<content-provider-cdn></content-provider-cdn>'
+  })
+  .when('/contentProvider/:id/origin_server', {
+    template: '<content-provider-server></content-provider-server>'
+  })
+  .when('/contentProvider/:id/users', {
+    template: '<content-provider-users></content-provider-users>'
+  })
+  .otherwise('/');
+})
+.config(function($httpProvider){
+
+  // add X-CSRFToken header for update, create, delete (!GET)
+  $httpProvider.interceptors.push('SetCSRFToken');
+})
+.factory('SetCSRFToken', function($cookies){
+  return {
+    request: function(request){
+
+      // if request is not HTML
+      if(request.url.indexOf('.html') === -1){
+        request.url += '?no_hyperlinks=1';
+      }
+
+      if(request.method !== 'GET'){
+        // request.headers['X-CSRFToken'] = $cookies.get('csrftoken');
+        request.headers['X-CSRFToken'] = $cookies.get('xoscsrftoken');
+      }
+      return request;
+    }
+  };
+})
+.service('ContentProvider', function($resource){
+  return $resource('/hpcapi/contentproviders/:id/', {id: '@id'}, {
+    'update': {method: 'PUT'}
+  });
+})
+.service('ServiceProvider', function($resource){
+  return $resource('/hpcapi/serviceproviders/:id/', {id: '@id'});
+})
+.service('CdnPrefix', function($resource){
+  return $resource('/hpcapi/cdnprefixs/:id/', {id: '@id'});
+})
+.service('OriginServer', function($resource){
+  return $resource('/hpcapi/originservers/:id/', {id: '@id'});
+})
+.service('User', function($resource){
+  return $resource('/xos/users/:id/', {id: '@id'});
+})
+.directive('cpActions', function(ContentProvider, $location){
+  return {
+    restrict: 'E',
+    scope: {
+      id: '=id',
+    },
+    bindToController: true,
+    controllerAs: 'vm',
+    templateUrl: '../../static/templates/contentProvider/cp_actions.html',
+    controller: function(){
+      this.deleteCp = function(id){
+        ContentProvider.delete({id: id}).$promise
+        .then(function(){
+          $location.url('/');
+        });
+      };
+    }
+  };
+})
+.directive('contentProviderList', function(ContentProvider, lodash){
+  return {
+    restrict: 'E',
+    controllerAs: 'vm',
+    scope: {},
+    templateUrl: '../../static/templates/contentProvider/cp_list.html',
+    controller: function(){
+      var _this = this;
+
+      ContentProvider.query().$promise
+      .then(function(cp){
+        _this.contentProviderList = cp;
+      })
+      .catch(function(e){
+        throw new Error(e);
+      });
+
+      this.deleteCp = function(id){
+        ContentProvider.delete({id: id}).$promise
+        .then(function(){
+          lodash.remove(_this.contentProviderList, {id: id});
+        });
+      };
+    }
+  };
+})
+.directive('contentProviderDetail', function(ContentProvider, ServiceProvider, $routeParams, $location){
+  return {
+    restrict: 'E',
+    controllerAs: 'vm',
+    scope: {},
+    templateUrl: '../../static/templates/contentProvider/cp_detail.html',
+    controller: function(){
+      this.pageName = 'detail';
+      var _this = this;
+
+      if($routeParams.id){
+        ContentProvider.get({id: $routeParams.id}).$promise
+        .then(function(cp){
+          _this.cp = cp;
+        }).catch(function(e){
+          _this.result = {
+            status: 0,
+            msg: e.data.detail
+          };
+        });
+      }
+      else{
+        _this.cp = new ContentProvider();
+      }
+
+      ServiceProvider.query().$promise
+      .then(function(sp){
+        _this.sp = sp;
+      });
+
+      this.saveContentProvider = function(cp){
+        var p, isNew = false;
+
+        if(cp.id){
+          p = cp.$update();
+        }
+        else{
+          isNew = true;
+          cp.name = cp.humanReadableName;
+          p = cp.$save();
+        }
+
+        p.then(function(res){
+          _this.result = {
+            status: 1,
+            msg: 'Content Provider Saved'
+          };
+          if(isNew){
+            $location.url('contentProvider/' + res.id + '/');
+          }
+        })
+        .catch(function(e){
+          _this.result = {
+            status: 0,
+            msg: e.data.detail
+          };
+        });
+      };
+    }
+  };
+})
+.directive('contentProviderCdn', function($routeParams, CdnPrefix, ContentProvider, lodash){
+  return{
+    restrict: 'E',
+    controllerAs: 'vm',
+    scope: {},
+    templateUrl: '../../static/templates/contentProvider/cp_cdn_prefix.html',
+    controller: function(){
+      var _this = this;
+
+      this.pageName = 'cdn';
+
+      if($routeParams.id){
+        ContentProvider.get({id: $routeParams.id}).$promise
+        .then(function(cp){
+          _this.cp = cp;
+        }).catch(function(e){
+          _this.result = {
+            status: 0,
+            msg: e.data.detail
+          };
+        });
+      }
+
+      CdnPrefix.query().$promise
+      .then(function(prf){
+        _this.prf = prf;
+        // set the active CdnPrefix for this contentProvider
+        _this.cp_prf = lodash.where(prf, {contentProvider: parseInt($routeParams.id)});
+      }).catch(function(e){
+        _this.result = {
+          status: 0,
+          msg: e.data.detail
+        };
+      });
+
+      this.addPrefix = function(prf){
+        prf.contentProvider = $routeParams.id;
+
+        var item = new CdnPrefix(prf);
+
+        item.$save()
+        .then(function(res){
+          _this.cp_prf.push(res);
+        })
+        .catch(function(e){
+          _this.result = {
+            status: 0,
+            msg: e.data.detail
+          };
+        });
+      };
+
+      this.removePrefix = function(item){
+        item.$delete()
+        .then(function(){
+          lodash.remove(_this.cp_prf, item);
+        })
+        .catch(function(e){
+          _this.result = {
+            status: 0,
+            msg: e.data.detail
+          };
+        });
+      };
+    }
+  };
+})
+.directive('contentProviderServer', function($routeParams, OriginServer, ContentProvider, lodash){
+  return{
+    restrict: 'E',
+    controllerAs: 'vm',
+    scope: {},
+    templateUrl: '../../static/templates/contentProvider/cp_origin_server.html',
+    controller: function(){
+      this.pageName = 'server';
+      this.protocols = {'http': 'HTTP', 'rtmp': 'RTMP', 'rtp': 'RTP','shout': 'SHOUTcast'};
+
+      var _this = this;
+
+      if($routeParams.id){
+        ContentProvider.get({id: $routeParams.id}).$promise
+        .then(function(cp){
+          _this.cp = cp;
+        }).catch(function(e){
+          _this.result = {
+            status: 0,
+            msg: e.data.detail
+          };
+        });
+      }
+
+      OriginServer.query({contentProvider: $routeParams.id}).$promise
+      .then(function(cp_os){
+        _this.cp_os = cp_os;
+      }).catch(function(e){
+        _this.result = {
+          status: 0,
+          msg: e.data.detail
+        };
+      });
+
+      this.addOrigin = function(os){
+        os.contentProvider = $routeParams.id;
+
+        var item = new OriginServer(os);
+
+        item.$save()
+        .then(function(res){
+          _this.cp_os.push(res);
+        })
+        .catch(function(e){
+          _this.result = {
+            status: 0,
+            msg: e.data.detail
+          };
+        });
+      };
+
+      this.removeOrigin = function(item){
+        item.$delete()
+        .then(function(){
+          lodash.remove(_this.cp_os, item);
+        })
+        .catch(function(e){
+          _this.result = {
+            status: 0,
+            msg: e.data.detail
+          };
+        });
+      };
+    }
+  };
+})
+.directive('contentProviderUsers', function($routeParams, ContentProvider, User, lodash){
+  return{
+    restrict: 'E',
+    controllerAs: 'vm',
+    scope: {},
+    templateUrl: '../../static/templates/contentProvider/cp_user.html',
+    controller: function(){
+      var _this = this;
+
+      this.pageName = 'user';
+
+      this.cp_users = [];
+
+      if($routeParams.id){
+        User.query().$promise
+        .then(function(users){
+          _this.users = users;
+          return ContentProvider.get({id: $routeParams.id}).$promise;
+        })
+        .then(function(res){
+          res.users = _this.populateUser(res.users, _this.users);
+          return res;
+        })
+        .then(function(cp){
+          _this.cp = cp;
+        }).catch(function(e){
+          _this.result = {
+            status: 0,
+            msg: e.data.detail
+          };
+        });
+      }
+
+      this.populateUser = function(ids, list){
+        for(var i = 0; i < ids.length; i++){
+          ids[i] = lodash.find(list, {id: ids[i]});
+        }
+        return ids;
+      };
+
+      this.addUserToCp = function(user){
+        _this.cp.users.push(user);
+      };
+
+      this.removeUserFromCp = function(user){
+        lodash.remove(_this.cp.users, user);
+      };
+
+      this.saveContentProvider = function(cp){
+
+        // flatten the user to id of array
+        cp.users = lodash.pluck(cp.users, 'id');
+
+        cp.$update()
+        .then(function(res){
+
+          _this.cp.users = _this.populateUser(res.users, _this.users);
+
+          _this.result = {
+            status: 1,
+            msg: 'Content Provider Saved'
+          };
+
+        })
+        .catch(function(e){
+          _this.result = {
+            status: 0,
+            msg: e.data.detail
+          };
+        });
+      };
+    }
+  };
+});
\ No newline at end of file