parent
1500a1b8b2
commit
d1923af517
@ -0,0 +1,237 @@ |
||||
### |
||||
LemonLDAP::NG base app module |
||||
|
||||
This file contains: |
||||
- 3 AngularJS directives (HTML attributes): |
||||
* `on-read-file` to get file content |
||||
* `resizer` to resize HTML div |
||||
* `trspan` to set translated message in HTML content |
||||
- a AngularJS factory to handle 401 Ajax responses |
||||
### |
||||
|
||||
llapp = angular.module 'llApp', [] |
||||
|
||||
# TRANSLATION SYSTEM |
||||
# |
||||
# It provides: |
||||
# - 3 functions to translate: |
||||
# * translate(word) |
||||
# * translateP(paragraph): only __words__ are translated |
||||
# * translateField(object, property) |
||||
# - an HTML attribute called 'trspan'. Exemple: <h3 trspan="portal"/> |
||||
|
||||
# $translator provider |
||||
|
||||
llapp.provider '$translator', -> |
||||
res = {} |
||||
# Language detection |
||||
if navigator |
||||
langs = [] |
||||
langs2 = [] |
||||
nlangs = [ navigator.language ] |
||||
if navigator.languages |
||||
nlangs = navigator.languages |
||||
for nl in nlangs |
||||
for al in window.availableLanguages |
||||
if al == nl |
||||
langs.push al |
||||
else if al.substring(0, 1) == nl.substring(0, 1) |
||||
langs2.push al |
||||
res.lang = if langs[0] then langs[0] else if langs2[0] then langs2[0] else 'en'; |
||||
else |
||||
res.lang = 'en' |
||||
|
||||
# Internal properties |
||||
res.deferredTr = [] |
||||
res.translationFields = {} |
||||
|
||||
# Translation methods |
||||
# 1. word translation |
||||
res.translate = (s) -> |
||||
if res.translationFields[s] |
||||
s = res.translationFields[s] |
||||
return s |
||||
|
||||
# 2. node field translation |
||||
res.translateField = (node, field) -> |
||||
res.translate node[field] |
||||
|
||||
# 3. paragraph translation (verify that json is available |
||||
res.translateP = (s) -> |
||||
if s and res.translationFields.portal |
||||
s = s.replace /__(\w+)__/g, (match,w) -> |
||||
res.translate w |
||||
s |
||||
|
||||
# Initialization |
||||
this.$get = [ '$q', '$http', ($q, $http) -> |
||||
res.last = '' |
||||
res.init = (lang) -> |
||||
lang = res.lang unless lang |
||||
d = $q.defer() |
||||
if res.last != lang |
||||
res.last = lang |
||||
$http.get("#{window.staticPrefix}languages/#{lang}.json").then (response) -> |
||||
res.translationFields = response.data |
||||
for h in res.deferredTr |
||||
h.e[h.f] res.translationFields[h.m] |
||||
res.deferredTr = [] |
||||
d.resolve "Translation files loaded" |
||||
, (response) -> |
||||
d.reject '' |
||||
else |
||||
d.resolve "No change" |
||||
d.promise |
||||
res |
||||
] |
||||
this |
||||
|
||||
# Translation directive (HTML trspan tag) |
||||
llapp.directive 'trspan', [ '$translator', ($translator) -> |
||||
restrict: 'A' |
||||
replace: false |
||||
transclude: true |
||||
scope: |
||||
trspan: "@" |
||||
link: (scope, elem, attr) -> |
||||
if $translator.translationFields.portal |
||||
attr.trspan = $translator.translate(attr.trspan) |
||||
# Deferred translations will be done after JSON download |
||||
else |
||||
$translator.deferredTr.push |
||||
e: elem |
||||
f: 'text' |
||||
m: attr.trspan |
||||
elem.text attr.trspan |
||||
template: '' |
||||
] |
||||
|
||||
# Form menu management |
||||
# |
||||
# Two parts: |
||||
# - $htmlParams: used to store values inserted as <script type="text/menu">. |
||||
# It provides menu() method to get them |
||||
# - HTML "script" element handler |
||||
llapp.provider '$htmlParams', -> |
||||
this.$get = -> |
||||
params = {} |
||||
set: (key, obj) -> |
||||
params[key] = obj |
||||
menu: -> |
||||
params.menu |
||||
# To be used later |
||||
params: -> |
||||
params.params |
||||
this |
||||
|
||||
llapp.directive 'script', ['$htmlParams', ($htmlParams) -> |
||||
restrict: 'E' |
||||
terminal: true |
||||
compile: (element, attr) -> |
||||
if t = attr.type.match /text\/(menu|parameters)/ |
||||
$htmlParams.set t[1], eval element[0].text |
||||
] |
||||
|
||||
# Modal controller used to display messages |
||||
|
||||
llapp.controller 'ModalInstanceCtrl', ['$scope', '$uibModalInstance', 'elem', 'set', 'init', ($scope, $uibModalInstance, elem, set, init) -> |
||||
oldvalue = null |
||||
$scope.elem = elem |
||||
$scope.set = set |
||||
$scope.result = init |
||||
$scope.staticPrefix = window.staticPrefix |
||||
currentNode = elem 'currentNode' |
||||
$scope.translateP = elem 'translateP' |
||||
if currentNode |
||||
oldValue = currentNode.data |
||||
$scope.currentNode = currentNode |
||||
|
||||
$scope.ok = -> |
||||
set('result', $scope.result) |
||||
$uibModalInstance.close(true) |
||||
|
||||
$scope.cancel = -> |
||||
if currentNode then $scope.currentNode.data = oldValue |
||||
$uibModalInstance.dismiss('cancel') |
||||
|
||||
# test if value is in select |
||||
$scope.inSelect = (value) -> |
||||
for i in $scope.currentNode.select |
||||
if i.k == value then return true |
||||
return false |
||||
] |
||||
|
||||
# File reader directive |
||||
# |
||||
# Add "onReadFile" HTML attribute to be used in a "file" input |
||||
# The content off attribute will be launched. |
||||
# |
||||
# Example: |
||||
# <input type="file" on-read-file="replaceContent($fileContent)"/> |
||||
|
||||
llapp.directive 'onReadFile', [ '$parse', ($parse) -> |
||||
restrict: 'A' |
||||
scope: false |
||||
link: (scope, element, attrs) -> |
||||
fn = $parse attrs.onReadFile |
||||
element.on 'change', (onChangeEvent) -> |
||||
reader = new FileReader() |
||||
reader.onload = (onLoadEvent) -> |
||||
scope.$apply () -> |
||||
fn scope, |
||||
$fileContent: onLoadEvent.target.result |
||||
reader.readAsText ((onChangeEvent.srcElement || onChangeEvent.target).files[0]) |
||||
] |
||||
|
||||
# Resize system |
||||
# |
||||
# Add a "resizer" HTML attribute |
||||
llapp.directive 'resizer', ['$document', ($document) -> |
||||
hsize = null |
||||
rsize = null |
||||
($scope, $element, $attrs) -> |
||||
$element.on 'mousedown', (event) -> |
||||
if $attrs.resizer == 'vertical' |
||||
rsize = $($attrs.resizerRight).width() + $($attrs.resizerLeft).width() |
||||
else |
||||
hsize = $($attrs.resizerTop).height() + $($attrs.resizerBottom).height() |
||||
event.preventDefault() |
||||
$document.on 'mousemove', mousemove |
||||
$document.on 'mouseup', mouseup |
||||
mousemove = (event) -> |
||||
# Handle vertical resizer |
||||
if $attrs.resizer == 'vertical' |
||||
x = event.pageX |
||||
if $attrs.resizerMax and x > $attrs.resizerMax |
||||
x = parseInt $attrs.resizerMax |
||||
$($attrs.resizerLeft).css |
||||
width: "#{x}px" |
||||
$($attrs.resizerRight).css |
||||
width: "#{rsize-x}px" |
||||
# Handle horizontal resizer |
||||
else |
||||
y = event.pageY - $('#navbar').height() |
||||
$($attrs.resizerTop).css |
||||
height: "#{y}px" |
||||
$($attrs.resizerBottom).css |
||||
height: "#{hsize-y}px" |
||||
mouseup = () -> |
||||
$document.unbind 'mousemove', mousemove |
||||
$document.unbind 'mouseup', mouseup |
||||
] |
||||
|
||||
### |
||||
# Authentication system |
||||
# |
||||
# If a 401 code is returned and if "Authorization" header contains an url, |
||||
# user is redirected to this url (but target is replaced by location.href |
||||
### |
||||
llapp.factory '$lmhttp', ['$q', '$location', ($q, $location) -> |
||||
responseError: (rejection) -> |
||||
if rejection.status == 401 and window.portal |
||||
window.location = "#{window.portal}?url=" + window.btoa(window.location).replace(/\//, '_') |
||||
] |
||||
|
||||
llapp.config [ '$httpProvider', ($httpProvider) -> |
||||
$httpProvider.interceptors.push '$lmhttp' |
||||
] |
@ -0,0 +1,722 @@ |
||||
### |
||||
LemonLDAP::NG Manager client |
||||
|
||||
This is the main app file. Other are: |
||||
- struct.json and js/confTree.js that contains the full tree |
||||
- translate.json that contains the keywords translation |
||||
|
||||
This file contains: |
||||
- the AngularJS controller |
||||
### |
||||
|
||||
llapp = angular.module 'llngManager', ['ui.tree', 'ui.bootstrap', 'llApp', 'ngCookies'] |
||||
|
||||
### |
||||
Main AngularJS controller |
||||
### |
||||
|
||||
llapp.controller 'TreeCtrl', [ |
||||
'$scope', '$http', '$location', '$q', '$uibModal', '$translator', '$cookies', '$htmlParams', |
||||
($scope, $http, $location, $q, $uibModal, $translator, $cookies, $htmlParams) -> |
||||
$scope.links = window.links |
||||
$scope.menu = $htmlParams.menu |
||||
$scope.menulinks = window.menulinks |
||||
$scope.staticPrefix = window.staticPrefix |
||||
$scope.formPrefix = window.formPrefix |
||||
$scope.availableLanguages = window.availableLanguages |
||||
$scope.waiting = true |
||||
$scope.showM = false |
||||
$scope.showT = false |
||||
$scope.form = 'home' |
||||
$scope.currentCfg = {} |
||||
$scope.confPrefix = window.confPrefix |
||||
$scope.message = {} |
||||
$scope.result = '' |
||||
|
||||
# Import translations functions |
||||
$scope.translateTitle = (node) -> |
||||
return $translator.translateField node, 'title' |
||||
$scope.translateP = $translator.translateP |
||||
$scope.translate = $translator.translate |
||||
|
||||
# HELP DISPLAY |
||||
$scope.helpUrl = 'start.html#configuration' |
||||
$scope.setShowHelp = (val) -> |
||||
val = !$scope.showH unless val? |
||||
$scope.showH = val |
||||
d = new Date(Date.now()) |
||||
d.setFullYear(d.getFullYear() + 1) |
||||
$cookies.put 'showhelp', (if val then 'true' else 'false'), {"expires": d} |
||||
$scope.showH = if $cookies.get('showhelp') == 'false' then false else true |
||||
$scope.setShowHelp(true) unless $scope.showH? |
||||
|
||||
# INTERCEPT AJAX ERRORS |
||||
readError = (response) -> |
||||
e = response.status |
||||
j = response.statusLine |
||||
$scope.waiting = false |
||||
if e == 403 |
||||
$scope.message = |
||||
title: 'forbidden' |
||||
message: '' |
||||
items: [] |
||||
else if e == 401 |
||||
console.log 'Authentication needed' |
||||
$scope.message = |
||||
title: 'authenticationNeeded' |
||||
message: '__waitOrF5__' |
||||
items: [] |
||||
else if e == 400 |
||||
$scope.message = |
||||
title: 'badRequest' |
||||
message: j |
||||
items: [] |
||||
else if e > 0 |
||||
$scope.message = |
||||
title: 'badRequest' |
||||
message: j |
||||
items: [] |
||||
else |
||||
$scope.message = |
||||
title: 'networkProblem' |
||||
message: '' |
||||
items: [] |
||||
return $scope.showModal 'message.html' |
||||
|
||||
# Modal launcher |
||||
$scope.showModal = (tpl, init) -> |
||||
modalInstance = $uibModal.open |
||||
templateUrl: tpl |
||||
controller: 'ModalInstanceCtrl' |
||||
size: 'lg' |
||||
resolve: |
||||
elem: -> |
||||
return (s) -> |
||||
return $scope[s] |
||||
set: -> |
||||
return (f, s) -> |
||||
$scope[f] = s |
||||
init: -> |
||||
return init |
||||
d = $q.defer() |
||||
modalInstance.result.then (msgok) -> |
||||
$scope.message = |
||||
title: '' |
||||
message: '' |
||||
items: [] |
||||
d.resolve msgok |
||||
,(msgnok) -> |
||||
$scope.message = |
||||
title: '' |
||||
message: '' |
||||
items: [] |
||||
d.reject msgnok |
||||
return modalInstance.result |
||||
|
||||
# FORM DISPLAY FUNCTIONS |
||||
|
||||
# Function called when a menu item is selected. It launch function stored in |
||||
# "action" or "title" |
||||
$scope.menuClick = (button) -> |
||||
if button.popup |
||||
window.open button.popup |
||||
else |
||||
button.action = button.title unless button.action |
||||
switch typeof button.action |
||||
when 'function' |
||||
button.action $scope.currentNode, $scope |
||||
when 'string' |
||||
$scope[button.action]() |
||||
else |
||||
console.log typeof button.action |
||||
$scope.showM = false |
||||
|
||||
# Display main form |
||||
$scope.home = -> |
||||
$scope.form = 'home' |
||||
$scope.showM = false |
||||
|
||||
# SAVE FUNCTIONS |
||||
|
||||
# Private method called by $scope.save() |
||||
_checkSaveResponse = (data) -> |
||||
$scope.message = |
||||
title: '' |
||||
message: '' |
||||
items: [] |
||||
$scope.confirmNeeded = true if data.message == '__needConfirmation__' |
||||
$scope.message.message = data.message if data.message |
||||
if data.details |
||||
for m of data.details when m != '__changes__' |
||||
$scope.message.items.push |
||||
message: m |
||||
items: data.details[m] |
||||
$scope.waiting = false |
||||
if data.result == 1 |
||||
# Force reloading page |
||||
$location.path '/confs/' |
||||
$scope.message.title = 'successfullySaved' |
||||
else |
||||
$scope.message.title = 'saveReport' |
||||
$scope.showModal 'message.html' |
||||
|
||||
# Main save function |
||||
$scope.save = -> |
||||
$scope.showModal('save.html').then -> |
||||
$scope.waiting = true |
||||
$scope.data.push |
||||
id: "cfgLog" |
||||
title: "cfgLog" |
||||
data: if $scope.result then $scope.result else '' |
||||
$http.post("#{window.confPrefix}?cfgNum=#{$scope.currentCfg.cfgNum}#{if $scope.forceSave then "&force=1" else ''}", $scope.data).then (response) -> |
||||
$scope.data.pop() |
||||
_checkSaveResponse response.data |
||||
,(response) -> |
||||
readError response |
||||
$scope.data.pop() |
||||
, -> |
||||
console.log 'Saving canceled' |
||||
$scope.showM = false |
||||
|
||||
# Raw save function |
||||
$scope.saveRawConf = ($fileContent) -> |
||||
$scope.waiting = true |
||||
$http.post("#{window.confPrefix}/raw", $fileContent).then (response) -> |
||||
_checkSaveResponse(response.data) |
||||
, readError |
||||
|
||||
# Restore raw conffunction |
||||
$scope.restore = -> |
||||
$scope.currentNode = null |
||||
$scope.form = 'restore' |
||||
|
||||
# Cancel save function |
||||
$scope.cancel = -> |
||||
$scope.currentNode.data = null |
||||
$scope.getKey($scope.currentNode) |
||||
|
||||
# NODES MANAGEMENT |
||||
id = 1 |
||||
$scope._findContainer = -> |
||||
return $scope._findScopeContainer().$modelValue |
||||
$scope._findScopeContainer = -> |
||||
cs = $scope.currentScope |
||||
while not cs.$modelValue.type.match(/Container$/) |
||||
cs = cs.$parentNodeScope |
||||
return cs |
||||
$scope._findScopeByKey = (k) -> |
||||
cs = $scope.currentScope |
||||
while not (cs.$modelValue.title == k) |
||||
cs = cs.$parentNodeScope |
||||
return cs |
||||
|
||||
# Add grant rule entry |
||||
$scope.newGrantRule = -> |
||||
node = $scope._findContainer() |
||||
l = node.nodes.length |
||||
n = if l > 0 then l - 1 else 0 |
||||
node.nodes.splice n, 0, |
||||
id: "#{node.id}/n#{id++}" |
||||
title: 'New rule' |
||||
re: '1' |
||||
comment: 'New rule' |
||||
data: 'Message' |
||||
type: "grant" |
||||
|
||||
# Add rules entry |
||||
$scope.newRule = -> |
||||
node = $scope._findContainer() |
||||
l = node.nodes.length |
||||
n = if l > 0 then l - 1 else 0 |
||||
node.nodes.splice n, 0, |
||||
id: "#{node.id}/n#{id++}" |
||||
title: 'New rule' |
||||
re: '^/new' |
||||
comment: 'New rule' |
||||
data: 'accept' |
||||
type: "rule" |
||||
|
||||
# Add form replay |
||||
$scope.newPost = -> |
||||
node = $scope._findContainer() |
||||
node.nodes.push |
||||
id: "#{node.id}/n#{id++}" |
||||
title: "/absolute/path/to/form" |
||||
data: {} |
||||
type: "post" |
||||
|
||||
$scope.newPostVar = -> |
||||
$scope.currentNode.data.vars = [] unless $scope.currentNode.data.vars? |
||||
$scope.currentNode.data.vars.push ['var1', '$uid'] |
||||
|
||||
# Add auth chain entry to authChoice |
||||
$scope.newAuthChoice = -> |
||||
node = $scope._findContainer() |
||||
node.nodes.push |
||||
id: "#{node.id}/n#{id++}" |
||||
title: "1_Key" |
||||
data: ['Null', 'Null', 'Null'] |
||||
type: "authChoice" |
||||
$scope.execFilters $scope._findScopeByKey 'authParams' |
||||
|
||||
# Add hash entry |
||||
$scope.newHashEntry = -> |
||||
node = $scope._findContainer() |
||||
node.nodes.push |
||||
id: "#{node.id}/n#{id++}" |
||||
title: 'new' |
||||
data: '' |
||||
type: "keyText" |
||||
|
||||
# Menu cat entry |
||||
$scope.newCat = -> |
||||
cs = $scope.currentScope |
||||
if cs.$modelValue.type == 'menuApp' |
||||
cs = cs.$parentNodeScope |
||||
cs.$modelValue.nodes.push |
||||
id: "#{cs.$modelValue.id}/n#{id++}" |
||||
title: "New category" |
||||
type: "menuCat" |
||||
nodes: [] |
||||
|
||||
# Menu app entry |
||||
$scope.newApp = -> |
||||
cs = $scope.currentScope |
||||
if cs.$modelValue.type == 'menuApp' |
||||
cs = cs.$parentNodeScope |
||||
cs.$modelValue.nodes.push |
||||
id: "#{cs.$modelValue.id}/n#{id++}" |
||||
title: "New application" |
||||
type: "menuApp" |
||||
data: |
||||
description: "New app description" |
||||
uri: "https://test.example.com/" |
||||
logo: "network.png" |
||||
display: "auto" |
||||
|
||||
# SAML attribute entry |
||||
$scope.addSamlAttribute = -> |
||||
node = $scope._findContainer() |
||||
node.nodes.push |
||||
id: "#{node.id}/n#{id++}" |
||||
title: 'new' |
||||
type: 'samlAttribute' |
||||
data: [0, 'New', '', ''] |
||||
|
||||
# Nodes with template |
||||
$scope.addVhost = -> |
||||
name = if $scope.domain then ".#{$scope.domain.data}" else '.example.com' |
||||
$scope.message = |
||||
title: 'virtualHostName' |
||||
field: 'hostname' |
||||
$scope.showModal('prompt.html', name).then -> |
||||
n= $scope.result |
||||
if n |
||||
node = $scope.addTemplateNode n, 'virtualHost' |
||||
delete node.nodes[0].cnodes |
||||
node.nodes[0].nodes = [ |
||||
id: "virtualHosts/new__#{n}/locationRules/default", |
||||
type: "rule", |
||||
title: "default", |
||||
comment: "", |
||||
re: "default", |
||||
data: "deny" |
||||
] |
||||
|
||||
$scope.duplicateVhost = -> |
||||
name = if $scope.domain then ".#{$scope.domain.data}" else '.example.com' |
||||
$scope.message = |
||||
title: 'virtualHostName', |
||||
field: 'hostname' |
||||
$scope.showModal('prompt.html', name).then -> |
||||
n = $scope.result |
||||
return $scope.duplicateNode n, 'virtualHost', $scope.currentNode.title |
||||
|
||||
$scope.addSamlIDP = -> |
||||
$scope.newTemplateNode 'samlIDPMetaDataNode', 'samlPartnerName', 'idp-example' |
||||
|
||||
$scope.addSamlSP = -> |
||||
$scope.newTemplateNode 'samlSPMetaDataNode', 'samlPartnerName', 'sp-example' |
||||
|
||||
$scope.addOidcOp = -> |
||||
$scope.newTemplateNode 'oidcOPMetaDataNode', 'oidcOPName', 'op-example' |
||||
|
||||
$scope.addOidcRp = -> |
||||
$scope.newTemplateNode 'oidcRPMetaDataNode', 'oidcRPName', 'rp-example' |
||||
|
||||
$scope.newTemplateNode = (type, title, init) -> |
||||
$scope.message = |
||||
title: title |
||||
field: 'name' |
||||
$scope.showModal('prompt.html', init).then -> |
||||
name = $scope.result |
||||
if (name) |
||||
$scope.addTemplateNode name, type |
||||
|
||||
$scope.addTemplateNode = (name, type) -> |
||||
cs = $scope.currentScope |
||||
while cs.$modelValue.title != "#{type}s" |
||||
cs = cs.$parentNodeScope |
||||
t = |
||||
id: "#{type}s/new__#{name}" |
||||
title: name |
||||
type: type |
||||
nodes: templates type, "new__#{name}" |
||||
cs.$modelValue.nodes.push t |
||||
cs.expand() |
||||
return t |
||||
|
||||
_getAll = (node) -> |
||||
d = $q.defer() |
||||
d2 = $q.defer() |
||||
if node._nodes |
||||
_stoggle node |
||||
d.resolve() |
||||
else if node.cnodes |
||||
_download(node).then -> |
||||
d.resolve() |
||||
else if node.nodes or node.data |
||||
d.resolve() |
||||
else |
||||
$scope.getKey(node).then -> |
||||
d.resolve() |
||||
d.promise.then -> |
||||
t = [] |
||||
if node.nodes |
||||
for n in node.nodes |
||||
t.push _getAll(n) |
||||
$q.all(t).then -> |
||||
d2.resolve() |
||||
return d2.promise |
||||
|
||||
$scope.duplicateNode = (name, type) -> |
||||
cs = $scope.currentScope |
||||
_getAll($scope.currentNode).then -> |
||||
while cs.$modelValue.title != "#{type}s" |
||||
cs = cs.$parentNodeScope |
||||
t = JSON.parse JSON.stringify($scope.currentNode).replace(new RegExp(idkey, 'g'), 'new__' + name) |
||||
t.id = "#{type}s/new__#{name}" |
||||
t.title = name |
||||
cs.$modelValue.nodes.push(t) |
||||
return t |
||||
|
||||
$scope.del = (a, i) -> |
||||
a.splice(i, 1) |
||||
|
||||
$scope.deleteEntry = -> |
||||
p = $scope.currentScope.$parentNodeScope |
||||
$scope.currentScope.remove() |
||||
$scope.displayForm p |
||||
|
||||
$scope.down = -> |
||||
id = $scope.currentNode.id |
||||
p = $scope.currentScope.$parentNodeScope.$modelValue |
||||
ind = p.nodes.length |
||||
for n, i in p.nodes |
||||
if n.id == id then ind = i |
||||
if ind < p.nodes.length - 1 |
||||
tmp = p.nodes[ind] |
||||
p.nodes[ind] = p.nodes[ind + 1] |
||||
p.nodes[ind + 1] = tmp |
||||
ind |
||||
|
||||
$scope.up = -> |
||||
id = $scope.currentNode.id |
||||
p = $scope.currentScope.$parentNodeScope.$modelValue |
||||
ind = -1 |
||||
for n, i in p.nodes |
||||
if n.id == id then ind = i |
||||
if ind > 0 |
||||
tmp = p.nodes[ind] |
||||
p.nodes[ind] = p.nodes[ind - 1] |
||||
p.nodes[ind - 1] = tmp |
||||
ind |
||||
|
||||
# test if value is in select |
||||
$scope.inSelect = (value) -> |
||||
for n in $scope.currentNode.select |
||||
return true if n.k == value |
||||
return false |
||||
|
||||
# This is for rule form: title = comment if defined, else title = re |
||||
$scope.changeRuleTitle = (node) -> |
||||
node.title = if node.comment.length > 0 then node.comment else node.re |
||||
|
||||
# Node opening |
||||
|
||||
# authParams mechanism: show used auth modules only (launched by stoggle) |
||||
$scope.filters = {} |
||||
$scope.execFilters = (scope) -> |
||||
scope = if scope then scope else $scope |
||||
for filter,func of $scope.filters |
||||
if $scope.filters.hasOwnProperty(filter) |
||||
return filterFunctions[filter](scope, $q, func) |
||||
false |
||||
|
||||
# To avoid binding all the tree, nodes are pushed in DOM only when opened |
||||
$scope.stoggle = (scope) -> |
||||
node = scope.$modelValue |
||||
_stoggle node |
||||
scope.toggle() |
||||
_stoggle = (node) -> |
||||
for n in ['nodes', 'nodes_cond'] |
||||
if node["_#{n}"] |
||||
node[n] = [] |
||||
for a in node["_#{n}"] |
||||
node[n].push a |
||||
delete node["_#{n}"] |
||||
# Call execFilter for authParams |
||||
if node._nodes_filter |
||||
if node.nodes |
||||
for n in node.nodes |
||||
n.onChange = $scope.execFilters |
||||
$scope.filters[node._nodes_filter] = node |
||||
$scope.execFilters() |
||||
|
||||
# Simple toggle management |
||||
$scope.toggle = (scope) -> |
||||
scope.toggle() |
||||
|
||||
# cnodes management: hash keys/values are loaded when parent node is opened |
||||
$scope.download = (scope) -> |
||||
node = scope.$modelValue |
||||
return _download(node) |
||||
_download = (node) -> |
||||
d = $q.defer() |
||||
d.notify 'Trying to get datas' |
||||
$scope.waiting = true |
||||
$http.get("#{window.confPrefix}#{$scope.currentCfg.cfgNum}/#{node.cnodes}").then (response) -> |
||||
data = response.data |
||||
# Manage datas errors |
||||
if not data |
||||
d.reject 'Empty response from server' |
||||
else if data.error |
||||
if data.error.match(/setDefault$/) |
||||
if node['default'] |
||||
node.nodes = node['default'].slice(0) |
||||
else |
||||
node.nodes = [] |
||||
delete node.cnodes |
||||
d.resolve 'Set data to default value' |
||||
else |
||||
d.reject "Server return an error: #{data.error}" |
||||
else |
||||
# Store datas |
||||
delete node.cnodes |
||||
if not node.type |
||||
node.type = 'keyTextContainer' |
||||
node.nodes = [] |
||||
# TODO: try/catch |
||||
for a in data |
||||
if a.template |
||||
a._nodes = templates a.template, a.title |
||||
node.nodes.push a |
||||
d.resolve 'OK' |
||||
$scope.waiting = false |
||||
, (response) -> |
||||
readError response |
||||
d.reject '' |
||||
return d.promise |
||||
|
||||
$scope.openCnode = (scope) -> |
||||
$scope.download(scope).then -> |
||||
scope.toggle() |
||||
|
||||
setHelp = (scope) -> |
||||
while !scope.$modelValue.help and scope.$parentNodeScope |
||||
scope = scope.$parentNodeScope |
||||
$scope.helpUrl = scope.$modelValue.help || 'start.html#configuration' |
||||
|
||||
# Form management |
||||
# |
||||
# `currentNode` contains the last select node |
||||
# |
||||
# method `diplayForm()`: |
||||
# - set the `form` property to the name of the form to download |
||||
# (`text` by default or `home` for node without `type` property) |
||||
# - launch getKeys to set `node.data` |
||||
# - hide tree when in XS size |
||||
# |
||||
$scope.displayForm = (scope) -> |
||||
node = scope.$modelValue |
||||
if node.cnodes |
||||
$scope.download scope |
||||
if node._nodes |
||||
$scope.stoggle scope |
||||
$scope.currentNode = node |
||||
$scope.currentScope = scope |
||||
f = if node.type then node.type else 'text' |
||||
if node.nodes || node._nodes || node.cnodes |
||||
$scope.form = if f != 'text' then f else 'mini' |
||||
else |
||||
$scope.form = f |
||||
# Get datas |
||||
$scope.getKey node |
||||
if node.type and node.type == 'simpleInputContainer' |
||||
for n in node.nodes |
||||
$scope.getKey(n) |
||||
$scope.showT = false |
||||
setHelp scope |
||||
|
||||
$scope.keyWritable = (scope) -> |
||||
node = scope.$modelValue |
||||
return if node.type and node.type.match /^(authChoice|keyText|virtualHost|rule|menuCat|menuApp|saml(Attribute|(IDP|SP)MetaDataNode))$/ then true else false |
||||
|
||||
# RSA keys generation |
||||
$scope.newRSAKey = -> |
||||
$scope.showModal('password.html').then -> |
||||
$scope.waiting = true |
||||
currentNode = $scope.currentNode |
||||
password = $scope.result |
||||
$http.post("#{window.confPrefix}/newRSAKey", {"password": password}).then (response) -> |
||||
currentNode.data[0].data = response.data.private |
||||
currentNode.data[1].data = password |
||||
currentNode.data[2].data = response.data.public |
||||
$scope.waiting = false |
||||
, readError |
||||
, -> |
||||
console.log('New key cancelled') |
||||
|
||||
$scope.newRSAKeyNoPassword = -> |
||||
$scope.waiting = true |
||||
currentNode = $scope.currentNode |
||||
$http.post("#{window.confPrefix}/newRSAKey", {"password": ''}).then (response) -> |
||||
currentNode.data[0].data = response.data.private |
||||
currentNode.data[1].data = response.data.public |
||||
$scope.waiting = false |
||||
, readError |
||||
|
||||
# method `getKey()`: |
||||
# - return a promise with the data: |
||||
# - from node when set |
||||
# - after downloading else |
||||
# |
||||
$scope.getKey = (node) -> |
||||
d = $q.defer() |
||||
if !node.data |
||||
$scope.waiting = true |
||||
if node.get and typeof(node.get) == 'object' |
||||
node.data = [] |
||||
tmp = [] |
||||
for n, i in node.get |
||||
node.data[i] = |
||||
title: n |
||||
id: n |
||||
tmp.push $scope.getKey(node.data[i]) |
||||
$q.all(tmp).then -> |
||||
d.resolve(node.data) |
||||
,(response) -> |
||||
d.reject response.statusLine |
||||
$scope.waiting = false |
||||
else |
||||
$http.get("#{window.confPrefix}#{$scope.currentCfg.cfgNum}/#{if node.get then node.get else node.title}").then (response) -> |
||||
# Set default value if response is null or if asked by server |
||||
data = response.data |
||||
if (data.value == null or (data.error and data.error.match /setDefault$/ ) ) and node['default'] != null |
||||
node.data = node['default'] |
||||
else |
||||
node.data = data.value |
||||
# Cast int as int (remember that booleans are int for Perl) |
||||
if node.type and node.type.match /^(int|bool|trool)$/ |
||||
node.data = parseInt(node.data) |
||||
# Split SAML types |
||||
else if node.type and node.type.match(/^(saml(Service|Assertion)|blackWhiteList)$/) and not (typeof node.data == 'object') |
||||
node.data = node.data.split ';' |
||||
$scope.waiting = false |
||||
d.resolve node.data |
||||
, (response) -> |
||||
readError response |
||||
d.reject response.status |
||||
else |
||||
d.resolve node.data |
||||
return d.promise |
||||
|
||||
# function `pathEvent(event, next; current)`: |
||||
# Called when $location.path() change, launch getCfg() with the new |
||||
# configuration number |
||||
pathEvent = (event, next, current) -> |
||||
n = next.match(new RegExp('#/confs/(latest|[0-9]+)')) |
||||
if n == null |
||||
$location.path '/confs/latest' |
||||
else |
||||
console.log "Trying to get cfg number #{n[1]}" |
||||
$scope.getCfg n[1] |
||||
$scope.$on '$locationChangeSuccess', pathEvent |
||||
|
||||
# function `getCfg(n) |
||||
# Download configuration metadatas |
||||
$scope.getCfg = (n) -> |
||||
if $scope.currentCfg.cfgNum != n |
||||
$http.get("#{window.confPrefix}#{n}").then (response) -> |
||||
$scope.currentCfg = response.data |
||||
d = new Date $scope.currentCfg.cfgDate * 1000 |
||||
$scope.currentCfg.date = d.toLocaleString() |
||||
console.log "Metadatas of cfg #{n} loaded" |
||||
$location.path "/confs/#{n}" |
||||
$scope.init() |
||||
, (response) -> |
||||
readError(response).then -> |
||||
$scope.currentCfg.cfgNum = 0 |
||||
$scope.init() |
||||
else |
||||
$scope.waiting = false |
||||
|
||||
# method `getLanguage(lang)` |
||||
# Launch init() after setting current language |
||||
$scope.getLanguage = (lang) -> |
||||
$scope.lang = lang |
||||
# Force reload home |
||||
$scope.form = 'white' |
||||
$scope.init() |
||||
$scope.showM = false |
||||
|
||||
# Initialization |
||||
|
||||
# Load JSON files: |
||||
# - struct.json: the main tree |
||||
# - languages/<lang>.json: the chosen language datas |
||||
$scope.init = -> |
||||
tmp = null |
||||
$scope.waiting = true |
||||
$scope.data = [] |
||||
$scope.confirmNeeded = false |
||||
$scope.forceSave = false |
||||
$q.all [ |
||||
$translator.init($scope.lang), |
||||
$http.get("#{window.staticPrefix}struct.json").then (response) -> |
||||
tmp = response.data |
||||
console.log("Structure loaded") |
||||
] |
||||
.then -> |
||||
console.log("Starting structure binding") |
||||
$scope.data = tmp |
||||
tmp = null |
||||
if $scope.currentCfg.cfgNum != 0 |
||||
setScopeVars $scope |
||||
else |
||||
$scope.message = |
||||
title: 'emptyConf' |
||||
message: '__zeroConfExplanations__' |
||||
$scope.showModal 'message.html' |
||||
$scope.form = 'home' |
||||
$scope.waiting = false |
||||
, readError |
||||
c = $location.path().match(new RegExp('^/confs/(latest|[0-9]+)')) |
||||
unless c |
||||
console.log "Redirecting to /confs/latest" |
||||
$location.path '/confs/latest' |
||||
|
||||
# File form function |
||||
$scope.replaceContentByUrl = (node, url) -> |
||||
$scope.waiting = true |
||||
$http.post(window.scriptname + "prx", {url: url}).then (response) -> |
||||
node.data = response.data.content |
||||
$scope.waiting = false |
||||
, readError |
||||
$scope.replaceContent = (node, $fileContent) -> |
||||
node.data = $fileContent |
||||
|
||||
# Import Filesaver.js saveAs() |
||||
$scope.saveAs = (content, type, filename) -> |
||||
saveAs(new Blob([content], {"type": type}), filename) |
||||
] |
@ -1 +1 @@ |
||||
(function(){var llapp=angular.module("llApp",[]);llapp.provider("$translator",$Translator);function $Translator(){var res={};if(navigator){var nlangs=[navigator.language];if(navigator.languages){nlangs=navigator.languages}var langs=[],langs2=[];nlangs.forEach(function(nl){availableLanguages.forEach(function(al){if(al==nl){langs.push(al)}else{if(al.substring(0,1)==nl.substring(0,1)){langs2.push(al)}}})});res.lang=langs[0]?langs[0]:langs2[0]?langs2[0]:"en"}else{res.lang="en"}res.deferredTr=[];res.translationFields={};res.translate=function(s){if(res.translationFields[s]){s=res.translationFields[s]}return s};res.translateField=function(node,field){return res.translate(node[field])};res.translateP=function(s){if(s&&res.translationFields.portal){s=s.replace(/__(\w+)__/g,function(match,w){return res.translate(w)})}return s};this.$get=["$q","$http",function($q,$http){res.last="";res.init=function(lang){if(!lang){lang=res.lang}var d=$q.defer();if(res.last!=lang){res.last=lang;$http.get(staticPrefix+"languages/"+lang+".json").then(function(response){res.translationFields=response.data;res.deferredTr.forEach(function(h){h.e[h.f](res.translationFields[h.m])});res.deferredTr=[];d.resolve("Translation files loaded")},function(resp){d.reject("")})}else{d.resolve("No change")}return d.promise};return res}]}llapp.directive("trspan",["$translator",function($translator){return{restrict:"A",replace:false,transclude:true,scope:{trspan:"@"},link:function(scope,elem,attr){if($translator.translationFields.portal){attr.trspan=$translator.translate(attr.trspan)}else{$translator.deferredTr.push({e:elem,f:"text",m:attr.trspan})}elem.text(attr.trspan)},template:""}}]);llapp.provider("$htmlParams",$HtmlParamsProvider);function $HtmlParamsProvider(){this.$get=function(){var params={};return{set:function(key,obj){params[key]=obj},menu:function(){return params.menu},params:function(){return params.params}}}}llapp.directive("script",["$htmlParams",function($htmlParams){return{restrict:"E",terminal:true,compile:function(element,attr){var t;if(t=attr.type.match(/text\/(menu|parameters)/)){$htmlParams.set(t[1],eval(element[0].text))}}}}]);llapp.controller("ModalInstanceCtrl",["$scope","$uibModalInstance","elem","set","init",function($scope,$uibModalInstance,elem,set,init){var oldValue;$scope.elem=elem;$scope.set=set;$scope.result=init;$scope.staticPrefix=staticPrefix;var currentNode=elem("currentNode");$scope.translateP=elem("translateP");if(currentNode){oldValue=currentNode.data;$scope.currentNode=currentNode}$scope.ok=function(){set("result",$scope.result);$uibModalInstance.close(true)};$scope.cancel=function(){if(currentNode){$scope.currentNode.data=oldValue}$uibModalInstance.dismiss("cancel")};$scope.inSelect=function(value){for(var i=0;i<$scope.currentNode.select.length;i++){if($scope.currentNode.select[i].k==value){return true}}return false}}]);llapp.directive("onReadFile",["$parse",function($parse){return{restrict:"A",scope:false,link:function(scope,element,attrs){var fn=$parse(attrs.onReadFile);element.on("change",function(onChangeEvent){var reader=new FileReader();reader.onload=function(onLoadEvent){scope.$apply(function(){fn(scope,{$fileContent:onLoadEvent.target.result})})};reader.readAsText((onChangeEvent.srcElement||onChangeEvent.target).files[0])})}}}]);llapp.directive("resizer",["$document",function($document){var rsize,hsize;return function($scope,$element,$attrs){$element.on("mousedown",function(event){if($attrs.resizer=="vertical"){rsize=$($attrs.resizerRight).width()+$($attrs.resizerLeft).width()}else{hsize=$($attrs.resizerTop).height()+$($attrs.resizerBottom).height()}event.preventDefault();$document.on("mousemove",mousemove);$document.on("mouseup",mouseup)});function mousemove(event){if($attrs.resizer=="vertical"){var x=event.pageX;if($attrs.resizerMax&&x>$attrs.resizerMax){x=parseInt($attrs.resizerMax)}$($attrs.resizerLeft).css({width:x+"px"});$($attrs.resizerRight).css({width:(rsize-x)+"px"})}else{var y=event.pageY-$("#navbar").height();$($attrs.resizerTop).css({height:y+"px"});$($attrs.resizerBottom).css({height:(hsize-y)+"px"})}}function mouseup(){$document.unbind("mousemove",mousemove);$document.unbind("mouseup",mouseup)}}}]);llapp.factory("$lmhttp",["$q","$location",function($q,$location){return{responseError:function(rejection){if(rejection.status==401&&window.portal){window.location=window.portal+"?url="+window.btoa(window.location).replace(/\//,"_")}}}}]);llapp.config(["$httpProvider",function($httpProvider){$httpProvider.interceptors.push("$lmhttp")}])})(); |
||||
(function(){var llapp;llapp=angular.module("llApp",[]);llapp.provider("$translator",function(){var al,j,k,langs,langs2,len,len1,nl,nlangs,ref,res;res={};if(navigator){langs=[];langs2=[];nlangs=[navigator.language];if(navigator.languages){nlangs=navigator.languages}for(j=0,len=nlangs.length;j<len;j++){nl=nlangs[j];ref=window.availableLanguages;for(k=0,len1=ref.length;k<len1;k++){al=ref[k];if(al===nl){langs.push(al)}else{if(al.substring(0,1)===nl.substring(0,1)){langs2.push(al)}}}}res.lang=langs[0]?langs[0]:langs2[0]?langs2[0]:"en"}else{res.lang="en"}res.deferredTr=[];res.translationFields={};res.translate=function(s){if(res.translationFields[s]){s=res.translationFields[s]}return s};res.translateField=function(node,field){return res.translate(node[field])};res.translateP=function(s){if(s&&res.translationFields.portal){s=s.replace(/__(\w+)__/g,function(match,w){return res.translate(w)})}return s};this.$get=["$q","$http",function($q,$http){res.last="";res.init=function(lang){var d;if(!lang){lang=res.lang}d=$q.defer();if(res.last!==lang){res.last=lang;$http.get(window.staticPrefix+"languages/"+lang+".json").then(function(response){var h,l,len2,ref1;res.translationFields=response.data;ref1=res.deferredTr;for(l=0,len2=ref1.length;l<len2;l++){h=ref1[l];h.e[h.f](res.translationFields[h.m])}res.deferredTr=[];return d.resolve("Translation files loaded")},function(response){return d.reject("")})}else{d.resolve("No change")}return d.promise};return res}];return this});llapp.directive("trspan",["$translator",function($translator){return{restrict:"A",replace:false,transclude:true,scope:{trspan:"@"},link:function(scope,elem,attr){if($translator.translationFields.portal){attr.trspan=$translator.translate(attr.trspan)}else{$translator.deferredTr.push({e:elem,f:"text",m:attr.trspan})}return elem.text(attr.trspan)},template:""}}]);llapp.provider("$htmlParams",function(){this.$get=function(){var params;params={};return{set:function(key,obj){return params[key]=obj},menu:function(){return params.menu},params:function(){return params.params}}};return this});llapp.directive("script",["$htmlParams",function($htmlParams){return{restrict:"E",terminal:true,compile:function(element,attr){var t;if(t=attr.type.match(/text\/(menu|parameters)/)){return $htmlParams.set(t[1],eval(element[0].text))}}}}]);llapp.controller("ModalInstanceCtrl",["$scope","$uibModalInstance","elem","set","init",function($scope,$uibModalInstance,elem,set,init){var currentNode,oldValue,oldvalue;oldvalue=null;$scope.elem=elem;$scope.set=set;$scope.result=init;$scope.staticPrefix=window.staticPrefix;currentNode=elem("currentNode");$scope.translateP=elem("translateP");if(currentNode){oldValue=currentNode.data;$scope.currentNode=currentNode}$scope.ok=function(){set("result",$scope.result);return $uibModalInstance.close(true)};$scope.cancel=function(){if(currentNode){$scope.currentNode.data=oldValue}return $uibModalInstance.dismiss("cancel")};return $scope.inSelect=function(value){var i,j,len,ref;ref=$scope.currentNode.select;for(j=0,len=ref.length;j<len;j++){i=ref[j];if(i.k===value){return true}}return false}}]);llapp.directive("onReadFile",["$parse",function($parse){return{restrict:"A",scope:false,link:function(scope,element,attrs){var fn;fn=$parse(attrs.onReadFile);return element.on("change",function(onChangeEvent){var reader;reader=new FileReader();reader.onload=function(onLoadEvent){return scope.$apply(function(){return fn(scope,{$fileContent:onLoadEvent.target.result})})};return reader.readAsText((onChangeEvent.srcElement||onChangeEvent.target).files[0])})}}}]);llapp.directive("resizer",["$document",function($document){var hsize,rsize;hsize=null;rsize=null;return function($scope,$element,$attrs){var mousemove,mouseup;$element.on("mousedown",function(event){if($attrs.resizer==="vertical"){rsize=$($attrs.resizerRight).width()+$($attrs.resizerLeft).width()}else{hsize=$($attrs.resizerTop).height()+$($attrs.resizerBottom).height()}event.preventDefault();$document.on("mousemove",mousemove);return $document.on("mouseup",mouseup)});mousemove=function(event){var x,y;if($attrs.resizer==="vertical"){x=event.pageX;if($attrs.resizerMax&&x>$attrs.resizerMax){x=parseInt($attrs.resizerMax)}$($attrs.resizerLeft).css({width:x+"px"});return $($attrs.resizerRight).css({width:(rsize-x)+"px"})}else{y=event.pageY-$("#navbar").height();$($attrs.resizerTop).css({height:y+"px"});return $($attrs.resizerBottom).css({height:(hsize-y)+"px"})}};return mouseup=function(){$document.unbind("mousemove",mousemove);return $document.unbind("mouseup",mouseup)}}}]);llapp.factory("$lmhttp",["$q","$location",function($q,$location){return{responseError:function(rejection){if(rejection.status===401&&window.portal){return window.location=(window.portal+"?url=")+window.btoa(window.location).replace(/\//,"_")}}}}]);llapp.config(["$httpProvider",function($httpProvider){return $httpProvider.interceptors.push("$lmhttp")}])}).call(this); |
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Loading…
Reference in new issue