Updated admin script to use coffescript instead of directly javascript

This commit is contained in:
Adolfo Gómez 2014-03-17 14:26:07 +00:00
parent a1e5a1357a
commit 7a0465fca5
31 changed files with 4400 additions and 391 deletions

View File

@ -0,0 +1,17 @@
"use strict"
@api = @api ? {}
@api.spreadsheet = @api.spreadsheet ? {}
$ = jQuery
@api.spreadsheet.cell = (data, type, style) ->
type = type or "String"
if style isnt `undefined`
style = " ss:StyleID=\"" + style + "\""
else
style = ""
"<Cell" + style + "><Data ss:Type=\"" + type + "\">" + data + "</Data></Cell>"
@api.spreadsheet.row = (cell) ->
"<Row>" + cell + "</Row>"
return

View File

@ -0,0 +1,143 @@
# jshint strict: true
# -------------------------------
# Templates related
# Inserted into api
# for the admin app
# -------------------------------
"use strict"
@api = @api ? {}
$ = jQuery
api = @api
# Registers Handlebar useful helpers
# Iterate thought dictionary
Handlebars.registerHelper "eachKey", (context, options) ->
ret = ""
first = true
for prop of context
ret = ret + options.fn(
key: prop
value: context[prop]
first: first
)
first = false
ret
# Equal comparision (like if helper, but with comparation)
# Use as block as {{#ifequals [element] [element]}}....{{/ifequals}}
Handlebars.registerHelper "ifequals", (context1, context2, options) ->
if context1 is context2
options.fn this
else
options.inverse this
# Belongs comparision (similar to "if xxx in yyyyy")
# Use as block as {{#ifbelong [element] [group]}}....{{/ifbelongs}}
Handlebars.registerHelper "ifbelongs", (context1, context2, options) ->
gui.doLog "belongs", context1, context2
unless $.inArray(context1, context2) is -1
gui.doLog "belongs is true"
options.fn this
else
options.inverse this
# Counters.
# Create a counter with {{counter [id] [startValue]}}
# increment the counter with {{inc_counter [id]}}
# get the counter value tiwh {{get_counter [id}}
# Values are stored on current
Handlebars.registerHelper "set_counter", (id, value, options) ->
options.data["_counter_" + id] = value
return
Handlebars.registerHelper "get_counter", (id, options) ->
options.data["_counter_" + id]
Handlebars.registerHelper "inc_counter", (id, options) ->
options.data["_counter_" + id] += 1
return
# For inserting "inline" javascript scripts, due to the fact that we cannot
# Insert "<script>...</script>" inside inline elements (they are already scripts)
Handlebars.registerHelper "javascript", (options) ->
new Handlebars.SafeString("<script>" + options.fn(this) + "</script>")
# Truncate chars, like django "truncatechars" filter
Handlebars.registerHelper "truncatechars", (len, value) ->
val = value.toString() # For Array objects, the toString method joins the array and returns one string containing each array element separated by commas
if val.length > len
val.substring(0, len - 3) + "..."
else
val
# Remove white spaces
Handlebars.registerHelper "clean_whitespace", (value) ->
val = value.toString() # For Array objects, the toString method joins the array and returns one string containing each array element separated by commas
val.replace RegExp(" ", "g"), ""
api.templates = {}
# Now initialize templates api
api.templates.cache = new api.cache("tmpls") # Will cache templates locally. If name contains
# '?', data will not be cached and always
# re-requested. We do not care about lang, because page will reload on language change
api.templates.get = (name, success_fnc) ->
$this = this
success_fnc = success_fnc or ->
api.doLog "Getting template " + name
if name.indexOf("?") is -1
if $this.cache.get(name + "------")
success_fnc $this.cache.get(name)
return
# Let's check if a "preloaded template" exists
else if document.getElementById("tmpl_" + name)
$this.cache.put name, "tmpl_" + name # In fact, this is not neccesary...
success_fnc "tmpl_" + name
return
api.doLog "Invoking ajax for ", api.url_for(name, "template")
$.ajax
url: api.url_for(name, "template")
type: "GET"
dataType: "text"
success: (data) ->
cachedId = "tmpl_" + name
$this.cache.put "_" + cachedId, $this.evaluate(data)
$this.cache.put name, cachedId
api.doLog "Success getting template \"" + name + "\"."
success_fnc cachedId
return
fail: (jqXHR, textStatus, errorThrown) ->
api.doLog jqXHR
api.doLog textStatus
apid.doLog errorThrown
return
return
# Simple JavaScript Templating, using HandleBars
api.templates.evaluate = (str, context) ->
# Figure out if we're getting a template, or if we need to
# load the template - and be sure to cache the result (compiled template).
cached = undefined
unless /\W/.test(str)
cached = @cache.get("_" + str)
if cached is `undefined`
cached = api.templates.evaluate(document.getElementById(str).innerHTML)
@cache.put "_" + str, cached
template = cached or Handlebars.compile(str)
(if context then template(context) else template)
return

View File

@ -101,6 +101,7 @@
return;
}
}
api.doLog('Invoking ajax for ', api.url_for(name, 'template'));
$.ajax({
url : api.url_for(name,'template'),
type : "GET",

View File

@ -0,0 +1,381 @@
# jshint strict: true
((api, $, undefined_) ->
"use strict"
api.tools = base64: (s) ->
window.btoa unescape(encodeURIComponent(s))
return
) window.api = window.api or {}, jQuery
# Insert strftime into tools
#
#strftime
#github.com/samsonjs/strftime
#@_sjs
#
#Copyright 2010 - 2013 Sami Samhuri <sami@samhuri.net>
#
#MIT License
#http://sjs.mit-license.org
#
initialsOf = (arr) ->
res = []
for v of arr
res.push arr[v].substr(0, 3)
res
# Added this to convert django format strings to c format string
# This is ofc, a "simplified" version, aimed to use date format used by
# DJANGO
# daylight saving
# if it is leap year
# Not so sure, not important i thing anyway :-)
# english ordinal suffix for day of month
# number of days of specified month, not important
# microseconds
# Seconds since EPOCH, not used
# Time zone offset in seconds, replaced by offset
# in ours/minutes :-)
strftime = (fmt, d, locale) ->
_strftime fmt, d, locale
# locale is optional
strftimeTZ = (fmt, d, locale, timezone) ->
if typeof locale is "number" and timezone is null
timezone = locale
locale = `undefined`
_strftime fmt, d, locale,
timezone: timezone
strftimeUTC = (fmt, d, locale) ->
_strftime fmt, d, locale,
utc: true
localizedStrftime = (locale) ->
(fmt, d, options) ->
strftime fmt, d, locale, options
# d, locale, and options are optional, but you can't leave
# holes in the argument list. If you pass options you have to pass
# in all the preceding args as well.
#
# options:
# - locale [object] an object with the same structure as DefaultLocale
# - timezone [number] timezone offset in minutes from GMT
_strftime = (fmt, d, locale, options) ->
options = options or {}
# d and locale are optional so check if d is really the locale
if d and not quacksLikeDate(d)
locale = d
d = `undefined`
d = d or new Date()
locale = locale or DefaultLocale
locale.formats = locale.formats or {}
# Hang on to this Unix timestamp because we might mess with it directly
# below.
timestamp = d.getTime()
d = dateToUTC(d) if options.utc or typeof options.timezone is "number"
d = new Date(d.getTime() + (options.timezone * 60000)) if typeof options.timezone is "number"
# Most of the specifiers supported by C's strftime, and some from Ruby.
# Some other syntax extensions from Ruby are supported: %-, %_, and %0
# to pad with nothing, space, or zero (respectively).
fmt.replace /%([-_0]?.)/g, (_, c) ->
mod = undefined
padding = undefined
if c.length is 2
mod = c[0]
# omit padding
if mod is "-"
padding = ""
# pad with space
else if mod is "_"
padding = " "
# pad with zero
else if mod is "0"
padding = "0"
else
# unrecognized, return the format
return _
c = c[1]
switch c
when "A"
locale.days[d.getDay()]
when "a"
locale.shortDays[d.getDay()]
when "B"
locale.months[d.getMonth()]
when "b"
locale.shortMonths[d.getMonth()]
when "C"
pad Math.floor(d.getFullYear() / 100), padding
when "D"
_strftime locale.formats.D or "%m/%d/%y", d, locale
when "d"
pad d.getDate(), padding
when "e"
d.getDate()
when "F"
_strftime locale.formats.F or "%Y-%m-%d", d, locale
when "H"
pad d.getHours(), padding
when "h"
locale.shortMonths[d.getMonth()]
when "I"
pad hours12(d), padding
when "j"
y = new Date(d.getFullYear(), 0, 1)
day = Math.ceil((d.getTime() - y.getTime()) / (1000 * 60 * 60 * 24))
pad day, 3
when "k"
pad d.getHours(), (if padding is null then " " else padding)
when "L"
pad Math.floor(timestamp % 1000), 3
when "l"
pad hours12(d), (if padding is null then " " else padding)
when "M"
pad d.getMinutes(), padding
when "m"
pad d.getMonth() + 1, padding
when "n"
"\n"
when "o"
String(d.getDate()) + ordinal(d.getDate())
when "P"
(if d.getHours() < 12 then locale.am else locale.pm)
when "p"
(if d.getHours() < 12 then locale.AM else locale.PM)
when "R"
_strftime locale.formats.R or "%H:%M", d, locale
when "r"
_strftime locale.formats.r or "%I:%M:%S %p", d, locale
when "S"
pad d.getSeconds(), padding
when "s"
Math.floor timestamp / 1000
when "T"
_strftime locale.formats.T or "%H:%M:%S", d, locale
when "t"
"\t"
when "U"
pad weekNumber(d, "sunday"), padding
when "u"
dayu = d.getDay()
(if dayu is 0 then 7 else dayu) # 1 - 7, Monday is first day of the
# week
when "v"
_strftime locale.formats.v or "%e-%b-%Y", d, locale
when "W"
pad weekNumber(d, "monday"), padding
when "w"
d.getDay() # 0 - 6, Sunday is first day of the
# week
when "Y"
d.getFullYear()
when "y"
yy = String(d.getFullYear())
yy.slice yy.length - 2
when "Z"
if options.utc
return "GMT"
else
tz = d.toString().match(/\((\w+)\)/)
return tz and tz[1] or ""
when "z"
if options.utc
return "+0000"
else
off_ = (if typeof options.timezone is "number" then options.timezone else -d.getTimezoneOffset())
return ((if off_ < 0 then "-" else "+")) + pad(Math.abs(off_ / 60)) + pad(off_ % 60)
else
c
dateToUTC = (d) ->
msDelta = (d.getTimezoneOffset() or 0) * 60000
new Date(d.getTime() + msDelta)
quacksLikeDate = (x) ->
i = 0
n = RequiredDateMethods.length
i = 0
while i < n
return false unless typeof x[RequiredDateMethods[i]] is "function"
++i
true
# Default padding is '0' and default length is 2, both are optional.
pad = (n, padding, length) ->
# pad(n, <length>)
if typeof padding is "number"
length = padding
padding = "0"
# Defaults handle pad(n) and pad(n, <padding>)
padding = "0" if padding is null
length = length or 2
s = String(n)
# padding may be an empty string, don't loop forever if it is
s = padding + s while s.length < length if padding
s
hours12 = (d) ->
hour = d.getHours()
if hour is 0
hour = 12
else hour -= 12 if hour > 12
hour
# Get the ordinal suffix for a number: st, nd, rd, or th
ordinal = (n) ->
i = n % 10
ii = n % 100
return "th" if (ii >= 11 and ii <= 13) or i is 0 or i >= 4
switch i
when 1
"st"
when 2
"nd"
when 3
"rd"
# firstWeekday: 'sunday' or 'monday', default is 'sunday'
#
# Pilfered & ported from Ruby's strftime implementation.
weekNumber = (d, firstWeekday) ->
firstWeekday = firstWeekday or "sunday"
# This works by shifting the weekday back by one day if we
# are treating Monday as the first day of the week.
wday = d.getDay()
if firstWeekday is "monday"
if wday is 0 # Sunday
wday = 6
else
wday--
firstDayOfYear = new Date(d.getFullYear(), 0, 1)
yday = (d - firstDayOfYear) / 86400000
weekNum = (yday + 7 - wday) / 7
Math.floor weekNum
"use strict"
namespace = api.tools
dayNames = [
gettext("Sunday")
gettext("Monday")
gettext("Tuesday")
gettext("Wednesday")
gettext("Thursday")
gettext("Friday")
gettext("Saturday")
]
monthNames = [
gettext("January")
gettext("February")
gettext("March")
gettext("April")
gettext("May")
gettext("June")
gettext("July")
gettext("August")
gettext("September")
gettext("October")
gettext("November")
gettext("December")
]
DefaultLocale =
days: dayNames
shortDays: initialsOf(dayNames)
months: monthNames
shortMonths: initialsOf(monthNames)
AM: "AM"
PM: "PM"
am: "am"
pm: "pm"
namespace.djangoFormat = (format) ->
format.replace /./g, (c) ->
switch c
when "a", "A"
"%p"
when "b", "d", "m", "w", "W", "y", "Y"
"%" + c
when "c"
"%FT%TZ"
when "D"
"%a"
when "e"
"%z"
when "f"
"%I:%M"
when "F"
"%F"
when "h", "g"
"%I"
when "H", "G"
"%H"
when "i"
"%M"
when "I"
""
when "j"
"%d"
when "l"
"%A"
when "L"
""
when "M"
"%b"
when "n"
"%m"
when "N"
"%b"
when "o"
"%W"
when "O"
"%z"
when "P"
"%R %p"
when "r"
"%a, %d %b %Y %T %z"
when "s"
"%S"
when "S"
""
when "t"
""
when "T"
"%Z"
when "u"
"0"
when "U"
""
when "z"
"%j"
when "Z"
"z"
else
c
namespace.strftime = strftime
namespace.strftimeTZ = strftime.strftimeTZ = strftimeTZ
namespace.strftimeUTC = strftime.strftimeUTC = strftimeUTC
namespace.localizedStrftime = strftime.localizedStrftime = localizedStrftime
RequiredDateMethods = [
"getTime"
"getTimezoneOffset"
"getDay"
"getDate"
"getMonth"
"getFullYear"
"getYear"
"getHours"
"getMinutes"
"getSeconds"
]
return

View File

@ -0,0 +1,383 @@
# jshint strict: true
"use strict"
@api = @api ? {}
$ = jQuery
api = @api
api.debug = off
api.doLog = (args...) ->
if api.debug
try
console.log.apply window, args
return
api.cacheTable = {}
api.cache = (cacheName) ->
new Cache(cacheName)
api.cache.clear = (cacheName) ->
if cacheName is `undefined`
api.cacheTable = {}
else
api.cacheTable[cacheName] = {}
return
api.url_for = (path, type='rest') ->
api.doLog 'Url for: ', path, ', ', type
switch type
when "template"
api.config.template_url + path
when "rest"
api.config.base_url + path
else
api.doLog 'Type of url not found: ' + type
throw "Type of url not found: " + type
api.defaultFail = (jqXHR, textStatus, errorThrown) ->
api.doLog jqXHR, ", ", textStatus, ", ", errorThrown
return
api.getJson = (path, options) ->
options = options or {}
success_fnc = options.success or ->
fail_fnc = options.fail or api.defaultFail
url = api.url_for(path)
api.doLog "Ajax GET Json for \"" + url + "\""
$.ajax
url: url
type: options.method or "GET"
dataType: "json"
success: (data) ->
api.doLog "Success on GET \"" + url + "\"."
api.doLog "Received ", data
success_fnc data
return
error: (jqXHR, textStatus, errorThrown) ->
api.doLog "Error on GET \"" + url + "\". ", textStatus, ", ", errorThrown
fail_fnc jqXHR, textStatus, errorThrown
return
beforeSend: (request) ->
request.setRequestHeader api.config.auth_header, api.config.token
return
return
api.putJson = (path, data, options) ->
options = options or {}
success_fnc = options.success or ->
fail_fnc = options.fail or api.defaultFail
url = api.url_for(path)
api.doLog "Ajax PUT Json for \"" + url + "\""
$.ajax
url: url
type: options.method or "PUT"
dataType: "json"
data: JSON.stringify(data)
success: (data) ->
api.doLog "Success on PUT \"" + url + "\"."
api.doLog "Received ", data
success_fnc data
return
error: (jqXHR, textStatus, errorThrown) ->
api.doLog "Error on PUT \"" + url + "\". ", textStatus, ", ", errorThrown
fail_fnc jqXHR, textStatus, errorThrown
return
beforeSend: (request) ->
request.setRequestHeader api.config.auth_header, api.config.token
return
return
api.deleteJson = (path, options) ->
options = options or {}
success_fnc = options.success or ->
fail_fnc = options.fail or api.defaultFail
url = api.url_for(path)
api.doLog "Ajax DELETE Json for \"" + url + "\""
$.ajax
url: url
type: "DELETE"
dataType: "json"
success: (data) ->
api.doLog "Success on DELETE \"" + url + "\"."
api.doLog "Received ", data
success_fnc data
return
error: (jqXHR, textStatus, errorThrown) ->
api.doLog "Error on DELETE \"" + url + "\". ", textStatus, ", ", errorThrown
fail_fnc jqXHR, textStatus, errorThrown
return
beforeSend: (request) ->
request.setRequestHeader api.config.auth_header, api.config.token
return
return
class Cache
constructor: (cacheName) ->
api.cacheTable[cacheName] = api.cacheTable[cacheName] or {}
@name = cacheName
@cache = api.cacheTable[cacheName]
get: (key, not_found_fnc) ->
not_found_fnc = not_found_fnc or ->
null
@cache[key] = not_found_fnc() if @cache[key] is null
@cache[key]
put: (key, value) ->
@cache[key] = value
return
class BasicModelRest
constructor: (path, options) ->
options = options or {}
path = path or ""
# Requests paths
@path = path
@getPath = options.getPath or path
@logPath = options.logPath or path
@putPath = options.putPath or path
@testPath = options.testPath or (path + "/test")
@delPath = options.delPath or path
@typesPath = options.typesPath or (path + "/types")
@guiPath = options.guiPath or (path + "/gui")
@tableInfoPath = options.tableInfoPath or (path + "/tableinfo")
@cache = api.cache("bmr" + path)
_requestPath: (path, options) ->
api.doLog "Requesting ", path, options
options = options or {}
success_fnc = options.success or ->
api.doLog "success function not provided for " + path
return
fail_fnc = options.fail or ->
api.doLog "failFnc not provided for " + path
cacheKey = options.cacheKey or path
api.doLog 'CacheKey ', cacheKey
if path is "."
success_fnc {}
return
if cacheKey isnt "." and @cache.get(cacheKey)
success_fnc @cache.get(cacheKey)
else
$this = @
api.doLog 'Obtaining json for ', path
api.getJson path,
success: (data) ->
$this.cache.put cacheKey, data unless cacheKey is "."
success_fnc data
return
fail: fail_fnc
return
get: (options) ->
options = options or {}
path = @getPath
path += "/" + options.id if options.id
api.doLog "get Options: ", options, path
@_requestPath path,
cacheKey: "."
success: options.success
fail: options.fail
list: (success_fnc, fail_fnc) ->
@get
id: ""
success: success_fnc
fail: fail_fnc
overview: (success_fnc, fail_fnc) ->
@get
id: "overview"
success: success_fnc
fail: fail_fnc
item: (itemId, success_fnc, fail_fnc) ->
@get
id: itemId
success: success_fnc
fail: fail_fnc
getLogs: (itemId, success_fnc, fail_fnc) ->
path = @logPath + "/" + itemId + "/" + "log"
@_requestPath path,
cacheKey: "."
success: success_fnc
fail: fail_fnc
put: (data, options) ->
options = options or {}
path = @putPath
path += "/" + options.id if options.id
api.putJson path, data,
success: options.success
fail: options.fail
return
create: (data, success_fnc, fail_fnc) ->
@put data,
success: success_fnc
fail: fail_fnc
save: (data, success_fnc, fail_fnc) ->
@put data,
id: data.id
success: success_fnc
fail: fail_fnc
test: (type, data, success_fnc, fail_fnc) ->
path = @testPath + "/" + type
api.putJson path, data,
success: success_fnc
fail: fail_fnc
method: "POST"
return
del: (id, success_fnc, fail_fnc) ->
path = @delPath + "/" + id
api.deleteJson path,
success: success_fnc
fail: fail_fnc
return
types: (success_fnc, fail_fnc) ->
@_requestPath @typesPath,
cacheKey: @typesPath
success: success_fnc
gui: (typeName, success_fnc, fail_fnc) ->
path = undefined
if typeName isnt `undefined`
path = [
this.guiPath
typeName
].join("/")
else
path = @guiPath
@_requestPath path,
cacheKey: "."
success: success_fnc
fail: fail_fnc
tableInfo: (success_fnc, fail_fnc) ->
success_fnc = success_fnc or ->
api.doLog "success not provided for tableInfo"
return
path = @tableInfoPath
@_requestPath path,
success: success_fnc
fail: fail_fnc
return
detail: (id, child, options) ->
options = options or {}
new DetailModelRestApi(this, id, child, options)
class DetailModelRestApi extends BasicModelRest
constructor: (parentApi, parentId, model, options) ->
super [
parentApi.path
parentId
model
].join("/")
@moptions = options
create: (data, success_fnc, fail_fnc) ->
@put data,
success: success_fnc
fail: fail_fnc
save: (data, success_fnc, fail_fnc) ->
@put data,
id: data.id
success: success_fnc
fail: fail_fnc
types: (success_fnc, fail_fnc) ->
if @moptions.types
@moptions.types success_fnc, fail_fnc
else
super success_fnc, fail_fnc
return
# Generic "Invoke" method (with no args, if needed, put them on "method" after "?" as normal url would be
invoke: (method, success_fnc, fail_fnc) ->
@base.get
id: method
success: success_fnc
fail: fail_fnc
# Populate api
api.providers = new BasicModelRest("providers")
# all services method used in providers
api.providers.allServices = (success_fnc, fail_fnc) ->
@get
id: "allservices"
success: success_fnc
fail: fail_fnc
api.providers.service = (id, success_fnc, fail_fnc) ->
@get
id: "service/" + id
success: success_fnc
fail: fail_fnc
api.authenticators = new BasicModelRest("authenticators")
# Search method used in authenticators
api.authenticators.search = (id, type, term, success_fnc, fail_fnc) ->
@get
id: id + "/search?type=" + encodeURIComponent(type) + "&term=" + encodeURIComponent(term)
success: success_fnc
fail: fail_fnc
api.osmanagers = new BasicModelRest("osmanagers")
api.transports = new BasicModelRest("transports")
api.networks = new BasicModelRest("networks")
api.servicesPools = new BasicModelRest("servicespools")
api.configuration = new BasicModelRest("config")
api.system = new BasicModelRest("system")
api.system.stats = (type, success_fnc, fail_fnc) ->
@get
id: "stats/" + type
success: success_fnc
fail: fail_fnc
return

View File

@ -128,12 +128,10 @@
// Public attributes
api.debug = false;
}(window.api = window.api || {}, jQuery));
// Cache related
function Cache(cacheName) {
"use strict";
api.cacheTable[cacheName] = api.cacheTable[cacheName] || {};
this.name = cacheName;
@ -142,7 +140,6 @@ function Cache(cacheName) {
Cache.prototype = {
get: function(key, not_found_fnc){
"use strict";
not_found_fnc = not_found_fnc || function() { return undefined; };
if( this.cache[key] === undefined ) {
@ -152,7 +149,6 @@ Cache.prototype = {
},
put: function(key, value) {
"use strict";
this.cache[key] = value;
},
};
@ -163,7 +159,6 @@ Cache.prototype = {
// code :-)
function BasicModelRest(path, options) {
"use strict";
options = options || {};
path = path || '';
// Requests paths
@ -185,7 +180,6 @@ BasicModelRest.prototype = {
// undefined -- > use path as key
// success: success fnc to execute in case of success
_requestPath: function(path, options) {
"use strict";
options = options || {};
var success_fnc = options.success || function(){api.doLog('success function not provided for '+path);};
var fail_fnc = options.fail;
@ -212,7 +206,6 @@ BasicModelRest.prototype = {
}
},
get: function(options) {
"use strict";
options = options || {};
var path = this.getPath;
@ -226,7 +219,6 @@ BasicModelRest.prototype = {
});
},
list: function(success_fnc, fail_fnc) { // This is "almost" an alias for get
"use strict";
return this.get({
id: '',
success: success_fnc,
@ -234,7 +226,6 @@ BasicModelRest.prototype = {
});
},
overview: function(success_fnc, fail_fnc) {
"use strict";
return this.get({
id: 'overview',
success: success_fnc,
@ -242,7 +233,6 @@ BasicModelRest.prototype = {
});
},
item: function(itemId, success_fnc, fail_fnc) {
"use strict";
return this.get({
id: itemId,
success: success_fnc,
@ -254,7 +244,6 @@ BasicModelRest.prototype = {
// Log methods
// -------------
getLogs: function(itemId, success_fnc, fail_fnc) {
"use strict";
var path = this.logPath + '/' + itemId + '/' + 'log';
return this._requestPath(path, {
cacheKey: '.',
@ -269,7 +258,6 @@ BasicModelRest.prototype = {
// -------------
put: function(data, options) {
"use strict";
options = options || {};
var path = this.putPath;
@ -282,7 +270,6 @@ BasicModelRest.prototype = {
});
},
create: function(data, success_fnc, fail_fnc) {
"use strict";
return this.put(data, {
success: success_fnc,
@ -290,7 +277,6 @@ BasicModelRest.prototype = {
});
},
save: function(data, success_fnc, fail_fnc) {
"use strict";
return this.put(data, {
id: data.id,
@ -301,8 +287,6 @@ BasicModelRest.prototype = {
// Testing
test: function(type, data, success_fnc, fail_fnc) {
"use strict";
var path = this.testPath + '/' + type;
api.putJson(path, data, {
@ -315,8 +299,6 @@ BasicModelRest.prototype = {
// Delete
// --------------
del: function(id, success_fnc, fail_fnc) {
"use strict";
var path = this.delPath + '/' + id;
api.deleteJson(path, {
@ -329,7 +311,6 @@ BasicModelRest.prototype = {
// Types methods
// --------------
types : function(success_fnc, fail_fnc) {
"use strict";
return this._requestPath(this.typesPath, {
cacheKey: this.typesPath,
success: success_fnc,
@ -340,7 +321,6 @@ BasicModelRest.prototype = {
// name: Name of the field
// value: value of the field (selected element in choice, text for inputs, etc....)
// gui: Description of the field (type, value or values, defvalue, ....
"use strict";
var path;
if( typeName !== undefined ) {
@ -355,7 +335,6 @@ BasicModelRest.prototype = {
});
},
tableInfo : function(success_fnc, fail_fnc) {
"use strict";
success_fnc = success_fnc || function(){api.doLog('success not provided for tableInfo');};
var path = this.tableInfoPath;
@ -366,7 +345,6 @@ BasicModelRest.prototype = {
},
detail: function(id, child, options) {
"use strict";
options = options || {};
return new DetailModelRestApi(this, id, child, options);
}
@ -375,7 +353,6 @@ BasicModelRest.prototype = {
// For REST of type /auth/[id]/users, /services/[id]/users, ...
function DetailModelRestApi(parentApi, parentId, model, options) {
"use strict";
this.options = options;
this.base = new BasicModelRest([parentApi.path, parentId, model].join('/'));
}
@ -383,35 +360,28 @@ function DetailModelRestApi(parentApi, parentId, model, options) {
DetailModelRestApi.prototype = {
// Generates a basic model with fixed methods for "detail" models
get: function(success_fnc, fail_fnc) {
"use strict";
return this.base.get(success_fnc, fail_fnc);
},
list: function(success_fnc, fail_fnc) { // This is "almost" an alias for get
"use strict";
return this.base.list(success_fnc, fail_fnc);
},
overview: function(success_fnc, fail_fnc) {
"use strict";
return this.base.overview(success_fnc, fail_fnc);
},
item: function(itemId, success_fnc, fail_fnc) {
"use strict";
return this.base.item(itemId, success_fnc, fail_fnc);
},
// -------------
// Log methods
// -------------
getLogs: function(itemId, success_fnc, fail_fnc) {
"use strict";
return this.base.getLogs(itemId, success_fnc, fail_fnc);
},
// Put methods
put: function(data, options) {
"use strict";
return this.base.put(data, options);
},
create: function(data, success_fnc, fail_fnc) {
"use strict";
return this.put(data, {
success: success_fnc,
@ -419,8 +389,6 @@ DetailModelRestApi.prototype = {
});
},
save: function(data, success_fnc, fail_fnc) {
"use strict";
return this.put(data, {
id: data.id,
success: success_fnc,
@ -429,24 +397,19 @@ DetailModelRestApi.prototype = {
},
// Testing
test: function(type, data, success_fnc, fail_fnc) {
"use strict";
return this.base.test(type, data, success_fnc, fail_fnc);
},
// --------------
// Delete
// --------------
del: function(id, success_fnc, fail_fnc) {
"use strict";
return this.base.del(id, success_fnc, fail_fnc);
},
tableInfo: function(success_fnc, fail_fnc) {
"use strict";
return this.base.tableInfo(success_fnc, fail_fnc);
},
types: function(success_fnc, fail_fnc) {
"use strict";
if( this.options.types ) {
this.options.types(success_fnc, fail_fnc);
} else {
@ -454,13 +417,11 @@ DetailModelRestApi.prototype = {
}
},
gui: function(typeName, success_fnc, fail_fnc) { // Typename can be "undefined" to request MAIN gui (it it exists ofc..)
"use strict";
return this.base.gui(typeName, success_fnc, fail_fnc);
},
// Generic "Invoke" method (with no args, if needed, put them on "method" after "?" as normal url would be
invoke: function(method, success_fnc, fail_fnc) {
"use strict";
return this.base.get({
id: method,
success: success_fnc,
@ -475,7 +436,6 @@ DetailModelRestApi.prototype = {
api.providers = new BasicModelRest('providers');
// all services method used in providers
api.providers.allServices = function(success_fnc, fail_fnc) {
"use strict";
return this.get({
id: 'allservices',
success: success_fnc,
@ -484,7 +444,6 @@ api.providers.allServices = function(success_fnc, fail_fnc) {
};
api.providers.service = function(id, success_fnc, fail_fnc) {
"use strict";
return this.get({
id: 'service/' + id,
success: success_fnc,
@ -496,7 +455,6 @@ api.providers.service = function(id, success_fnc, fail_fnc) {
api.authenticators = new BasicModelRest('authenticators');
// Search method used in authenticators
api.authenticators.search = function(id, type, term, success_fnc, fail_fnc) {
"use strict";
return this.get({
id: id + '/search?type=' + encodeURIComponent(type) + '&term=' + encodeURIComponent(term),
success: success_fnc,
@ -513,12 +471,10 @@ api.configuration = new BasicModelRest('config');
api.system = new BasicModelRest('system');
api.system.stats = function(type, success_fnc, fail_fnc) {
"use strict";
return this.get({
id: 'stats/' + type,
success: success_fnc,
fail: fail_fnc
});
};
}(window.api = window.api || {}, jQuery));

View File

@ -0,0 +1,398 @@
# jshint strict: true
gui.authenticators = new GuiElement(api.authenticators, "auth")
gui.authenticators.link = (event) ->
"use strict"
# Button definition to trigger "Test" action
testButton = testButton:
text: gettext("Test authenticator")
css: "btn-info"
# Clears the log of the detail, in this case, the log of "users"
# Memory saver :-)
detailLogTable = null
clearDetailLog = ->
if detailLogTable
$tbl = $(detailLogTable).dataTable()
$tbl.fnClearTable()
$tbl.fnDestroy()
$("#user-log-placeholder").empty()
detailLogTable = null
return
# Clears the details
# Memory saver :-)
prevTables = []
clearDetails = ->
$.each prevTables, (undefined_, tbl) ->
$tbl = $(tbl).dataTable()
$tbl.fnClearTable()
$tbl.fnDestroy()
return
clearDetailLog()
$("#users-placeholder").empty()
$("#groups-placeholder").empty()
$("#logs-placeholder").empty()
$("#detail-placeholder").addClass "hidden"
prevTables = []
return
# Search button event generator for user/group
searchForm = (parentModalId, type, id, title, searchLabel, resultsLabel) ->
errorModal = gui.failRequestModalFnc(gettext("Search error"))
srcSelector = parentModalId + " input[name=\"name\"]"
$(parentModalId + " .button-search").on "click", ->
api.templates.get "search", (tmpl) -> # Get form template
modalId = gui.launchModal(title, api.templates.evaluate(tmpl,
search_label: searchLabel
results_label: resultsLabel
),
actionButton: "<button type=\"button\" class=\"btn btn-success button-accept\">" + gettext("Accept") + "</button>"
)
$searchInput = $(modalId + " input[name=\"search\"]")
$select = $(modalId + " select[name=\"results\"]")
$searchButton = $(modalId + " .button-do-search")
$saveButton = $(modalId + " .button-accept")
$searchInput.val $(srcSelector).val()
$saveButton.on "click", ->
value = $select.val()
if value
$(srcSelector).val value
$(modalId).modal "hide"
return
$searchButton.on "click", ->
$searchButton.addClass "disabled"
term = $searchInput.val()
api.authenticators.search id, type, term, ((data) ->
$searchButton.removeClass "disabled"
$select.empty()
gui.doLog data
$.each data, (undefined_, value) ->
$select.append "<option value=\"" + value.id + "\">" + value.id + " (" + value.name + ")</option>"
return
return
), (jqXHR, textStatus, errorThrown) ->
$searchButton.removeClass "disabled"
errorModal jqXHR, textStatus, errorThrown
return
return
$(modalId + " form").submit (event) ->
event.preventDefault()
$searchButton.click()
return
$searchButton.click() if $searchInput.val() isnt ""
return
return
return
api.templates.get "authenticators", (tmpl) ->
gui.clearWorkspace()
gui.appendToWorkspace api.templates.evaluate(tmpl,
auths: "auths-placeholder"
users: "users-placeholder"
users_log: "users-log-placeholder"
groups: "groups-placeholder"
logs: "logs-placeholder"
)
gui.setLinksEvents()
# Append tabs click events
$(".bottom_tabs").on "click", (event) ->
gui.doLog event.target
setTimeout (->
$($(event.target).attr("href") + " span.fa-refresh").click()
return
), 10
return
tableId = gui.authenticators.table(
container: "auths-placeholder"
rowSelect: "single"
buttons: [
"new"
"edit"
"delete"
"xls"
]
onRowDeselect: ->
clearDetails()
return
onRowSelect: (selected) ->
# We can have lots of users, so memory can grow up rapidly if we do not keep thins clena
# To do so, we empty previous table contents before storing new table contents
# Anyway, TabletTools will keep "leaking" memory, but we can handle a little "leak" that will be fixed as soon as we change the section
clearDetails()
$("#detail-placeholder").removeClass "hidden"
gui.tools.blockUI()
id = selected[0].id
type = gui.authenticators.types[selected[0].type]
gui.doLog "Type", type
user = new GuiElement(api.authenticators.detail(id, "users"), "users")
group = new GuiElement(api.authenticators.detail(id, "groups"), "groups")
grpTable = group.table(
container: "groups-placeholder"
rowSelect: "single"
buttons: [
"new"
"edit"
"delete"
"xls"
]
onLoad: (k) ->
gui.tools.unblockUI()
return
onEdit: (value, event, table, refreshFnc) ->
exec = (groups_all) ->
gui.tools.blockUI()
api.templates.get "group", (tmpl) -> # Get form template
group.rest.item value.id, (item) -> # Get item to edit
# Creates modal
modalId = gui.launchModal(gettext("Edit group") + " <b>" + item.name + "</b>", api.templates.evaluate(tmpl,
id: item.id
type: item.type
groupname: item.name
groupname_label: type.groupNameLabel
comments: item.comments
state: item.state
external: type.isExternal
canSearchGroups: type.canSearchGroups
groups: item.groups
groups_all: groups_all
))
gui.tools.applyCustoms modalId
gui.tools.unblockUI()
$(modalId + " .button-accept").click ->
fields = gui.forms.read(modalId)
gui.doLog "Fields", fields
group.rest.save fields, ((data) -> # Success on put
$(modalId).modal "hide"
refreshFnc()
gui.notify gettext("Group saved"), "success"
return
), gui.failRequestModalFnc("Error saving group", true)
return
return
return
return
if value.type is "meta"
# Meta will get all groups
group.rest.overview (groups) ->
exec groups
return
else
exec()
return
onNew: (t, table, refreshFnc) ->
exec = (groups_all) ->
gui.tools.blockUI()
api.templates.get "group", (tmpl) -> # Get form template
# Creates modal
modalId = gui.launchModal(gettext("New group"), api.templates.evaluate(tmpl,
type: t
groupname_label: type.groupNameLabel
external: type.isExternal
canSearchGroups: type.canSearchGroups
groups: []
groups_all: groups_all
))
gui.tools.unblockUI()
gui.tools.applyCustoms modalId
searchForm modalId, "group", id, gettext("Search groups"), gettext("Group"), gettext("Groups found") # Enable search button click, if it exist ofc
$(modalId + " .button-accept").click ->
fields = gui.forms.read(modalId)
gui.doLog "Fields", fields
group.rest.create fields, ((data) -> # Success on put
$(modalId).modal "hide"
refreshFnc()
gui.notify gettext("Group saved"), "success"
return
), gui.failRequestModalFnc(gettext("Group saving error"), true)
return
return
return
if t is "meta"
# Meta will get all groups
group.rest.overview (groups) ->
exec groups
return
else
exec()
return
onDelete: gui.methods.del(group, gettext("Delete group"), gettext("Group deletion error"))
)
tmpLogTable = null
# New button will only be shown on authenticators that can create new users
usrButtons = [
"edit"
"delete"
"xls"
]
usrButtons = ["new"].concat(usrButtons) if type.canCreateUsers # New is first button
usrTable = user.table(
container: "users-placeholder"
rowSelect: "single"
onRowSelect: (uselected) ->
gui.tools.blockUI()
uId = uselected[0].id
clearDetailLog()
tmpLogTable = user.logTable(uId,
container: "users-log-placeholder"
onLoad: ->
detailLogTable = tmpLogTable
gui.tools.unblockUI()
return
)
return
onRowDeselect: ->
clearDetailLog()
return
buttons: usrButtons
deferedRender: true # Use defered rendering for users, this table can be "huge"
scrollToTable: false
onLoad: (k) ->
gui.tools.unblockUI()
return
onRefresh: ->
$("#users-log-placeholder").empty() # Remove logs on detail refresh
return
onEdit: (value, event, table, refreshFnc) ->
password = "#æð~¬ŋ@æß”¢€~½¬@#~þ¬@|" # Garbage for password (to detect change)
gui.tools.blockUI()
api.templates.get "user", (tmpl) -> # Get form template
group.rest.overview (groups) -> # Get groups
user.rest.item value.id, (item) -> # Get item to edit
# Creates modal
modalId = gui.launchModal(gettext("Edit user") + " <b>" + value.name + "</b>", api.templates.evaluate(tmpl,
id: item.id
username: item.name
username_label: type.userNameLabel
realname: item.real_name
comments: item.comments
state: item.state
staff_member: item.staff_member
is_admin: item.is_admin
needs_password: type.needsPassword
password: (if type.needsPassword then password else `undefined`)
password_label: type.passwordLabel
groups_all: groups
groups: item.groups
external: type.isExternal
canSearchUsers: type.canSearchUsers
))
gui.tools.applyCustoms modalId
gui.tools.unblockUI()
$(modalId + " .button-accept").click ->
fields = gui.forms.read(modalId)
# If needs password, and password has changed
gui.doLog "passwords", type.needsPassword, password, fields.password
delete fields.password if fields.password is password if type.needsPassword
gui.doLog "Fields", fields
user.rest.save fields, ((data) -> # Success on put
$(modalId).modal "hide"
refreshFnc()
gui.notify gettext("User saved"), "success"
return
), gui.failRequestModalFnc(gettext("User saving error"), true)
return
return
return
return
return
onNew: (undefined_, table, refreshFnc) ->
gui.tools.blockUI()
api.templates.get "user", (tmpl) -> # Get form template
group.rest.overview (groups) -> # Get groups
# Creates modal
modalId = gui.launchModal(gettext("New user"), api.templates.evaluate(tmpl,
username_label: type.userNameLabel
needs_password: type.needsPassword
password_label: type.passwordLabel
groups_all: groups
groups: []
external: type.isExternal
canSearchUsers: type.canSearchUsers
))
gui.tools.applyCustoms modalId
gui.tools.unblockUI()
searchForm modalId, "user", id, gettext("Search users"), gettext("User"), gettext("Users found") # Enable search button click, if it exist ofc
$(modalId + " .button-accept").click ->
fields = gui.forms.read(modalId)
# If needs password, and password has changed
gui.doLog "Fields", fields
user.rest.create fields, ((data) -> # Success on put
$(modalId).modal "hide"
refreshFnc()
gui.notify gettext("User saved"), "success"
return
), gui.failRequestModalFnc(gettext("User saving error"), true)
return
return
return
return
onDelete: gui.methods.del(user, gettext("Delete user"), gettext("User deletion error"))
)
logTable = gui.authenticators.logTable(id,
container: "logs-placeholder"
)
# So we can destroy the tables beforing adding new ones
prevTables.push grpTable
prevTables.push usrTable
prevTables.push logTable
false
onRefresh: ->
$("#users-placeholder").empty() # Remove detail on parent refresh
return
onNew: gui.methods.typedNew(gui.authenticators, gettext("New authenticator"), gettext("Authenticator creation error"), testButton)
onEdit: gui.methods.typedEdit(gui.authenticators, gettext("Edit authenticator"), gettext("Authenticator saving error"), testButton)
onDelete: gui.methods.del(gui.authenticators, gettext("Delete authenticator"), gettext("Authenticator deletion error"))
)
return
false

View File

@ -0,0 +1,48 @@
gui.configuration = new BasicGuiElement("Clear cache")
gui.configuration.link = ->
"use strict"
api.templates.get "configuration", (tmpl) ->
api.configuration.overview ((data) ->
gui.doLog data
gui.clearWorkspace()
gui.appendToWorkspace api.templates.evaluate(tmpl,
config: data
)
gui.setLinksEvents()
$("#form_config .form-control").each (i, element) ->
$(element).attr "data-val", $(element).val()
return
# Add handlers to buttons
$("#form_config .button-undo").on "click", (event) ->
fld = $(this).attr("data-fld")
gui.doLog fld, $("#" + fld).val()
$("#" + fld).val $("#" + fld).attr("data-val")
return
$("#form_config .button-save").on "click", (event) ->
cfg = {}
$("#form_config .form-control").each (i, element) ->
$element = $(element)
unless $element.attr("data-val") is $element.val()
section = $element.attr("data-section")
key = $element.attr("data-key")
cfg[section] = {} if cfg[section] is `undefined`
cfg[section][key] = value: $element.val()
return
gui.doLog cfg
unless $.isEmptyObject(cfg)
api.configuration.save cfg, (->
gui.showDashboard()
gui.notify gettext("Configuration saved"), "success"
return
), gui.failRequestModalFnc
return
return
), gui.failRequestModalFnc
return
return

View File

@ -0,0 +1,42 @@
# jshint strict: true
gui.connectivity =
transports: new GuiElement(api.transports, "trans")
networks: new GuiElement(api.networks, "nets")
gui.connectivity.link = (event) ->
"use strict"
api.templates.get "connectivity", (tmpl) ->
gui.clearWorkspace()
gui.appendToWorkspace api.templates.evaluate(tmpl,
transports: "transports-placeholder"
networks: "networks-placeholder"
)
gui.connectivity.transports.table
rowSelect: "single"
container: "transports-placeholder"
buttons: [
"new"
"edit"
"delete"
"xls"
]
onNew: gui.methods.typedNew(gui.connectivity.transports, gettext("New transport"), gettext("Transport creation error"))
onEdit: gui.methods.typedEdit(gui.connectivity.transports, gettext("Edit transport"), gettext("Transport saving error"))
onDelete: gui.methods.del(gui.connectivity.transports, gettext("Delete transport"), gettext("Transport deletion error"))
gui.connectivity.networks.table
rowSelect: "single"
container: "networks-placeholder"
buttons: [
"new"
"edit"
"delete"
"xls"
]
onNew: gui.methods.typedNew(gui.connectivity.networks, gettext("New network"), gettext("Network creation error"))
onEdit: gui.methods.typedEdit(gui.connectivity.networks, gettext("Edit network"), gettext("Network saving error"))
onDelete: gui.methods.del(gui.connectivity.networks, gettext("Delete network"), gettext("Network deletion error"))
return
return

View File

@ -0,0 +1,42 @@
gui.dashboard = new BasicGuiElement("Dashboard")
gui.dashboard.link = (event) ->
"use strict"
gui.clearWorkspace()
api.templates.get "dashboard", (tmpl) ->
api.system.overview (data) ->
gui.doLog "enter dashboard"
gui.appendToWorkspace api.templates.evaluate(tmpl,
users: data.users
services: data.services
user_services: data.user_services
restrained_services_pools: data.restrained_services_pools
)
gui.setLinksEvents()
$.each [
"assigned"
"inuse"
], (index, stat) ->
api.system.stats stat, (data) ->
d = []
$.each data, (index, value) ->
d.push [
value.stamp * 1000
value.value
]
return
gui.doLog "Data", d
$.plot "#placeholder-" + stat + "-chart", [d],
xaxis:
mode: "time"
timeformat: api.tools.djangoFormat(django.formats.SHORT_DATE_FORMAT)
return
return
return
return
return

View File

@ -0,0 +1,27 @@
#------------------------
# Os managers
#------------------------
gui.osmanagers = new GuiElement(api.osmanagers, "osm")
gui.osmanagers.link = (event) ->
"use strict"
api.templates.get "osmanagers", (tmpl) ->
gui.clearWorkspace()
gui.appendToWorkspace api.templates.evaluate(tmpl,
osmanagers: "osmanagers-placeholder"
)
gui.osmanagers.table
container: "osmanagers-placeholder"
rowSelect: "single"
buttons: [
"new"
"edit"
"delete"
"xls"
]
onNew: gui.methods.typedNew(gui.osmanagers, gettext("New OSManager"), gettext("OSManager creation error"))
onEdit: gui.methods.typedEdit(gui.osmanagers, gettext("Edit OSManager"), gettext("OSManager saving error"))
onDelete: gui.methods.del(gui.osmanagers, gettext("Delete OSManager"), gettext("OSManager deletion error"))
return
return

View File

@ -0,0 +1,142 @@
# jshint strict: true
gui.providers = new GuiElement(api.providers, "provi")
gui.providers.link = (event) ->
"use strict"
# Button definition to trigger "Test" action
testButton = testButton:
text: gettext("Test")
css: "btn-info"
detailLogTable = null
clearDetailLog = ->
if detailLogTable
$tbl = $(detailLogTable).dataTable()
$tbl.fnClearTable()
$tbl.fnDestroy()
$("#services-log-placeholder").empty()
detailLogTable = `undefined`
return
prevTables = []
clearDetails = ->
gui.doLog "Clearing details"
$.each prevTables, (undefined_, tbl) ->
$tbl = $(tbl).dataTable()
$tbl.fnClearTable()
$tbl.fnDestroy()
return
clearDetailLog()
prevTables = []
$("#services-placeholder").empty()
$("#logs-placeholder").empty()
$("#detail-placeholder").addClass "hidden"
return
api.templates.get "providers", (tmpl) ->
gui.clearWorkspace()
gui.appendToWorkspace api.templates.evaluate(tmpl,
providers: "providers-placeholder"
services: "services-placeholder"
services_log: "services-log-placeholder"
logs: "logs-placeholder"
)
gui.setLinksEvents()
# Append tabs click events
$(".bottom_tabs").on "click", (event) ->
gui.doLog event.target
setTimeout (->
$($(event.target).attr("href") + " span.fa-refresh").click()
return
), 10
return
tableId = gui.providers.table(
container: "providers-placeholder"
rowSelect: "single"
onCheck: (check, items) -> # Check if item can be deleted
#if( check == 'delete' ) {
# for( var i in items ) {
# if( items[i].services_count > 0)
# return false;
# }
# return true;
# }
true
onRowDeselect: ->
clearDetails()
return
onRowSelect: (selected) ->
gui.tools.blockUI()
clearDetails()
$("#detail-placeholder").removeClass "hidden"
id = selected[0].id
# Giving the name compossed with type, will ensure that only styles will be reattached once
services = new GuiElement(api.providers.detail(id, "services"), "services-" + selected[0].type)
tmpLogTable = null
servicesTable = services.table(
container: "services-placeholder"
rowSelect: "single"
onRowSelect: (sselected) ->
gui.tools.blockUI()
sId = sselected[0].id
clearDetailLog()
tmpLogTable = services.logTable(sId,
container: "services-log-placeholder"
onLoad: ->
detailLogTable = tmpLogTable
gui.tools.unblockUI()
return
)
return
onRowDeselect: ->
clearDetailLog()
return
onCheck: (check, items) ->
if check is "delete"
for i of items
return false if items[i].deployed_services_count > 0
return true
true
buttons: [
"new"
"edit"
"delete"
"xls"
]
onEdit: gui.methods.typedEdit(services, gettext("Edit service"), gettext("Service creation error"), testButton)
onNew: gui.methods.typedNew(services, gettext("New service"), gettext("Service saving error"), testButton)
onDelete: gui.methods.del(services, gettext("Delete service"), gettext("Service deletion error"), testButton)
scrollToTable: false
onLoad: (k) ->
gui.tools.unblockUI()
return
)
logTable = gui.providers.logTable(id,
container: "logs-placeholder"
)
prevTables.push servicesTable
prevTables.push logTable
return
buttons: [
"new"
"edit"
"delete"
"xls"
]
onNew: gui.methods.typedNew(gui.providers, gettext("New services provider"), gettext("Services provider creation error"), testButton)
onEdit: gui.methods.typedEdit(gui.providers, gettext("Edit services provider"), gettext("Services Provider saving error"), testButton)
onDelete: gui.methods.del(gui.providers, gettext("Delete services provider"), gettext("Services Provider deletion error"))
)
return
false

View File

@ -0,0 +1,488 @@
# jshint strict: true
gui.servicesPools = new GuiElement(api.servicesPools, "servicespools")
gui.servicesPools.link = (event) ->
"use strict"
gui.clearWorkspace()
# Clears the details
# Memory saver :-)
prevTables = []
clearDetails = ->
$.each prevTables, (undefined_, tbl) ->
$tbl = $(tbl).dataTable()
$tbl.fnClearTable()
$tbl.fnDestroy()
return
$("#assigned-services-placeholder_tbl").empty()
$("#assigned-services-placeholder_log").empty()
$("#cache-placeholder_tbl").empty()
$("#cache-placeholder_log").empty()
$("#transports-placeholder").empty()
$("#groups-placeholder").empty()
$("#logs-placeholder").empty()
$("#detail-placeholder").addClass "hidden"
prevTables = []
return
# On change base service
preFnc = (formId) ->
$fld = $(formId + " [name=\"service_id\"]")
$osmFld = $(formId + " [name=\"osmanager_id\"]")
selectors = []
$.each [
"initial_srvs"
"cache_l1_srvs"
"cache_l2_srvs"
"max_srvs"
], (index, value) ->
selectors.push formId + " [name=\"" + value + "\"]"
return
$cacheFlds = $(selectors.join(","))
$cacheL2Fld = $(formId + " [name=\"cache_l2_srvs\"]")
$publishOnSaveFld = $(formId + " [name=\"publish_on_save\"]")
$fld.on "change", (event) ->
unless $fld.val() is -1
api.providers.service $fld.val(), (data) ->
gui.doLog "Onchange", data
if data.info.needs_manager is false
$osmFld.prop "disabled", "disabled"
else
$osmFld.prop "disabled", false
if data.info.uses_cache is false
$cacheFlds.prop "disabled", "disabled"
else
$cacheFlds.prop "disabled", false
if data.info.uses_cache_l2 is false
$cacheL2Fld.prop "disabled", "disabled"
else
$cacheL2Fld.prop "disabled", false
if data.info.needs_publication is false
$publishOnSaveFld.bootstrapSwitch "setDisabled", true
else
$publishOnSaveFld.bootstrapSwitch "setDisabled", false
$osmFld.selectpicker "refresh" if $osmFld.hasClass("selectpicker")
return
return
return
# Fill "State" for cached and assigned services
fillState = (data) ->
$.each data, (index, value) ->
if value.state is "U"
if value.os_state isnt "" and value.os_state isnt "U"
value.state = gettext("Waiting OS")
else
value.state = gettext("Ready")
return
if value.state is "R"
value.state = gettext("Waiting for removal")
return
if value.state is "M"
value.state = gettext("Removing")
return
if value.state is "S"
value.state = gettext("Removed")
return
if value.state is "E"
value.state = gettext("Error")
return
if value.state is "P"
value.state = gettext("Generating")
return
value.state = gettext("Unknown")
return
return
# Fills up the list of available services
api.providers.allServices (services) ->
availableServices = {}
$.each services, (undefined_, service) ->
availableServices[service.id] = service
return
gui.doLog "Available services", availableServices
api.templates.get "services_pool", (tmpl) ->
gui.appendToWorkspace api.templates.evaluate(tmpl,
deployed_services: "deployed-services-placeholder"
assigned_services: "assigned-services-placeholder"
cache: "cache-placeholder"
groups: "groups-placeholder"
transports: "transports-placeholder"
publications: "publications-placeholder"
logs: "logs-placeholder"
)
gui.setLinksEvents()
# Append tabs click events
$(".bottom_tabs").on "click", (event) ->
gui.doLog event.target
setTimeout (->
$($(event.target).attr("href") + " span.fa-refresh").click()
return
), 10
return
#
# * Services pools part
#
servicesPoolsTable = gui.servicesPools.table(
container: "deployed-services-placeholder"
rowSelect: "single"
buttons: [
"new"
"edit"
"delete"
"xls"
]
onRowDeselect: ->
clearDetails()
return
onRowSelect: (selected) ->
servPool = selected[0]
gui.doLog "Selected services pool", servPool
clearDetails()
service = null
try
service = availableServices[servPool.service_id]
catch e
gui.doLog "Exception on rowSelect", e
gui.notify "Service pool " + gettext("error"), "danger"
return
if service isnt null
$("#detail-placeholder").removeClass "hidden"
else
$("#detail-placeholder").addClass "hidden"
return
#
# * Cache Part
#
cachedItems = null
# If service does not supports cache, do not show it
# Shows/hides cache
if service.info.uses_cache or service.info.uses_cache_l2
$("#cache-placeholder_tab").removeClass "hidden"
cachedItems = new GuiElement(api.servicesPools.detail(servPool.id, "cache"), "cache")
# Cached items table
prevCacheLogTbl = null
cachedItemsTable = cachedItems.table(
container: "cache-placeholder_tbl"
buttons: [
"delete"
"xls"
]
rowSelect: "single"
onData: (data) ->
fillState data
return
onRowSelect: (selected) ->
cached = selected[0]
if prevCacheLogTbl
$tbl = $(prevCacheLogTbl).dataTable()
$tbl.fnClearTable()
$tbl.fnDestroy()
prevCacheLogTbl = cachedItems.logTable(cached.id,
container: "cache-placeholder_log"
)
return
onDelete: gui.methods.del(cachedItems, gettext("Remove Cache element"), gettext("Deletion error"))
)
prevTables.push cachedItemsTable
else
$("#cache-placeholder_tab").addClass "hidden"
#
# * Groups part
#
groups = null
# Shows/hides groups
if service.info.must_assign_manually is false
$("#groups-placeholder_tab").removeClass "hidden"
groups = new GuiElement(api.servicesPools.detail(servPool.id, "groups"), "groups")
# Groups items table
groupsTable = groups.table(
container: "groups-placeholder"
rowSelect: "single"
buttons: [
"new"
"delete"
"xls"
]
onNew: (value, table, refreshFnc) ->
api.templates.get "pool_add_group", (tmpl) ->
api.authenticators.overview (data) ->
modalId = gui.launchModal(gettext("Add group"), api.templates.evaluate(tmpl,
auths: data
))
$(modalId + " #id_auth_select").on "change", (event) ->
auth = $(modalId + " #id_auth_select").val()
api.authenticators.detail(auth, "groups").overview (data) ->
$select = $(modalId + " #id_group_select")
$select.empty()
$.each data, (undefined_, value) ->
$select.append "<option value=\"" + value.id + "\">" + value.name + "</option>"
return
# Refresh selectpicker if item is such
$select.selectpicker "refresh" if $select.hasClass("selectpicker")
return
return
$(modalId + " .button-accept").on "click", (event) ->
auth = $(modalId + " #id_auth_select").val()
group = $(modalId + " #id_group_select").val()
if auth is -1 or group is -1
gui.notify gettext("You must provide authenticator and group"), "danger"
else # Save & close modal
groups.rest.create
id: group
, (data) ->
$(modalId).modal "hide"
refreshFnc()
return
return
# Makes form "beautyfull" :-)
gui.tools.applyCustoms modalId
return
return
return
onDelete: gui.methods.del(groups, gettext("Remove group"), gettext("Group removal error"))
onData: (data) ->
$.each data, (undefined_, value) ->
value.group_name = "<b>" + value.auth_name + "</b>\\" + value.name
return
return
)
prevTables.push groupsTable
else
$("#groups-placeholder_tab").addClass "hidden"
#
# * Assigned services part
#
prevAssignedLogTbl = null
assignedServices = new GuiElement(api.servicesPools.detail(servPool.id, "services"), "services")
assignedServicesTable = assignedServices.table(
container: "assigned-services-placeholder_tbl"
rowSelect: "single"
buttons: (if service.info.must_assign_manually then [
"new"
"delete"
"xls"
] else [
"delete"
"xls"
])
onRowSelect: (selected) ->
service = selected[0]
if prevAssignedLogTbl
$tbl = $(prevAssignedLogTbl).dataTable()
$tbl.fnClearTable()
$tbl.fnDestroy()
prevAssignedLogTbl = assignedServices.logTable(service.id,
container: "assigned-services-placeholder_log"
)
return
onDelete: gui.methods.del(assignedServices, gettext("Remove Assigned service"), gettext("Deletion error"))
)
# Log of assigned services (right under assigned services)
prevTables.push assignedServicesTable
#
# * Transports part
#
transports = new GuiElement(api.servicesPools.detail(servPool.id, "transports"), "transports")
# Transports items table
transportsTable = transports.table(
container: "transports-placeholder"
rowSelect: "single"
buttons: [
"new"
"delete"
"xls"
]
onNew: (value, table, refreshFnc) ->
api.templates.get "pool_add_transport", (tmpl) ->
api.transports.overview (data) ->
modalId = gui.launchModal(gettext("Add transport"), api.templates.evaluate(tmpl,
transports: data
))
$(modalId + " .button-accept").on "click", (event) ->
transport = $(modalId + " #id_transport_select").val()
if transport is -1
gui.notify gettext("You must provide a transport"), "danger"
else # Save & close modal
transports.rest.create
id: transport
, (data) ->
$(modalId).modal "hide"
refreshFnc()
return
return
# Makes form "beautyfull" :-)
gui.tools.applyCustoms modalId
return
return
return
onDelete: gui.methods.del(transports, gettext("Remove transport"), gettext("Transport removal error"))
onData: (data) ->
$.each data, (undefined_, value) ->
style = "display:inline-block; background: url(data:image/png;base64," + value.type.icon + "); " + "width: 16px; height: 16px; vertical-align: middle;"
value.trans_type = value.type.name
value.name = "<span style=\"" + style + "\"></span> " + value.name
return
return
)
prevTables.push transportsTable
#
# * Publications part
#
publications = null
if service.info.needs_publication
$("#publications-placeholder_tab").removeClass "hidden"
pubApi = api.servicesPools.detail(servPool.id, "publications")
publications = new GuiElement(pubApi, "publications")
# Publications table
publicationsTable = publications.table(
container: "publications-placeholder"
rowSelect: "single"
buttons: [
"new"
{
text: gettext("Cancel")
css: "disabled"
click: (val, value, btn, tbl, refreshFnc) ->
gui.promptModal gettext("Publish"), gettext("Cancel publication"),
onYes: ->
pubApi.invoke val.id + "/cancel", ->
refreshFnc()
return
return
return
select: (val, value, btn, tbl, refreshFnc) ->
unless val
$(btn).removeClass("btn3d-info").addClass "disabled"
return
# Waiting for publication, Preparing or running
$(btn).removeClass("disabled").addClass "btn3d-info" if [
"P"
"W"
"L"
].indexOf(val.state) > 0
return
}
"xls"
]
onNew: (action, tbl, refreshFnc) ->
gui.promptModal gettext("Publish"), gettext("Launch new publication?"),
onYes: ->
pubApi.invoke "publish", (->
refreshFnc()
return
), gui.failRequestModalFnc(gettext("Failed creating publication"))
return
return
)
prevTables.push publicationsTable
else
$("#publications-placeholder_tab").addClass "hidden"
#
# * Log table
#
logTable = gui.servicesPools.logTable(servPool.id,
container: "logs-placeholder"
)
prevTables.push logTable
return
# Pre-process data received to add "icon" to deployed service
onData: (data) ->
gui.doLog "onData", data
$.each data, (index, value) ->
try
service = availableServices[value.service_id]
if service is `undefined`
value.parent = gettext("undefined")
return
style = "display:inline-block; background: url(data:image/png;base64," + service.info.icon + "); " + "width: 16px; height: 16px; vertical-align: middle;"
if value.restrained
value.name = "<span class=\"fa fa-exclamation text-danger\"></span> " + value.name
value.state = gettext("Restrained")
value.name = "<span style=\"" + style + "\"></span> " + value.name
value.parent = service.name
catch e
value.name = "<span class=\"fa fa-asterisk text-alert\"></span> " + value.name
value.parent = gettext("unknown (needs reload)")
return
return
onNew: gui.methods.typedNew(gui.servicesPools, gettext("New service pool"), "Service pool " + gettext("creation error"),
guiProcessor: (guiDef) -> # Create has "save on publish" field
gui.doLog guiDef
newDef = [].concat(guiDef).concat([
name: "publish_on_save"
value: true
gui:
label: gettext("Publish on creation")
tooltip: gettext("If selected, will initiate the publication inmediatly after creation")
type: "checkbox"
order: 150
defvalue: true
])
gui.doLog newDef
newDef
preprocessor: preFnc
)
onEdit: gui.methods.typedEdit(gui.servicesPools, gettext("Edit") + " service pool", "Service pool " + gettext("saving error"))
onDelete: gui.methods.del(gui.servicesPools, gettext("Delete") + " service pool", "Service pool " + gettext("deletion error"))
)
return
return
return

View File

@ -0,0 +1,16 @@
# jshint strict: true
# Basic GUI components
# Tools
gui.clear_cache = new BasicGuiElement("Flush cache")
gui.clear_cache.link = ->
"use strict"
api.getJson "cache/flush",
success: ->
gui.launchModal gettext("Cache"), gettext("Cache has been flushed"),
actionButton: " "
return
return

View File

@ -0,0 +1,599 @@
# jshint strict: true
# Operations commmon to most elements
@BasicGuiElement = (name) ->
"use strict"
@name = name
return
@GuiElement = (restItem, name, typesFunction) ->
"use strict"
@rest = restItem
@name = name
@types = {}
@init()
return
# all gui elements has, at least, name && type
# Types must include, at least: type, icon
@GuiElement:: =
init: ->
"use strict"
gui.doLog "Initializing " + @name
self = this
@rest.types (data) ->
styles = ""
alreadyAttached = $("#gui-style-" + self.name).length isnt 0
$.each data, (index, value) ->
className = self.name + "-" + value.type
self.types[value.type] = value
self.types[value.type].css = className
gui.doLog "Creating style for " + className
unless alreadyAttached
style = "." + className + " { display:inline-block; background: url(data:image/png;base64," + value.icon + "); " + "width: 16px; height: 16px; vertical-align: middle; } "
styles += style
return
if styles isnt ""
# If style already attached, do not re-attach it
styles = "<style id=\"gui-style-" + self.name + "\" media=\"screen\">" + styles + "</style>"
$(styles).appendTo "head"
return
return
# Options: dictionary
# container: container ID of parent for the table. If undefined, table will be appended to workspace
# buttons: array of visible buttons (strings), valid are [ 'new', 'edit', 'refresh', 'delete', 'xls' ],
# Can contain also objects with {'text': ..., 'fnc': ...}
# rowSelect: type of allowed row selection, valid values are 'single' and 'multi'
# scrollToTable: if True, will scroll page to show table
# deferedRender: if True, datatable will be created with "bDeferRender": true, that will improve a lot creation of huge tables
#
# onData: Event (function). If defined, will be invoked on data load (to allow preprocess of data)
# onLoad: Event (function). If defined, will be invoked when table is fully loaded.
# Receives 1 parameter, that is the gui element (GuiElement) used to render table
# onRowSelect: Event (function). If defined, will be invoked when a row of table is selected
# Receives 3 parameters:
# 1.- the array of selected items data (objects, as got from api...get)
# 2.- the DataTable that raised the event
# 3.- the DataTableTools that raised the event
# onRowDeselect: Event (function). If defined, will be invoked when a row of table is deselected
# Receives 3 parameters:
# 1.- the array of selected items data (objects, as got from api...get)
# 2.- the DataTable that raised the event
# onCheck: Event (function),
# It determines the state of buttons on selection: if returns "true", the indicated button will be enabled, and disabled if returns "false"
# Receives 2 parameters:
# 1.- the event fired, that can be "edit" or "delete"
# 2.- the selected items data (array of selected elements, as got from api...get. In case of edit, array length will be 1)
# onNew: Event (function). If defined, will be invoked when "new" button is pressed
# Receives 4 parameters:
# 1.- the selected item data (single object, as got from api...get)
# 2.- the event that fired this (new, delete, edit, ..)
# 3.- the DataTable that raised the event
# onEdit: Event (function). If defined, will be invoked when "edit" button is pressed
# Receives 4 parameters:
# 1.- the selected item data (single object, as got from api...get)
# 2.- the event that fired this (new, delete, edit, ..)
# 3.- the DataTable that raised the event
# onDelete: Event (function). If defined, will be invoked when "delete" button is pressed
# Receives 4 parameters:
# 1.- the selected item data (single object, as got from api...get)
# 2.- the event that fired this (new, delete, edit, ..)
# 4.- the DataTable that raised the event
table: (tblParams) ->
"use strict"
tblParams = tblParams or {}
gui.doLog "Composing table for " + @name
tableId = @name + "-table"
self = this # Store this for child functions
# ---------------
# Cells renderers
# ---------------
# Empty cells transform
renderEmptyCell = (data) ->
return "-" if data is ""
data
# Icon renderer, based on type (created on init methods in styles)
renderTypeIcon = (data, type, value) ->
if type is "display"
self.types[value.type] = self.types[value.type] or {}
css = self.types[value.type].css or "fa fa-asterisk"
"<span class=\"" + css + "\"></span> " + renderEmptyCell(data)
else
renderEmptyCell data
# Custom icon renderer, in fact span with defined class
renderIcon = (icon) ->
(data, type, full) ->
if type is "display"
"<span class=\"" + icon + "\"></span> " + renderEmptyCell(data)
else
renderEmptyCell data
# Custom icon based on type
renderIconDict = (iconDict) ->
(data, type, value) ->
if type is "display"
"<span class=\"" + iconDict[value.type] + "\"></span> " + renderEmptyCell(data)
else
renderEmptyCell data
# Text transformation, dictionary based
renderTextTransform = (dict) ->
(data, type, full) ->
dict[data] or renderEmptyCell(data)
@rest.tableInfo (data) -> # Gets tableinfo data (columns, title, visibility of fields, etc...
row_style = data["row-style"]
gui.doLog row_style
title = data.title
columns = []
$.each data.fields, (index, value) ->
for v of value
opts = value[v]
column = mData: v
column.sTitle = opts.title
column.mRender = renderEmptyCell
column.sWidth = opts.width if opts.width
column.bVisible = (if opts.visible is `undefined` then true else opts.visible)
column.bSortable = opts.sortable if opts.sortable isnt `undefined`
column.bSearchable = opts.searchable if opts.searchable isnt `undefined`
if opts.type and column.bVisible
switch opts.type
when "date"
column.sType = "uds-date"
column.mRender = gui.tools.renderDate(api.tools.djangoFormat(get_format("SHORT_DATE_FORMAT")))
when "datetime"
column.sType = "uds-date"
column.mRender = gui.tools.renderDate(api.tools.djangoFormat(get_format("SHORT_DATETIME_FORMAT")))
when "time"
column.sType = "uds-date"
column.mRender = gui.tools.renderDate(api.tools.djangoFormat(get_format("TIME_FORMAT")))
when "iconType"
#columnt.sType = 'html'; // html is default, so this is not needed
column.mRender = renderTypeIcon
when "icon"
column.mRender = renderIcon(opts.icon) if opts.icon isnt `undefined`
when "icon_dict"
column.mRender = renderIconDict(opts.icon_dict) if opts.icon_dict isnt `undefined`
when "dict"
column.mRender = renderTextTransform(opts.dict) if opts.dict isnt `undefined`
else
column.sType = opts.type
columns.push column
return
# Responsive style for tables, using tables.css and this code generates the "titles" for vertical display on small sizes
$("#style-" + tableId).remove() # Remove existing style for table before adding new one
$(api.templates.evaluate("tmpl_comp_responsive_table",
tableId: tableId
columns: columns
)).appendTo "head"
self.rest.overview (data) -> # Gets "overview" data for table (table contents, but resume form)
tblParams.onData data if tblParams.onData
table = gui.table(title, tableId)
if tblParams.container is `undefined`
gui.appendToWorkspace "<div class=\"row\"><div class=\"col-lg-12\">" + table.text + "</div></div>"
else
$("#" + tblParams.container).empty()
$("#" + tblParams.container).append table.text
# What execute on refresh button push
onRefresh = tblParams.onRefresh or ->
refreshFnc = ->
# Refreshes table content
tbl = $("#" + tableId).dataTable()
# Clears selection first
TableTools.fnGetInstance(tableId).fnSelectNone()
#if( data.length > 1000 )
gui.tools.blockUI()
self.rest.overview (data) -> # Restore overview
tblParams.onData data if tblParams.onData
setTimeout (->
tbl.fnClearTable()
tbl.fnAddData data
onRefresh self
gui.tools.unblockUI()
return
), 0
return
# End restore overview
false # This may be used on button or href, better disable execution of it
btns = []
if tblParams.buttons
# Generic click handler generator for this table
clickHandlerFor = (handler, action, newHandler) ->
handleFnc = handler or (val, action, tbl) ->
gui.doLog "Default handler called for ", action
return
(btn) ->
tbl = $("#" + tableId).dataTable()
val = @fnGetSelectedData()[0]
setTimeout (->
if newHandler
handleFnc action, tbl, refreshFnc
else
handleFnc val, action, tbl, refreshFnc
return
), 0
return
onCheck = tblParams.onCheck or -> # Default oncheck always returns true
true
# methods for buttons on row select
editSelected = (btn, obj, node) ->
sel = @fnGetSelectedData()
enable = (if sel.length is 1 then onCheck("edit", sel) else false)
if enable
$(btn).removeClass("disabled").addClass "btn3d-success"
else
$(btn).removeClass("btn3d-success").addClass "disabled"
return
deleteSelected = (btn, obj, node) ->
sel = @fnGetSelectedData()
enable = (if sel.length is 1 then onCheck("delete", sel) else false)
if enable
$(btn).removeClass("disabled").addClass "btn3d-warning"
else
$(btn).removeClass("btn3d-warning").addClass "disabled"
return
$.each tblParams.buttons, (index, value) -> # Iterate through button definition
btn = null
switch value
when "new"
if Object.keys(self.types).length isnt 0
menuId = gui.genRamdonId("dd-")
ordered = []
$.each self.types, (k, v) ->
ordered.push
type: k
css: v.css
name: v.name
description: v.description
return
ordered = ordered.sort((a, b) ->
a.name.localeCompare b.name
)
btn =
sExtends: "div"
sButtonText: api.templates.evaluate("tmpl_comp_dropdown",
label: gui.config.dataTableButtons["new"].text
css: gui.config.dataTableButtons["new"].css
id: menuId
tableId: tableId
columns: columns
menu: ordered
)
else
btn =
sExtends: "text"
sButtonText: gui.config.dataTableButtons["new"].text
sButtonClass: gui.config.dataTableButtons["new"].css
fnClick: clickHandlerFor(tblParams.onNew, "new", true)
when "edit"
btn =
sExtends: "text"
sButtonText: gui.config.dataTableButtons.edit.text
fnSelect: editSelected
fnClick: clickHandlerFor(tblParams.onEdit, "edit")
sButtonClass: gui.config.dataTableButtons.edit.css
when "delete"
btn =
sExtends: "text"
sButtonText: gui.config.dataTableButtons["delete"].text
fnSelect: deleteSelected
fnClick: clickHandlerFor(tblParams.onDelete, "delete")
sButtonClass: gui.config.dataTableButtons["delete"].css
when "refresh"
btn =
sExtends: "text"
sButtonText: gui.config.dataTableButtons.refresh.text
fnClick: refreshFnc
sButtonClass: gui.config.dataTableButtons.refresh.css
when "xls"
btn =
sExtends: "text"
sButtonText: gui.config.dataTableButtons.xls.text
fnClick: -> # Export to excel
api.templates.get "spreadsheet", (tmpl) ->
styles = bold: "s21"
headings = []
rows = []
$.each columns, (index, heading) ->
return if heading.bVisible is false
headings.push api.spreadsheet.cell(heading.sTitle, "String", styles.bold)
return
rows.push api.spreadsheet.row(headings)
$.each data, (index1, row) ->
cells = []
$.each columns, (index2, col) ->
return if col.bVisible is false
type = (if col.sType is "numeric" then "Number" else "String")
cells.push api.spreadsheet.cell(row[col.mData], type)
return
rows.push api.spreadsheet.row(cells)
return
ctx =
creation_date: (new Date()).toISOString()
worksheet: title
columns_count: headings.length
rows_count: rows.length
rows: rows.join("\n")
gui.doLog ctx
setTimeout (->
saveAs new Blob([api.templates.evaluate(tmpl, ctx)],
type: "application/vnd.ms-excel"
), title + ".xls"
return
), 20
return
return
# End export to excell
sButtonClass: gui.config.dataTableButtons.xls.css
else # Custom button, this has to be
try
css = ((if value.css then value.css + " " else "")) + gui.config.dataTableButtons.custom.css
btn =
sExtends: "text"
sButtonText: value.text
sButtonClass: css
if value.click
btn.fnClick = (btn) ->
tbl = $("#" + tableId).dataTable()
val = @fnGetSelectedData()[0]
setTimeout (->
value.click val, value, btn, tbl, refreshFnc
return
), 0
return
if value.select
btn.fnSelect = (btn) ->
tbl = $("#" + tableId).dataTable()
val = @fnGetSelectedData()[0]
setTimeout (->
value.select val, value, btn, tbl, refreshFnc
return
), 0
return
catch e
gui.doLog "Button", value, e
btns.push btn if btn
return
# End buttoon iteration
# Initializes oTableTools
oTableTools =
aButtons: btns
sRowSelect: tblParams.rowSelect or "none"
if tblParams.onRowSelect
rowSelectedFnc = tblParams.onRowSelect
oTableTools.fnRowSelected = ->
rowSelectedFnc @fnGetSelectedData(), $("#" + tableId).dataTable(), self
return
if tblParams.onRowDeselect
rowDeselectedFnc = tblParams.onRowDeselect
oTableTools.fnRowDeselected = ->
rowDeselectedFnc @fnGetSelectedData(), $("#" + tableId).dataTable(), self
return
dataTableOptions =
aaData: data
aaSorting: [[
0
"asc"
]]
aoColumns: columns
oLanguage: gui.config.dataTablesLanguage
oTableTools: oTableTools
sPaginationType: "bootstrap"
# First is upper row,
# second row is lower
# (pagination) row
sDom: "<'row'<'col-xs-8'T><'col-xs-4'f>r>t<'row'<'col-xs-5'i><'col-xs-7'p>>"
bDeferRender: tblParams.deferedRender or false
# If row is "styled"
if row_style.field
field = row_style.field
dct = row_style.dict
prefix = row_style.prefix
dataTableOptions.fnCreatedRow = (nRow, aData, iDataIndex) ->
v = (if dct isnt `undefined` then dct[@fnGetData(iDataIndex)[field]] else @fnGetData(iDataIndex)[field])
$(nRow).addClass prefix + v
gui.doLog prefix + v
return
$("#" + tableId).dataTable dataTableOptions
# Fix 3dbuttons
gui.tools.fix3dButtons "#" + tableId + "_wrapper .btn-group-3d"
# Fix form
$("#" + tableId + "_filter input").addClass "form-control"
# Add refresh action to panel
$(table.refreshSelector).click refreshFnc
# Add tooltips to "new" buttons
$("#" + table.panelId + " [data-toggle=\"tooltip\"]").tooltip
container: "body"
delay:
show: 1000
hide: 100
placement: "auto right"
# And the handler of the new "dropdown" button links
if tblParams.onNew # If onNew, set the handlers for dropdown
$("#" + table.panelId + " [data-type]").on "click", (event) ->
event.preventDefault()
tbl = $("#" + tableId).dataTable()
# Executes "onNew" outside click event
type = $(this).attr("data-type")
setTimeout (->
tblParams.onNew type, tbl, refreshFnc
return
), 0
return
if tblParams.scrollToTable is true
tableTop = $("#" + tableId).offset().top
$("html, body").scrollTop tableTop
# if table rendered event
tblParams.onLoad self if tblParams.onLoad
return
return
# End Overview data
# End Tableinfo data
"#" + tableId
logTable: (itemId, tblParams) ->
"use strict"
tblParams = tblParams or {}
gui.doLog "Composing log for " + @name
tableId = @name + "-table-log"
self = this # Store this for child functions
# Renderers for columns
refreshFnc = ->
# Refreshes table content
tbl = $("#" + tableId).dataTable()
gui.tools.blockUI()
self.rest.getLogs itemId, (data) ->
setTimeout (->
tbl.fnClearTable()
tbl.fnAddData data
gui.tools.unblockUI()
return
), 0
return
# End restore overview
false # This may be used on button or href, better disable execution of it
# Log level "translator" (renderer)
logRenderer = gui.tools.renderLogLovel()
# Columns description
columns = [
{
mData: "date"
sTitle: gettext("Date")
sType: "uds-date"
asSorting: [
"desc"
"asc"
]
mRender: gui.tools.renderDate(api.tools.djangoFormat(get_format("SHORT_DATE_FORMAT") + " " + get_format("TIME_FORMAT")))
bSortable: true
bSearchable: true
}
{
mData: "level"
sTitle: gettext("level")
mRender: logRenderer
sWidth: "5em"
bSortable: true
bSearchable: true
}
{
mData: "source"
sTitle: gettext("source")
sWidth: "5em"
bSortable: true
bSearchable: true
}
{
mData: "message"
sTitle: gettext("message")
bSortable: true
bSearchable: true
}
]
table = gui.table(tblParams.title or gettext("Logs"), tableId)
if tblParams.container is `undefined`
gui.appendToWorkspace "<div class=\"row\"><div class=\"col-lg-12\">" + table.text + "</div></div>"
else
$("#" + tblParams.container).empty()
$("#" + tblParams.container).append table.text
# Responsive style for tables, using tables.css and this code generates the "titles" for vertical display on small sizes
$("#style-" + tableId).remove() # Remove existing style for table before adding new one
$(api.templates.evaluate("tmpl_comp_responsive_table",
tableId: tableId
columns: columns
)).appendTo "head"
self.rest.getLogs itemId, (data) ->
gui.doLog data
$("#" + tableId).dataTable
aaData: data
aaSorting: [[
0
"desc"
]]
oTableTools:
aButtons: []
aoColumns: columns
oLanguage: gui.config.dataTablesLanguage
sDom: "<'row'<'col-xs-8'T><'col-xs-4'f>r>t<'row'<'col-xs-5'i><'col-xs-7'p>>"
bDeferRender: tblParams.deferedRender or false
fnCreatedRow: (nRow, aData, iDataIndex) ->
v = "log-" + logRenderer(@fnGetData(iDataIndex).level)
$(nRow).addClass v
return
# Fix form
$("#" + tableId + "_filter input").addClass "form-control"
# Add refresh action to panel
$(table.refreshSelector).click refreshFnc
# if table rendered event
tblParams.onLoad self if tblParams.onLoad
return
"#" + tableId

View File

@ -0,0 +1,285 @@
# jshint strict: true
((gui, $, undefined_) ->
"use strict"
gui.forms = {}
gui.forms.callback = (formSelector, method, params, success_fnc) ->
path = "gui/callback/" + method
p = []
$.each params, (index, val) ->
p.push val.name + "=" + encodeURIComponent(val.value)
return
path = path + "?" + p.join("&")
api.getJson path,
success: success_fnc
return
# Returns form fields that will manage a gui description (new or edit)
gui.forms.fieldsToHtml = (itemGui, item, editing) ->
html = ""
fillers = [] # Fillers (callbacks)
originalValues = {} # Initial stored values (defaults to "reset" form and also used on fillers callback to try to restore previous value)
# itemGui is expected to have fields sorted by .gui.order (REST api returns them sorted)
$.each itemGui, (index, f) ->
# Not exactly a field, maybe some other info...
return if f.gui is `undefined`
# Fix multiline text fields to textbox
f.gui.type = "textbox" if f.gui.type is "text" and f.gui.multiline
value = item[f.name] or f.gui.value or f.gui.defvalue
# We need to convert "array" values for multichoices to single list of ids (much more usable right here)
if f.gui.type is "multichoice"
newValue = []
$.each value, (undefined_, val) ->
newValue.push val.id
return
value = newValue
originalValues[f.name] = value # Store original value
html += api.templates.evaluate("tmpl_fld_" + f.gui.type,
value: value # If no value present, use default value
values: f.gui.values
label: f.gui.label
length: f.gui.length
multiline: f.gui.multiline
readonly: (if editing then f.gui.rdonly else false) # rdonly applies just to editing
required: f.gui.required
tooltip: f.gui.tooltip
type: f.gui.type
name: f.name
css: "modal_field_data"
)
# if this field has a filler (callback to get data)
if f.gui.fills
gui.doLog "This field has a filler"
fillers.push
name: f.name
callbackName: f.gui.fills.callbackName
parameters: f.gui.fills.parameters
return
html: html
fillers: fillers
originalValues: originalValues
gui.forms.fromFields = (fields, item) ->
editing = item isnt `undefined` # Locate real Editing
item = item or id: ""
form = "<form class=\"form-horizontal\" role=\"form\">" + "<input type=\"hidden\" name=\"id\" class=\"modal_field_data\" value=\"" + item.id + "\">"
fillers = []
originalValues = {}
if fields.tabs
id = "tab-" + Math.random().toString().split(".")[1] # Get a random base ID for tab entries
tabs = []
tabsContent = []
active = " active in"
$.each fields.tabs, (index, tab) ->
h = gui.forms.fieldsToHtml(tab.fields, item)
tabsContent.push "<div class=\"tab-pane fade" + active + "\" id=\"" + id + index + "\">" + h.html + "</div>"
tabs.push "<li><a href=\"#" + id + index + "\" data-toggle=\"tab\">" + tab.title + "</a></li>"
active = ""
fillers = fillers.concat(h.fillers) # Fillers (callback based)
$.extend originalValues, h.originalValues # Original values
gui.doLog "Fillers:", h.fillers
return
form += "<ul class=\"nav nav-tabs\">" + tabs.join("\n") + "</ul><div class=\"tab-content\">" + tabsContent.join("\n") + "</div>"
else
h = gui.forms.fieldsToHtml(fields, item, editing)
form += h.html
fillers = fillers.concat(h.fillers)
$.extend originalValues, h.originalValues
form += "</form>"
gui.doLog "Original values: ", originalValues
# Init function for callbacks.
# Callbacks can only be attached to "Selects", but it's parameters can be got from any field
# This needs the "form selector" as base for setting callbacks, etc..
init = (formSelector) ->
gui.doLog formSelector, fillers
onChange = (filler) ->
->
gui.doLog "Onchange invoked for ", filler
# Attach on change method to each filler, and after that, all
params = []
$.each filler.parameters, (undefined_, p) ->
val = $(formSelector + " [name=\"" + p + "\"]").val()
params.push
name: p
value: val
return
gui.forms.callback formSelector, filler.callbackName, params, (data) ->
$.each data, (undefined_, sel) ->
# Update select contents with returned values
$select = $(formSelector + " [name=\"" + sel.name + "\"]")
$select.empty()
$.each sel.values, (undefined_, value) ->
$select.append "<option value=\"" + value.id + "\">" + value.text + "</option>"
return
$select.val originalValues[sel.name]
# Refresh selectpicker if item is such
$select.selectpicker "refresh" if $select.hasClass("selectpicker")
# Trigger change for the changed item
$select.trigger "change"
return
return
return
# Sets the "on change" event for select with fillers (callbacks that fills other fields)
$.each fillers, (undefined_, f) ->
$(formSelector + " [name=\"" + f.name + "\"]").on "change", onChange(f)
return
# Trigger first filler if it exists, this will cascade rest of "changes" if they exists
$(formSelector + " [name=\"" + fillers[0].name + "\"]").trigger "change" if fillers.length
return
html: form # Returns the form and a initialization function for the form, that must be invoked to start it
init: init
# Reads fields from a form
gui.forms.read = (formSelector) ->
res = {}
$(formSelector + " .modal_field_data").each (i, field) ->
$field = $(field)
if $field.attr("name") # Is a valid field
name = $field.attr("name")
if $field.attr("type") is "checkbox"
res[name] = $field.is(":checked")
else
res[name] = $field.val()
res[name] = [] if res[name] is null and $field.is("select")
return
gui.doLog res
res
# Options has this keys:
# title
# fields
# item
# success
# buttons: Array of buttons to be added to footer, with:
# text --> text of button
# css --> button style (btn-default, btn-warning, ...). If not defined, 'btn-default' will be used
# action --> function to be executed. Will be passed 3 parameters: event, formSelector and closeFnc
# (use gui.forms.read(form selector) to get fields, closeFnc() to close form if desired)
# Failed operations will show a modal with server error
gui.forms.launchModal = (options, onSuccess) ->
options = options or {}
id = "modal-" + Math.random().toString().split(".")[1] # Get a random ID for this modal
ff = gui.forms.fromFields(options.fields, options.item)
footer = ""
clickEventHandlers = []
if options.buttons
$.each options.buttons, (index, value) ->
_id = id + "-footer-" + index
css = value.css or "btn-default"
clickEventHandlers.push
id: "#" + _id
action: value.action
footer += "<button id=\"" + _id + "\" type=\"button\" class=\"pull-left btn " + css + "\">" + value.text + "</button>"
return
gui.appendToWorkspace gui.modal(id, options.title, ff.html,
footer: footer
)
id = "#" + id # for jQuery
formSelector = id + " form"
closeFnc = ->
$(id).modal "hide"
return
ff.init id if ff.init
# Append click events for custom buttons on footer
$.each clickEventHandlers, (undefined_, value) ->
if value.action
$(value.id).on "click", (event) ->
value.action event, formSelector, closeFnc
return
return
# Get form
$form = $(id + " form")
gui.tools.applyCustoms id
# Validation
$form.validate
debug: true
errorClass: "text-danger"
validClass: "has-success"
highlight: (element) ->
$(element).closest(".form-group").addClass "has-error"
return
success: (element) ->
$(element).closest(".form-group").removeClass "has-error"
$(element).remove()
return
# And catch "accept" (default is "Save" in fact) button click
$("#{id} .button-accept").click ->
return unless $form.valid()
if options.success
options.success formSelector, closeFnc # Delegate close to to onSuccess
return
else
closeFnc()
return
# If preprocessors of modal (maybe custom event handlers)
options.preprocessor id if options.preprocessor
# Launch modal
$(id).modal(keyboard: false).on "hidden.bs.modal", ->
$(id).remove()
return
return
# simple gui generators
gui.forms.guiField = (name, type, label, tooltip, value, values, length, multiline, readonly, required) ->
length = length or 128
multiline = (if multiline isnt `undefined` then multiline else 0)
readonly = readonly or false
required = required or false
name: name
gui:
defvalue: value
value: value
values: values
label: label
length: length
multiline: multiline
rdonly: readonly # rdonly applies just to editing
required: required
tooltip: tooltip
type: type
return
) window.gui = window.gui or {}, jQuery

View File

@ -0,0 +1,106 @@
# jshint strict: true
((gui, $, undefined_) ->
"use strict"
gui.tools =
blockUI: (message) ->
message = message or "<h1><span class=\"fa fa-spinner fa-spin\"></span> " + gettext("Just a moment...") + "</h1>"
$.blockUI message: message
return
unblockUI: ->
$.unblockUI()
$(".DTTT_collection_background").remove()
return
fix3dButtons: (selector) ->
selector = selector or ""
selector += " .btn3d"
console.log selector
$.each $(selector), (index, value) ->
# If no events associated, return
return if $._data(value, "events") is `undefined`
$this = $(this)
clkEvents = []
# Store old click events, so we can reconstruct click chain later
$.each $._data(value, "events").click, (index, fnc) ->
clkEvents.push fnc
return
$this.unbind "click"
# If Mousedown registers a temporal mouseUp event on parent, to lauch button click
$this.mousedown (event) ->
$("body").mouseup (e) ->
# Remove temporal mouseup handler
$(this).unbind "mouseup"
# If movement of mouse is not too far... (16 px maybe well for 3d buttons?)
x = event.pageX - e.pageX
y = event.pageY - e.pageY
dist_square = x * x + y * y
if dist_square < 16 * 16
# Register again old event handlers
$.each clkEvents, (index, fnc) ->
$this.click fnc.handler
return
$this.click()
$this.unbind "click"
return
return
return
return
applyCustoms: (selector) ->
# Activate "custom" styles
$(selector + " input:checkbox").bootstrapSwitch()
# Activate "cool" selects
$(selector + " .selectpicker").selectpicker()
# TEST: cooler on mobile devices
$(selector + " .selectpicker").selectpicker "mobile" if /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent)
# Activate tooltips
$(selector + " [data-toggle=\"tooltip\"]").tooltip
delay:
show: 1000
hide: 100
placement: "auto right"
# Fix 3d buttons
gui.tools.fix3dButtons selector
return
# Datetime renderer (with specified format)
renderDate: (format) ->
(data, type, full) ->
"<span data-date=\"" + data + "\">" + api.tools.strftime(format, new Date(data * 1000)) + "</span>"
# Log level rendererer
renderLogLovel: ->
levels =
10000: "OTHER"
20000: "DEBUG"
30000: "INFO"
40000: "WARN"
50000: "ERROR"
60000: "FATAL"
(data, type, full) ->
levels[data] or "OTHER"
return
) window.gui = window.gui or {}, jQuery

View File

@ -0,0 +1,396 @@
# jshint strict: true
((gui, $, undefined_) ->
"use strict"
# "public" methods
gui.doLog = (args...)->
if gui.debug
try
console.log.apply window, args
return
# nothing can be logged
gui.config = gui.config or {}
# Several convenience "constants" for tables
gui.config.dataTablesLanguage =
sLengthMenu: gettext("_MENU_ records per page")
sZeroRecords: gettext("Empty")
sInfo: gettext("Records _START_ to _END_ of _TOTAL_")
sInfoEmpty: gettext("No records")
sInfoFiltered: gettext("(filtered from _MAX_ total records)")
sProcessing: gettext("Please wait, processing")
sSearch: gettext("Filter")
sInfoThousands: django.formats.THOUSAND_SEPARATOR
oPaginate:
sFirst: "<span class=\"fa fa-fast-backward \"></span> "
sLast: "<span class=\"fa fa-fast-forward\"></span> "
sNext: "<span class=\"fa fa-forward\"></span> "
sPrevious: "<span class=\"fa fa-backward\"></span> "
gui.config.dataTableButtons =
new:
text: "<span class=\"fa fa-pencil\"></span> <span class=\"label-tbl-button\">" + gettext("New") + "</span>"
css: "btn btn3d btn3d-primary btn3d-tables"
edit:
text: "<span class=\"fa fa-edit\"></span> <span class=\"label-tbl-button\">" + gettext("Edit") + "</span>"
css: "btn disabled btn3d-default btn3d btn3d-tables"
delete:
text: "<span class=\"fa fa-trash-o\"></span> <span class=\"label-tbl-button\">" + gettext("Delete") + "</span>"
css: "btn disabled btn3d-default btn3d btn3d-tables"
xls:
text: "<span class=\"fa fa-save\"></span> <span class=\"label-tbl-button\">" + gettext("Xls") + "</span>"
css: "btn btn3d-info btn3d btn3d-tables"
custom:
text: null
css: "btn btn3d-default btn3d btn3d-tables"
gui.genRamdonId = (prefix) ->
prefix = prefix or ""
prefix + Math.random().toString().split(".")[1]
gui.table = (title, table_id, options) ->
options = options or {}
panelId = "panel-" + table_id
text: api.templates.evaluate("tmpl_comp_table",
panelId: panelId
icon: options.icon or "table"
size: options.size or 12
title: title
table_id: table_id
)
panelId: panelId
refreshSelector: "#" + panelId + " span.fa-refresh"
gui.breadcrumbs = (path) ->
items = path.split("/")
active = items.pop()
list = ""
$.each items, (index, value) ->
list += "<li><a href=\"#\">" + value + "</a></li>"
return
list += "<li class=\"active\">" + active + "</li>"
"<div class=\"row\"><div class=\"col-lg-12\"><ol class=\"breadcrumb\">" + list + "</ol></div></div>"
# By default, actionButton has class "button-accept", so you can use returned id + this class to select it
# and do whatever is needed (for example, insert an "on click" event (this method returns id without '#'
# Example: $('#' + id + ' .button-accept').on('click', ...
gui.modal = (id, title, content, options) ->
options = options or {}
api.templates.evaluate "tmpl_comp_modal",
id: id
title: title
content: content
footer: options.footer
button1: options.closeButton
button2: options.actionButton
# As previous, this creates the modal and shows it. in this case, the id of the modal returned already has '#'
gui.launchModal = (title, content, options) ->
options = options or {}
id = gui.genRamdonId("modal-") # Get a random ID for this modal
gui.appendToWorkspace gui.modal(id, title, content, options)
id = "#" + id # for jQuery
$(id).modal().on "hidden.bs.modal", ->
$(id).remove()
return
id
gui.notify = (message, type) ->
gui.launchModal "<b class=\"text-" + type + "\">" + gettext("Message") + "</b>", "<span class=\"text-" + type + "\">" + message + "</span>",
actionButton: " "
return
gui.failRequestModalFnc = (title) ->
(jqXHR, textStatus, errorThrown) -> # fail on put
gui.tools.unblockUI()
gui.launchModal "<b class=\"text-danger\">" + title + "</b>", jqXHR.responseText,
actionButton: " "
return
gui.promptModal = (title, question, options) ->
options = options or {}
options.actionButton = "<button type=\"button\" class=\"btn btn-primary button-yes\">" + (options.yesButton or gettext("yes")) + "</button>"
options.closeButton = "<button type=\"button\" class=\"btn btn-danger button-no\">" + (options.yesButton or gettext("no")) + "</button>"
onYes = options.onYes or ->
onNo = options.onNo or ->
modalId = gui.launchModal(title, question, options)
$(modalId + " .button-yes").on "click", (event) ->
$(modalId).modal "hide"
onYes()
return
$(modalId + " .button-no").on "click", (event) ->
$(modalId).modal "hide"
onNo()
return
return
gui.clearWorkspace = ->
$("#content").empty()
$("#minimized").empty()
return
gui.appendToWorkspace = (data) ->
$(data).appendTo "#content"
return
# Clean up several "internal" data
# I have discovered some "items" that are keep in memory, or that adds garbage to body (datatable && tabletools mainly)
# Whenever we change "section", we clean up as much as we can, so we can keep things as clean as possible
# Main problem where comming with "tabletools" and keeping references to all instances created
gui.cleanup = ->
gui.doLog "Cleaning up things"
# Tabletools creates divs at end that do not get removed, here is a good place to ensure there is no garbage left behind
# And anyway, if this div does not exists, it creates a new one...
$(".DTTT_dropdown").remove() # Tabletools keep adding garbage to end of body on each new table creation, so we simply remove it on each new creation
TableTools._aInstances = [] # Same for internal references
TableTools._aListeners = []
# Destroy any created datatable
$.each $.fn.dataTable.fnTables(), (undefined_, tbl) ->
$tbl = $(tbl).dataTable()
$tbl.fnClearTable() # Removing data first makes things much faster
$tbl.fnDestroy()
return
return
gui.setLinksEvents = ->
sidebarLinks = [
{
id: "lnk-dashboard"
exec: gui.dashboard.link
cleanup: true
}
{
id: "lnk-service_providers"
exec: gui.providers.link
cleanup: true
}
{
id: "lnk-authenticators"
exec: gui.authenticators.link
cleanup: true
}
{
id: "lnk-osmanagers"
exec: gui.osmanagers.link
cleanup: true
}
{
id: "lnk-connectivity"
exec: gui.connectivity.link
cleanup: true
}
{
id: "lnk-deployed_services"
exec: gui.servicesPools.link
cleanup: true
}
{
id: "lnk-clear_cache"
exec: gui.clear_cache.link
cleanup: false
}
{
id: "lnk-configuration"
exec: gui.configuration.link
cleanup: false
}
]
$.each sidebarLinks, (index, value) ->
gui.doLog "Adding " + value.id
$("." + value.id).unbind("click").click (event) ->
event.preventDefault()
$(".navbar-toggle").trigger "click" unless $(".navbar-toggle").css("display") is "none"
gui.cleanup() if value.cleanup
$("html, body").scrollTop 0
value.exec event
return
return
return
gui.init = ->
# Load jquery validator strings
$.extend $.validator.messages,
required: gettext("This field is required.")
remote: gettext("Please fix this field.")
email: gettext("Please enter a valid email address.")
url: gettext("Please enter a valid URL.")
date: gettext("Please enter a valid date.")
dateISO: gettext("Please enter a valid date (ISO).")
number: gettext("Please enter a valid number.")
digits: gettext("Please enter only digits.")
creditcard: gettext("Please enter a valid credit card number.")
equalTo: gettext("Please enter the same value again.")
maxlength: $.validator.format(gettext("Please enter no more than {0} characters."))
minlength: $.validator.format(gettext("Please enter at least {0} characters."))
rangelength: $.validator.format(gettext("Please enter a value between {0} and {1} characters long."))
range: $.validator.format(gettext("Please enter a value between {0} and {1}."))
max: $.validator.format(gettext("Please enter a value less than or equal to {0}."))
min: $.validator.format(gettext("Please enter a value greater than or equal to {0}."))
# Set blockui params
$.blockUI.defaults.baseZ = 2000
$.fn.dataTableExt.oSort["uds-date-pre"] = (s) ->
parseInt s.split("\"")[1], 10
# Sort for "date" columns (our "dates" are in fact postfix dates rendered as dates with locale format
$.fn.dataTableExt.oSort["uds-date-asc"] = (x, y) ->
val = ((if (x < y) then -1 else ((if (x > y) then 1 else 0))))
val
$.fn.dataTableExt.oSort["uds-date-desc"] = (x, y) ->
val = ((if (x < y) then 1 else ((if (x > y) then -1 else 0))))
val
# Wait a bit before activating links to give tome tine to initializations
setTimeout (->
gui.setLinksEvents()
gui.dashboard.link()
return
), 500
return
gui.showDashboard = ->
gui.dashboard.link()
return
# Generic "methods" for editing, creating, etc...
gui.methods = {}
gui.methods.typedTestButton = (rest, text, css, type) ->
[
text: text
css: css
action: (event, form_selector, closeFnc) ->
fields = gui.forms.read(form_selector)
gui.doLog "Fields: ", fields
rest.test type, fields, ((data) ->
gui.launchModal gettext("Test result"), data,
actionButton: " "
return
), gui.failRequestModalFnc(gettext("Test error"))
return
]
# "Generic" edit method to set onEdit table
gui.methods.typedEdit = (parent, modalTitle, modalErrorMsg, options) ->
options = options or {}
(value, event, table, refreshFnc) ->
gui.tools.blockUI()
parent.rest.gui value.type, ((guiDefinition) ->
buttons = null
buttons = gui.methods.typedTestButton(parent.rest, options.testButton.text, options.testButton.css, value.type) if options.testButton
tabs = (if options.guiProcessor then options.guiProcessor(guiDefinition) else guiDefinition) # Preprocess fields (probably generate tabs...)
parent.rest.item value.id, (item) ->
gui.tools.unblockUI()
gui.forms.launchModal
title: modalTitle + " <b>" + value.name + "</b>"
fields: tabs
item: item
preprocessor: options.preprocessor
buttons: buttons
success: (form_selector, closeFnc) ->
fields = gui.forms.read(form_selector)
fields.data_type = value.type
fields = (if options.fieldsProcessor then options.fieldsProcessor(fields) else fields)
parent.rest.save fields, ((data) -> # Success on put
closeFnc()
refreshFnc()
gui.notify gettext("Edition successfully done"), "success"
return
), gui.failRequestModalFnc(modalErrorMsg, true) # Fail on put, show modal message
return
return
return
), gui.failRequestModalFnc(modalErrorMsg, true)
return
# "Generic" new method to set onNew table
gui.methods.typedNew = (parent, modalTitle, modalErrorMsg, options) ->
options = options or {}
(type, table, refreshFnc) ->
gui.tools.blockUI()
parent.rest.gui type, ((guiDefinition) ->
gui.tools.unblockUI()
buttons = null
buttons = gui.methods.typedTestButton(parent.rest, options.testButton.text, options.testButton.css, type) if options.testButton
tabs = (if options.guiProcessor then options.guiProcessor(guiDefinition) else guiDefinition) # Preprocess fields (probably generate tabs...)
title = modalTitle
title += " " + gettext("of type") + " <b>" + parent.types[type].name + "</b>" if parent.types[type] isnt `undefined`
gui.forms.launchModal
title: title
fields: tabs
item: `undefined`
preprocessor: options.preprocessor
buttons: buttons
success: (form_selector, closeFnc) ->
fields = gui.forms.read(form_selector)
fields.data_type = type if parent.types[type] isnt `undefined`
fields = (if options.fieldsProcessor then options.fieldsProcessor(fields) else fields) # Process fields before creating?
parent.rest.create fields, ((data) -> # Success on put
closeFnc()
refreshFnc()
gui.notify gettext("Creation successfully done"), "success"
return
), gui.failRequestModalFnc(modalErrorMsg, true) # Fail on put, show modal message
return
return
), gui.failRequestModalFnc(modalErrorMsg, true)
return
gui.methods.del = (parent, modalTitle, modalErrorMsg) ->
(value, event, table, refreshFnc) ->
gui.doLog value
name = value.name or value.friendly_name
content = gettext("Are you sure do you want to delete ") + "<b>" + name + "</b>"
modalId = gui.launchModal(modalTitle, content,
actionButton: "<button type=\"button\" class=\"btn btn-danger button-accept\">" + gettext("Delete") + "</button>"
)
$(modalId + " .button-accept").click ->
$(modalId).modal "hide"
parent.rest.del value.id, (->
refreshFnc()
gui.notify gettext("Item deleted"), "success"
return
), gui.failRequestModalFnc(modalErrorMsg)
return
return
# Public attributes
gui.debug = true
return
) window.gui = window.gui or {}, jQuery

View File

@ -92,31 +92,31 @@
<script src="{% get_static_prefix %}adm/js/handlebars-v1.3.0.js"></script>
<!-- First all api related stuff -->
<script src="{% get_static_prefix %}adm/js/api.js"></script>
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/api.coffee"></script>
<!-- utilities attached to api -->
<script src="{% get_static_prefix %}adm/js/api-tools.js"></script>
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/api-tools.coffee"></script>
<!-- templates related, inserts itself into api -->
<script src="{% get_static_prefix %}adm/js/api-templates.js"></script>
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/api-templates.coffee"></script>
<!-- export to xls, inserts itself into api -->
<script src="{% get_static_prefix %}adm/js/api-spreadsheet.js"></script>
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/api-spreadsheet.coffee"></script>
<script src="{% get_static_prefix %}adm/js/gui.js"></script>
<script src="{% get_static_prefix %}adm/js/gui-tools.js"></script>
<script src="{% get_static_prefix %}adm/js/gui-form.js"></script>
<script src="{% get_static_prefix %}adm/js/gui-element.js"></script>
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/gui.coffee"></script>
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/gui-tools.coffee"></script>
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/gui-form.coffee"></script>
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/gui-element.coffee"></script>
<!-- user interface management -->
<script src="{% get_static_prefix %}adm/js/gui-definition.js"></script>
<script src="{% get_static_prefix %}adm/js/gui-d-dashboard.js"></script>
<script src="{% get_static_prefix %}adm/js/gui-d-services.js"></script>
<script src="{% get_static_prefix %}adm/js/gui-d-authenticators.js"></script>
<script src="{% get_static_prefix %}adm/js/gui-d-osmanagers.js"></script>
<script src="{% get_static_prefix %}adm/js/gui-d-connectivity.js"></script>
<script src="{% get_static_prefix %}adm/js/gui-d-servicespools.js"></script>
<script src="{% get_static_prefix %}adm/js/gui-d-config.js"></script>
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/gui-definition.coffee"></script>
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/gui-d-dashboard.coffee"></script>
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/gui-d-services.coffee"></script>
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/gui-d-authenticators.coffee"></script>
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/gui-d-osmanagers.coffee"></script>
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/gui-d-connectivity.coffee"></script>
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/gui-d-servicespools.coffee"></script>
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/gui-d-config.coffee"></script>
<script>
$(function() {

View File

@ -0,0 +1,7 @@
{% extends "uds/html5/templates/base.html" %}
{% load i18n %}
{% block body %}
{% endblock %}

View File

@ -0,0 +1,29 @@
{% extends "uds/html5/templates/base.html" %}
{% load i18n static %}
{% block title %}{% trans "Login redirection to UDS" %}{% endblock %}
{% block script %}
<script src="{% get_static_prefix %}js/PluginDetect_Java.js" type="text/javascript" ></script>
<script type="text/javascript">
function isJavaAvailable() {
if( PluginDetect.isMinVersion('Java', '1,6,0,0','{% get_static_prefix %}/other/getJavaInfo.jar') == 1 )
return true;
return false;
}
$(document).ready(function() {
var java = isJavaAvailable() ? "y" : "n";
window.location.replace("{% url "uds.web.views.authJava" idAuth=idAuth hasJava='' %}" + java);
});
</script>
{% endblock %}
{% block body %}
<!--<div>
<a href="<a href="{% url "uds.web.views.index" %}">{% trans "Go to main page" %}
</div>-->
{% endblock %}

View File

@ -0,0 +1,21 @@
{% extends "uds/html5/templates/base.html" %}
{% load i18n %}
{% block body %}
<div class="panel panel-info">
<div class="panel-heading">
{% trans "Downloads" %}
</div>
<div class="panel-body">
<p>{% trans "This page contains a list of downloadables provided by different modules" %}</p>
<div class="list-group">
{% for f in files %}
<a class="list-group-item" href="{% url "uds.web.views.download" idDownload=f.id %}">
<h4 class="list-group-item-heading">{{ f.name }}</h4>
<p class="list-group-item-text">{{ f.comment|safe }}</p>
</a>
{% endfor %}
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,14 @@
extends "uds/html5/templates/base.html"
- load i18n
block body
div.panel.panel-info
div.panel-heading
- trans "Downloads"
div.panel-body
p
- trans "This page contains a list of downloadables provided by different modules"
div.list-group
for f in files
a.list-group-item(href!="{% url "uds.web.views.download" idDownload=f.id %}")
h4.list-group-item-heading: f.name
p.list-group-item-text: {{ f.comment|safe }}

View File

@ -0,0 +1,18 @@
{% extends "uds/html5/templates/base.html" %}
{% load i18n static %}
{% block title %}{% trans 'Error' %}{% endblock %}
{% block css %}
<style>
.center {text-align: center; margin-left: auto; margin-right: auto; margin-bottom: auto; margin-top: auto;}
</style>
{% endblock %}
{% block body %}
<div class="jumbotron center">
<h1>{% trans 'An error occurred' %}</h1>
<br />
<p>{% trans "Error" %}: <b class="text-danger">{{ errorString }}</b></p>
<br />
<a href="{% url 'uds.web.views.index' %}" class="btn btn-lg btn-info"><i class="fa fa-home"></i> {% trans "Back to services list" %}</a>
</div>
{% endblock %}

View File

@ -0,0 +1,131 @@
{% extends "uds/html5/templates/base.html" %}
{% load i18n html5 %}
{% block title %}{% trans 'Available services list' %}{% endblock %}
{% block css %}
<style media="screen">
.list-btn-group {
margin-bottom: 24px;
margin-right: 24px;
}
.list-btn {
padding-top: 24px;
height: 96px;
overflow: hidden;
}
.list-btn-reduced {
width: 96px;
}
.list-btn-normal {
width: 112px;
}
.list-dropdown-toggle {
width: 16px;
}
.transport {
width: 48px;
height: 48px;
}
/* We need to write under button */
.btn-group {
max-width: 114px;
}
{% ifbrowser firefox %}
{% else %}
.btn .caret {
margin-left: -4px;
}
{% endifbrowser %}
</style>
{% endblock %}
{% block js %}
<script>
$(function() {
{% if not java %}
$("a.nojava").click( function() {
$('#no-java-dialog').modal({
keyboard: false
})
return false;
});
{% endif %}
$('.transport-caption-small').popover({container: 'body', trigger: 'hover', delay: { show: 500, hide: 100 }, placement: 'auto bottom'});
});
</script>
{% endblock %}
{% block body %}
<h2>{% trans "Services" %}</h2>
<div class="clearfix">
{% for ser in services %}
{% if ser.transports %}
{% with trans=ser.transports|first numTransports=ser.transports|length %}
<div class="btn-group list-btn-group">
<a class="{% if not java and trans.needsJava %} nojava{% endif %} btn list-btn btn-default list-btn-{% if numTransports > 1 %}reduced{% else %}normal{% endif %}"
href="{% url "uds.web.views.service" idService=ser.id idTransport=trans.id %}"
title="{{ ser.name }}"
data-toggle="tooltip">
<img src="{% url "uds.web.views.serviceImage" idImage=ser.imageId %}" class="transport" alt="{{ ser.name }}" />
</a>
{% if numTransports > 1 %}
<button type="button" class="btn list-btn btn-default dropdown-toggle list-dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span>
<span class="sr-only">{% trans 'transports' %}</span>
</button>
<ul class="dropdown-menu" role="menu">
{% for trans in ser.transports %}
{% if not forloop.first %}
<li><a{% if not java and trans.needsJava %} class="nojava"{% endif %} href="{% url "uds.web.views.service" idService=ser.id idTransport=trans.id %}"><img src="{% url "uds.web.views.transportIcon" idTrans=trans.id %}" alt="{{ trans.name }}" />{{ trans.name }}</a></li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
<div class="transport-caption{% if ser.name|length > 16 %}-small{% endif %} text-center" data-content="{{ser.name|capfirst}}">{{ ser.name|capfirst|truncatechars:16 }}</div>
</div>
{% endwith %}
{% endif %}
{% endfor %}
</div>
{% if not java %}
<div class="modal fade" id="no-java-dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">{% trans "Java not found" %}</h4>
</div>
<div class="modal-body">
<p>{% trans "Java is not available on your browser, and the selected transport needs it." %}</p>
<p>{% trans "Please, install latest version from" %} <a href="http://www.java.com" target="_blank">{% trans "Java website" %}</a> {% trans "and restart browser" %}</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
{% endif %}
{% if user.isStaff %}
<div class="panel panel-warning">
<div class="panel-heading">
{% trans "Administrator info panel" %}
</div>
<div class="panel-body">
<p>{% trans "Ip" %}: {{ ip }}</p>
<p>{% trans "Networks" %}: {{ nets }}</p>
<p>{% trans "Transports" %}: {{ transports }}</p>
<p>{% trans "User Agent" %}: {{ request.META.HTTP_USER_AGENT }}</p>
<p>{% trans "OS" %}: {{ request.session.OS.OS }}</p>
</div>
</div>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,107 @@
{% extends "uds/html5/templates/base.html" %}
{% load i18n static html5 %}
{% block title %}{% trans 'Welcome to UDS' %}{% endblock %}
{% block js %}
<script src="{% get_static_prefix %}js/PluginDetect_Java.js" type="text/javascript" ></script>
<script type="text/javascript">
function isJavaAvailable() {
if( PluginDetect.isMinVersion('Java', '1,6,0,0','{% get_static_prefix %}/other/getJavaInfo.jar') == 1 )
return true;
//PluginDetect.getVersion('Java', '{% get_static_prefix %}/other/getJavaInfo.jar')
return false;
}
function chkNonStandardAuth(val, nonStandard) {
if( $.inArray(val, nonStandard) >= 0) {
var vv = val;
$("#login").hide(300, function() {
$.get('{%url "uds.web.views.customAuth" idAuth=''%}' + vv, function(data) {
$('#nonStandardLogin').html(data);
$('#nonStandard').show(300);
});
});
}
}
$(document).ready(function() {
$('#id_java').val(isJavaAvailable() ? "y" : "n");
var nonStandard = $('#id_nonStandard').val();
if( nonStandard.length == 0 )
nonStandard = [];
else
nonStandard = nonStandard.split(',');
var standard = $('#id_standard').val();
if( standard.length == 0 )
standard = [];
else
standard = standard.split(',');
$('#id_authenticator').change(function() { chkNonStandardAuth($(this).val(), nonStandard) });
if( standard.length > 0 ) {
$('#backToLogin').click(function() {
$('#id_authenticator').val(standard[0]);
$('#nonStandard').hide(300, function() {
$('#nonStandardLogin').html('');
$("#login").show(300);
});
return false;
});
} else {
$('#divBackToLogin').html('');
}
// Check initial value (may default auth is non Standard)
chkNonStandardAuth($('#id_authenticator').val(), nonStandard);
});
</script>
{% endblock %}
{% block body %}
<div class="container">
<div class="row" id="login">
<div class="col-md-4 col-md-offset-4">
<h1 class="text-center login-title">{% trans 'Welcome to UDS' %}</h1>
<div class="account-wall">
<img class="profile-img" src="{% get_static_prefix %}img/login-img.png" alt="">
<form id="loginform" class="form-signin" name="loginform" method="post">
{% if form.errors %}
<div class="alert alert-dismissable alert-danger">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
{% trans 'invalid credentials'|capfirst %}
</div>
{% endif %}
{% csrf_token %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
<div class="form-group">
<input id="id_{{form.user.name}}" name="{{form.user.name}}" type="text" class="form-control" placeholder="{% trans 'username'|capfirst %}" autofocus required>
<input id="id_{{form.password.name}}" name="{{form.password.name}}" type="password" class="form-control" placeholder="{% trans 'password'|capfirst %}">
{{ form.authenticator }}
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit"><span class="fa fa-sign-in"></span> {% trans 'Sign in' %}</button>
<div id="nonStandard" style="display: none">
<div id="nonStandardLogin" class="form">
non standard logins
</div>
<div id="divBackToLogin">
<a href="#" id="backToLogin" title="{% trans "Back to login" %}">{% trans "Back to login" %}</a>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="row" id="customHtml">
{% autoescape off %}
{{ customHtml }}
{% endautoescape %}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,14 @@
{% extends "uds/html5/templates/base.html" %}
{% load i18n %}
{% load static %}
{% block title %}{% trans "UDS User Preferences" %}{% endblock %}
{% block body %}
<form name="form1" method="post" class="form-preferences">
{% csrf_token %}
{% autoescape off %}{{ prefs_form }}{% endautoescape %}
<a href="{% url "uds.web.views.index" %}" class="btn btn-warning">{% trans "Cancel" %}</a>
<button type="submit" class="btn btn-success pull-right">{% trans "Save Preferences" %}</button>
</form>
{% endblock %}

View File

@ -0,0 +1,19 @@
{% extends "uds/html5/templates/base.html" %}
{% load i18n static %}
{% block title %}{% trans 'The service is not ready' %}{% endblock %}
{% block css %}
<style>
.center {text-align: center; margin-left: auto; margin-right: auto; margin-bottom: auto; margin-top: auto;}
</style>
{% endblock %}
{% block body %}
<div class="jumbotron center">
<h1> {% trans 'The service is not ready' %}</h1>
<br />
<p>{% trans 'The service is not ready at this moment. Please, try it again after a few seconds.' %}</p>
<h5>{% trans 'The service you has requested was not ready, and is being created right now. It will be availabe shortly' %}</h5>
<br />
<a href="{% url 'uds.web.views.index' %}" class="btn btn-lg btn-info"><i class="fa fa-home"></i> {% trans "Back" %}</a>
</div>
{% endblock %}

View File

@ -0,0 +1,20 @@
{% extends "uds/html5/templates/base.html" %}
{% load i18n static %}
{% block body %}
<div class="panel panel-default text-center">
<div id="transport">
</div>
</div>
<div class="text-center">
<a href="{% url 'uds.web.views.index' %}" class="btn btn-info"><i class="fa fa-home"></i> {% trans "Back to services list" %}</a>
</div>
{% endblock %}
{% block js %}
<script>
$(document).ready(function(){
$('#transport').append("{{ transport|escapejs }}");
});
</script>
{% endblock %}

View File

@ -0,0 +1,57 @@
{% load i18n html5 static %}
<nav class="navbar navbar-default navbar-fixed-top" role="navigation">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
<span class="sr-only">{% trans 'toggle navigation'|capfirst %}</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a href="{% url 'uds.web.views.index' %}"><span class="navbar-brand">Universal Desktop Services</span></a>
</div>
<div class="collapse navbar-collapse navbar-ex1-collapse">
<ul class="nav navbar-nav">
</ul>
<form id="form_language" action="{% url "django.views.i18n.set_language" %}" method="post">
{% csrf_token %}
<input id="id_language" type="hidden" name="language" value=""/>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">{% trans 'About' %}</a></li>
<li class="dropdown">
{% for lang in LANGUAGES %}
{% if lang.0 == LANGUAGE_CODE %}
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="glyphicon bfh-flag-{{ lang.0|country }}"></i>{% trans lang.1|capfirst %}<b class="caret"></b></a>
{% endif %}
{% endfor %}
<ul class="dropdown-menu">
{% for lang in LANGUAGES %}
{% if lang.0 != LANGUAGE_CODE %}
<li><a href="#" onclick='$("#id_language").val("{{ lang.0 }}"); $("#form_language").submit()'><i class="glyphicon bfh-flag-{{ lang.0|country }}"></i>{% trans lang.1|capfirst %}</a></li>
{% endif %}
{% endfor %}
</ul>
</li>
{% if user and not user.is_anonymous %}
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-user"></i> {{ user.real_name }} <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="{% url "uds.web.views.prefs" %}"><span class="fa fa-edit"></span> {% trans 'Preferences' %}</a></li>
{% if user.staff_member or user.is_admin %}
<li><a href="{% url "uds.web.views.download" idDownload='' %}"><span class="fa fa-download"></span> {% trans "Downloads" %}</a></li>
{% ifbrowser ie<8 %}
{% else %}
<li><a href="{% url "uds.admin.views.index" %}"><span class="fa fa-dashboard"></span> Dashboard</a></li>
{% endifbrowser %}
{% endif %}
<li><a href="{% url 'uds.web.views.logout' %}"><span class="fa fa-power-off text-danger"></span> {% trans 'Logout' %}</a></li>
</ul>
</li>
{% endif %}
</ul>
</form>
</div>
</nav>

View File

@ -0,0 +1,102 @@
{% load l10n i18n static html5 compress %}{% spaceless %}
{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
<!DOCTYPE html>{% endspaceless %}
<html lang='{{LANGUAGE_CODE}}'>
<head>
<title>{% block title %}{% endblock %}</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="robots" content="noindex, nofollow" />
{% block meta %}{% endblock %}
{% block icon %}<link href="{% get_static_prefix %}img/favicon.png" rel="icon" type="image/x-icon" />{% endblock %}
<!-- Bootstrap -->
{% compress css %}
<link href="{% get_static_prefix %}css/bootstrap.min.css" rel="stylesheet" media="screen">
<link href="{% get_static_prefix %}css/font-awesome.min.css" rel="stylesheet" media="screen">
{% endcompress %}
{% ifbrowser ie<9 %}
<script src="{% get_static_prefix %}/js/respond.min.js"></script>
{% endifbrowser %}
{% compress css %}
<link href="{% get_static_prefix %}css/bootstrap-theme.min.css" rel="stylesheet" media="screen">
<link href="{% get_static_prefix %}css/bootstrap-formhelpers.min.css" rel="stylesheet" media="screen">
<link href="{% get_static_prefix %}css/bootstrap-select.min.css" rel="stylesheet" media="screen">
{% ifbrowser ie<10 %}
<link href="{% get_static_prefix %}css/fix.css" rel="stylesheet" media="screen">
{% endifbrowser %}
<link href="{% get_static_prefix %}css/bootstrap-responsive.min.css" rel="stylesheet" media="screen">
<link href="{% get_static_prefix %}css/bootstrap-switch.css" rel="stylesheet" media="screen">
<link href="{% get_static_prefix %}css/uds-html5.css" rel="stylesheet" media="screen">
{% endcompress %}
{% compress css %}
{% ifbrowser ie<9 %}
<script src="{% get_static_prefix %}/js/html5shiv.js"></script>
{% endifbrowser %}
{% block css %}{% endblock %}
{% endcompress %}
</head>
<body>
<!-- Navigation bar -->
{% block menu %}{% include 'uds/html5/snippets/navbar.html' %}{% endblock %}
<!-- End of menu -->
<div class="st-sticky-wrap">
<div class="container">
{% ifbrowser ie<10 %}
<div class="alert alert-dismissable alert-danger">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
{% trans 'Your browser is supported only partially. Please, upgrade it to a modern html5 browser like Firefox, Chrome, Opera, ... (IE must be 10 or better)' %}
</div>
{% endifbrowser %}
{% block messages %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-dismissable {{ message.tags }}">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
{{ message }}
</div>
{% endfor %}
{% endif %}
{% endblock %}
<!-- Content -->
{% block body %}{% endblock %}
<!-- End of content -->
</div> <!-- End of container -->
<div class="st-sticky-push"></div>
</div>
<footer>
{% block footer %}
<a href="http://www.udsenterprise.com">&copy; 2012-2014 Virtual Cable S.L.U.</a>
{% endblock %}
</footer>
<script src="{% url 'uds.web.views.jsCatalog' LANGUAGE_CODE %}"></script>
{% compress js %}
<script src="{% get_static_prefix %}js/jquery-1.10.2.min.js"></script>
<script src="{% get_static_prefix %}js/jquery.cookie.js"></script>
<script src="{% get_static_prefix %}js/bootstrap.min.js"></script>
<script src="{% get_static_prefix %}js/bootstrap-switch.min.js"></script>
<script src="{% get_static_prefix %}js/bootstrap-select.min.js"></script>
<script>
$(function(){
$('.selectpicker').selectpicker();
});
</script>
{% block js %}{% endblock %}
{% endcompress %}
</body>
</html>