Here are the files.
class Person < Vue
  def initialize
    @committer = {}
    @response = nil
  end

  def render
    # usage information for authenticated users (owner, secretary, etc.)
    if @auth
      _div.alert.alert_success 'Double click on a field in this color to edit.'
    end

    _h2 "#{@committer.id}@apache.org"

    # Name
    _PersonName person: self, edit: @edit

    _div.row do
      _div.name 'LDAP Create Date'
      _div.value do
        _ @committer.createTimestamp
      end
    end

    # Personal URL
    if @committer.urls || @auth
      @committer.urls ||= []
      _PersonUrls person: self, edit: @edit
    end

    # PMCs
    noPMCsub = false    
    pmcs = @committer.pmcs
    unless pmcs.empty?
      _div.row do
        _div.name 'PMCs'
        _div.value do
          _ul pmcs do |pmc|
            _li {
              _a pmc, href: "committee/#{pmc}"
              if @committer.privateNosub
                if @committer.privateNosub.include? pmc
                  noPMCsub = true
                  _b " (*)"
                end
              end
              if @committer.chairOf.include? pmc
                _ ' (chair)'
              end
              unless @committer.committees.include?(pmc)
                _b ' (not in LDAP committee group)'
              end
            }
          end
          if noPMCsub
                  _br
            _p {
              _ '(*) could not find a subscription to the private@ mailing list for this PMC'
              _br
              _ 'Perhaps the subscription address is not listed in the LDAP record'
              _br 
              _ '(Note that digest subscriptions are not currently included)'
            }
          end
        end
      end
    end

    # Committees
    missingPMCs = false
    committees = @committer.committees
    unless committees.empty?
      _div.row do
        _div.name 'Committees'
        _div.value do
          noPMCsub = false
          _ul committees do |pmc|
            next if  @committer.pmcs.include? pmc
            missingPMCs = true
            _li {
              _a pmc, href: "committee/#{pmc}"
              if @committer.chairOf.include? pmc
                _ ' (chair)'
              end
            }
          end
          if missingPMCs
            _ 'In LDAP committee group, but not on the corresponding PMC'
          else
            _ '(excludes PMCs listed above)'
          end
        end
      end
    end

    # Committer
    commit_list = @committer.committer
    unless commit_list.all? {|pmc| committees.include? pmc}
      _div.row do
        _div.name 'Committer'
        _div.value do
          _ul commit_list do |pmc|
            next if committees.include? pmc
            _li {_a pmc, href: "committee/#{pmc}"}
          end
        end
      end
    end

    # Groups
    unless @committer.groups.empty?
      _div.row do
        _div.name 'Groups'
        _div.value do
          _ul @committer.groups do |group|
            next if group == 'apldap'
       
            if group == 'committers'
              _li {_a group, href: "committer/"}
            elsif group == 'member'
              _li {_a group, href: "members"}
            else
              _li {_a group, href: "group/#{group}"}
            end
            if @committer.chairOf.length > 0 and not @committer.groups.include? 'pmc-chairs'
              _ '[Missing: pmc-chairs]'
            end
          end
        end
      end
    end

    # Podlings
    unless @committer.podlings.empty?
      _div.row do
        _div.name 'Podlings'
        _div.value do
          _ul @committer.podlings do |podlings|
            _li {_a podlings, href: "ppmc/#{podlings}"}
          end
        end
      end
    end

    # Non-PMCs
    nonpmcs = @committer.nonpmcs
    unless nonpmcs.empty?
      _div.row do
        _div.name 'non-PMCs'
        _div.value do
          _ul nonpmcs do |nonpmc|
            _li {
              _a nonpmc, href: "nonpmc/#{nonpmc}"
              if @committer.nonPMCchairOf.include? nonpmc
                _ ' (chair)'
              end
            }
          end
        end
      end
    end
    
    # Email addresses
    # always present
    _PersonEmailForwards person: self, edit: @edit

    # always present (even if an empty array)
    _PersonEmailAlt person: self, edit: @edit

    if @committer.email_other
      _PersonEmailOther person: self # not editable
    end

    # Moderates
    if @committer.moderates and @committer.moderates.keys().length > 0
      _div.row do
        _div.name 'Moderates'
        _div.value do
          _ul @committer.moderates.keys() do |list_name|
            _li do
              _a list_name, href: 'https://lists.apache.org/list.html?' +
                list_name
              _span " as "
              _span @committer.moderates[list_name].join(', ')
            end
          end
          _ "(last checked #{@committer.modtime})"
        end
      end
    end

    # subscriptions
    if @committer.subscriptions
      _div.row do
        _div.name 'Subscriptions'
        _div.value do
          _ul @committer.subscriptions do |list_email|
            _li do
              _a list_email[0],
                href: 'https://lists.apache.org/list.html?' + list_email[0]
              _span " as "
              _span list_email[1]
            end
          end
          _ "(last checked #{@committer.subtime})"
        end
      end
    end

    # digests
    if @committer.digests
      _div.row do
        _div.name 'Digest Subscriptions'
        _div.value do
          _ul @committer.digests do |list_email|
            _li do
              _a list_email[0],
                href: 'https://lists.apache.org/list.html?' + list_email[0]
              _span " as "
              _span list_email[1]
            end
          end
          _ "(last checked #{@committer.digtime})"
        end
      end
    end

    # PGP keys
    if @committer.pgp || @auth
      @committer.pgp ||= []
      _PersonPgpKeys person: self, edit: @edit
    end

    # hosts    
    _div.row do
      _div.name 'Host Access'
      _div.value do
        # pre avoids wrapping on hyphens and reduces number of lines on the page
        _pre @committer.host.join(' ')
      end
    end

    if @committer.inactive
      _div.row do
        _div.name 'Inactive (cannot login)'
        _div.value @committer.inactive
      end
    end

    # SSH keys
    if @committer.ssh || @auth
      @committer.ssh ||= []
      _PersonSshKeys person: self, edit: @edit
    end

    # GitHub username
    if @committer.githubUsername || @auth
      @committer.githubUsername ||= []
      _PersonGitHub person: self, edit: @edit
    end

    if @committer.member
      _PersonMemberStatus person: self, edit: @edit

      # Members.txt
      if @committer.member.info
        _PersonMemberText person: self, edit: @edit
      end

      if @committer.member.nomination
        _div.row do
          _div.name 'Nomination'
          _div.value {_pre @committer.member.nomination}
        end
      end

    end

    # Forms on file (only present if env.user is a member)
    if @committer.forms
      _PersonForms person: self
    end

    # SpamAssassin score
    _PersonSascore person: self, edit: @edit

    # modal dialog for dry run results and errors
    _div.modal.fade.wide_form tabindex: -1 do
      _div.modal_dialog do
        _div.modal_content do
          _div.modal_header do
            _button.close 'x', data_dismiss: 'modal'
            _h4 @response_title
          end
          _div.modal_body do
            _textarea value: @response, readonly: true
          end
          _div.modal_footer do
            _button.btn.btn_default 'Close', data_dismiss: 'modal'
          end
        end
      end
    end
  end

  # initialize committer, determine if user is authorized to make
  # changes, map Vue model to React model
  def created()
    @committer = @@committer
    @auth = (@@auth.id == @@committer.id or @@auth.secretary or @@auth.root)

    # map Vue model to React model
    self.state = self['$data']
    self.props = self['$props']
  end

  # on initial display, look for add editable rows, highlight them,
  # and watch for double clicks on them
  def mounted()
    return unless @auth

    Array(document.querySelectorAll('div.row[data-edit]')).each do |div|
      div.addEventListener('dblclick', self.dblclick)
      div.querySelector('div.name').classList.add 'bg-success'
      console.log('mounted div: ' + div)
      console.log('div.dataset.edit: ' + div.dataset.edit)
    end
  end

  # when a double click occurs, toggle the associated state
  def dblclick(event)
    row = event.currentTarget
console.log(' dblclick event.currentTarget: ' + row)
console.log(' dblclick event.currentTarget.dataset: ' + row.dataset)
console.log(' dblclick event.currentTarget.dataset.edit: ' + row.dataset.edit)
    if row.dataset.edit == @edit
      @edit = nil
    else
      @edit = row.dataset.edit
    end

    window.getSelection().removeAllRanges()
  end

  # after update, register event listeners on forms
  def updated()
    Array(document.querySelectorAll('div[data-edit]')).each do |tr|
      form = tr.querySelector('form')
      if form
        form.setAttribute 'data-action', tr.getAttribute('data-edit')
        jQuery('input[type=submit],button', form).click(self.submit)
      end
    end
  end

  # submit form using AJAX
  def submit(event)
    event.preventDefault()
    form = jQuery(event.currentTarget).closest('form')
    target = event.target
    parent = event.currentTarget
    memstatElement = target.closest('.row')

    # if button is cancel, don't submit but remove @edit form buttons
    console.log(' submit event: ' + event)
    console.log(' submit target: ' + target)
    console.log(' submit parent: ' + parent)
    console.log(' submit parent.getAttribute(data_cancel): ' + parent.getAttribute('data_cancel'))
    console.log(' submit target.dataset_edit: ' + target.dataset_edit)
    console.log(' submit target.dataset: ' + target.dataset)
    console.log(' submit target.dataset.edit: ' + target.dataset.edit)
    console.log(' submit memstatElement.dataset.edit: ' + memstatElement.dataset.edit)
    cancel_submit = target.getAttribute('data_cancel')
    console.log(' submit cancel_submit: ' + cancel_submit)


    if cancel_submit
      memstatElement.dataset.edit = nil
      @edit = nil
      return
    end

    # serialize form
    formData = form.serializeArray();

    # add button if it has a value
    if target and target.getAttribute('name') and target.getAttribute('value')
      formData.push name: target.getAttribute('name'),
        value: target.getAttribute('value')
    end

    # indicate dryrun is requested if option or control key is down
    if event.altKey or event.ctrlKey
      formData.unshift name: 'dryrun', value: true 
    end

    # issue request
    jQuery.ajax(
      method: (form[0].method || 'GET').upcase(),
      url: document.location.href + '/' + form[0].getAttribute('data-action'),
      data: formData,
      dataType: 'json',

      success: ->(response) {
        @committer = response.committer if response.committer

        # turn off edit mode on this field
        row = form.closest('.row')[0]
        @edit = nil if row and row.dataset.edit == @edit
      },

      error: ->(response) {
        json = response.responseJSON
        if json.exception
          @response_title = json.exception
          @response = JSON.stringify(json, nil, 2)
          jQuery('div.modal').modal('show')
        else
          alert response.statusText
        end
      },

      complete: ->(response) do
        json = response.responseJSON
        # show results of dryrun
        if formData[0] and formData[0].name == 'dryrun'
          @response_title = 'Dry run results'
          @response = JSON.stringify(json, nil, 2)
          jQuery('div.modal').modal('show')
        end

        if json.error
          @response_title = 'Error occurred'
          @response = JSON.stringify(json, nil, 2)
          jQuery('div.modal').modal('show')
        end

        # reenable form for later reuse
        jQuery('input, button', form).prop('disabled', false)
      end
    )

    # disable input
    jQuery('input, button', form).prop('disabled', true)
  end
end
#
# Render and edit a person's member status
#

class PersonMemberStatus < Vue
  def render
    committer = @@person.state.committer
    owner = @@person.props.auth.id == committer.id

    _div.row data_edit: ('memstat' if @@person.props.auth.secretary or owner) do
      _div.name 'Member status'

      if committer.member.status
        _div.value do
          _span committer.member.status
def canx(event)
  console.log('canx called from Cancel button')
  parent = event.currentTarget

  memstatElement = parent.closest('.row')

  console.log('event: ' + event + ' parent: ' + parent)
  console.log('parent: ' + parent + ' memstatElement: ' + memstatElement)
  console.log('preventing default')
  console.log('memstatElement.dataset: ' +memstatElement.dataset)
  console.log('memstatElement.dataset.edit: ' + memstatElement.dataset.edit)

  #event.preventDefault()
  #memstatElement.dataset.edit = nil

end
         if @@edit == :memstat
           opt = { year: 'numeric', month: 'long' } # Suggested date
           dod = Date.new.toLocaleDateString('en-US', opt)
           _form.inline method: 'post' do
             # Cancel this form (implemented in main.js submit(event)
             _button.btn.btn_secondary 'Cancel', data_cancel:true, onClick:canx
             # These actions are only for the person's own use
             if (owner)
               if committer.member.status.include? 'Active'
                 if committer.forms['emeritus_request']
                   _button.btn.btn_primary 'rescind emeritus request',
                     name: 'action', value: 'rescind_emeritus'
                 else
                   _button.btn.btn_primary 'request emeritus status',
                     name: 'action', value: 'request_emeritus'
                 end
               end
             end
             # These actions are only for secretary's use
             if (@@person.props.auth.secretary)
               if committer.member.status.include? 'Active'
                 _button.btn.btn_primary 'move to emeritus',
                   name: 'action', value: 'emeritus'
                 _button.btn.btn_primary 'move to deceased',
                   name: 'action', value: 'deceased'
                 _input 'dod', name: 'dod', value: dod
               elsif committer.member.status.include? 'Emeritus'
                 _button.btn.btn_primary 'move to active',
                   name: 'action', value: 'active'
                 _button.btn.btn_primary 'move to deceased',
                   name: 'action', value: 'deceased'
                 _input 'dod', name: 'dod', value: dod
               elsif committer.member.status.include? 'Deceased'
                 _button.btn.btn_primary 'move to active',
                   name: 'action', value: 'active'
                 _button.btn.btn_primary 'move to emeritus',
                   name: 'action', value: 'emeritus'
               end
             end
           end
         end
        end
      end
    end
  end
end
And the console logs:
[Log]  dblclick event.currentTarget: [object HTMLDivElement] (app.js, line 3268)
[Log]  dblclick event.currentTarget.dataset: [object DOMStringMap] (app.js, 
line 3269)
[Log]  dblclick event.currentTarget.dataset.edit: memstat (app.js, line 3270)
[Log] canx called from Cancel button (app.js, line 4021)
[Log] event: [object MouseEvent] parent: [object HTMLButtonElement] (app.js, 
line 4024)
[Log] parent: [object HTMLButtonElement] memstatElement: [object 
HTMLDivElement] (app.js, line 4025)
[Log] preventing default (app.js, line 4026)
[Log] memstatElement.dataset: [object DOMStringMap] (app.js, line 4027)
[Log] memstatElement.dataset.edit: memstat (app.js, line 4028)
[Log]  submit event: [object Object] (app.js, line 3291)
[Log]  submit target: [object HTMLButtonElement] (app.js, line 3292)
[Log]  submit parent: [object HTMLButtonElement] (app.js, line 3293)
[Log]  submit parent.getAttribute(data_cancel): null (app.js, line 3294)
[Log]  submit target.dataset_edit: undefined (app.js, line 3295)
[Log]  submit target.dataset: [object DOMStringMap] (app.js, line 3296)
[Log]  submit target.dataset.edit: undefined (app.js, line 3297)
[Log]  submit memstatElement.dataset.edit: memstat (app.js, line 3298)
[Log]  submit cancel_submit: null (app.js, line 3300)

> On May 26, 2020, at 2:19 PM, Sam Ruby <ru...@intertwingly.net> wrote:
> 
> On Tue, May 26, 2020 at 1:32 AM Craig Russell <apache....@gmail.com> wrote:
>> 
>> But calling preventDefault on the event doesn't appear to do anything. It 
>> still calls the POST behavior, and does not cause the inline edit menu to 
>> disappear. And setting the edit function to nil doesn't do anything either.
>> 
>> Maybe there is something else that I need to do?
> 
> I'll admit that I'm not clear on what you are trying to accomplish,
> but apparently the problem here is that there is another piece of code
> that attaches an event handler (and calls preventDefault):
> 
> https://github.com/apache/whimsy/blob/d3246f107a35f4d989350f4c1ca64366c98ef423/www/roster/views/person/main.js.rb#L341
> 
> Perhaps it would be best to add an attribute to the button, and have
> the submit method remove the buttons and exit early:
> 
> diff --git a/www/roster/views/person/main.js.rb
> b/www/roster/views/person/main.js.rb
> index 1b5ffdb8..f498489f 100644
> --- a/www/roster/views/person/main.js.rb
> +++ b/www/roster/views/person/main.js.rb
> @@ -355,6 +355,12 @@ class Person < Vue
>     form = jQuery(event.currentTarget).closest('form')
>     target = event.target
> 
> +    # if button is a cancel button, don't submit and remove buttons
> +    if target.getAttribute('data-cancel')
> +      @edit = null
> +      return
> +    end
> +
>     # serialize form
>     formData = form.serializeArray();
> 
> diff --git a/www/roster/views/person/memstat.js.rb
> b/www/roster/views/person/memstat.js.rb
> index 39367c40..e8d3d318 100644
> --- a/www/roster/views/person/memstat.js.rb
> +++ b/www/roster/views/person/memstat.js.rb
> @@ -35,6 +35,8 @@ class PersonMemberStatus < Vue
>                _button.btn.btn_primary 'move to emeritus',
>                  name: 'action', value: 'emeritus'
>              end
> +
> +              _button.btn.btn_secondary 'cancel', data_cancel: true
>            end
>          end
>         end
> 
> - Sam Ruby

Craig L Russell
c...@apache.org

Reply via email to