From 7a0465fca5eca361bdb02c12f05b52e05eff847e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20G=C3=B3mez?= Date: Mon, 17 Mar 2014 14:26:07 +0000 Subject: [PATCH] Updated admin script to use coffescript instead of directly javascript --- .../uds/static/adm/js/api-spreadsheet.coffee | 17 + .../uds/static/adm/js/api-templates.coffee | 143 ++++ server/src/uds/static/adm/js/api-templates.js | 1 + server/src/uds/static/adm/js/api-tools.coffee | 381 ++++++++++ server/src/uds/static/adm/js/api.coffee | 383 ++++++++++ server/src/uds/static/adm/js/api.js | 706 ++++++++---------- .../static/adm/js/gui-d-authenticators.coffee | 398 ++++++++++ .../src/uds/static/adm/js/gui-d-config.coffee | 48 ++ .../static/adm/js/gui-d-connectivity.coffee | 42 ++ .../uds/static/adm/js/gui-d-dashboard.coffee | 42 ++ .../uds/static/adm/js/gui-d-osmanagers.coffee | 27 + .../uds/static/adm/js/gui-d-services.coffee | 142 ++++ .../static/adm/js/gui-d-servicespools.coffee | 488 ++++++++++++ .../uds/static/adm/js/gui-definition.coffee | 16 + .../src/uds/static/adm/js/gui-element.coffee | 599 +++++++++++++++ server/src/uds/static/adm/js/gui-form.coffee | 285 +++++++ server/src/uds/static/adm/js/gui-tools.coffee | 106 +++ server/src/uds/static/adm/js/gui.coffee | 396 ++++++++++ server/src/uds/templates/uds/admin/index.html | 32 +- .../src/uds/templates/uds/pruebas/about.html | 7 + .../uds/templates/uds/pruebas/detectJava.html | 29 + .../uds/templates/uds/pruebas/downloads.html | 21 + .../uds/templates/uds/pruebas/downloads.jade | 14 + .../src/uds/templates/uds/pruebas/error.html | 18 + .../src/uds/templates/uds/pruebas/index.html | 131 ++++ .../src/uds/templates/uds/pruebas/login.html | 107 +++ .../src/uds/templates/uds/pruebas/prefs.html | 14 + .../uds/pruebas/service_not_ready.html | 19 + .../templates/uds/pruebas/show_transport.html | 20 + .../uds/pruebas/snippets/navbar.html | 57 ++ .../templates/uds/pruebas/templates/base.html | 102 +++ 31 files changed, 4400 insertions(+), 391 deletions(-) create mode 100644 server/src/uds/static/adm/js/api-spreadsheet.coffee create mode 100644 server/src/uds/static/adm/js/api-templates.coffee create mode 100644 server/src/uds/static/adm/js/api-tools.coffee create mode 100644 server/src/uds/static/adm/js/api.coffee create mode 100644 server/src/uds/static/adm/js/gui-d-authenticators.coffee create mode 100644 server/src/uds/static/adm/js/gui-d-config.coffee create mode 100644 server/src/uds/static/adm/js/gui-d-connectivity.coffee create mode 100644 server/src/uds/static/adm/js/gui-d-dashboard.coffee create mode 100644 server/src/uds/static/adm/js/gui-d-osmanagers.coffee create mode 100644 server/src/uds/static/adm/js/gui-d-services.coffee create mode 100644 server/src/uds/static/adm/js/gui-d-servicespools.coffee create mode 100644 server/src/uds/static/adm/js/gui-definition.coffee create mode 100644 server/src/uds/static/adm/js/gui-element.coffee create mode 100644 server/src/uds/static/adm/js/gui-form.coffee create mode 100644 server/src/uds/static/adm/js/gui-tools.coffee create mode 100644 server/src/uds/static/adm/js/gui.coffee create mode 100644 server/src/uds/templates/uds/pruebas/about.html create mode 100644 server/src/uds/templates/uds/pruebas/detectJava.html create mode 100644 server/src/uds/templates/uds/pruebas/downloads.html create mode 100644 server/src/uds/templates/uds/pruebas/downloads.jade create mode 100644 server/src/uds/templates/uds/pruebas/error.html create mode 100644 server/src/uds/templates/uds/pruebas/index.html create mode 100644 server/src/uds/templates/uds/pruebas/login.html create mode 100644 server/src/uds/templates/uds/pruebas/prefs.html create mode 100644 server/src/uds/templates/uds/pruebas/service_not_ready.html create mode 100644 server/src/uds/templates/uds/pruebas/show_transport.html create mode 100644 server/src/uds/templates/uds/pruebas/snippets/navbar.html create mode 100644 server/src/uds/templates/uds/pruebas/templates/base.html diff --git a/server/src/uds/static/adm/js/api-spreadsheet.coffee b/server/src/uds/static/adm/js/api-spreadsheet.coffee new file mode 100644 index 00000000..51135311 --- /dev/null +++ b/server/src/uds/static/adm/js/api-spreadsheet.coffee @@ -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 = "" + "" + data + "" + +@api.spreadsheet.row = (cell) -> + "" + cell + "" + +return diff --git a/server/src/uds/static/adm/js/api-templates.coffee b/server/src/uds/static/adm/js/api-templates.coffee new file mode 100644 index 00000000..8a9bc32e --- /dev/null +++ b/server/src/uds/static/adm/js/api-templates.coffee @@ -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 "" inside inline elements (they are already scripts) +Handlebars.registerHelper "javascript", (options) -> + new Handlebars.SafeString("") + + +# 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 diff --git a/server/src/uds/static/adm/js/api-templates.js b/server/src/uds/static/adm/js/api-templates.js index 0fc39d91..0cf8abb4 100644 --- a/server/src/uds/static/adm/js/api-templates.js +++ b/server/src/uds/static/adm/js/api-templates.js @@ -101,6 +101,7 @@ return; } } + api.doLog('Invoking ajax for ', api.url_for(name, 'template')); $.ajax({ url : api.url_for(name,'template'), type : "GET", diff --git a/server/src/uds/static/adm/js/api-tools.coffee b/server/src/uds/static/adm/js/api-tools.coffee new file mode 100644 index 00000000..cf270c18 --- /dev/null +++ b/server/src/uds/static/adm/js/api-tools.coffee @@ -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 +# +#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, ) + if typeof padding is "number" + length = padding + padding = "0" + + # Defaults handle pad(n) and pad(n, ) + 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 diff --git a/server/src/uds/static/adm/js/api.coffee b/server/src/uds/static/adm/js/api.coffee new file mode 100644 index 00000000..14722cce --- /dev/null +++ b/server/src/uds/static/adm/js/api.coffee @@ -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 diff --git a/server/src/uds/static/adm/js/api.js b/server/src/uds/static/adm/js/api.js index ba9045eb..27495aae 100644 --- a/server/src/uds/static/adm/js/api.js +++ b/server/src/uds/static/adm/js/api.js @@ -128,397 +128,353 @@ // 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; - this.cache = api.cacheTable[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 ) { - this.cache[key] = not_found_fnc(); - } - return this.cache[key]; - }, + // Cache related + function Cache(cacheName) { + api.cacheTable[cacheName] = api.cacheTable[cacheName] || {}; - put: function(key, value) { - "use strict"; - this.cache[key] = value; - }, -}; - - -// Great part of UDS REST api provides same methods. -// We will take advantage of this and save a lot of nonsense, prone to failure -// code :-) - -function BasicModelRest(path, options) { - "use strict"; - options = options || {}; - path = path || ''; - // Requests paths - this.path = path; - this.getPath = options.getPath || path; - this.logPath = options.logPath || path; - this.putPath = options.putPath || path; - this.testPath = options.testPath || (path + '/test'); - this.delPath = options.delPath || path; - this.typesPath = options.typesPath || (path + '/types'); - this.guiPath = options.guiPath || (path + '/gui'); - this.tableInfoPath = options.tableInfoPath || (path + '/tableinfo'); - this.cache = api.cache('bmr'+path); -} - -BasicModelRest.prototype = { - // options: - // cacheKey: '.' --> do not cache (undefined will set cacheKey to current path) - // undefined -- > use path as key - // success: success fnc to execute in case of success - _requestPath: function(path, options) { - "use strict"; + this.name = cacheName; + this.cache = api.cacheTable[cacheName]; + } + + Cache.prototype = { + get: function(key, not_found_fnc){ + not_found_fnc = not_found_fnc || function() { return undefined; }; + + if( this.cache[key] === undefined ) { + this.cache[key] = not_found_fnc(); + } + return this.cache[key]; + }, + + put: function(key, value) { + this.cache[key] = value; + }, + }; + + + // Great part of UDS REST api provides same methods. + // We will take advantage of this and save a lot of nonsense, prone to failure + // code :-) + + function BasicModelRest(path, options) { options = options || {}; - var success_fnc = options.success || function(){api.doLog('success function not provided for '+path);}; - var fail_fnc = options.fail; - var cacheKey = options.cacheKey || path; + path = path || ''; + // Requests paths + this.path = path; + this.getPath = options.getPath || path; + this.logPath = options.logPath || path; + this.putPath = options.putPath || path; + this.testPath = options.testPath || (path + '/test'); + this.delPath = options.delPath || path; + this.typesPath = options.typesPath || (path + '/types'); + this.guiPath = options.guiPath || (path + '/gui'); + this.tableInfoPath = options.tableInfoPath || (path + '/tableinfo'); + this.cache = api.cache('bmr'+path); + } + + BasicModelRest.prototype = { + // options: + // cacheKey: '.' --> do not cache (undefined will set cacheKey to current path) + // undefined -- > use path as key + // success: success fnc to execute in case of success + _requestPath: function(path, options) { + options = options || {}; + var success_fnc = options.success || function(){api.doLog('success function not provided for '+path);}; + var fail_fnc = options.fail; + var cacheKey = options.cacheKey || path; + + if( path == '.' ) { + success_fnc({}); + return; + } + + if (cacheKey != '.' && this.cache.get(cacheKey)) { + success_fnc(this.cache.get(cacheKey)); + } else { + var $this = this; + api.getJson(path, { + success: function(data) { + if( cacheKey != '.' ) { + $this.cache.put(cacheKey, data); + } + success_fnc(data); + }, + fail: fail_fnc, + }); + } + }, + get: function(options) { + options = options || {}; + + var path = this.getPath; + if ( options.id ) + path += '/' + options.id; + return this._requestPath(path, { + cacheKey: '.', // Right now, do not cache any "get" method + success: options.success, + fail: options.fail + + }); + }, + list: function(success_fnc, fail_fnc) { // This is "almost" an alias for get + return this.get({ + id: '', + success: success_fnc, + fail: fail_fnc + }); + }, + overview: function(success_fnc, fail_fnc) { + return this.get({ + id: 'overview', + success: success_fnc, + fail: fail_fnc + }); + }, + item: function(itemId, success_fnc, fail_fnc) { + return this.get({ + id: itemId, + success: success_fnc, + fail: fail_fnc + }); + + }, + // ------------- + // Log methods + // ------------- + getLogs: function(itemId, success_fnc, fail_fnc) { + var path = this.logPath + '/' + itemId + '/' + 'log'; + return this._requestPath(path, { + cacheKey: '.', + success: success_fnc, + fail: fail_fnc + }); + + }, - if( path == '.' ) { - success_fnc({}); - return; - } + // ------------- + // Put methods + // ------------- - if (cacheKey != '.' && this.cache.get(cacheKey)) { - success_fnc(this.cache.get(cacheKey)); - } else { - var $this = this; - api.getJson(path, { - success: function(data) { - if( cacheKey != '.' ) { - $this.cache.put(cacheKey, data); - } - success_fnc(data); - }, + put: function(data, options) { + options = options || {}; + + var path = this.putPath; + if ( options.id ) + path += '/' + options.id; + + api.putJson(path, data, { + success: options.success, + fail: options.fail + }); + }, + create: function(data, success_fnc, fail_fnc) { + + return this.put(data, { + success: success_fnc, + fail: fail_fnc + }); + }, + save: function(data, success_fnc, fail_fnc) { + + return this.put(data, { + id: data.id, + success: success_fnc, + fail: fail_fnc + }); + }, + + // Testing + test: function(type, data, success_fnc, fail_fnc) { + var path = this.testPath + '/' + type; + + api.putJson(path, data, { + success: success_fnc, + fail: fail_fnc, + method: 'POST' + }); + }, + // -------------- + // Delete + // -------------- + del: function(id, success_fnc, fail_fnc) { + var path = this.delPath + '/' + id; + + api.deleteJson(path, { + success: success_fnc, + fail: fail_fnc + }); + }, + + // -------------- + // Types methods + // -------------- + types : function(success_fnc, fail_fnc) { + return this._requestPath(this.typesPath, { + cacheKey: this.typesPath, + success: success_fnc, + }); + }, + gui: function(typeName, success_fnc, fail_fnc) { + // GUI returns a dict, that contains: + // 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, .... + + var path; + if( typeName !== undefined ) { + path = [this.guiPath, typeName].join('/'); + } else { + path = this.guiPath; + } + return this._requestPath(path, { + cacheKey: '.', // Gui is not cacheable, it's dynamic and can change from call to call + success: success_fnc, fail: fail_fnc, }); - } - }, - get: function(options) { - "use strict"; - options = options || {}; - - var path = this.getPath; - if ( options.id ) - path += '/' + options.id; - return this._requestPath(path, { - cacheKey: '.', // Right now, do not cache any "get" method - success: options.success, - fail: options.fail + }, + tableInfo : function(success_fnc, fail_fnc) { + success_fnc = success_fnc || function(){api.doLog('success not provided for tableInfo');}; - }); - }, - list: function(success_fnc, fail_fnc) { // This is "almost" an alias for get - "use strict"; - return this.get({ - id: '', - success: success_fnc, - fail: fail_fnc - }); - }, - overview: function(success_fnc, fail_fnc) { - "use strict"; - return this.get({ - id: 'overview', - success: success_fnc, - fail: fail_fnc - }); - }, - item: function(itemId, success_fnc, fail_fnc) { - "use strict"; - return this.get({ - id: itemId, - success: success_fnc, - fail: fail_fnc - }); + var path = this.tableInfoPath; + this._requestPath(path, { + success: success_fnc, + fail: fail_fnc, + }); + }, - }, - // ------------- - // Log methods - // ------------- - getLogs: function(itemId, success_fnc, fail_fnc) { - "use strict"; - var path = this.logPath + '/' + itemId + '/' + 'log'; - return this._requestPath(path, { - cacheKey: '.', - success: success_fnc, - fail: fail_fnc - }); - - }, - - // ------------- - // Put methods - // ------------- - - put: function(data, options) { - "use strict"; - options = options || {}; - - var path = this.putPath; - if ( options.id ) - path += '/' + options.id; - - api.putJson(path, data, { - success: options.success, - fail: options.fail - }); - }, - create: function(data, success_fnc, fail_fnc) { - "use strict"; - - return this.put(data, { - success: success_fnc, - fail: fail_fnc - }); - }, - save: function(data, success_fnc, fail_fnc) { - "use strict"; - - return this.put(data, { - id: data.id, - success: success_fnc, - fail: fail_fnc - }); - }, - - // Testing - test: function(type, data, success_fnc, fail_fnc) { - "use strict"; - - var path = this.testPath + '/' + type; - - api.putJson(path, data, { - success: success_fnc, - fail: fail_fnc, - method: 'POST' - }); - }, - // -------------- - // Delete - // -------------- - del: function(id, success_fnc, fail_fnc) { - "use strict"; - - var path = this.delPath + '/' + id; - - api.deleteJson(path, { - success: success_fnc, - fail: fail_fnc - }); - }, - - // -------------- - // Types methods - // -------------- - types : function(success_fnc, fail_fnc) { - "use strict"; - return this._requestPath(this.typesPath, { - cacheKey: this.typesPath, - success: success_fnc, - }); - }, - gui: function(typeName, success_fnc, fail_fnc) { - // GUI returns a dict, that contains: - // 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 ) { - path = [this.guiPath, typeName].join('/'); - } else { - path = this.guiPath; + detail: function(id, child, options) { + options = options || {}; + return new DetailModelRestApi(this, id, child, options); } - return this._requestPath(path, { - cacheKey: '.', // Gui is not cacheable, it's dynamic and can change from call to call - success: success_fnc, - fail: fail_fnc, - }); - }, - tableInfo : function(success_fnc, fail_fnc) { - "use strict"; - success_fnc = success_fnc || function(){api.doLog('success not provided for tableInfo');}; - - var path = this.tableInfoPath; - this._requestPath(path, { - success: success_fnc, - fail: fail_fnc, - }); - }, - detail: function(id, child, options) { - "use strict"; - options = options || {}; - return new DetailModelRestApi(this, id, child, options); + }; + + // For REST of type /auth/[id]/users, /services/[id]/users, ... + function DetailModelRestApi(parentApi, parentId, model, options) { + this.options = options; + this.base = new BasicModelRest([parentApi.path, parentId, model].join('/')); } - -}; - -// 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('/')); -} - -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, - fail: fail_fnc - }); - }, - save: function(data, success_fnc, fail_fnc) { - "use strict"; - - return this.put(data, { - id: data.id, - success: success_fnc, - fail: fail_fnc - }); - }, - // 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 { - return this.base.types(success_fnc, fail_fnc); - } - }, - 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); - }, + DetailModelRestApi.prototype = { + // Generates a basic model with fixed methods for "detail" models + get: function(success_fnc, fail_fnc) { + return this.base.get(success_fnc, fail_fnc); + }, + list: function(success_fnc, fail_fnc) { // This is "almost" an alias for get + return this.base.list(success_fnc, fail_fnc); + }, + overview: function(success_fnc, fail_fnc) { + return this.base.overview(success_fnc, fail_fnc); + }, + item: function(itemId, success_fnc, fail_fnc) { + return this.base.item(itemId, success_fnc, fail_fnc); + }, + // ------------- + // Log methods + // ------------- + getLogs: function(itemId, success_fnc, fail_fnc) { + return this.base.getLogs(itemId, success_fnc, fail_fnc); + }, + // Put methods + put: function(data, options) { + return this.base.put(data, options); + }, + create: function(data, success_fnc, fail_fnc) { + + return this.put(data, { + success: success_fnc, + fail: fail_fnc + }); + }, + save: function(data, success_fnc, fail_fnc) { + return this.put(data, { + id: data.id, + success: success_fnc, + fail: fail_fnc + }); + }, + // Testing + test: function(type, data, success_fnc, fail_fnc) { + return this.base.test(type, data, success_fnc, fail_fnc); + }, + // -------------- + // Delete + // -------------- + del: function(id, success_fnc, fail_fnc) { + return this.base.del(id, success_fnc, fail_fnc); + }, + + tableInfo: function(success_fnc, fail_fnc) { + return this.base.tableInfo(success_fnc, fail_fnc); + }, + types: function(success_fnc, fail_fnc) { + if( this.options.types ) { + this.options.types(success_fnc, fail_fnc); + } else { + return this.base.types(success_fnc, fail_fnc); + } + }, + gui: function(typeName, success_fnc, fail_fnc) { // Typename can be "undefined" to request MAIN gui (it it exists ofc..) + 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) { + return this.base.get({ + id: method, + success: success_fnc, + fail: 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, + // Populate api + + api.providers = new BasicModelRest('providers'); + // all services method used in providers + api.providers.allServices = function(success_fnc, fail_fnc) { + return this.get({ + id: 'allservices', success: success_fnc, fail: fail_fnc }); - }, + }; -}; - -// Populate api - -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, - fail: fail_fnc - }); -}; - -api.providers.service = function(id, success_fnc, fail_fnc) { - "use strict"; - return this.get({ - id: 'service/' + id, - success: success_fnc, - fail: 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, - 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 = function(type, success_fnc, fail_fnc) { - "use strict"; - return this.get({ - id: 'stats/' + type, - success: success_fnc, - fail: fail_fnc - }); -}; - - + api.providers.service = function(id, success_fnc, fail_fnc) { + return this.get({ + id: 'service/' + id, + success: success_fnc, + fail: fail_fnc + }); + }; + + + api.authenticators = new BasicModelRest('authenticators'); + // Search method used in authenticators + api.authenticators.search = function(id, type, term, success_fnc, fail_fnc) { + return this.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 = function(type, success_fnc, fail_fnc) { + return this.get({ + id: 'stats/' + type, + success: success_fnc, + fail: fail_fnc + }); + }; +}(window.api = window.api || {}, jQuery)); diff --git a/server/src/uds/static/adm/js/gui-d-authenticators.coffee b/server/src/uds/static/adm/js/gui-d-authenticators.coffee new file mode 100644 index 00000000..b3d605fc --- /dev/null +++ b/server/src/uds/static/adm/js/gui-d-authenticators.coffee @@ -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: "" + ) + $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 "" + 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") + " " + item.name + "", 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") + " " + value.name + "", 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 \ No newline at end of file diff --git a/server/src/uds/static/adm/js/gui-d-config.coffee b/server/src/uds/static/adm/js/gui-d-config.coffee new file mode 100644 index 00000000..50d375bb --- /dev/null +++ b/server/src/uds/static/adm/js/gui-d-config.coffee @@ -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 \ No newline at end of file diff --git a/server/src/uds/static/adm/js/gui-d-connectivity.coffee b/server/src/uds/static/adm/js/gui-d-connectivity.coffee new file mode 100644 index 00000000..a9a72677 --- /dev/null +++ b/server/src/uds/static/adm/js/gui-d-connectivity.coffee @@ -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 \ No newline at end of file diff --git a/server/src/uds/static/adm/js/gui-d-dashboard.coffee b/server/src/uds/static/adm/js/gui-d-dashboard.coffee new file mode 100644 index 00000000..76bf92c2 --- /dev/null +++ b/server/src/uds/static/adm/js/gui-d-dashboard.coffee @@ -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 \ No newline at end of file diff --git a/server/src/uds/static/adm/js/gui-d-osmanagers.coffee b/server/src/uds/static/adm/js/gui-d-osmanagers.coffee new file mode 100644 index 00000000..33a5c797 --- /dev/null +++ b/server/src/uds/static/adm/js/gui-d-osmanagers.coffee @@ -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 \ No newline at end of file diff --git a/server/src/uds/static/adm/js/gui-d-services.coffee b/server/src/uds/static/adm/js/gui-d-services.coffee new file mode 100644 index 00000000..e5be443a --- /dev/null +++ b/server/src/uds/static/adm/js/gui-d-services.coffee @@ -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 \ No newline at end of file diff --git a/server/src/uds/static/adm/js/gui-d-servicespools.coffee b/server/src/uds/static/adm/js/gui-d-servicespools.coffee new file mode 100644 index 00000000..30175d2c --- /dev/null +++ b/server/src/uds/static/adm/js/gui-d-servicespools.coffee @@ -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 "" + 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 = "" + value.auth_name + "\\" + 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 = " " + 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 = " " + value.name + value.state = gettext("Restrained") + value.name = " " + value.name + value.parent = service.name + catch e + value.name = " " + 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 \ No newline at end of file diff --git a/server/src/uds/static/adm/js/gui-definition.coffee b/server/src/uds/static/adm/js/gui-definition.coffee new file mode 100644 index 00000000..884c9e17 --- /dev/null +++ b/server/src/uds/static/adm/js/gui-definition.coffee @@ -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 \ No newline at end of file diff --git a/server/src/uds/static/adm/js/gui-element.coffee b/server/src/uds/static/adm/js/gui-element.coffee new file mode 100644 index 00000000..3f014133 --- /dev/null +++ b/server/src/uds/static/adm/js/gui-element.coffee @@ -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 = "" + $(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" + " " + renderEmptyCell(data) + else + renderEmptyCell data + + + # Custom icon renderer, in fact span with defined class + renderIcon = (icon) -> + (data, type, full) -> + if type is "display" + " " + renderEmptyCell(data) + else + renderEmptyCell data + + + # Custom icon based on type + renderIconDict = (iconDict) -> + (data, type, value) -> + if type is "display" + " " + 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 "
" + table.text + "
" + 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 "
" + table.text + "
" + 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 \ No newline at end of file diff --git a/server/src/uds/static/adm/js/gui-form.coffee b/server/src/uds/static/adm/js/gui-form.coffee new file mode 100644 index 00000000..3a037188 --- /dev/null +++ b/server/src/uds/static/adm/js/gui-form.coffee @@ -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 = "
" + "" + 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 "
" + h.html + "
" + tabs.push "
  • " + tab.title + "
  • " + active = "" + fillers = fillers.concat(h.fillers) # Fillers (callback based) + $.extend originalValues, h.originalValues # Original values + gui.doLog "Fillers:", h.fillers + return + + form += "
      " + tabs.join("\n") + "
    " + tabsContent.join("\n") + "
    " + else + h = gui.forms.fieldsToHtml(fields, item, editing) + form += h.html + fillers = fillers.concat(h.fillers) + $.extend originalValues, h.originalValues + 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 "" + 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 += "" + 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 \ No newline at end of file diff --git a/server/src/uds/static/adm/js/gui-tools.coffee b/server/src/uds/static/adm/js/gui-tools.coffee new file mode 100644 index 00000000..2f7b6872 --- /dev/null +++ b/server/src/uds/static/adm/js/gui-tools.coffee @@ -0,0 +1,106 @@ +# jshint strict: true +((gui, $, undefined_) -> + "use strict" + gui.tools = + blockUI: (message) -> + message = message or "

    " + gettext("Just a moment...") + "

    " + $.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) -> + "" + api.tools.strftime(format, new Date(data * 1000)) + "" + + + # 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 \ No newline at end of file diff --git a/server/src/uds/static/adm/js/gui.coffee b/server/src/uds/static/adm/js/gui.coffee new file mode 100644 index 00000000..931c466f --- /dev/null +++ b/server/src/uds/static/adm/js/gui.coffee @@ -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: " " + sLast: " " + sNext: " " + sPrevious: " " + + gui.config.dataTableButtons = + new: + text: " " + gettext("New") + "" + css: "btn btn3d btn3d-primary btn3d-tables" + + edit: + text: " " + gettext("Edit") + "" + css: "btn disabled btn3d-default btn3d btn3d-tables" + + delete: + text: " " + gettext("Delete") + "" + css: "btn disabled btn3d-default btn3d btn3d-tables" + + xls: + text: " " + gettext("Xls") + "" + 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 += "
  • " + value + "
  • " + return + + list += "
  • " + active + "
  • " + "
      " + list + "
    " + + + # 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 "" + gettext("Message") + "", "" + message + "", + actionButton: " " + + return + + gui.failRequestModalFnc = (title) -> + (jqXHR, textStatus, errorThrown) -> # fail on put + gui.tools.unblockUI() + gui.launchModal "" + title + "", jqXHR.responseText, + actionButton: " " + + return + + gui.promptModal = (title, question, options) -> + options = options or {} + options.actionButton = "" + options.closeButton = "" + 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 + " " + value.name + "" + 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") + " " + parent.types[type].name + "" 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 ") + "" + name + "" + modalId = gui.launchModal(modalTitle, content, + actionButton: "" + ) + $(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 \ No newline at end of file diff --git a/server/src/uds/templates/uds/admin/index.html b/server/src/uds/templates/uds/admin/index.html index b8abcc39..46e0df7d 100644 --- a/server/src/uds/templates/uds/admin/index.html +++ b/server/src/uds/templates/uds/admin/index.html @@ -92,31 +92,31 @@ - + - + - + - + - - - - + + + + - - - - - - - - + + + + + + + + + +{% endblock %} + +{% block body %} + +{% endblock %} \ No newline at end of file diff --git a/server/src/uds/templates/uds/pruebas/downloads.html b/server/src/uds/templates/uds/pruebas/downloads.html new file mode 100644 index 00000000..a1d1464d --- /dev/null +++ b/server/src/uds/templates/uds/pruebas/downloads.html @@ -0,0 +1,21 @@ +{% extends "uds/html5/templates/base.html" %} +{% load i18n %} + +{% block body %} +
    +
    + {% trans "Downloads" %} +
    +
    +

    {% trans "This page contains a list of downloadables provided by different modules" %}

    +
    + {% for f in files %} + +

    {{ f.name }}

    +

    {{ f.comment|safe }}

    +
    + {% endfor %} +
    +
    +
    +{% endblock %} diff --git a/server/src/uds/templates/uds/pruebas/downloads.jade b/server/src/uds/templates/uds/pruebas/downloads.jade new file mode 100644 index 00000000..489e655c --- /dev/null +++ b/server/src/uds/templates/uds/pruebas/downloads.jade @@ -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 }} diff --git a/server/src/uds/templates/uds/pruebas/error.html b/server/src/uds/templates/uds/pruebas/error.html new file mode 100644 index 00000000..3e30dbf5 --- /dev/null +++ b/server/src/uds/templates/uds/pruebas/error.html @@ -0,0 +1,18 @@ +{% extends "uds/html5/templates/base.html" %} +{% load i18n static %} + +{% block title %}{% trans 'Error' %}{% endblock %} +{% block css %} + +{% endblock %} +{% block body %} +
    +

    {% trans 'An error occurred' %}

    +
    +

    {% trans "Error" %}: {{ errorString }}

    +
    + {% trans "Back to services list" %} +
    +{% endblock %} \ No newline at end of file diff --git a/server/src/uds/templates/uds/pruebas/index.html b/server/src/uds/templates/uds/pruebas/index.html new file mode 100644 index 00000000..37449f7a --- /dev/null +++ b/server/src/uds/templates/uds/pruebas/index.html @@ -0,0 +1,131 @@ +{% extends "uds/html5/templates/base.html" %} +{% load i18n html5 %} + +{% block title %}{% trans 'Available services list' %}{% endblock %} + +{% block css %} + +{% endblock %} + +{% block js %} + +{% endblock %} + +{% block body %} +

    {% trans "Services" %}

    +
    + {% for ser in services %} + {% if ser.transports %} + {% with trans=ser.transports|first numTransports=ser.transports|length %} +
    + + {{ ser.name }} + + {% if numTransports > 1 %} + + + {% endif %} +
    {{ ser.name|capfirst|truncatechars:16 }}
    +
    + {% endwith %} + {% endif %} + {% endfor %} +
    + +{% if not java %} + +{% endif %} + +{% if user.isStaff %} +
    +
    + {% trans "Administrator info panel" %} +
    +
    +

    {% trans "Ip" %}: {{ ip }}

    +

    {% trans "Networks" %}: {{ nets }}

    +

    {% trans "Transports" %}: {{ transports }}

    +

    {% trans "User Agent" %}: {{ request.META.HTTP_USER_AGENT }}

    +

    {% trans "OS" %}: {{ request.session.OS.OS }}

    +
    +
    +{% endif %} +{% endblock %} \ No newline at end of file diff --git a/server/src/uds/templates/uds/pruebas/login.html b/server/src/uds/templates/uds/pruebas/login.html new file mode 100644 index 00000000..b0ef6a8a --- /dev/null +++ b/server/src/uds/templates/uds/pruebas/login.html @@ -0,0 +1,107 @@ +{% extends "uds/html5/templates/base.html" %} +{% load i18n static html5 %} + +{% block title %}{% trans 'Welcome to UDS' %}{% endblock %} + +{% block js %} + + +{% endblock %} + +{% block body %} +
    +
    +
    +

    {% trans 'Welcome to UDS' %}

    + +
    +
    +
    + {% autoescape off %} + {{ customHtml }} + {% endautoescape %} +
    +
    +{% endblock %} \ No newline at end of file diff --git a/server/src/uds/templates/uds/pruebas/prefs.html b/server/src/uds/templates/uds/pruebas/prefs.html new file mode 100644 index 00000000..fb36132f --- /dev/null +++ b/server/src/uds/templates/uds/pruebas/prefs.html @@ -0,0 +1,14 @@ +{% extends "uds/html5/templates/base.html" %} +{% load i18n %} +{% load static %} + +{% block title %}{% trans "UDS User Preferences" %}{% endblock %} + +{% block body %} +
    + {% csrf_token %} + {% autoescape off %}{{ prefs_form }}{% endautoescape %} + {% trans "Cancel" %} + +
    +{% endblock %} \ No newline at end of file diff --git a/server/src/uds/templates/uds/pruebas/service_not_ready.html b/server/src/uds/templates/uds/pruebas/service_not_ready.html new file mode 100644 index 00000000..1869d565 --- /dev/null +++ b/server/src/uds/templates/uds/pruebas/service_not_ready.html @@ -0,0 +1,19 @@ +{% extends "uds/html5/templates/base.html" %} +{% load i18n static %} + +{% block title %}{% trans 'The service is not ready' %}{% endblock %} +{% block css %} + +{% endblock %} +{% block body %} +
    +

    {% trans 'The service is not ready' %}

    +
    +

    {% trans 'The service is not ready at this moment. Please, try it again after a few seconds.' %}

    +
    {% trans 'The service you has requested was not ready, and is being created right now. It will be availabe shortly' %}
    +
    + {% trans "Back" %} +
    +{% endblock %} \ No newline at end of file diff --git a/server/src/uds/templates/uds/pruebas/show_transport.html b/server/src/uds/templates/uds/pruebas/show_transport.html new file mode 100644 index 00000000..79bb1141 --- /dev/null +++ b/server/src/uds/templates/uds/pruebas/show_transport.html @@ -0,0 +1,20 @@ +{% extends "uds/html5/templates/base.html" %} +{% load i18n static %} + +{% block body %} +
    +
    +
    +
    + +{% endblock %} + +{% block js %} + +{% endblock %} \ No newline at end of file diff --git a/server/src/uds/templates/uds/pruebas/snippets/navbar.html b/server/src/uds/templates/uds/pruebas/snippets/navbar.html new file mode 100644 index 00000000..7187ed51 --- /dev/null +++ b/server/src/uds/templates/uds/pruebas/snippets/navbar.html @@ -0,0 +1,57 @@ +{% load i18n html5 static %} + diff --git a/server/src/uds/templates/uds/pruebas/templates/base.html b/server/src/uds/templates/uds/pruebas/templates/base.html new file mode 100644 index 00000000..84710707 --- /dev/null +++ b/server/src/uds/templates/uds/pruebas/templates/base.html @@ -0,0 +1,102 @@ +{% load l10n i18n static html5 compress %}{% spaceless %} +{% get_current_language as LANGUAGE_CODE %} +{% get_available_languages as LANGUAGES %} +{% endspaceless %} + + + {% block title %}{% endblock %} + + + + + {% block meta %}{% endblock %} + + {% block icon %}{% endblock %} + + + {% compress css %} + + + {% endcompress %} + + {% ifbrowser ie<9 %} + + {% endifbrowser %} + + {% compress css %} + + + + + {% ifbrowser ie<10 %} + + {% endifbrowser %} + + + + + + {% endcompress %} + + {% compress css %} + {% ifbrowser ie<9 %} + + {% endifbrowser %} + + {% block css %}{% endblock %} + {% endcompress %} + + + + + {% block menu %}{% include 'uds/html5/snippets/navbar.html' %}{% endblock %} + +
    +
    + {% ifbrowser ie<10 %} +
    + + {% 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)' %} +
    + {% endifbrowser %} + {% block messages %} + {% if messages %} + {% for message in messages %} +
    + + {{ message }} +
    + {% endfor %} + {% endif %} + {% endblock %} + + {% block body %}{% endblock %} + +
    +
    +
    + + + + {% compress js %} + + + + + + + + + {% block js %}{% endblock %} + {% endcompress %} + + + \ No newline at end of file