Skip to content

Commit d0aa32f

Browse files
Merge pull request #451 from RocketChat/settings-users
Settings users
2 parents 2cd1b0e + f17cc08 commit d0aa32f

14 files changed

+329
-3
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Meteor.methods
2+
setUserActiveStatus: (userId, active) ->
3+
Meteor.users.update userId, { $set: { active: active } }
4+
return true

client/routes/router.coffee

+6
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ FlowRouter.route '/changeavatar',
3939
action: ->
4040
BlazeLayout.render 'main', {center: 'avatarPrompt'}
4141

42+
FlowRouter.route '/settings/users',
43+
name: 'settings-users'
44+
45+
action: ->
46+
BlazeLayout.render 'main', {center: 'settingsUsers'}
47+
4248
FlowRouter.route '/settings/:group?',
4349
name: 'settings'
4450

client/stylesheets/base.less

+45
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,30 @@ blockquote {
216216
top: 10px;
217217
font-weight: 400;
218218
}
219+
.icon-spin4 {
220+
position: absolute;
221+
color: @secondary-font-color;
222+
right: 5px;
223+
top: 10px;
224+
font-weight: 400;
225+
-webkit-animation-name: spin;
226+
-webkit-animation-duration: 2000ms;
227+
-webkit-animation-iteration-count: infinite;
228+
-webkit-animation-timing-function: linear;
229+
-moz-animation-name: spin;
230+
-moz-animation-duration: 2000ms;
231+
-moz-animation-iteration-count: infinite;
232+
-moz-animation-timing-function: linear;
233+
-ms-animation-name: spin;
234+
-ms-animation-duration: 2000ms;
235+
-ms-animation-iteration-count: infinite;
236+
-ms-animation-timing-function: linear;
237+
238+
animation-name: spin;
239+
animation-duration: 2000ms;
240+
animation-iteration-count: infinite;
241+
animation-timing-function: linear;
242+
}
219243
input {
220244
padding-left: 25px;
221245
}
@@ -256,6 +280,27 @@ blockquote {
256280
}
257281
}
258282

283+
@-ms-keyframes spin {
284+
from { -ms-transform: rotate(0deg); }
285+
to { -ms-transform: rotate(360deg); }
286+
}
287+
@-moz-keyframes spin {
288+
from { -moz-transform: rotate(0deg); }
289+
to { -moz-transform: rotate(360deg); }
290+
}
291+
@-webkit-keyframes spin {
292+
from { -webkit-transform: rotate(0deg); }
293+
to { -webkit-transform: rotate(360deg); }
294+
}
295+
@keyframes spin {
296+
from {
297+
transform:rotate(0deg);
298+
}
299+
to {
300+
transform:rotate(360deg);
301+
}
302+
}
303+
259304
.rocket-h2 {
260305
font-weight: 300;
261306
text-transform: uppercase;

client/views/settings/settings.coffee

-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
Template.settings.helpers
22
isAdmin: ->
33
return Meteor.user().admin is true
4-
groups: ->
5-
return Settings.find({type: 'group'}).fetch()
64
group: ->
75
group = FlowRouter.getParam('group')
86
group ?= Settings.findOne({ type: 'group' })?._id

client/views/settings/settingsFlex.coffee

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Template.settingsFlex.helpers
22
groups: ->
3-
return Settings.find({type: 'group'}).fetch()
3+
return Settings.find({type: 'group'}, { sort: { sort: 1, i18nLabel: 1 } }).fetch()
44
label: ->
55
return TAPi18next.t @i18nLabel
66

client/views/settings/settingsFlex.html

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ <h4>{{_ "Settings"}}</h4>
1212
<a href="{{pathFor 'settings' group=_id}}">{{_ i18nLabel}}</a>
1313
</li>
1414
{{/each}}
15+
<li>
16+
<a href="{{pathFor 'settings-users'}}">{{_ "Users"}}</a>
17+
</li>
1518
</ul>
1619
</div>
1720
</div>
+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
Template.settingsUsers.helpers
2+
isAdmin: ->
3+
return Meteor.user().admin is true
4+
isReady: ->
5+
return Template.instance().ready?.get()
6+
users: ->
7+
filter = _.trim Template.instance().filter?.get()
8+
if filter
9+
filterReg = new RegExp filter, "i"
10+
query = { $or: [ { username: filterReg }, { name: filterReg }, { "emails.address": filterReg } ] }
11+
else
12+
query = {}
13+
return Meteor.users.find(query, { limit: Template.instance().limit?.get(), sort: { username: 1 } }).fetch()
14+
name: ->
15+
return if @name then @name else TAPi18next.t 'project:Unnamed'
16+
email: ->
17+
return @emails?[0]?.address
18+
flexOpened: ->
19+
return 'opened' if Session.equals('flexOpened', true)
20+
arrowPosition: ->
21+
return 'left' unless Session.equals('flexOpened', true)
22+
userData: ->
23+
return Meteor.users.findOne Session.get 'settingsUsersSelected'
24+
phoneNumber: ->
25+
return '' unless @phoneNumber
26+
if @phoneNumber.length > 10
27+
return "(#{@phoneNumber.substr(0,2)}) #{@phoneNumber.substr(2,5)}-#{@phoneNumber.substr(7)}"
28+
else
29+
return "(#{@phoneNumber.substr(0,2)}) #{@phoneNumber.substr(2,4)}-#{@phoneNumber.substr(6)}"
30+
lastLogin: ->
31+
if @lastLogin
32+
return moment(@lastLogin).format('LLL')
33+
utcOffset: ->
34+
if @utcOffset?
35+
if @utcOffset > 0
36+
@utcOffset = "+#{@utcOffset}"
37+
38+
return "UTC #{@utcOffset}"
39+
40+
Template.settingsUsers.onCreated ->
41+
instance = @
42+
@limit = new ReactiveVar 50
43+
@filter = new ReactiveVar ''
44+
@ready = new ReactiveVar true
45+
46+
@autorun ->
47+
filter = instance.filter.get()
48+
limit = instance.limit.get()
49+
subscription = instance.subscribe 'fullUsers', filter, limit
50+
instance.ready.set subscription.ready()
51+
52+
Template.settingsUsers.onRendered ->
53+
Tracker.afterFlush ->
54+
SideNav.setFlex "settingsFlex"
55+
SideNav.openFlex()
56+
57+
Template.settingsUsers.events
58+
'keydown #users-filter': (e) ->
59+
if e.which is 13
60+
e.stopPropagation()
61+
e.preventDefault()
62+
63+
'keyup #users-filter': (e, t) ->
64+
e.stopPropagation()
65+
e.preventDefault()
66+
t.filter.set e.currentTarget.value
67+
68+
'click .flex-tab .more': ->
69+
if (Session.get('flexOpened'))
70+
Session.set('flexOpened',false)
71+
else
72+
Session.set('flexOpened', true)
73+
74+
'click .user-info': (e) ->
75+
e.preventDefault()
76+
Session.set 'settingsUsersSelected', $(e.currentTarget).data('id')
77+
Session.set 'flexOpened', true
78+
79+
'click .deactivate': ->
80+
Meteor.call 'setUserActiveStatus', Session.get('settingsUsersSelected'), false, (error, result) ->
81+
if result
82+
toastr.success t('User_has_been_deactivated')
83+
if error
84+
toastr.error error.reason
85+
86+
'click .activate': ->
87+
Meteor.call 'setUserActiveStatus', Session.get('settingsUsersSelected'), true, (error, result) ->
88+
if result
89+
toastr.success t('User_has_been_activated')
90+
if error
91+
toastr.error error.reason
92+
93+
'click .delete': ->
94+
swal {
95+
title: t('Are_you_sure')
96+
text: t('Delete_User_Warning')
97+
type: 'warning'
98+
showCancelButton: true
99+
confirmButtonColor: '#DD6B55'
100+
confirmButtonText: t('Yes_delete_it')
101+
cancelButtonText: t('Cancel')
102+
closeOnConfirm: false
103+
html: false
104+
}, ->
105+
swal
106+
title: t('Deleted')
107+
text: t('User_has_been_deleted')
108+
type: 'success'
109+
timer: 2000
110+
showConfirmButton: false
111+
112+
Meteor.call 'deleteUser', Session.get('settingsUsersSelected'), (error, result) ->
113+
if error
114+
toastr.error error.reason
+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<template name="settingsUsers">
2+
<section class="page-container page-list">
3+
<head class="fixed-title">
4+
{{> burger}}
5+
<h2>
6+
<span class="room-title">{{_ "Users"}}</span>
7+
</h2>
8+
</head>
9+
<div class="content">
10+
{{#unless isAdmin}}
11+
<p>You are not authorized to view this page.</p>
12+
{{else}}
13+
<form class="search-form" role="form">
14+
<div class="input-line search">
15+
<input type="text" id="users-filter" placeholder="{{_ "Search"}}" dir="auto">
16+
<i class="icon-search"></i>
17+
{{#unless isReady}}<i class="icon-spin4"></i>{{/unless}}
18+
</div>
19+
</form>
20+
{{#if isReady}}
21+
<div class="results">
22+
{{{_ "Showing_results" users.length}}}
23+
</div>
24+
<div class="list">
25+
{{#each users}}
26+
<div class="user-info" data-id="{{_id}}">
27+
<li class='user-image status-{{status}}'>
28+
{{> avatar username=username}}
29+
<h3>{{name}}</h3>
30+
</li>
31+
<ul>
32+
<li>@{{username}}</li>
33+
{{#if email}}<li>{{email}}</li>{{/if}}
34+
</ul>
35+
</div>
36+
{{/each}}
37+
</div>
38+
{{else}}
39+
<div class="results">
40+
{{{_ "Loading..."}}}
41+
</div>
42+
{{/if}}
43+
{{/unless}}
44+
</div>
45+
</section>
46+
<section class="flex-tab">
47+
<div class="control">
48+
<button class="more"><span class="arrow {{arrowPosition}}"></span></button>
49+
</div>
50+
{{#if flexOpened}}
51+
<div class="content">
52+
<div class="user-view">
53+
{{#with userData}}
54+
<div class="about clearfix">
55+
<div class="thumb">
56+
{{> avatar username=username}}
57+
</div>
58+
<div class="info">
59+
<h3>{{name}}</h3>
60+
<p><i class="icon-at"></i> {{username}}</p>
61+
{{#if utcOffset}}<p><i class="icon-location"></i> {{utcOffset}}</p>{{/if}}
62+
{{#each emails}} <p><i class="icon-mail"></i> {{address}}{{#if verified}}&nbsp;<i class="icon-ok"></i>{{/if}}</p> {{/each}}
63+
{{#each phone}} <p><i class="icon-phone"></i> {{phoneNumber}}</p> {{/each}}
64+
{{#if lastLogin}} <p><i class="icon-clock"></i> {{_ "Last_seen"}}: {{lastLogin}}</p> {{/if}}
65+
</div>
66+
</div>
67+
<nav>
68+
{{#if active}}
69+
<button class='button deactivate'><span><i class='icon-block'></i> {{_ "Deactivate"}}</span></button>
70+
{{else}}
71+
<button class='button activate'><span><i class='icon-ok-circled'></i> {{_ "Activate"}}</span></button>
72+
{{/if}}
73+
<button class='button delete red'><span><i class='icon-trash'></i> {{_ "Delete"}}</span></button>
74+
</nav>
75+
{{/with}}
76+
</div>
77+
</div>
78+
{{/if}}
79+
</section>
80+
</template>

i18n/en.i18n.json

+3
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,11 @@
8484
"Language" : "Language",
8585
"Language_Version" : "English Version",
8686
"Last_message" : "Last message",
87+
"Last_seen" : "Last seen",
8788
"Leave_room" : "Leave room",
8889
"line" : "line",
8990
"Load_more" : "Load more",
91+
"Loading..." : "Loading...",
9092
"Loading_suggestion" : "Loading suggestions...",
9193
"Login" : "Login",
9294
"Login_with" : "Login with %s",
@@ -181,6 +183,7 @@
181183
"Submit" : "Submit",
182184
"The_field_is_required" : "The field %s is required.",
183185
"True" : "True",
186+
"Unnamed" : "Unnamed",
184187
"Use_initials_avatar" : "Use your username initials",
185188
"use_menu" : "Use the side menu to access your rooms and chats",
186189
"Use_service_avatar" : "Use %s avatar",

server/lib/accounts.coffee

+4
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ Accounts.validateLoginAttempt (login) ->
5353
if login.allowed isnt true
5454
return login.allowed
5555

56+
if login.user?.active isnt true
57+
throw new Meteor.Error 'inactive-user', 'Your_user_has_been_deactivated'
58+
return false
59+
5660
if login.type is 'password' and RocketChat.settings.get 'Accounts_denyUnverifiedEmails' is true
5761
validEmail = login.user.emails.filter (email) ->
5862
return email.verified is true

server/methods/deleteUser.coffee

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Meteor.methods
2+
deleteUser: (userId) ->
3+
if not Meteor.userId()
4+
throw new Meteor.Error('invalid-user', "[methods] deleteUser -> Invalid user")
5+
6+
user = Meteor.users.findOne Meteor.userId()
7+
unless user?.admin is true
8+
throw new Meteor.Error 'not-authorized', '[methods] deleteUser -> Not authorized'
9+
10+
return true
11+
# Meteor.users.remove userId
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Meteor.methods
2+
setUserActiveStatus: (userId, active) ->
3+
if not Meteor.userId()
4+
throw new Meteor.Error 'invalid-user', '[methods] setUserActiveStatus -> Invalid user'
5+
6+
user = Meteor.users.findOne Meteor.userId()
7+
unless user?.admin is true
8+
throw new Meteor.Error 'not-authorized', '[methods] setUserActiveStatus -> Not authorized'
9+
10+
Meteor.users.update userId, { $set: { active: active } }
11+
12+
if active is false
13+
Meteor.users.update userId, { $set: { "services.resume.loginTokens" : [] } }
14+
15+
return true

0 commit comments

Comments
 (0)