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