WIP - Append relative files (#1661)
This commit is contained in:
parent
b7a78b500e
commit
4a8a614bc9
161
lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Viewer.pm
Normal file
161
lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Viewer.pm
Normal file
|
@ -0,0 +1,161 @@
|
|||
# This module implements all the methods that responds to '/confs/*' requests
|
||||
# It contains 2 sections:
|
||||
# - initialization methods
|
||||
# - upload method
|
||||
#
|
||||
# Read methods are inherited from Lemonldap::NG::Common::Conf::RESTServer
|
||||
package Lemonldap::NG::Manager::Viewer;
|
||||
|
||||
use 5.10.0;
|
||||
use utf8;
|
||||
use Mouse;
|
||||
use Lemonldap::NG::Common::Conf::Constants;
|
||||
use Lemonldap::NG::Common::UserAgent;
|
||||
use Crypt::OpenSSL::RSA;
|
||||
use Convert::PEM;
|
||||
use URI::URL;
|
||||
|
||||
use feature 'state';
|
||||
|
||||
extends 'Lemonldap::NG::Common::Conf::RESTServer';
|
||||
|
||||
our $VERSION = '2.0.2';
|
||||
|
||||
#############################
|
||||
# I. INITIALIZATION METHODS #
|
||||
#############################
|
||||
|
||||
use constant defaultRoute => 'viewer.html';
|
||||
|
||||
has ua => ( is => 'rw' );
|
||||
|
||||
sub addRoutes {
|
||||
my ( $self, $conf ) = @_;
|
||||
$self->ua( Lemonldap::NG::Common::UserAgent->new($conf) );
|
||||
|
||||
# HTML template
|
||||
$self->addRoute( 'viewer.html', undef, ['GET'] )
|
||||
|
||||
# READ
|
||||
# Special keys
|
||||
->addRoute(
|
||||
confs => {
|
||||
':cfgNum' => [
|
||||
qw(virtualHosts samlIDPMetaDataNodes samlSPMetaDataNodes
|
||||
applicationList oidcOPMetaDataNodes oidcRPMetaDataNodes
|
||||
casSrvMetaDataNodes casAppMetaDataNodes
|
||||
authChoiceModules grantSessionRules combModules
|
||||
openIdIDPList)
|
||||
]
|
||||
},
|
||||
['GET']
|
||||
)
|
||||
|
||||
# Other keys
|
||||
->addRoute( confs => { ':cfgNum' => { '*' => 'getKey' } }, ['GET'] )
|
||||
|
||||
# Difference between confs
|
||||
->addRoute( diff => { ':conf1' => { ':conf2' => 'diff' } } )
|
||||
->addRoute( 'diff.html', undef, ['GET'] )
|
||||
}
|
||||
|
||||
# 35 - New RSA key pair on demand
|
||||
# --------------------------
|
||||
|
||||
|
||||
# 36 - URL File loader
|
||||
# ---------------
|
||||
|
||||
##@method public PSGI-JSON-response prx()
|
||||
# Load file using posted URL and return its content
|
||||
#
|
||||
#@return PSGI JSON response
|
||||
sub prx {
|
||||
my ( $self, $req, @others ) = @_;
|
||||
return $self->sendError( $req, 'There is no subkey for "prx"', 400 )
|
||||
if (@others);
|
||||
my $query = $req->jsonBodyToObj;
|
||||
return $self->sendError( $req, 'Missing parameter', 400 )
|
||||
unless ( $query->{url} );
|
||||
return $self->sendError( $req, 'Bad parameter', 400 )
|
||||
unless ( $query->{url} =~ m#^(?:f|ht)tps?://\w# );
|
||||
$self->ua->timeout(10);
|
||||
|
||||
my $response = $self->ua->get( $query->{url} );
|
||||
unless ( $response->code == 200 ) {
|
||||
return $self->sendError( $req,
|
||||
$response->code . " (" . $response->message . ")", 400 );
|
||||
}
|
||||
unless ( $response->header('Content-Type') =~
|
||||
m#^(?:application/json|(?:application|text)/.*xml).*$# )
|
||||
{
|
||||
return $self->sendError( $req,
|
||||
'Content refused for security reason (neither XML or JSON)', 400 );
|
||||
}
|
||||
return $self->sendJSONresponse( $req, { content => $response->content } );
|
||||
}
|
||||
|
||||
######################
|
||||
# IV. Upload methods #
|
||||
######################
|
||||
|
||||
# - getConfByNum: override SUPER method to be able to use Zero
|
||||
|
||||
|
||||
sub getConfByNum {
|
||||
my ( $self, $cfgNum, @args ) = @_;
|
||||
unless ( %{ $self->currentConf }
|
||||
and $cfgNum == $self->currentConf->{cfgNum} )
|
||||
{
|
||||
my $tmp;
|
||||
if ( $cfgNum == 0 ) {
|
||||
require Lemonldap::NG::Manager::Conf::Zero;
|
||||
$tmp = Lemonldap::NG::Manager::Conf::Zero::zeroConf();
|
||||
$self->currentConf($tmp);
|
||||
}
|
||||
else {
|
||||
$tmp = $self->SUPER::getConfByNum( $cfgNum, @args );
|
||||
return undef unless ( defined $tmp );
|
||||
}
|
||||
}
|
||||
return $cfgNum;
|
||||
}
|
||||
|
||||
sub diff {
|
||||
my ( $self, $req, @path ) = @_;
|
||||
return $self->sendError( $req, 'to many arguments in path info', 400 )
|
||||
if (@path);
|
||||
my @cfgNum =
|
||||
( scalar( $req->param('conf1') ), scalar( $req->param('conf2') ) );
|
||||
my @conf;
|
||||
$self->logger->debug(" Loading confs");
|
||||
|
||||
# Load the 2 configurations
|
||||
for ( my $i = 0 ; $i < 2 ; $i++ ) {
|
||||
if ( %{ $self->currentConf }
|
||||
and $cfgNum[$i] == $self->currentConf->{cfgNum} )
|
||||
{
|
||||
$conf[$i] = $self->currentConf;
|
||||
}
|
||||
else {
|
||||
$conf[$i] = $self->confAcc->getConf(
|
||||
{ cfgNum => $cfgNum[$i], raw => 1, noCache => 1 } );
|
||||
return $self->sendError(
|
||||
$req,
|
||||
"Configuration $cfgNum[$i] not available $Lemonldap::NG::Common::Conf::msg",
|
||||
400
|
||||
) unless ( $conf[$i] );
|
||||
}
|
||||
}
|
||||
require Lemonldap::NG::Manager::Conf::Diff;
|
||||
return $self->sendJSONresponse(
|
||||
$req,
|
||||
[
|
||||
$self->Lemonldap::NG::Manager::Conf::Diff::diff(
|
||||
$conf[0], $conf[1]
|
||||
)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
1;
|
793
lemonldap-ng-manager/site/coffee/viewer.coffee
Normal file
793
lemonldap-ng-manager/site/coffee/viewer.coffee
Normal file
|
@ -0,0 +1,793 @@
|
|||
###
|
||||
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 d.promise
|
||||
|
||||
# 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.needConfirm
|
||||
$scope.message.message = data.message if data.message
|
||||
if data.details
|
||||
for m of data.details when m != '__changes__'
|
||||
if m == '__needConfirmation__'
|
||||
$scope.message.items.unshift
|
||||
message: m
|
||||
items: data.details[m]
|
||||
else
|
||||
$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'
|
||||
|
||||
# Download raw conf
|
||||
$scope.downloadConf = () ->
|
||||
window.open $scope.confPrefix + $scope.currentCfg.cfgNum + '?full=1'
|
||||
|
||||
# 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.push
|
||||
id: "#{node.id}/n#{id++}"
|
||||
title: 'New rule'
|
||||
re: 'Message'
|
||||
comment: 'New rule'
|
||||
data: '1'
|
||||
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"
|
||||
|
||||
# Combination module
|
||||
$scope.newCmbMod = ->
|
||||
node = $scope._findContainer()
|
||||
node.nodes.push
|
||||
id: "#{node.id}/n#{id++}"
|
||||
title: 'new'
|
||||
type: 'cmbModule'
|
||||
data:
|
||||
type: 'LDAP'
|
||||
for: '0'
|
||||
over: []
|
||||
$scope.execFilters $scope._findScopeByKey 'authParams'
|
||||
|
||||
$scope.newCmbOver = ->
|
||||
d = $scope.currentNode.data
|
||||
d.over = [] unless d.over
|
||||
d.over.push ["new#{id++}", '']
|
||||
|
||||
$scope.newChoiceOver = ->
|
||||
d = $scope.currentNode.data
|
||||
console.log "data", d
|
||||
d[5] = [] unless d[5]
|
||||
d[5].push ["new#{id++}", '']
|
||||
|
||||
# Add host
|
||||
$scope.addHost = () ->
|
||||
cn = $scope.currentNode
|
||||
cn.data=[] unless cn.data
|
||||
cn.data.push
|
||||
k: "newHost"
|
||||
h: [{"k":"key","v":"uid"}]
|
||||
|
||||
# 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
|
||||
$scope.addTemplateNode n, 'virtualHost'
|
||||
|
||||
$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.addCasSrv = ->
|
||||
$scope.newTemplateNode 'casSrvMetaDataNode', 'casPartnerName', 'srv-example'
|
||||
|
||||
$scope.addCasApp = ->
|
||||
$scope.newTemplateNode 'casAppMetaDataNode', 'casPartnerName', 'app-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}"
|
||||
setDefault t.nodes
|
||||
cs.$modelValue.nodes.push t
|
||||
cs.expand()
|
||||
return t
|
||||
|
||||
setDefault = (node) ->
|
||||
for n in node
|
||||
if n.cnodes and n.default
|
||||
delete n.cnodes
|
||||
n._nodes = n.default
|
||||
if n._nodes
|
||||
setDefault n._nodes
|
||||
else if n.default or n.default == 0
|
||||
n.data = n.default
|
||||
node
|
||||
|
||||
_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, idkey) ->
|
||||
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 window.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
|
||||
# regexp-assemble of:
|
||||
# authChoice
|
||||
# keyText
|
||||
# cmbModule
|
||||
# virtualHost
|
||||
# rule
|
||||
# menuCat
|
||||
# menuApp
|
||||
# samlAttribute
|
||||
# samlIDPMetaDataNode
|
||||
# samlSPMetaDataNode
|
||||
return if node.type and node.type.match /^(?:(?:saml(?:(?:ID|S)PMetaDataNod|Attribut)|(?:cmbMod|r)ul|authChoic)e|(?:virtualHos|keyTex)t|menu(?:App|Cat))$/ 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$/
|
||||
node.data = parseInt(node.data, 10)
|
||||
# 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
|
||||
# Colorized link
|
||||
$scope.activeModule = "conf"
|
||||
$scope.myStyle = {color: '#ffb84d'}
|
||||
|
||||
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)
|
||||
|
||||
# Save as pem, text,...
|
||||
$scope.saveAsPem = (cs,scope) ->
|
||||
scope.saveAs "#{cs.data[0].data}\n#{cs.data[2].data}", 'application/x-pem-file', "#{cs.title}.pem"
|
||||
$scope.saveAsText = (cs,scope) ->
|
||||
scope.saveAs cs.data, 'text/plain', "#{cs.title}.txt"
|
||||
]
|
200
lemonldap-ng-manager/site/templates/viewer.tpl
Normal file
200
lemonldap-ng-manager/site/templates/viewer.tpl
Normal file
|
@ -0,0 +1,200 @@
|
|||
<TMPL_INCLUDE NAME="header.tpl">
|
||||
|
||||
<title>LemonLDAP::NG Manager</title>
|
||||
<link rel="prefetch" href="<TMPL_VAR NAME="STATIC_PREFIX">forms/home.html" />
|
||||
<link rel="prefetch" href="<TMPL_VAR NAME="STATIC_PREFIX">struct.json" />
|
||||
</head>
|
||||
|
||||
<body ng-app="llngManager" ng-controller="TreeCtrl" ng-csp>
|
||||
|
||||
<TMPL_INCLUDE NAME="menubar.tpl">
|
||||
|
||||
<div id="content" class="row container-fluid">
|
||||
|
||||
<TMPL_INCLUDE NAME="tree.tpl">
|
||||
|
||||
<!-- Right(main) div -->
|
||||
<div id="right" class="col-lg-8 col-md-8 col-sm-7 col-xs-12 scrollable" ng-class="{'hidden-xs':showT&&!showM}">
|
||||
<!-- Form container -->
|
||||
<div id="top">
|
||||
<!-- Menu buttons -->
|
||||
<div class="lmmenu navbar navbar-default" ng-class="{'hidden-xs':!showM}">
|
||||
<div class="navbar-collapse" ng-class="{'collapse':!showM}" id="formmenu">
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a class="link" ng-click="home()"><i class="glyphicon glyphicon-home"></i></a></li>
|
||||
<li>
|
||||
<a id="save" class="link" ng-click="save()" tabIndex="-1">
|
||||
<i class="glyphicon glyphicon-cloud-upload"></i>
|
||||
{{translate('save')}}
|
||||
</a>
|
||||
</li>
|
||||
<li >
|
||||
<input id="forcesave" type="checkbox" ng-model="forceSave" uib-tooltip="{{translate('forceSave')}}" tooltip-placement="right" ng-show="confirmNeeded||currentCfg.next" role="checkbox" aria-label="Force save">
|
||||
</li>
|
||||
<li uib-dropdown>
|
||||
<a id="navmenu" name="menu" uib-dropdown-toggle data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><i class="glyphicon glyphicon-cog"></i> {{translate('browse')}} <span class="caret"></span></a>
|
||||
<ul uib-dropdown-menu aria-labelled-by="navmenu">
|
||||
<li ng-class="{'disabled':!currentCfg.prev}"><a class="link" ng-click="currentCfg.prev && getCfg(currentCfg.prev)" title="Configuration {{currentCfg.prev}}"><i class="glyphicon glyphicon-arrow-left"></i> {{translate('previous')}}</a></li>
|
||||
<li ng-class="{'disabled':!currentCfg.next}"><a class="link" ng-click="currentCfg.next && getCfg(currentCfg.next)" title="Configuration {{currentCfg.next}}"><i class="glyphicon glyphicon-arrow-right"></i> {{translate('next')}}</a></a></li>
|
||||
<li><a class="link" ng-click="getCfg('latest')" title="Latest configuration"><i class="glyphicon glyphicon-refresh"></i> {{translate('latest')}}</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="link hidden-xs" ng-click="setShowHelp()"><i class="glyphicon" ng-class="{'glyphicon-eye-close': showH,'glyphicon-eye-open': !showH}" ></i> {{ translate((showH ? 'hideHelp' : 'showHelp')) }}</a></li>
|
||||
<li ng-repeat="button in menu()" ng-include="'menubutton.html'"></li>
|
||||
<li uib-dropdown class="visible-xs">
|
||||
<a id="langmenu" name="menu" uib-dropdown-toggle data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{translate('menu')}} <span class="caret"></span></a>
|
||||
<ul uib-dropdown-menu aria-labelled-by="langmenu" role="grid">
|
||||
<li ng-repeat="link in links"><a href="{{link.target}}" role="row"><i ng-if="link.icon" class="glyphicon glyphicon-{{link.icon}}"></i> {{translate(link.title)}}</a></li>
|
||||
<li ng-repeat="menulink in menulinks"><a href="{{menulink.target}}" role="row"><i ng-if="menulink.icon" class="glyphicon glyphicon-{{menulink.icon}}"></i> {{translate(menulink.title)}}</a></li>
|
||||
<li ng-include="'languages.html'"></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<form class="form-group slide-animate-container" ng-include="formPrefix+form+'.html'" scope="$scope" />
|
||||
</div>
|
||||
<!-- Help container -->
|
||||
<div id="bottom" ng-if="showH" class="hidden-xs">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<iframe id="helpframe" width="100%" height="100%" ng-src="{{translate('/doc/')+'pages/documentation/current/'+helpUrl}}" frameborder="0"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- HTML recursive templates (used in `ng-repeat... ng-include="'template.html'") -->
|
||||
<!-- Tree nested node template -->
|
||||
<script type="text/ng-template" id="nodes_renderer.html">
|
||||
<div ui-tree-handle class="tree-node panel-info" ng-class="{'bg-info':this.$modelValue===currentNode,'tree-node-default':this.$modelValue!==currentNode}">
|
||||
<!-- Glyph icons -->
|
||||
<span ng-switch="node.nodes||node.nodes_cond?1:((node._nodes&&node._nodes.length>0)||(node._nodes_cond&&node._nodes_cond.length>0)?3:(node.cnodes&&node.cnodes.length>0?2:0))">
|
||||
<!-- Undownloaded nodes (hash data)-->
|
||||
<a class="btn btn-sm" id="a-{{node.id}}" ng-switch-when="2" ng-click="openCnode(this)">
|
||||
<span class="glyphicon glyphicon-chevron-right"></span>
|
||||
</a>
|
||||
<!-- Javascript nodes not yet bind to DOM -->
|
||||
<a class="btn btn-sm" id="a-{{node.id}}" ng-switch-when="3" ng-click="stoggle(this)">
|
||||
<span class="glyphicon" ng-class="{'glyphicon-chevron-right': collapsed, 'glyphicon-chevron-down': !collapsed}"></span>
|
||||
</a>
|
||||
<!-- Nodes already loaded and binded -->
|
||||
<a class="btn btn-sm" id="a-{{node.id}}" ng-switch-when="1" ng-click="toggle(this)">
|
||||
<span class="glyphicon" ng-class="{'glyphicon-chevron-right': collapsed, 'glyphicon-chevron-down': !collapsed}"></span>
|
||||
</a>
|
||||
<!-- Leaf -->
|
||||
<a class="btn btn-sm" ng-switch-default ng-click="displayForm(this)">
|
||||
<span class="glyphicon glyphicon-pencil"></span>
|
||||
</a>
|
||||
</span>
|
||||
<!-- Node text with/without translation -->
|
||||
<span id="t-{{node.id}}" ng-if="keyWritable(this)" ng-click="displayForm(this)">{{node.title}}</span>
|
||||
<span id="t-{{node.id}}" ng-if="!keyWritable(this)" ng-click="displayForm(this)" trspan="{{node.title}}" />
|
||||
</div>
|
||||
<!-- Subnodes -->
|
||||
<ol ui-tree-nodes="btn btn-sm" ng-model="node.nodes" ng-class="{hidden: collapsed}">
|
||||
<li ng-repeat="node in node.nodes track by node.id" ui-tree-node ng-include="'nodes_renderer.html'" collapsed="true"></li>
|
||||
</ol>
|
||||
<!-- Filtered subnodes (authParams mechanism) -->
|
||||
<ol ui-tree-nodes="btn btn-sm" ng-model="node.nodes_cond" ng-class="{hidden: collapsed}">
|
||||
<li ng-repeat="(name,node) in node.nodes_cond track by node.id" ng-if="node.show" ui-tree-node ng-include="'nodes_renderer.html'" collapsed="true"></li>
|
||||
</ol>
|
||||
</script>
|
||||
|
||||
<!-- Prompt -->
|
||||
<script type="text/ng-template" id="prompt.html">
|
||||
<div role="alertdialog" aria-labelledby="ptitle" aria-describedby="ptitle">
|
||||
<div class="modal-header">
|
||||
<h3 id="ptitle" class="modal-title" trspan="{{elem('message').title}}" />
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="input-group maxw">
|
||||
<label class="input-group-addon" id="promptlabel" for="promptinput" trspan="{{elem('message').field}}"/>
|
||||
<input id="promptinput" class="form-control" ng-model="result" aria-describedby="promptlabel"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" id="promptok" ng-click="ok()" trspan="ok" role="button"></button>
|
||||
<button class="btn btn-warning" ng-click="cancel()" trspan="cancel" role="button"></button>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<!-- Message display -->
|
||||
<script type="text/ng-template" id="message.html">
|
||||
<div role="alertdialog" aria-labelledby="mtitle" aria-describedby="mbody">
|
||||
<div class="modal-header">
|
||||
<h3 id="mtitle" class="modal-title" trspan="{{elem('message').title}}" />
|
||||
</div>
|
||||
<div id="mbody" class="modal-body">
|
||||
<div class="modal-p">{{translateP(elem('message').message)}}</div>
|
||||
<ul class="main-modal-ul" ng-model="elem('message').items">
|
||||
<li ng-repeat="item in elem('message').items" ng-include="'messageitem.html'"/>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" id="messageok" ng-click="ok()" trspan="ok" role="button"></button>
|
||||
<button class="btn btn-warning" ng-click="cancel()" ng-if="elem('message').displayCancel" trspan="cancel" role="button"></button>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/ng-template" id="messageitem.html">
|
||||
<div class="modal-p">{{translateP(item.message)}}</div>
|
||||
<ul class="modal-ul" ng-model="item.items">
|
||||
<li ng-repeat="item in item.items" ng-include="'messageitem.html'"/>
|
||||
</ul>
|
||||
</script>
|
||||
|
||||
<!-- Password question -->
|
||||
<script type="text/ng-template" id="password.html">
|
||||
<div role="alertdialog" aria-labelledby="pwtitle" aria-describedby="pwtitle">
|
||||
<div class="modal-header">
|
||||
<h3 id="pwtitle" class="modal-title" trspan="enterPassword" />
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="input-group maxw">
|
||||
<label class="input-group-addon" id="mlabel" for="mdPwd" trspan="password"/>
|
||||
<input id="mdPwd" class="form-control" ng-model="result" aria-describedby="mlabel"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" id="passwordok" ng-click="ok()" trspan="ok" role="button"></button>
|
||||
<button class="btn btn-warning" ng-click="cancel()" trspan="cancel" role="button"></button>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<!-- Save confirm -->
|
||||
<script type="text/ng-template" id="save.html">
|
||||
<div role="alertdialog" aria-labelledby="stitle" aria-describedby="sbody">
|
||||
<div class="modal-header">
|
||||
<h3 id="stitle" class="modal-title" trspan="savingConfirmation" />
|
||||
</div>
|
||||
<div id="sbody" class="modal-body">
|
||||
<div class="input-group maxw">
|
||||
<label id="slabel" class="input-group-addon" for="longtextinput" trspan="cfgLog"/>
|
||||
<textarea id="longtextinput" rows="5" class="form-control" ng-model="result" aria-describedby="slabel" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" id="saveok" ng-click="ok()" trspan="ok" role="button"></button>
|
||||
<button class="btn btn-warning" ng-click="cancel()" trspan="cancel" role="button"></button>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<TMPL_INCLUDE NAME="scripts.tpl">
|
||||
|
||||
<!-- //if:jsminified
|
||||
<script type="text/javascript" src="<TMPL_VAR NAME="STATIC_PREFIX">js/conftree.min.js"></script>
|
||||
<script type="text/javascript" src="<TMPL_VAR NAME="STATIC_PREFIX">js/filterFunctions.min.js"></script>
|
||||
<script type="text/javascript" src="<TMPL_VAR NAME="STATIC_PREFIX">js/manager.min.js"></script>
|
||||
//else -->
|
||||
<script type="text/javascript" src="<TMPL_VAR NAME="STATIC_PREFIX">js/conftree.js"></script>
|
||||
<script type="text/javascript" src="<TMPL_VAR NAME="STATIC_PREFIX">js/filterFunctions.js"></script>
|
||||
<script type="text/javascript" src="<TMPL_VAR NAME="STATIC_PREFIX">js/manager.js"></script>
|
||||
<!-- //endif -->
|
||||
|
||||
<TMPL_INCLUDE NAME="footer.tpl">
|
Loading…
Reference in New Issue
Block a user