diff --git a/apps/static/css/plugins/toastr/toastr.min.css b/apps/static/css/plugins/toastr/toastr.min.css new file mode 100644 index 000000000..567716ea2 --- /dev/null +++ b/apps/static/css/plugins/toastr/toastr.min.css @@ -0,0 +1,222 @@ +.toast-title { + font-weight: 700 +} + +.toast-message { + -ms-word-wrap: break-word; + word-wrap: break-word +} + +.toast-message a, .toast-message label { + color: #fff +} + +.toast-message a:hover { + color: #ccc; + text-decoration: none +} + +.toast-close-button { + position: relative; + right: -.3em; + top: -.3em; + float: right; + font-size: 20px; + font-weight: 700; + color: #fff; + -webkit-text-shadow: 0 1px 0 #fff; + text-shadow: 0 1px 0 #fff; + opacity: .8; + -ms-filter: alpha(Opacity=80); + filter: alpha(opacity=80) +} + +.toast-close-button:focus, .toast-close-button:hover { + color: #000; + text-decoration: none; + cursor: pointer; + opacity: .4; + -ms-filter: alpha(Opacity=40); + filter: alpha(opacity=40) +} + +button.toast-close-button { + padding: 0; + cursor: pointer; + background: 0 0; + border: 0; + -webkit-appearance: none +} + +.toast-top-center { + top: 0; + right: 0; + width: 100% +} + +.toast-bottom-center { + bottom: 0; + right: 0; + width: 100% +} + +.toast-top-full-width { + top: 0; + right: 0; + width: 100% +} + +.toast-bottom-full-width { + bottom: 0; + right: 0; + width: 100% +} + +.toast-top-left { + top: 12px; + left: 12px +} + +.toast-top-right { + top: 12px; + right: 12px +} + +.toast-bottom-right { + right: 12px; + bottom: 12px +} + +.toast-bottom-left { + bottom: 12px; + left: 12px +} + +#toast-container { + position: fixed; + z-index: 999999 +} + +#toast-container * { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box +} + +#toast-container > div { + position: relative; + overflow: hidden; + margin: 0 0 6px; + padding: 15px 15px 15px 50px; + width: 300px; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + background-position: 15px center; + background-repeat: no-repeat; + -moz-box-shadow: 0 0 12px #999; + -webkit-box-shadow: 0 0 12px #999; + box-shadow: 0 0 12px #999; + color: #fff; + opacity: .8; + -ms-filter: alpha(Opacity=80); + filter: alpha(opacity=80) +} + +#toast-container > :hover { + -moz-box-shadow: 0 0 12px #000; + -webkit-box-shadow: 0 0 12px #000; + box-shadow: 0 0 12px #000; + opacity: 1; + -ms-filter: alpha(Opacity=100); + filter: alpha(opacity=100); + cursor: pointer +} + +#toast-container > .toast-info { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=) !important +} + +#toast-container > .toast-error { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=) !important +} + +#toast-container > .toast-success { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==) !important +} + +#toast-container > .toast-warning { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=) !important +} + +#toast-container.toast-bottom-center > div, #toast-container.toast-top-center > div { + width: 300px; + margin: auto +} + +#toast-container.toast-bottom-full-width > div, #toast-container.toast-top-full-width > div { + width: 96%; + margin: auto +} + +.toast { + background-color: #030303 +} + +.toast-success { + background-color: #51a351 +} + +.toast-error { + background-color: #bd362f +} + +.toast-info { + background-color: #2f96b4 +} + +.toast-warning { + background-color: #f89406 +} + +.toast-progress { + position: absolute; + left: 0; + bottom: 0; + height: 4px; + background-color: #000; + opacity: .4; + -ms-filter: alpha(Opacity=40); + filter: alpha(opacity=40) +} + +@media all and (max-width: 240px) { + #toast-container > div { + padding: 8px 8px 8px 50px; + width: 11em + } + + #toast-container .toast-close-button { + right: -.2em; + top: -.2em + } +} + +@media all and (min-width: 241px) and (max-width: 480px) { + #toast-container > div { + padding: 8px 8px 8px 50px; + width: 18em + } + + #toast-container .toast-close-button { + right: -.2em; + top: -.2em + } +} + +@media all and (min-width: 481px) and (max-width: 768px) { + #toast-container > div { + padding: 15px 15px 15px 50px; + width: 25em + } +} \ No newline at end of file diff --git a/apps/static/js/jumpserver.js b/apps/static/js/jumpserver.js index b872558f3..8567b418a 100644 --- a/apps/static/js/jumpserver.js +++ b/apps/static/js/jumpserver.js @@ -151,4 +151,25 @@ function getIDall() { check_array.push(id); }); return check_array.join(","); -} \ No newline at end of file +} + +function APIUpdateAttr(url, body, success, error, method) { + $.ajax({ + url: url, + type: method || "PATCH", + data: body + }).done(function(data, textStatue, jqXHR) { + if (typeof success === 'function') { + return success(data) + } else { + toastr.success('Update Success!') + } + }).fail(function(jqXHR, textStatue, errorThrown) { + if (typeof error === 'function') { + return error(errorThrown) + } else { + toastr.error('Error occurred while updating.') + } + }) + return true; +} diff --git a/apps/static/js/plugins/toastr/toastr.min.js b/apps/static/js/plugins/toastr/toastr.min.js new file mode 100644 index 000000000..cf70cf63d --- /dev/null +++ b/apps/static/js/plugins/toastr/toastr.min.js @@ -0,0 +1,2 @@ +!function(e){e(["jquery"],function(e){return function(){function t(e,t,n){return f({type:O.error,iconClass:g().iconClasses.error,message:e,optionsOverride:n,title:t})}function n(t,n){return t||(t=g()),v=e("#"+t.containerId),v.length?v:(n&&(v=c(t)),v)}function i(e,t,n){return f({type:O.info,iconClass:g().iconClasses.info,message:e,optionsOverride:n,title:t})}function o(e){w=e}function s(e,t,n){return f({type:O.success,iconClass:g().iconClasses.success,message:e,optionsOverride:n,title:t})}function a(e,t,n){return f({type:O.warning,iconClass:g().iconClasses.warning,message:e,optionsOverride:n,title:t})}function r(e){var t=g();v||n(t),l(e,t)||u(t)}function d(t){var i=g();return v||n(i),t&&0===e(":focus",t).length?void h(t):void(v.children().length&&v.remove())}function u(t){for(var n=v.children(),i=n.length-1;i>=0;i--)l(e(n[i]),t)}function l(t,n){return t&&0===e(":focus",t).length?(t[n.hideMethod]({duration:n.hideDuration,easing:n.hideEasing,complete:function(){h(t)}}),!0):!1}function c(t){return v=e("
").attr("id",t.containerId).addClass(t.positionClass).attr("aria-live","polite").attr("role","alert"),v.appendTo(e(t.target)),v}function p(){return{tapToDismiss:!0,toastClass:"toast",containerId:"toast-container",debug:!1,showMethod:"fadeIn",showDuration:300,showEasing:"swing",onShown:void 0,hideMethod:"fadeOut",hideDuration:1e3,hideEasing:"swing",onHidden:void 0,extendedTimeOut:1e3,iconClasses:{error:"toast-error",info:"toast-info",success:"toast-success",warning:"toast-warning"},iconClass:"toast-info",positionClass:"toast-top-right",timeOut:5e3,titleClass:"toast-title",messageClass:"toast-message",target:"body",closeHtml:'',newestOnTop:!0,preventDuplicates:!1,progressBar:!1}}function m(e){w&&w(e)}function f(t){function i(t){return!e(":focus",l).length||t?(clearTimeout(O.intervalId),l[r.hideMethod]({duration:r.hideDuration,easing:r.hideEasing,complete:function(){h(l),r.onHidden&&"hidden"!==b.state&&r.onHidden(),b.state="hidden",b.endTime=new Date,m(b)}})):void 0}function o(){(r.timeOut>0||r.extendedTimeOut>0)&&(u=setTimeout(i,r.extendedTimeOut),O.maxHideTime=parseFloat(r.extendedTimeOut),O.hideEta=(new Date).getTime()+O.maxHideTime)}function s(){clearTimeout(u),O.hideEta=0,l.stop(!0,!0)[r.showMethod]({duration:r.showDuration,easing:r.showEasing})}function a(){var e=(O.hideEta-(new Date).getTime())/O.maxHideTime*100;f.width(e+"%")}var r=g(),d=t.iconClass||r.iconClass;if("undefined"!=typeof t.optionsOverride&&(r=e.extend(r,t.optionsOverride),d=t.optionsOverride.iconClass||d),r.preventDuplicates){if(t.message===C)return;C=t.message}T++,v=n(r,!0);var u=null,l=e("
"),c=e("
"),p=e("
"),f=e("
"),w=e(r.closeHtml),O={intervalId:null,hideEta:null,maxHideTime:null},b={toastId:T,state:"visible",startTime:new Date,options:r,map:t};return t.iconClass&&l.addClass(r.toastClass).addClass(d),t.title&&(c.append(t.title).addClass(r.titleClass),l.append(c)),t.message&&(p.append(t.message).addClass(r.messageClass),l.append(p)),r.closeButton&&(w.addClass("toast-close-button").attr("role","button"),l.prepend(w)),r.progressBar&&(f.addClass("toast-progress"),l.prepend(f)),l.hide(),r.newestOnTop?v.prepend(l):v.append(l),l[r.showMethod]({duration:r.showDuration,easing:r.showEasing,complete:r.onShown}),r.timeOut>0&&(u=setTimeout(i,r.timeOut),O.maxHideTime=parseFloat(r.timeOut),O.hideEta=(new Date).getTime()+O.maxHideTime,r.progressBar&&(O.intervalId=setInterval(a,10))),l.hover(s,o),!r.onclick&&r.tapToDismiss&&l.click(i),r.closeButton&&w&&w.click(function(e){e.stopPropagation?e.stopPropagation():void 0!==e.cancelBubble&&e.cancelBubble!==!0&&(e.cancelBubble=!0),i(!0)}),r.onclick&&l.click(function(){r.onclick(),i()}),m(b),r.debug&&console&&console.log(b),l}function g(){return e.extend({},p(),b.options)}function h(e){v||(v=n()),e.is(":visible")||(e.remove(),e=null,0===v.children().length&&(v.remove(),C=void 0))}var v,w,C,T=0,O={error:"error",info:"info",success:"success",warning:"warning"},b={clear:r,remove:d,error:t,getContainer:n,info:i,options:{},subscribe:o,success:s,version:"2.1.0",warning:a};return b}()})}("function"==typeof define&&define.amd?define:function(e,t){"undefined"!=typeof module&&module.exports?module.exports=t(require("jquery")):window.toastr=t(window.jQuery)}); +//# sourceMappingURL=/toastr.js.map \ No newline at end of file diff --git a/apps/templates/_foot_js.html b/apps/templates/_foot_js.html index ca20a0423..624d32a01 100644 --- a/apps/templates/_foot_js.html +++ b/apps/templates/_foot_js.html @@ -3,54 +3,54 @@ + - - \ No newline at end of file +// textarea rows +$('textarea').attr('rows', 5) + + diff --git a/apps/templates/_head_css_js.html b/apps/templates/_head_css_js.html index 08728fbc0..bc968ca90 100644 --- a/apps/templates/_head_css_js.html +++ b/apps/templates/_head_css_js.html @@ -3,6 +3,7 @@ + diff --git a/apps/templates/rest_framework/base.html b/apps/templates/rest_framework/base.html new file mode 100644 index 000000000..7f5170a62 --- /dev/null +++ b/apps/templates/rest_framework/base.html @@ -0,0 +1,285 @@ +{% load staticfiles %} +{% load i18n %} +{% load rest_framework %} + + + + + {% block head %} + + {% block meta %} + + + {% endblock %} + + {% block title %}{% if name %}{{ name }} – {% endif %}Django REST framework{% endblock %} + + {% block style %} + {% block bootstrap_theme %} + + + {% endblock %} + + + + {% endblock %} + + {% endblock %} + + + {% block body %} + + +
+ {% block navbar %} + + {% endblock %} + +
+ {% block breadcrumbs %} + + {% endblock %} + + +
+ + {% if 'GET' in allowed_methods %} +
+
+ {% if api_settings.URL_FORMAT_OVERRIDE %} +
+ GET + + + +
+ {% else %} + GET + {% endif %} +
+
+ {% endif %} + + {% if options_form %} +
+ +
+ {% endif %} + + {% if delete_form %} + + + + + {% endif %} + + {% if filter_form %} + + {% endif %} + +
+ +
+ {% block description %} + {{ description }} + {% endblock %} +
+ + {% if paginator %} + + {% endif %} + +
+
{{ request.method }} {{ request.get_full_path }}
+
+ +
+
HTTP {{ response.status_code }} {{ response.status_text }}{% autoescape off %}
+  {% for key, val in response_headers.items %}{{ key }}: {{ val|break_long_headers|urlize_quoted_links }}
+  {% endfor %}
+  {{ content|urlize_quoted_links }}
{% endautoescape %} +
+
+ + {% if display_edit_forms %} + {% if post_form or raw_data_post_form %} +
+ {% if post_form %} + + {% endif %} + +
+ {% if post_form %} +
+ {% with form=post_form %} +
+
+ {% csrf_token %} + {{ post_form }} +
+ +
+
+
+ {% endwith %} +
+ {% endif %} + +
+ {% with form=raw_data_post_form %} +
+
+ {% include "rest_framework/raw_data_form.html" %} +
+ +
+
+
+ {% endwith %} +
+
+
+ {% endif %} + + {% if put_form or raw_data_put_form or raw_data_patch_form %} +
+ {% if put_form %} + + {% endif %} + +
+ {% if put_form %} +
+
+
+ {{ put_form }} +
+ +
+
+
+
+ {% endif %} + +
+ {% with form=raw_data_put_or_patch_form %} +
+
+ {% include "rest_framework/raw_data_form.html" %} +
+ {% if raw_data_put_form %} + + {% endif %} + {% if raw_data_patch_form %} + + {% endif %} +
+
+
+ {% endwith %} +
+
+
+ {% endif %} + {% endif %} +
+
+
+ + {% if filter_form %} + {{ filter_form }} + {% endif %} + + {% block script %} + + + + + + + + + {% endblock %} + + + {% endblock %} + diff --git a/apps/users/api.py b/apps/users/api.py index 489ec35e5..ee7b5f233 100644 --- a/apps/users/api.py +++ b/apps/users/api.py @@ -3,11 +3,9 @@ import logging -from rest_framework import generics, mixins, status, permissions -from rest_framework.views import APIView -from rest_framework.response import Response +from rest_framework import generics -from .serializers import UserSerializer, UserGroupSerializer, UserActiveSerializer +from .serializers import UserSerializer, UserGroupSerializer, UserAttributeSerializer, UserGroupEditSerializer from .models import User, UserGroup @@ -33,16 +31,6 @@ class UserDetailDeleteUpdateApi(generics.RetrieveUpdateDestroyAPIView): # return super(UserDetailDeleteUpdateApi, self).get(request, *args, **kwargs) -class UserActiveApi(generics.RetrieveUpdateDestroyAPIView): - queryset = User.objects.all() - serializer_class = UserActiveSerializer - - # def put(self, request, *args, **kwargs): - # for k, v in request.META.items(): - # logger.debug("%s --> %s" % (k, v)) - # return super(UserActiveApi, self).put(request, *args, **kwargs) - - class UserGroupListAddApi(generics.ListCreateAPIView): queryset = UserGroup.objects.all() serializer_class = UserGroupSerializer @@ -52,3 +40,12 @@ class UserGroupDetailDeleteUpdateApi(generics.RetrieveUpdateDestroyAPIView): queryset = UserGroup.objects.all() serializer_class = UserGroupSerializer + +class UserAttributeApi(generics.RetrieveUpdateDestroyAPIView): + queryset = User.objects.all() + serializer_class = UserAttributeSerializer + + +class UserGroupEditApi(generics.RetrieveUpdateAPIView): + queryset = User.objects.all() + serializer_class = UserGroupEditSerializer diff --git a/apps/users/serializers.py b/apps/users/serializers.py index d61842415..808bb7349 100644 --- a/apps/users/serializers.py +++ b/apps/users/serializers.py @@ -17,15 +17,24 @@ class UserSerializer(serializers.ModelSerializer): ] -class UserActiveSerializer(serializers.ModelSerializer): - class Meta: - model = User - fields = ['is_active'] - - class UserGroupSerializer(serializers.ModelSerializer): users = serializers.HyperlinkedRelatedField(many=True, read_only=True, view_name='users:user-detail-api') class Meta: model = UserGroup fields = '__all__' + + +class UserAttributeSerializer(serializers.ModelSerializer): + + class Meta: + model = User + fields = ['avatar', 'wechat', 'phone', 'enable_otp', 'comment', 'is_active', 'name'] + + +class UserGroupEditSerializer(serializers.ModelSerializer): + groups = serializers.PrimaryKeyRelatedField(many=True, queryset=UserGroup.objects.all()) + + class Meta: + model = User + fields = ['id', 'groups'] diff --git a/apps/users/templates/users/user_detail.html b/apps/users/templates/users/user_detail.html index ec1da2f72..6a622c09a 100644 --- a/apps/users/templates/users/user_detail.html +++ b/apps/users/templates/users/user_detail.html @@ -25,7 +25,7 @@
- {{ user.name }} + {{ user_object.name }}
@@ -49,56 +49,56 @@ - + {% trans 'Name' %}: - {{ user.name }} + {{ user_object.name }} {% trans 'Username' %}: - {{ user.username }} + {{ user_object.username }} {% trans 'Email' %}: - {{ user.email }} + {{ user_object.email }} - {% if user.phone %} + {% if user_object.phone %} {% trans 'Phone' %}: - {{ user.phone }} + {{ user_object.phone }} {% endif %} - {% if user.wechat %} + {% if user_object.wechat %} {% trans 'Wechat' %}: - {{ user.wechat }} + {{ user_object.wechat }} {% endif %} {% trans 'Role' %}: - {{ user.get_role_display }} + {{ user_object.get_role_display }} {% trans 'Date expired' %}: - {{ user.date_expired|date:"Y-m-j H:i:s" }} + {{ user_object.date_expired|date:"Y-m-j H:i:s" }} {% trans 'Created by' %}: - {{ user.created_by }} + {{ user_object.created_by }} {% trans 'Date joined' %}: - {{ user.date_joined|date:"Y-m-j H:i:s" }} + {{ user_object.date_joined|date:"Y-m-j H:i:s" }} {% trans 'Last login' %}: - {{ user.last_login|date:"Y-m-j H:i:s" }} + {{ user_object.last_login|date:"Y-m-j H:i:s" }} {% trans 'Comment' %}: - {{ user.comment }} + {{ user_object.comment }} @@ -118,7 +118,7 @@
- +