diff --git a/server/src/uds/REST/methods/users_groups.py b/server/src/uds/REST/methods/users_groups.py index 1095a60aa..ae14488f5 100644 --- a/server/src/uds/REST/methods/users_groups.py +++ b/server/src/uds/REST/methods/users_groups.py @@ -46,6 +46,8 @@ from uds.models import Authenticator, User, Group from uds.core.auths.User import User as aUser from uds.core.managers import cryptoManager from uds.REST import RequestError +from uds.core.ui.images import DEFAULT_THUMB_BASE64 +from .user_services import AssignedService from uds.REST.model import DetailHandler @@ -55,9 +57,16 @@ logger = logging.getLogger(__name__) # Details of /auth +def getPoolsForGroups(groups): + for g in groups: + for servicePool in g.deployedServices.all(): + yield servicePool + class Users(DetailHandler): + custom_methods = ['servicesPools', 'userServices'] + @staticmethod def uuid_to_id(iterator): for v in iterator: @@ -166,9 +175,40 @@ class Users(DetailHandler): return 'deleted' + def servicesPools(self, parent, item): + uuid = processUuid(item) + user = parent.users.get(uuid=processUuid(uuid)) + res = [] + for i in getPoolsForGroups(user.groups.all()): + res.append({ + 'id': i.uuid, + 'name': i.name, + 'thumb': i.image.thumb64 if i.image is not None else DEFAULT_THUMB_BASE64, + 'user_services_count': i.userServices.count(), + 'state': _('With errors') if i.isRestrained() else _('Ok'), + }) + + return res + + def userServices(self, parent, item): + uuid = processUuid(item) + user = parent.users.get(uuid=processUuid(uuid)) + res = [] + for i in user.userServices.all(): + if i.state == State.USABLE: + v = AssignedService.itemToDict(i) + v['pool'] = i.deployed_service.name + v['pool_id'] = i.deployed_service.uuid + res.append(v) + + return res + + class Groups(DetailHandler): + custom_methods = ['servicesPools'] + def getItems(self, parent, item): try: multi = False @@ -287,3 +327,18 @@ class Groups(DetailHandler): self.invalidItemException() return 'deleted' + + def servicesPools(self, parent, item): + uuid = processUuid(item) + group = parent.groups.get(uuid=processUuid(uuid)) + res = [] + for i in getPoolsForGroups((group,)): + res.append({ + 'id': i.uuid, + 'name': i.name, + 'thumb': i.image.thumb64 if i.image is not None else DEFAULT_THUMB_BASE64, + 'user_services_count': i.userServices.count(), + 'state': _('With errors') if i.isRestrained() else _('Ok'), + }) + + return res diff --git a/server/src/uds/models/ServicesPool.py b/server/src/uds/models/ServicesPool.py index 18836ad49..2b9041320 100644 --- a/server/src/uds/models/ServicesPool.py +++ b/server/src/uds/models/ServicesPool.py @@ -63,7 +63,7 @@ from datetime import datetime, timedelta import logging import pickle -__updated__ = '2017-01-20' +__updated__ = '2017-01-23' logger = logging.getLogger(__name__) diff --git a/server/src/uds/osmanagers/WindowsOsManager/WinDomainOsManager.py b/server/src/uds/osmanagers/WindowsOsManager/WinDomainOsManager.py index 6b38126b5..f99119d2b 100644 --- a/server/src/uds/osmanagers/WindowsOsManager/WinDomainOsManager.py +++ b/server/src/uds/osmanagers/WindowsOsManager/WinDomainOsManager.py @@ -44,8 +44,8 @@ class WinDomainOsManager(WindowsOsManager): if values is not None: if values['domain'] == '': raise osmanagers.OSManager.ValidationException(_('Must provide a domain!')) - if values['domain'].find('.') == -1: - raise osmanagers.OSManager.ValidationException(_('Must provide domain in FQDN')) + # if values['domain'].find('.') == -1: + # raise osmanagers.OSManager.ValidationException(_('Must provide domain in FQDN')) if values['account'] == '': raise osmanagers.OSManager.ValidationException(_('Must provide an account to add machines to domain!')) if values['account'].find('\\') != -1: diff --git a/server/src/uds/static/adm/js/api-tools.coffee b/server/src/uds/static/adm/js/api-tools.coffee index b1020c2b1..b2f1d80e0 100644 --- a/server/src/uds/static/adm/js/api-tools.coffee +++ b/server/src/uds/static/adm/js/api-tools.coffee @@ -1,7 +1,7 @@ -# jshint strict: true +# jshint strict: true ((api, $, undefined_) -> "use strict" - api.tools = + api.tools = base64: (s) -> window.btoa unescape(encodeURIComponent(s)) input2timeStamp: (inputDate, inputTime) -> @@ -36,7 +36,7 @@ capitalize: (str) -> str = str.toLowerCase() return str.substr(0,1).toUpperCase() + str.substr(1) - + return str isEmpty: (obj) -> @@ -110,7 +110,7 @@ localizedStrftime = (locale) -> # - 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 @@ -118,13 +118,13 @@ _strftime = (fmt, d, locale, options) -> 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). @@ -133,20 +133,20 @@ _strftime = (fmt, d, locale, options) -> padding = null 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] @@ -256,17 +256,17 @@ quacksLikeDate = (x) -> # 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" length ?= 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 @@ -295,7 +295,7 @@ ordinal = (n) -> # 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() diff --git a/server/src/uds/static/adm/js/gui-d-authenticators.coffee b/server/src/uds/static/adm/js/gui-d-authenticators.coffee index 5c3cccda4..1ebadac9a 100644 --- a/server/src/uds/static/adm/js/gui-d-authenticators.coffee +++ b/server/src/uds/static/adm/js/gui-d-authenticators.coffee @@ -174,8 +174,10 @@ gui.authenticators.link = (event) -> id = selected[0].id type = gui.authenticators.types[selected[0].type] gui.doLog "Type", type - user = new GuiElement(api.authenticators.detail(id, "users", { permission: selected[0].permission }), "users") - group = new GuiElement(api.authenticators.detail(id, "groups", { permission: selected[0].permission }), "groups") + userAPI = api.authenticators.detail(id, "users", { permission: selected[0].permission }) + user = new GuiElement(userAPI, "users") + groupAPI = api.authenticators.detail(id, "groups", { permission: selected[0].permission }) + group = new GuiElement(groupAPI, "groups") grpTable = group.table( icon: 'groups' container: "groups-placeholder" @@ -292,6 +294,127 @@ gui.authenticators.link = (event) -> # New button will only be shown on authenticators that can create new users usrButtons = [ "edit" + { + text: gui.tools.iconAndText( 'fa-info', gettext('Information') ) + css: "disabled" + disabled: true + + click: (vals, value, btn, tbl, refreshFnc) -> + + if vals.length > 1 + return + + val = vals[0] + userAPI.invoke val.id + "/servicesPools", (pools) -> + userAPI.invoke val.id + "/userServices", (userServices) -> + user.rest.item val.id, (item) -> + group.rest.overview (groups) -> # Get groups + gui.doLog "Pools", pools + api.templates.get "user-info", (tmpl) -> + content = api.templates.evaluate(tmpl, + id: 'information', + groups_all: groups + groups: item.groups + pools: pools, + userServices: userServices, + goClass: 'goLink' + ) + modalId = gui.launchModal(gettext('User information'), content, + actionButton: " " + ) + + $('#information-groups-table').DataTable( + colReorder: true + stateSave: true + paging: true + info: false + autoWidth: false + lengthChange: false + pageLength: 10 + + columnDefs: [ + { 'width': '100%', 'targets': 0 }, + ] + + ordering: true + order: [[ 0, 'asc' ]] + + dom: '<>fr<"uds-table"t>ip' + + language: gui.config.dataTablesLanguage + ) + + + $('#information-pools-table').DataTable( + colReorder: true + stateSave: true + paging: true + info: false + autoWidth: false + lengthChange: false + pageLength: 10 + + columnDefs: [ + { 'width': '50%', 'targets': 0 }, + { 'width': '120px', 'targets': 1 }, + { 'width': '40px', 'targets': 2 }, + { 'width': '160px', 'targets': 3 }, + ] + + ordering: true + order: [[ 1, 'asc' ]] + + dom: '<>fr<"uds-table"t>ip' + + language: gui.config.dataTablesLanguage + ) + + $('#information-userservices-table').DataTable( + colReorder: true + stateSave: true + paging: true + info: false + autoWidth: false + lengthChange: false + pageLength: 10 + + columnDefs: [ + { 'width': '25%', 'targets': 0 }, + { 'width': '25%', 'targets': 1 }, + { 'width': '120px', 'targets': 2 }, + { 'width': '20%', 'targets': 3 }, + { 'width': '20%', 'targets': 4 }, + ] + + ordering: true + order: [[ 1, 'asc' ]] + + dom: '<>fr<"uds-table"t>ip' + + language: gui.config.dataTablesLanguage + ) + + $('.goLink').on('click', (event) -> + $this = $(this); + event.preventDefault(); + gui.lookupUuid = $this.attr('href').substr(1) + $(modalId).modal('hide') + setTimeout( -> + $(".lnk-deployed_services").click(); + , 500); + ) + + return + + select: (vals, value, btn, tbl, refreshFnc) -> + unless vals.length == 1 + $(btn).addClass("disabled").prop('disabled', true) + return + + $(btn).removeClass("disabled").prop('disabled', false) + return + + } "delete" "xls" ] diff --git a/server/src/uds/static/adm/js/gui-d-services.coffee b/server/src/uds/static/adm/js/gui-d-services.coffee index 91a816e2b..f79d163b1 100644 --- a/server/src/uds/static/adm/js/gui-d-services.coffee +++ b/server/src/uds/static/adm/js/gui-d-services.coffee @@ -3,9 +3,6 @@ gui.providers = new GuiElement(api.providers, "provi") gui.providers.link = (event) -> "use strict" - iconAndText = (icon, text) -> - ' ' + text + '' - # Button definition to trigger "Test" action testButton = testButton: text: gettext("Test") @@ -112,7 +109,7 @@ gui.providers.link = (event) -> "new" "edit" { - text: iconAndText( 'fa-info', gettext('Information') ) + text: gui.tools.iconAndText( 'fa-info', gettext('Information') ) css: "disabled" disabled: true click: (vals, value, btn, tbl, refreshFnc) -> @@ -204,7 +201,7 @@ gui.providers.link = (event) -> "edit" { permission: api.permissions.MANAGEMENT - text: iconAndText('fa-ambulance', gettext("Maintenance")) + text: gui.tools.iconAndText('fa-ambulance', gettext("Maintenance")) css: "disabled" disabled: true click: (vals, value, btn, tbl, refreshFnc) -> @@ -230,14 +227,14 @@ gui.providers.link = (event) -> select: (vals, value, btn, tbl, refreshFnc) -> unless vals.length == 1 $(btn).removeClass("btn-warning").removeClass("btn-info").addClass("disabled").prop('disabled', true) - $(btn).empty().append(iconAndText('fa-ambulance', gettext("Maintenance"))) + $(btn).empty().append(gui.tools.iconAndText('fa-ambulance', gettext("Maintenance"))) return val = vals[0] if val.maintenance_mode is false - content = iconAndText('fa-ambulance', gettext('Enter maintenance Mode')) + content = gui.tools.iconAndText('fa-ambulance', gettext('Enter maintenance Mode')) cls = 'btn-warning' else - content = iconAndText('fa-truck',gettext('Exit Maintenance Mode')) + content = gui.tools.iconAndText('fa-truck',gettext('Exit Maintenance Mode')) cls = 'btn-info' $(btn).removeClass("disabled").addClass(cls).prop('disabled', false) diff --git a/server/src/uds/static/adm/js/gui-tools.coffee b/server/src/uds/static/adm/js/gui-tools.coffee index 4d4dad69b..547a3563d 100644 --- a/server/src/uds/static/adm/js/gui-tools.coffee +++ b/server/src/uds/static/adm/js/gui-tools.coffee @@ -173,4 +173,7 @@ (data, type, full) -> levels[data] or "OTHER" + iconAndText: (icon, text) -> + ' ' + text + '' + return diff --git a/server/src/uds/templates/uds/admin/index.html b/server/src/uds/templates/uds/admin/index.html index f87edc3bf..0ea274d93 100644 --- a/server/src/uds/templates/uds/admin/index.html +++ b/server/src/uds/templates/uds/admin/index.html @@ -193,6 +193,7 @@ {% js_template 'providers' %} {% js_template 'service-info' %} {% js_template 'authenticators' %} + {% js_template 'user-info' %} {% js_template 'osmanagers' %} {% js_template 'connectivity' %} {% js_template 'services_pool' %} diff --git a/server/src/uds/templates/uds/admin/tmpl/user-info.html b/server/src/uds/templates/uds/admin/tmpl/user-info.html new file mode 100644 index 000000000..12a469d80 --- /dev/null +++ b/server/src/uds/templates/uds/admin/tmpl/user-info.html @@ -0,0 +1,86 @@ +{% load i18n html5 %} +{% verbatim %} + +
+
+
+
+ + + + + + + + {{#each groups_all }} + {{# ifbelongs id ../groups }} + + + + {{/ ifbelongs }} + {{/each }} + +
{% endverbatim %}{% trans 'Group' %}{% verbatim %}
{{ name }}
+
+
+
+
+
+
+ + + + + + + + + + + {{#each pools }} + + + + + + + {{/each }} + +
{% endverbatim %}{% trans 'Pool' %}{% verbatim %}{% endverbatim %}{% trans 'State' %}{% verbatim %}{% endverbatim %}{% trans 'Image' %}{% verbatim %}
{{ name }} {{ state }}{{ user_services_count }}
+
+
+
+
+
+
+ + + + + + + + + + + + {{#each userServices }} + + + + + + + + {{/each }} + +
{% endverbatim %}{% trans 'Unique ID' %}{% verbatim %}{% endverbatim %}{% trans 'Friendly Name' %}{% verbatim %}{% endverbatim %}{% trans 'In Use' %}{% verbatim %}{% endverbatim %}{% trans 'IP' %}{% verbatim %}{% endverbatim %}{% trans 'Service Pool' %}{% verbatim %}
{{ unique_id }}{{ friendly_name }}{{#if in_use }}{% endverbatim %}{% trans 'Yes' %}{% verbatim %}{{ else }}{% endverbatim %}{% trans 'No' %}{% verbatim %}{{/if }}{{ ip }}{{ pool }}
+
+
+
+
+{% endverbatim %} diff --git a/server/src/uds/templates/uds/html5/download_client.html b/server/src/uds/templates/uds/html5/download_client.html index 259e72282..6b80fdf44 100644 --- a/server/src/uds/templates/uds/html5/download_client.html +++ b/server/src/uds/templates/uds/html5/download_client.html @@ -9,30 +9,30 @@

{% trans 'Download UDS Plugin for' %} {{ os|osName }}

-

{% trans 'In order to be able to execute UDS services, you need to have UDS plugin installed.' %}

+

{% trans 'In order to be able to execute UDS services, you need to download and install UDS Plugin.' %}

{{ os|pluginDownloadUrl|safe }}
-

{% trans 'Or download another version' %}

-

{% trans 'If your platform couldn’t be detected correctly, you can manually download the version required for your Operating System.' %}

+

{% trans 'Or download for other operating system' %}

+

{% trans 'In case that your operating system has not been correctly detected, you can download manually from' %}

{% if os != 'linux' %}

- {% trans 'Linux UDS plugin' %} + {% trans 'Download Linux UDS plugin' %}

{% endif %} {% if os != 'windows' %}

- {% trans 'Windows UDS plugin' %} + {% trans 'Download Windows UDS plugin' %}

{% endif %} {% if os != 'mac' %}

- {% trans 'Mac OSX (>10.5) UDS plugin' %} + {% trans 'Download Mac OS X (>10.5) UDS plugin' %}

{% endif %}

{% if request.user %} -

{% trans 'If you already have UDS Plugin installed and you can still see this message, you can disable automatic detection here.' %}

+

{% trans 'If you already have UDS Plugin installed but this message persists to appear, you can disable automatic detection here' %}