vines-services 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,70 +1,109 @@
1
1
  # encoding: UTF-8
2
+
2
3
  module Vines
3
4
  class Storage
4
5
  class CouchDB < Storage
6
+ alias :_services_find_user :find_user
7
+
8
+ # Override the user's roster with an auto-populated roster containing
9
+ # the services and systems to which the user has permission to access.
10
+ def find_user(jid)
11
+ user = _services_find_user(jid)
12
+ return unless user
13
+ user.roster = []
14
+
15
+ systems, users = find_users.partition {|u| u['system'] }
16
+ systems.map! {|u| u['_id'].sub('user:', '') }
17
+ return user if systems.include?(user.jid.to_s)
18
+ services = find_services(user.jid)
19
+
20
+ users.each do |row|
21
+ id = row['_id'].sub('user:', '')
22
+ user.roster << Contact.new(
23
+ :jid => id,
24
+ :name => row['name'],
25
+ :subscription => 'both',
26
+ :groups => ['People']) unless id == jid.to_s
27
+ end
28
+
29
+ services.each do |row|
30
+ user.roster << Contact.new(
31
+ :jid => row['jid'],
32
+ :name => row['name'],
33
+ :subscription => 'to',
34
+ :groups => ['Services'])
35
+ end
36
+
37
+ find_systems(services).each do |name, groups|
38
+ id = JID.new(name.dup, user.jid.domain).to_s
39
+ user.roster << Contact.new(
40
+ :jid => id,
41
+ :name => name,
42
+ :subscription => 'to',
43
+ :groups => ['Systems', *groups]) if systems.include?(id)
44
+ end
5
45
 
6
- ALL_SERVICES = '/_design/Service/_view/by_name'.freeze
46
+ user.roster << Contact.new(
47
+ :jid => "vines.#{user.jid.domain}",
48
+ :name => 'Vines',
49
+ :subscription => 'to')
50
+ user
51
+ end
7
52
 
8
- # The existing storage class needs another method for our custom roster queries
9
- # The default escaping in URLs makes this method necessary
10
- def get_services
11
- http = EM::HttpRequest.new("#{@url}#{ALL_SERVICES}").get
12
- http.errback { yield }
53
+ # Return the User documents for all users and systems.
54
+ def find_users
55
+ url = "%s/_design/User/_view/all?reduce=false&include_docs=true" % @url
56
+ http = EM::HttpRequest.new(url).get
57
+ http.errback { yield [] }
13
58
  http.callback do
14
59
  doc = if http.response_header.status == 200
15
- JSON.parse(http.response) rescue nil
60
+ rows = JSON.parse(http.response)['rows'] rescue []
61
+ rows.map {|row| row['doc'] }
16
62
  end
17
- yield doc
63
+ yield doc || []
18
64
  end
19
65
  end
66
+ fiber :find_users
20
67
 
21
- #In order to supply the correct vines roster logic, we need to over ride the default
22
- #User creation of in the vines server. This method is much more effecient than
23
- #looking up roster memberships each time the roster is sent.
24
- def find_user(jid)
25
- jid = JID.new(jid || '').bare.to_s
26
- if jid.empty? then yield; return end
27
- get("user:#{jid}") do |doc|
28
- user = if doc && doc['type'] == 'User'
29
- User.new(:jid => jid).tap do |user|
30
- user.name, user.password = doc.values_at('name', 'password')
31
- if doc['roster'] != ""
32
- (doc['roster'] || {}).each_pair do |jid, props|
33
- user.roster << Contact.new(
34
- :jid => jid,
35
- :name => props['name'],
36
- :subscription => props['subscription'],
37
- :ask => props['ask'],
38
- :groups => props['groups'] || [])
39
- end
40
- end
41
- add_user_roster_services(user)
42
- end
68
+ # Return the Service documents to which this JID has permission to
69
+ # access.
70
+ def find_services(jid)
71
+ url = "%s/_design/Service/_view/by_user?reduce=false&key=%s" % [@url, escape(jid.to_s).to_json]
72
+ http = EM::HttpRequest.new(url).get
73
+ http.errback { yield [] }
74
+ http.callback do
75
+ doc = if http.response_header.status == 200
76
+ rows = JSON.parse(http.response)['rows'] rescue []
77
+ rows.map {|row| row['value'] }
43
78
  end
44
- yield user
79
+ yield doc || []
45
80
  end
46
81
  end
47
- fiber :find_user
82
+ fiber :find_services
48
83
 
49
- # We will go find each service that contains this user jid in the
50
- # users of the service document.
51
- def add_user_roster_services(user)
52
- self.get_services do |cdoc|
53
- if cdoc
54
- rows = cdoc['rows'].map do |row|
55
- if row['value']['users'].include?(user.jid.to_s)
56
- jid = JID.new("#{row['value']['jid']}").bare.to_s
57
- user.roster << Contact.new(
58
- :jid => jid,
59
- :name => row['value']['name'],
60
- :subscription => "both",
61
- :ask => "subscribe",
62
- :groups => ["Vines"])
84
+ # Find the systems that belong to these services. Return a Hash of
85
+ # system name to list of service names to which it belongs.
86
+ def find_systems(services)
87
+ keys = services.map {|row| [0, row['_id']] }
88
+ url = "%s/_design/System/_view/memberships?reduce=false" % @url
89
+ http = EM::HttpRequest.new(url).post(
90
+ head: {'Content-Type' => 'application/json'},
91
+ body: {keys: keys}.to_json)
92
+ http.errback { yield [] }
93
+ http.callback do
94
+ doc = if http.response_header.status == 200
95
+ rows = JSON.parse(http.response)['rows'] rescue []
96
+ Hash.new {|h, k| h[k] = [] }.tap do |systems|
97
+ rows.each do |row|
98
+ service = services.find {|s| s['_id'] == row['key'][1] }
99
+ systems[row['value']['name']] << service['name']
63
100
  end
64
101
  end
65
102
  end
103
+ yield doc || []
66
104
  end
67
105
  end
106
+ fiber :find_systems
68
107
  end
69
108
  end
70
109
  end
@@ -6,7 +6,7 @@ module Vines
6
6
  class Service < CouchRest::Model::Base
7
7
  extend Storage::CouchDB::ClassMethods
8
8
 
9
- KEYS = %w[_id name code accounts users jid created_at modified_at].freeze
9
+ KEYS = %w[_id name code accounts users jid created_at updated_at].freeze
10
10
  VIEW_ID = "_design/System".freeze
11
11
  VIEW_NAME = "System/memberships".freeze
12
12
 
@@ -6,7 +6,7 @@ module Vines
6
6
  class System < CouchRest::Model::Base
7
7
  extend Storage::CouchDB::ClassMethods
8
8
 
9
- KEYS = %w[_id ohai created_at modified_at].freeze
9
+ KEYS = %w[_id ohai created_at updated_at].freeze
10
10
  VIEW_NAME = "System/memberships".freeze
11
11
 
12
12
  attr_writer :services
@@ -6,7 +6,7 @@ module Vines
6
6
  class Upload < CouchRest::Model::Base
7
7
  extend Storage::CouchDB::ClassMethods
8
8
 
9
- KEYS = %w[_id name size labels created_at modified_at].freeze
9
+ KEYS = %w[_id name size labels created_at updated_at].freeze
10
10
 
11
11
  property :name, String
12
12
  property :size, Integer
@@ -6,7 +6,7 @@ module Vines
6
6
  class User < CouchRest::Model::Base
7
7
  extend Storage::CouchDB::ClassMethods
8
8
 
9
- KEYS = %w[_id name permissions system created_at modified_at].freeze
9
+ KEYS = %w[_id name permissions system created_at updated_at].freeze
10
10
 
11
11
  before_save :enforce_constraints
12
12
  after_destroy :remove_references
@@ -51,6 +51,16 @@ module Vines
51
51
  end
52
52
  end
53
53
 
54
+ # Grant this user all possible permissions.
55
+ def admin!
56
+ write_attribute('permissions', {
57
+ 'systems' => true,
58
+ 'services' => true,
59
+ 'files' => true,
60
+ 'users' => true
61
+ })
62
+ end
63
+
54
64
  def permissions=(perms)
55
65
  perms ||= {}
56
66
  self.manage_systems = perms['systems']
@@ -59,7 +69,7 @@ module Vines
59
69
  self.manage_users = perms['users']
60
70
  end
61
71
 
62
- def password=(desired)
72
+ def plain_password=(desired)
63
73
  desired = (desired || '').strip
64
74
  raise 'password too short' if desired.size < (system ? 128 : 8)
65
75
  write_attribute('password', BCrypt::Password.create(desired))
@@ -68,7 +78,7 @@ module Vines
68
78
  def change_password(previous, desired)
69
79
  hash = BCrypt::Password.new(password) rescue nil
70
80
  raise 'password failure' unless hash && hash == previous
71
- self.password = desired
81
+ self.plain_password = desired
72
82
  end
73
83
 
74
84
  def jid
@@ -100,20 +100,13 @@ module Vines
100
100
  end
101
101
 
102
102
  def create_views
103
- # FIXME Use views in CouchRest::Model classes to populate db
104
- designs = {}
105
-
106
103
  EM.run do
107
- http('', :put) do # create db
108
- designs.each do |name, views|
109
- get("/_design/#{name}") do |doc|
110
- doc ||= {"_id" => "_design/#{name}"}
111
- doc['language'] = 'javascript'
112
- doc['views'] = views
113
- save(doc) { EM.stop }
114
- end
104
+ Fiber.new do
105
+ CouchRest::Model::Base.subclasses.each do |klass|
106
+ klass.save_design_doc! if klass.respond_to?(:save_design_doc!)
115
107
  end
116
- end
108
+ EM.stop
109
+ end.resume
117
110
  end
118
111
  end
119
112
 
@@ -146,6 +139,9 @@ module Vines
146
139
  *url, _ = @url.split('/')
147
140
  server = CouchRest::Server.new(url.join('/'))
148
141
  CouchRest::Model::Base.database = server.database(database)
142
+ CouchRest::Model::Base.configure do |config|
143
+ config.auto_update_design_doc = false
144
+ end
149
145
  end
150
146
 
151
147
  def escape(jid)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Vines
4
4
  module Services
5
- VERSION = '0.1.0'
5
+ VERSION = '0.1.1'
6
6
  end
7
7
  end
@@ -18,6 +18,6 @@ class PriorityQueueTest < MiniTest::Unit::TestCase
18
18
  end
19
19
  assert queue.empty?
20
20
  assert_equal 0, queue.size
21
- assert_equal nums.sort {|a, b| -(a <=> b) }, popped
21
+ assert_equal nums.sort, popped
22
22
  end
23
23
  end
@@ -1,5 +1,11 @@
1
1
  class Api
2
+ USERS = 'http://getvines.com/protocol/users'
3
+
2
4
  constructor: (@session) ->
5
+ @user = null
6
+ @session.onRoster =>
7
+ this.get USERS, jid: @session.bareJid(), (result) =>
8
+ @user = result
3
9
 
4
10
  jid: -> "vines.#{@session.bareJid().split('@')[1]}"
5
11
 
@@ -9,8 +9,7 @@ class FilesPage
9
9
  jid: @api.jid
10
10
  size: this.size
11
11
  complete: (file) =>
12
- this.fileNode(file)
13
- this.findFiles name: file.name
12
+ this.findFile file.name
14
13
 
15
14
  findLabels: ->
16
15
  $('#labels').empty()
@@ -20,7 +19,7 @@ class FilesPage
20
19
  labelNodeList: (label)->
21
20
  text = if label.size == 1 then 'file' else 'files'
22
21
  node = $("""
23
- <li data-name="" style='display:none;'>
22
+ <li data-name="">
24
23
  <span class="text"></span>
25
24
  <span class="count">#{label.size} #{text}</span>
26
25
  </li>
@@ -28,7 +27,6 @@ class FilesPage
28
27
  $('.text', node).text label.name
29
28
  node.attr 'data-name', label.name
30
29
  node.click (event) => this.selectLabel(event)
31
- node.fadeIn(100)
32
30
 
33
31
  selectLabel: (event) ->
34
32
  name = $(event.currentTarget).attr 'data-name'
@@ -41,19 +39,35 @@ class FilesPage
41
39
  @api.get FILES, criteria, (result) =>
42
40
  this.fileNode row for row in result.rows
43
41
 
44
- fileNode: (file) ->
42
+ findFile: (name) ->
43
+ @api.get FILES, name: name, (result) =>
44
+ this.fileNode result
45
+
46
+ updateFileNode: (file) ->
47
+ node = $ "#files li[data-id='#{file.id}']"
45
48
  size = this.size file.size
46
- if !file.created_at
47
- file.created_at = Date()
48
49
  time = this.date file.created_at
50
+ node.data 'file', file
51
+ $('h2', node).text file.name
52
+ $('.size', node).text size
53
+ $('.time', node).text time
54
+ node.attr 'data-name', file.name
55
+ node.attr 'data-size', size
56
+ node.attr 'data-created', time
57
+
58
+ fileNode: (file) ->
59
+ if $("#files li[data-id='#{file.id}']").length > 0
60
+ this.updateFileNode file
61
+ return
62
+
49
63
  node = $("""
50
- <li data-id="#{file.id}" data-name="" data-size="#{size}" data-created="#{time}">
64
+ <li data-id="#{file.id}">
51
65
  <div class="file-icon">
52
- <span class="size">#{size}</span>
66
+ <span class="size"></span>
53
67
  </div>
54
68
  <h2></h2>
55
69
  <footer>
56
- <span class="time">#{time}</span>
70
+ <span class="time"></span>
57
71
  <ul class="labels"></ul>
58
72
  <form class="add-label">
59
73
  <div class="add-label-button"></div>
@@ -68,10 +82,6 @@ class FilesPage
68
82
  </li>
69
83
  """).appendTo '#files'
70
84
 
71
- node.data 'file', file
72
- $('h2', node).text file.name
73
- node.attr 'data-name', file.name
74
-
75
85
  new Button $('.file-icon', node).get(0), ICONS.page2,
76
86
  scale: 1.0
77
87
  translation: '-2 0'
@@ -87,7 +97,8 @@ class FilesPage
87
97
  $('.add-label-button', node).click ->
88
98
  $('form.add-label input[type="text"]', node).show()
89
99
 
90
- this.labelNode node, label for label in file.labels
100
+ this.updateFileNode file
101
+ this.labelNode(node, label) for label in file.labels
91
102
 
92
103
  labelNode: (node, label) ->
93
104
  labels = $('.labels', node)
@@ -115,8 +126,9 @@ class FilesPage
115
126
  file = node.data 'file'
116
127
  file.labels.push label for label in labels
117
128
  @api.save FILES, file, (result) ->
118
- this.labelNode node, label for label in labels
119
- this.findLabels()
129
+ for label in labels
130
+ this.labelNode node, label
131
+ this.updateLabelCount label, 1
120
132
  false
121
133
 
122
134
  removeLabel: (node, item) ->
@@ -125,10 +137,21 @@ class FilesPage
125
137
  file.labels = (label for label in file.labels when label != remove)
126
138
  @api.save FILES, file, (result) ->
127
139
  item.fadeOut 200, -> item.remove()
128
- this.findLabels()
140
+ this.updateLabelCount remove, -1
141
+
142
+ updateLabelCount: (name, inc) ->
143
+ el = $ "#labels li[data-name='#{name}'] .count"
144
+ if el.length > 0
145
+ count = parseInt(el.text().split(' ')[0]) + inc
146
+ text = if count == 1 then 'file' else 'files'
147
+ el.text "#{count} #{text}"
148
+ else
149
+ this.labelNodeList(name: name, size: 1)
129
150
 
130
151
  deleteFile: (node) ->
131
152
  @api.remove FILES, node.attr('data-id'), (result) =>
153
+ for label in node.data('file').labels
154
+ this.updateLabelCount label, -1
132
155
  node.fadeOut 200, -> node.remove()
133
156
  false
134
157
 
@@ -188,31 +211,35 @@ class FilesPage
188
211
  </div>
189
212
  """).appendTo '#container'
190
213
 
191
- $('#file-chooser').change (event) =>
192
- @uploads.queue event.target.files
193
- $('#file-chooser').val ''
214
+ if @api.user.permissions.files
215
+ $('#file-chooser').change (event) =>
216
+ @uploads.queue event.target.files
217
+ $('#file-chooser').val ''
194
218
 
195
- $('#file-form').submit ->
196
- $('#file-chooser').click()
197
- false
219
+ $('#file-form').submit ->
220
+ $('#file-chooser').click()
221
+ false
198
222
 
199
- $('#upload-dnd').bind 'dragenter', (event) ->
200
- event.stopPropagation()
201
- event.preventDefault()
202
- $('#upload-dnd').css 'color', '#444'
223
+ $('#upload-dnd').bind 'dragenter', (event) ->
224
+ event.stopPropagation()
225
+ event.preventDefault()
226
+ $('#upload-dnd').css 'color', '#444'
203
227
 
204
- $('#upload-dnd').bind 'dragleave', (event) ->
205
- $('#upload-dnd').css 'color', '#ababab'
228
+ $('#upload-dnd').bind 'dragleave', (event) ->
229
+ $('#upload-dnd').css 'color', '#ababab'
206
230
 
207
- $('#upload-dnd').bind 'dragover', (event) ->
208
- event.stopPropagation()
209
- event.preventDefault()
231
+ $('#upload-dnd').bind 'dragover', (event) ->
232
+ event.stopPropagation()
233
+ event.preventDefault()
210
234
 
211
- $('#upload-dnd').bind 'drop', (event) =>
212
- event.stopPropagation()
213
- event.preventDefault()
214
- $('#upload-dnd').css 'color', '#ababab'
215
- @uploads.queue event.originalEvent.dataTransfer.files
235
+ $('#upload-dnd').bind 'drop', (event) =>
236
+ event.stopPropagation()
237
+ event.preventDefault()
238
+ $('#upload-dnd').css 'color', '#ababab'
239
+ @uploads.queue event.originalEvent.dataTransfer.files
240
+ else
241
+ $('#upload-dnd').text "Your account cannot upload files."
242
+ $('#file-form').remove()
216
243
 
217
244
  this.findLabels()
218
245
  this.findFiles()
@@ -278,7 +305,6 @@ class FilesPage
278
305
  upload.start()
279
306
  else
280
307
  @sending = null
281
- this.fileNode(upload.file)
282
308
 
283
309
  find: (file) ->
284
310
  (up for up in @uploads when up.file.name == file.name).shift()
@@ -173,9 +173,10 @@ class ServicesPage
173
173
  criteria you define. Send a command to the service and it runs
174
174
  on every system in the group.
175
175
  </p>
176
- <input type="submit" id="blank-slate-add" value="Create a New Service"/>
176
+ <input type="submit" id="blank-slate-add" value="Add Service"/>
177
177
  </form>
178
178
  """).appendTo '#beta'
179
+ $('#blank-slate-add').remove() unless @api.user.permissions.services
179
180
  $('#blank-slate').submit =>
180
181
  this.drawEditor()
181
182
  false
@@ -218,6 +219,8 @@ class ServicesPage
218
219
  new Button '#add-service', ICONS.plus
219
220
  new Button '#remove-service', ICONS.minus
220
221
 
222
+ $('#alpha-controls div').remove() unless @api.user.permissions.services
223
+
221
224
  this.drawBlankSlate()
222
225
 
223
226
  $('#add-service').click => this.drawEditor()
@@ -272,7 +275,7 @@ class ServicesPage
272
275
  <input id="name" type="text"/>
273
276
  <p id="name-error" class="error"></p>
274
277
  <label for="syntax">Criteria</label>
275
- <textarea id="syntax" placeholder="fqdn like 'www.*' and platform in ['fedora', 'mac_os_x']"></textarea>
278
+ <textarea id="syntax" placeholder="fqdn starts with 'www.' and platform is 'mac_os_x'"></textarea>
276
279
  <p id="syntax-status"></p>
277
280
  </fieldset>
278
281
  </section>
@@ -31,7 +31,7 @@ class SetupPage
31
31
  """).appendTo '#services'
32
32
  $('label', node).text service.name
33
33
  $('#services input[type="checkbox"]').val @selected.services if @selected
34
- if @selected && !@selected.permissions['services']
34
+ if @selected && !@api.user.permissions.services
35
35
  $('#services input[type="checkbox"]').prop 'disabled', true
36
36
 
37
37
  findUsers: ->
@@ -48,17 +48,15 @@ class SetupPage
48
48
 
49
49
  userNode: (user) ->
50
50
  node = $("""
51
- <li data-name="" data-jid="" id="#{user.jid}">
51
+ <li data-name="" data-jid="#{user.jid}" id="#{user.jid}">
52
52
  <span class="text"></span>
53
- <span class="jid"></span>
53
+ <span class="jid">#{user.jid}</span>
54
54
  </li>
55
55
  """).appendTo '#users'
56
56
 
57
57
  name = this.userName(user)
58
58
  $('.text', node).text name
59
- $('.jid', node).text user.jid
60
59
  node.attr 'data-name', name
61
- node.attr 'data-jid', user.jid
62
60
  node.click (event) => this.selectUser event.currentTarget
63
61
  node
64
62
 
@@ -105,10 +103,18 @@ class SetupPage
105
103
  $('#beta-header').text 'Users'
106
104
  this.drawUsers()
107
105
  this.drawUserBlankSlate()
106
+ if @api.user.permissions.users
107
+ $('#beta-controls div').show()
108
+ else
109
+ $('#beta-controls div').hide()
108
110
  when 'systems-nav'
109
111
  $('#beta-header').text 'Systems'
110
112
  this.drawUsers()
111
113
  this.drawSystemBlankSlate()
114
+ if @api.user.permissions.systems
115
+ $('#beta-controls div').show()
116
+ else
117
+ $('#beta-controls div').hide()
112
118
 
113
119
  toggleForm: (form, fn) ->
114
120
  form = $(form)
@@ -136,6 +142,12 @@ class SetupPage
136
142
  $('#password-error').text 'Password must be at least 8 characters.'
137
143
  valid = false
138
144
 
145
+ # admin updating a user's password
146
+ if @session.bareJid() != @selected.jid
147
+ if password1 != password2
148
+ $('#password-error').text 'Passwords must match.'
149
+ valid = false
150
+
139
151
  else # new user
140
152
  if node == ''
141
153
  $('#user-name-error').text 'User name is required.'
@@ -160,7 +172,7 @@ class SetupPage
160
172
  valid
161
173
 
162
174
  saveUser: ->
163
- return unless this.validateUser()
175
+ return false unless this.validateUser()
164
176
  user =
165
177
  jid: $('#jid').val()
166
178
  username: $('#user-name').val()
@@ -201,7 +213,7 @@ class SetupPage
201
213
  valid
202
214
 
203
215
  saveSystem: ->
204
- return unless this.validateSystem()
216
+ return false unless this.validateSystem()
205
217
  user =
206
218
  jid: $('#jid').val()
207
219
  username: $('#user-name').val()
@@ -229,14 +241,18 @@ class SetupPage
229
241
 
230
242
  drawUserBlankSlate: ->
231
243
  $('#charlie').empty()
244
+ msg = if @api.user.permissions.users
245
+ 'Select a user account to update or add a new user.'
246
+ else
247
+ 'Select a user account to update.'
248
+
232
249
  $("""
233
250
  <form id="blank-slate">
234
- <p>
235
- Select a user account to edit or add a new user.
236
- </p>
251
+ <p>#{msg}</p>
237
252
  <input type="submit" id="blank-slate-add" value="Add User"/>
238
253
  </form>
239
254
  """).appendTo '#charlie'
255
+ $('#blank-slate-add').remove() unless @api.user.permissions.users
240
256
  $('#blank-slate').submit =>
241
257
  this.drawUserEditor()
242
258
  false
@@ -252,6 +268,7 @@ class SetupPage
252
268
  <input type="submit" id="blank-slate-add" value="Add System"/>
253
269
  </form>
254
270
  """).appendTo '#charlie'
271
+ $('#blank-slate-add').remove() unless @api.user.permissions.systems
255
272
  $('#blank-slate').submit =>
256
273
  this.drawSystemEditor()
257
274
  false
@@ -295,6 +312,7 @@ class SetupPage
295
312
  </div>
296
313
  <div id="charlie" class="primary column x-fill y-fill"></div>
297
314
  """).appendTo '#container'
315
+
298
316
  this.drawUserBlankSlate()
299
317
 
300
318
  $('#setup li').click (event) => this.selectTask event
@@ -308,6 +326,9 @@ class SetupPage
308
326
  new Button '#add-user', ICONS.plus
309
327
  new Button '#remove-user', ICONS.minus
310
328
 
329
+ $('#beta-controls div').hide() unless @api.user.permissions.users
330
+ $('#systems-nav').hide() unless @api.user.permissions.systems
331
+
311
332
  $('#add-user').click =>
312
333
  if $('#users-nav').hasClass 'selected'
313
334
  this.drawUserEditor()
@@ -326,7 +347,7 @@ class SetupPage
326
347
  list: '#users'
327
348
  icon: '#search-users-icon'
328
349
  form: '#search-users-form'
329
- attrs: ['data-jid']
350
+ attrs: ['data-jid', 'data-name']
330
351
  open: fn
331
352
  close: fn
332
353
 
@@ -402,7 +423,9 @@ class SetupPage
402
423
  """).prependTo '#jid-fields'
403
424
 
404
425
  $('#name').focus()
405
-
426
+ if @session.bareJid() != user.jid
427
+ $('#password1-label').text 'Password'
428
+ $('#password2-label').text 'Password Again'
406
429
  $('#jid').val user.jid
407
430
  $('#name').val user.name
408
431
  $('#user-name').val user.jid.split('@')[0]