You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@whimsical.apache.org by Sam Ruby <ru...@apache.org> on 2016/02/28 19:11:39 UTC
[whimsy.git] [2/2] Commit 7d57311: rough in edit of sascore
Commit 7d573112d4e873a15a0bcb6d26862e0926806127:
rough in edit of sascore
Branch: refs/heads/master
Author: Sam Ruby <ru...@intertwingly.net>
Committer: Sam Ruby <ru...@intertwingly.net>
Pusher: rubys <ru...@apache.org>
------------------------------------------------------------
www/roster/main.rb | ++++++
www/roster/models/committer.rb | ++++
www/roster/public/stylesheets/app.css | +
www/roster/views/actions/committer.json.rb | ++++++++++++++
www/roster/views/committer.html.rb | + -
www/roster/views/committer.js.rb | +++++++++ ---
------------------------------------------------------------
209 changes: 174 additions, 35 deletions.
------------------------------------------------------------
diff --git a/www/roster/main.rb b/www/roster/main.rb
index 00463de..6eb4dcb 100755
--- a/www/roster/main.rb
+++ b/www/roster/main.rb
@@ -71,11 +71,17 @@
end
get '/committer/:name' do |name|
+ @auth = Auth.info(env)
@committer = Committer.serialize(name, env)
pass unless @committer
_html :committer
end
+post '/committer/:name' do |name|
+ @userid = name
+ _json :'/actions/committer'
+end
+
get '/group/:name.json' do |name|
_json Group.serialize(name)
end
diff --git a/www/roster/models/committer.rb b/www/roster/models/committer.rb
index f881607..9f69614 100644
--- a/www/roster/models/committer.rb
+++ b/www/roster/models/committer.rb
@@ -28,6 +28,10 @@ def self.serialize(id, env)
response[:pgp] = person.pgp_key_fingerprints
end
+ if person.attrs['asf-sascore']
+ response[:sascore] = person.attrs['asf-sascore'].first
+ end
+
if person.attrs['githubUsername']
response[:githubUsername] = person.githubUsername
end
diff --git a/www/roster/public/stylesheets/app.css b/www/roster/public/stylesheets/app.css
index 28f3e93..a2a91c4 100644
--- a/www/roster/public/stylesheets/app.css
+++ b/www/roster/public/stylesheets/app.css
@@ -17,6 +17,7 @@ table.wide {color: #000; border-collapse: separate; font-size: 100%}
.issue, .not-found {color: red; font-weight:bold}
.chair {color: green; font-weight:bold}
.member {font-weight: bold}
+.editable td:first-child {color: blue}
thead:hover th.sorting-desc:after {content: ' \25bc'}
thead:hover th.sorting-asc:after {content: ' \25b2'}
diff --git a/www/roster/views/actions/committer.json.rb b/www/roster/views/actions/committer.json.rb
new file mode 100644
index 0000000..046c520
--- /dev/null
+++ b/www/roster/views/actions/committer.json.rb
@@ -0,0 +1,28 @@
+#
+# Update LDAP attributes for a committer
+#
+
+# probably not needed as LDAP will fail anyway, but ensure that the user
+# has authority to update fields
+unless
+ env.user == @userid or
+ ASF::Service.find('asf-secretary').members.include? ASF::Person.find(env.user)
+then
+ raise Error.new('unauthorized')
+end
+
+# update LDAP
+if env.password
+ ASF::LDAP.bind(env.user, env.password) do
+ person = ASF::Person.find(@userid)
+
+ if @sascore
+ person.modify 'asf-sascore', @sascore
+ end
+ end
+else
+ STDERR.puts 'unable to access password'
+end
+
+# return updated committer info
+Committer.serialize(@userid, env)
diff --git a/www/roster/views/committer.html.rb b/www/roster/views/committer.html.rb
index 1811077..97dae72 100644
--- a/www/roster/views/committer.html.rb
+++ b/www/roster/views/committer.html.rb
@@ -17,6 +17,6 @@
_script src: 'app.js'
_.render '#main' do
- _Committer committer: @committer
+ _Committer committer: @committer, auth: @auth
end
end
diff --git a/www/roster/views/committer.js.rb b/www/roster/views/committer.js.rb
index 7d4934a..fd4fd50 100644
--- a/www/roster/views/committer.js.rb
+++ b/www/roster/views/committer.js.rb
@@ -1,73 +1,82 @@
class Committer < React
+ def initialize
+ @committer = {}
+ end
+
def render
- committer = @@committer
+ # usage information for authenticated users (owner, secretary, etc.)
+ if @auth
+ _div.alert.alert_success 'Double click on a fields in this color to edit.'
+ end
- _h2 "#{committer.id}@apache.org"
+ _h2 "#{@committer.id}@apache.org"
_table.wide do
_tr do
_td 'Name'
_td do
- name = committer.name
+ name = @committer.name
if name.public_name==name.legal_name and name.public_name==name.ldap
- _span committer.name.public_name
+ _span @committer.name.public_name
else
_ul do
- _li "#{committer.name.public_name} (public name)"
+ _li "#{@committer.name.public_name} (public name)"
if name.legal_name and name.legal_name != name.public_name
- _li "#{committer.name.legal_name} (legal name)"
+ _li "#{@committer.name.legal_name} (legal name)"
end
if name.ldap and name.ldap != name.public_name
- _li "#{committer.name.ldap} (ldap)"
+ _li "#{@committer.name.ldap} (ldap)"
end
end
end
end
end
- if committer.urls
+ if @committer.urls
_tr do
_td 'Personal URL'
_td do
- _ul committer.urls do |url|
+ _ul @committer.urls do |url|
_a url, href: url
end
end
end
end
- unless committer.committees.empty?
+ committees = @committer.committees
+ unless committees.empty?
_tr do
_td 'Committees'
_td do
- _ul committer.committees do |pmc|
+ _ul committees do |pmc|
_li {_a pmc, href: "committee/#{pmc}"}
end
end
end
end
- unless committer.committer.all? {|pmc| committer.committees.include? pmc}
+ commit_list = @committer.committer
+ unless commit_list.all? {|pmc| committees.include? pmc}
_tr do
_td 'Committer'
_td do
- _ul committer.committer do |pmc|
- next if committer.committees.include? pmc
+ _ul commit_list do |pmc|
+ next if committees.include? pmc
_li {_a pmc, href: "committee/#{pmc}"}
end
end
end
end
- unless committer.groups.empty?
+ unless @committer.groups.empty?
_tr do
_td 'Groups'
_td do
- _ul committer.groups do |group|
+ _ul @committer.groups do |group|
if group == 'committers'
_li {_a group, href: "committer/"}
elsif group == 'member'
@@ -80,11 +89,11 @@ def render
end
end
- if committer.mail
+ if @committer.mail
_tr do
_td 'Email addresses'
_td do
- _ul committer.mail do |url|
+ _ul @committer.mail do |url|
_li do
_a url, href: 'mailto:' + url
end
@@ -93,52 +102,143 @@ def render
end
end
- if committer.pgp
+ if @committer.pgp
_tr do
_td 'PGP key'
- _td committer.pgp
+ _td @committer.pgp
end
end
- if committer.sascore
- _tr.editable do
- _td 'SpamAssassin score'
- _td committer.sascore
+ _tr data_edit: 'sascore' do
+ _td 'SpamAssassin score'
+ _td do
+ if @edit_sascore
+ _form method: 'post' do
+ _input type: 'number', min: 0, max: 10,
+ name: 'sascore', defaultValue: @committer.sascore
+ _input type: 'submit', value: 'submit'
+ end
+ else
+ _span @committer.sascore
+ end
end
end
- if committer.githubUsername
+ if @committer.githubUsername
_tr do
_td 'GitHub username'
_td do
- _a committer.githubUsername, href:
- "https://github.com/" + committer.githubUsername
+ _a @committer.githubUsername, href:
+ "https://github.com/" + @committer.githubUsername
end
end
end
- if committer.member
- if committer.member.status
+ if @committer.member
+ if @committer.member.status
_tr do
_td 'Member status'
- _td committer.member.status
+ _td @committer.member.status
end
end
- if committer.member.info
+ if @committer.member.info
_tr do
_td 'Members.txt'
- _td {_pre committer.member.info}
+ _td {_pre @committer.member.info}
end
end
- if committer.member.nomination
+ if @committer.member.nomination
_tr do
_td 'nomination'
- _td {_pre committer.member.nomination}
+ _td {_pre @committer.member.nomination}
end
end
end
end
end
+
+ # capture committer on initial load
+ def componentWillMount()
+ self.componentWillReceiveProps()
+ end
+
+ # replace committer on change, determine if user is authorized to make
+ # changes
+ def componentWillReceiveProps()
+ if @committer.id != @@committer.id
+ @committer = @@committer
+ @auth = (@@auth.id == @@committer.id or @@auth.secretary or @@auth.root)
+ end
+ end
+
+ # on initial display, look for add editable rows, highlight them,
+ # and watch for double clicks on them
+ def componentDidMount()
+ return unless @auth
+ Array(document.querySelectorAll('tr[data-edit]')).each do |tr|
+ tr.addEventListener('dblclick', self.dblclick)
+ tr.querySelector('td').classList.add 'bg-success'
+ end
+ end
+
+ # when a double click occurs, toggle the associated state
+ def dblclick(event)
+ tr = event.currentTarget
+ field = "edit_#{tr.dataset.edit}"
+ changes = {}
+ changes[field] = !self.state[field]
+ self.setState changes
+ window.getSelection().removeAllRanges()
+ end
+
+ # after update, register event listeners on forms
+ def componentDidUpdate()
+ Array(document.querySelectorAll('tr[data-edit]')).each do |tr|
+ form = tr.querySelector('form')
+ form.addEventListener 'submit', self.submit if form
+ end
+ end
+
+ # submit form using AJAX
+ def submit(event)
+ event.preventDefault()
+ form = event.currentTarget
+ target = form.target
+
+ jQuery.ajax(
+ method: (form.method || 'GET').upcase(),
+ data: jQuery(form).serialize(),
+ dataType: 'json',
+
+ success: ->(response) {
+ @committer = response
+
+ # turn off edit mode on this field
+ tr = jQuery(document.querySelector('form')).closest('tr')[0]
+ if tr
+ field = "edit_#{tr.dataset.edit}"
+ changes = {}
+ changes[field] = false
+ self.setState changes
+ end
+ },
+
+ error: ->(response) {
+ alert response.statusText
+ },
+
+ complete: ->(response) do
+ # reenable form for later reuse
+ Array(form.querySelectorAll('input')).each do |input|
+ input.disabled = false
+ end
+ end
+ )
+
+ Array(form.querySelectorAll('input')).each do |input|
+ input.disabled = true
+ end
+ end
end